您的位置 首页 元件

关于arm 的字节对齐

一.什么是字节对齐,为什么要对齐?现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始…

一.什么是字节对齐,为什么要对齐?

  现代核算机中内存空间都是依照byte区分的,从理论上讲好像对任何类型的变量的拜访能够从任何地址开端,但实践状况是在拜访特定类型变量的时分经常在特定的内存地址拜访,这就需求各种类型数据依照必定的规则在空间上摆放,而不是次序的一个接一个的排放,这便是对齐。
  对齐的效果和原因:各个硬件渠道对存储空间的处理上有很大的不同。一些渠道对某些特定类型的数据只能从某些特定地址开端存取。比方有些架构的CPU在拜访一个没有进行对齐的变量的时分会产生过错,那么在这种架构下编程有必要确保字节对齐.其他渠道或许没有这种状况,可是最常见的是假如不依照合适其渠道要求对数据寄存进行对齐,会在存取功率上带来丢失。比方有些渠道每次读都是从偶地址开端,假如一个int型(假定为32位系统)假如寄存在偶地址开端的当地,那么一个读周期就能够读出这32bit,而假如寄存在奇地址开端的当地,就需求2个读周期,并对两次读出的成果的凹凸字节进行凑集才干得到该32bit数据。明显在读取功率上下降许多。

二.编译器是依照什么样的准则进行对齐的?
  先让咱们看四个重要的基本概念:
  1.数据类型自身的对齐值:关于char型数据,其自身对齐值为1,关于short型为2,关于int,float,double类型,其自身对齐值为4,单位字节。
  2.结构体的自身对齐值:其成员中自身对齐值最大的那个值。
  3.指定对齐值:#pragma pack (value)时的指定对齐值value。
  4.数据成员和结构体的有用对齐值:数据成员(数据类型)和数据结构的自身对齐值和指定对齐值中小的那个值。(数据成员对齐了数据结构天然也就对齐了)
  有了这些值,咱们就能够很便利的来评论详细数据结构的成员和其自身的对齐方法。有用对齐值N是终究用来决议数据寄存地址方法的值,最重要。有用对齐N,便是表明“对齐在N上”,也便是说该数据的”寄存开端地址%N=0″.而数据结构中的数据变量都是按界说的先后次序来排放的。第一个数据变量的开端地址便是数据结构的开端地址。结构体的成员变量要对齐排放,结构体自身也要根据自身的有用对齐值圆整(便是结构体成员变量占用总长度需求是对结构体有用对齐值的整数倍,结合下面比如了解)。这样就不难了解上面的几个比如的值了。

比如剖析:
剖析比如B;
struct B
{
char b;
int a;
short c;
};

  假定B从地址空间0x0000开端排放。该比如中没有界说指定对齐值,在笔者环境下,该值默以为4。第一个成员变量b的自身对齐值是1,比指定或许默许指定对齐值4小,所以其有用对齐值为1,所以其寄存地址0x0000契合0x0000%1=0.第二个成员变量a,其自身对齐值为4,所以有用对齐值也为4,所以只能寄存在开端地址为0x0004到0x0007这四个接连的字节空间中,复核0x0004%4=0,且紧靠第一个变量。第三个变量c,自身对齐值为 2,所以有用对齐值也是2,能够寄存在0x0008到0x0009这两个字节空间中,契合0x0008%2=0。所以从0x0000到0x0009寄存的都是B内容。再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b)所以便是4,所以结构体的有用对齐值也是4。根据结构体圆整的要求, 0x0009到0x0000=10字节,(10+2)%4=0。所以0x0000A到0x000B也为结构体B所占用。故B从0x0000到0x000B 共有12个字节,sizeof(struct B)=12;其实假如就这一个就来说它已将满意字节对齐了, 因为它的开端地址是0,因而肯定是对齐的,之所以在后边弥补2个字节,是因为编译器为了完成结构数组的存取功率,试想假如咱们界说了一个结构B的数组,那么第一个结构开端地址是0没有问题,可是第二个结构呢?依照数组的界说,数组中一切元素都是紧挨着的,假如咱们不把结构的巨细弥补为4的整数倍,那么下一个结构的开端地址将是0x0000A,这明显不能满意结构的地址对齐了,因而咱们要把结构弥补成有用对齐巨细的整数倍.其实比如:关于char型数据,其自身对齐值为1,关于short型为2,关于int,float,double类型,其自身对齐值为4,这些已有类型的自身对齐值也是根据数组考虑的,仅仅因为这些类型的长度已知了,所以他们的自身对齐值也就已知了.
同理,剖析上面比如C:

__align(2) struct C
{
char b;
int a;
short c;
};*撤销指定对齐,康复缺省对齐*

  第一个变量b的自身对齐值为1,指定对齐值为2,所以,其有用对齐值为1,假定C从0x0000开端,那么b寄存在0x0000,契合0x0000%1= 0;第二个变量,自身对齐值为4,指定对齐值为2,所以有用对齐值为2,所以次序寄存在0x0002、0x0003、0x0004、0x0005四个接连字节中,契合0x0002%2=0。第三个变量c的自身对齐值为2,所以有用对齐值为2,次序寄存
在0x0006、0x0007中,契合 0x0006%2=0。所以从0x0000到0x00007共八字节寄存的是C的变量。又C的自身对齐值为4,所以C的有用对齐值为2。又8%2=0,C 只占用0x0000到0x0007的八个字节。所以sizeof(struct C)=8.

三.在ADS编译器中的实例.
#pragma pack(push) //保存对齐状况
//设定为4字节对齐
__align(4) struct test
{
char m1;
double m4;
int m3;
};
  以上结构的巨细为16,下面剖析其存储状况,首先为m1分配空间,其偏移量为0,满意咱们自己设定的对齐方法(4字节对齐),m1占用1个字节。接着开端为m4分配空间,这时其偏移量为1,需求补足3个字节,这样使偏移量满意为n=4的倍数(因为sizeof(double)大于n),m4占用8个字节。接着为m3分配空间,这时其偏移量为12,满意为4的倍数,m3占用4个字节。这时现已为一切成员变量分配了空间,共分配了16个字节,满意为n的倍数。假如把上面的 __align(4) struct test 改为 __align(16) struct test ,那么咱们能够得到结构的巨细为24。
=========
编译器不同在寄存结构体方法或许不同,因而对齐也会有不同

ARM中字节对齐的深入探讨

ARM中字节对齐的深入探讨
阅读了yos的文章《内存对齐问题学习小结》,深有体会,看来在进行指针操作时,有必要进行强制类型转化,不然或许呈现料想不到的过错。

在我的一个项目中,需求进行数据包解码,相同呈现数据对齐的问题,却没能找到好的解决方法问题如下
CPU ARM7 ,编译环境 Keil RVCT3.0

#pragma pack(1)
typedef struct {
uint8 u8a[3];
uint8 u8b[4];
uint16 u16c;
uint32 u32d;
uint32 u32e;
} TSTBLK,* PTSTBLK;

主程序
main
{
uint16 i;
PTSTBLKpTST;

uint16 u16k;
uint32 u32m,u32l;
uint8DBUF[200];

for(i= 0 ;i<200;i++)DBUF[i]=i; pTST=(PTSTBLK)DBUF;// 1
u16k = (pTST->u16c);// 2
u32m = pTST->u32d;// 3
u32l = pTST->u32e;// 4
//以下句子是防止 u16k,u32m,u32l被优化掉
i = u16k;
i = (uint16)u32m;
i = (uint16)u32l;
}

运转句子1 后, 结构体中的
u8a[] = 0x00~0x02
u8a[] = 0x03~0x06
u16c = 0x0807
u32d = 0x0C0B0A09
u32e = 0x100F0E0D
明显以上成果是咱们所需求的,正确!
但持续运转 2,3,4得到
u16k = 0x0706
u32m = 0x080B0A09
u32l = 0x0C0F0E0D
字节对齐产生了问题,乱了!
乱得还不轻 u32m 没有等于0x0B0A0908
u32m 没有等于0x0B0A0908
u32m 也没有等于0x0F0E0D0C
why?

我试图用u16k = (uint16)(pTST->u16c);
u32m = (uint32)pTST->u32d;
去修正,但无效!
其实pTST->u16c自身便是16位的,强制转到16位天然没有任何含义。

你说的状况关于ARM CPU的确存在,但关于其它系统结构就不会呈现
这是一个典型的ARM非对齐拜访的问题。#pragma pack(1)能确保你的结构体中的数据是紧缩对齐的(在内存中是顺次摆放的)。那么关于
#pragma pack(1)
typedef struct {
uint8 u8a[3];
uint8 u8b[4];
uint16 u16c;
uint32 u32d;
uint32 u32e;
} TSTBLK,* PTSTBLK;
假定该结构体寄存的基地址为0,则u8a[3]坐落0-2字节, u8b[4]坐落3-6字节,u16c坐落7-8字节,顺次类推。那么当咱们去拜访u16c时编译器会编译成一条拜访地址为7的半字读的汇编语言,而地址为7关于半字读来说是一个非对齐拜访,CPU就主动会把地址变成把最低位疏忽,也是说CPU读的实践地址为6,所以读u16c得到的是6 、7两个字节即u16c=0x0706。关于字的拜访CPU会疏忽低两位地址,剖析方法与前相同。你的两个32位数我不能了解,你怎么或许得到那样的成果,是不是写错了哦??我觉得你应该别离得到0x0B0A0908 0x0F0E0D0C才对。当然还触及一个字节序的问题。
这一个问题在ARM CPU中会呈现,但关于POWERPC的CPU或X86的CPU你的代码就不会呈现问题,这都是CPU对非对齐拜访选用的处理方法不同形成,POWERPC X86 会把非半字的非对齐拜访变成两个字节拜访,因为不会呈现上面问题。MIPS我没有去研讨过。
关于ARM CPU把结构体改为:
#pragma pack(1)
typedef struct {
uint8 u8b[4];
uint32 u32d;
uint32 u32e;
uint16 u16c;
uint8 u8a[3];
} TSTBLK,* PTSTBLK;
整个结构体依然只占23个字节,但应该不会呈现前面的问题。

__packed 能解决问题
谢谢我们,用__packed 能够解决问题,但我没了解__packed 和#pragma pack(1)的差异,请问谁能再解说得清楚些?

我从前的试验成果的确是 u32m = 0x080B0A09u32l = 0x0C0F0E0D
为什么不是0x0B0A0908 和0x0F0E0D0C,原因不详

我不同意更改结构体,因为数据结构是规则死了,不能随意改
关于ARM CPU把结构体改为:
#pragma pack(1)
typedef struct {
uint8 u8b[4];
uint32 u32d;
uint32 u32e;
uint16 u16c;
uint8 u8a[3];
} TSTBLK,* PTSTBLK;

uboot中,ARM系统下,设置变量4字节对齐

调试程序遇到因为buffer地址不是4字节对齐,一切底层去:

u32 *p =(u32 *)buf;

使得数据复制有误。所以,去参阅了uboot中其他人的做法:

\board\s1845\flash.c中的:

#define __align__ __attribute__ ((aligned (8)))
static __align__ ulong precmd0[2]= { 0x00aa00aa, 0x00aa00aa };

所以,此处就能够这么做,使一个字符数组变量4字节对齐的:

static__attribute__ ((aligned (4)))unsigned char data_buf[MAX_PAGE_SIZE];

ARM 的 RealView中的解说:

4.4.2.__attribute__((aligned))

aligned类型特点指定类型的最低对齐要求。

Note

此类型特点是 ARM 编译器支撑的 GNU 编译器扩展。

Copyright 2007-2009 ARM Limited. All rights reserved. ARM DUI 0348BC
Non-Confidential, Unrestricted Access

4.5.3 __attribute__((aligned))
aligned 变量特点指定变量或结构字段的最低对齐要求(按字节核算)。
留意
此变量特点是 ARM 编译器支撑的 GNU 编译器扩展。
示例
int Variable_Attributes_aligned_0 __attribute__ ((aligned (16)));

short Variable_Attributes_aligned_1[3] __attribute__ ((aligned));

2.1.157 –unaligned_access, –no_unaligned_access
此选项启用或禁用根据 ARM 系统结构的处理器上的未对齐数据拜访。
缺省设置
关于支撑未对齐数据拜访的根据 ARM 系统结构的处理器,缺省为
–unaligned_access。这包括:
• 根据 ARMv6 系统结构的一切处理器
• 根据 ARMv7-A 和 ARMv7-R 系统结构的处理器。
关于不支撑未对齐数据拜访的根据 ARM 系统结构的处理器,缺省为
–no_unaligned_access。这包括:
• 根据 ARMv6 曾经版别的系统结构的一切处理器
• 根据 ARMv7-M 系统结构的处理器。
用法
–unaligned_access
在支撑未对齐数据拜访的处理器(如 –cpu=ARM1136J-S)上运用
–unaligned_access 可加速对紧缩结构的拜访速度。
若要启用未对齐支撑,有必要履行下列操作:
• 在初始化代码中铲除 CP15 寄存器 1 的 A 位(即位 1)。
• 在初始化代码中设置 CP15 寄存器 1 的 U 位(即位 22)。
U 位的初始值由内核的 UBITINIT 输入确认。
RVCT 库包括旨在运用未对齐拜访的某些库函数的特别版别。在启
用未对齐拜访支撑的状况下,RVCT 东西将运用这些库函数从未对
齐拜访中获益。
–no_unaligned_access
运用 –no_unaligned_access 可在 ARMv6 处理器上制止生成未对齐
字和半字拜访。
若要在不运用未对齐拜访的状况下在 ARMv6 方针上启用对四字节
求模的对齐查看,有必要履行下列操作:
• 在初始化代码中设置 CP15 寄存器 1 的 A 位(即位 1)。
• 在初始化代码中设置 CP15 寄存器 1 的 U 位(即位 22)。
U 位的初始值由内核的 UBITINIT 输入确认。

留意
ARM 处理器内核不支撑未对齐双字拜访,例如对 long long 整数的
未对齐拜访。双字拜访有必要是八字节或四字节对齐的。
编译器不支撑对八字节求模的对齐查看。也便是说,编译器(或
更详细地说是 RVCT 东西集)不支撑 CP15 寄存器 1 中的装备 U =
0、A = 1。
RVCT 库包括旨在运用未对齐拜访的某些库函数的特别版别。若要
在禁用未对齐拜访支撑的状况下制止运用这些高档库函数,则在
编译 C 和 C++ 源文件以及汇编语言源文件组合的状况下,需求同
时在编译器命令行和汇编器命令行中指定 –no_unaligned_access。

约束
仅当软件中的对齐支撑选项与处理器内核中的对齐支撑选项相匹配时,针对支
持未对齐数据拜访的处理器而编译的代码才干正确运转。

2。GNU C 扩展之__attribute__ 机制简介

aligned (alignment)
该特点规则变量或结构体成员的最小的对齐格局,以字节为单位。例如:

int x __attribute__ ((aligned (16))) = 0; 编译器将以16字节(留意是字节byte不是位bit)对齐的方法分配一个变量。也能够对结构体成员变量设置该特点,例如,创立一个双字对齐的int对,能够这么写:

struct foo { int x[2] __attribute__ ((aligned (8))); }; 如上所述,你能够手动指定对齐的格局,相同,你也能够运用默许的对齐方法。假如aligned后边不紧跟一个指定的数字值,那么编译器将根据你的方针机器状况运用最大最有利的对齐方法。例如:

short array[3] __attribute__ ((aligned)); 挑选针对方针机器最大的对齐方法,能够进步复制操作的功率。
aligned特点使被设置的目标占用更多的空间,相反的,运用packed能够减小目标占用的空间。
需求留意的是,attribute特点的效能与你的连接器也有关,假如你的连接器最大只支撑16字节对齐,那么你此刻界说32字节对齐也是杯水车薪的。
packed
运用该特点能够使得变量或许结构体成员运用最小的对齐方法,即对变量是一字节对齐,对域(field)是位对齐。
下面的比如中,x成员变量运用了该特点,则其值将紧放置在a的后边:

struct test{char a;int x[2] __attribute__ ((packed));};

3.关于ARM-GCC的ALIGN问题

最终在CSDN上查到arm-linux-gcc的方法(以下是引证):

1.在makefile里加-fpack-struct 选项,这样的话对一切的结构按一字节对齐.

不得不说,的确有那么些质量较差的程序或许需求你部分天然对齐,部分一字 节对齐,此刻

2. typedef struct pack{

}__attribute__((packed))

可运用__attribute__特点

由此可见对齐的参数是与渠道相关的,gcc的通用性也解决不了方法。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/xinpin/yuanjian/276660.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部