找回密码
 注册
搜索
热搜: 回贴
微赢网络技术论坛 门户 服务器 Linux/BSD 查看内容

关于“内存对齐”的思考

2009-12-20 13:39| 发布者: admin| 查看: 46| 评论: 0|原作者: 江海



1. 什么是内存对齐
内存对齐的问题主要存在于理解struct等复合结构在内存中的存储结构。
在C语言中,结构是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如数组、结构、联合等)的数据单元。在结构中,编译器为结构的每个成员按其自然对界(alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,但不一定是相邻存储,第一个成员的地址和整个结构的地址相同。
由此引出内存对齐的概念:许多实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个对其参数k(通常它为4或8)的倍数,这就是所谓的内存对齐。
引入内存对齐的原因一方面在于硬件取指的方便,例如在32位总线系统上,如果一个int变量(4字节)放在一个4的倍数开始的内存地址中,则CPU可以一次将其数值读出,否则的话就要分两次才能读出。另一个重要的原因在于移植性的要求,也就是说不是所有的硬件平台都能访问任意地址上的任意数据的,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。引入内存对齐的目的主要是为了可移植性以及最大限度提升硬件性能。
2. 内存对齐规则
内存对齐与平台,编译器都有一定的关系,比较流行的关于内存对齐的规则是这样来描述的:
1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
3、结合1、2两项推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。
但是,这种流行的说法并不完善。比如在Linux环境中,gcc对于预编译指令#pragma pack (N)有一定的限制,不允许N超过gcc的默认值;此外,可以使用__attribute((aligned(n)))来强制定义某个数据成员或者整个结构的对齐规则,但是这种定义同样受到系统默认值以及#pragma pack (N)预编译指令的限制。另外,如果结构成员仍是结构,则上述的第二条就不正确了。最后,上述的说法没有对结构的内存对齐规则以及由此产生的对占用存储空间大小的影响作出说明。
参照流行的关于内存对齐规则的描述,以及在Linux系统中实际的程序测试结果,总结了如下四条内存对齐规则。在规则描述中使用了内存对齐参数的概念(有的文献中称其为对齐模数),其意为,如果某结构成员的内存对齐参数为n,则该结构成员一定在该参数整倍数的地址单元处开始。
1. 结构体成员在内存中的起始位置为该成员内存对齐参数的整数倍(前一字段的末尾和该字段之间可能需要补空位);
2. 结构体成员的变量对齐参数可由__attribute((aligned(n)))强制定义,强制定义失败的情况下,为全局内存对齐参数(由#define pack (N)指定或使用默认值)和该成员所占空间大小之间较小的一个。如果该成员也是结构体,请参照3;
3. 整个结构的内存对齐参数同样可由__attribute((aligned(n)))强制定义,强制定义失败的情况下,取该结构中所有成员内存对齐参数中最大的;
4. 结构占有存储空间的大小必须为该结构内存对齐参数的整数倍(可能会在最后补相应数目的空位);
5. 以上规则是结合流行的关于内存对齐的规则描述基于Linux系统和gcc编译环境总结的,没有在Windows环境进行过验证,但是两者之间不会有大的差异。
3. 设置内存对齐参数规则
设置内存对齐参数包含两方面的意思:通过预编译指令#pragma pack (N)进行全局内存对齐参数设置;通过__attribute((aligned(n)))强制定义某个结构成员的内存对齐参数。
根据网上查找材料分析,并结合在Linux环境下程序实际测试的结果,总结设置内存对齐参数的规则如下:
1. 可通过#pragma pack (N)设置全局内存对齐参数。但在Linux,gcc编译环境下,N的值超过默认值4之后将不能生效,gcc将忽略该预编译指令(Windows中的限制尚不了解)。也就是说,在Linux,gcc编译环境下只能将这个数值降低,不能提高。另外,N必须是2的非负整数次幂。
2. 可通过__attribute((aligned(n)))强制定义结构成员或者整个结构的内存对齐参数,n也必须是2的非负整数次幂。但是,这里有两个限制:一是不能通过这种用法降低某个结构成员和整个结构的内存对齐参数;二是在设置全局内存对齐参数的预编译指令生效时,不能通过这种用法使某个结构成员或者整个结构的内存对齐参数超过全局内存对齐参数。
3. 上述两项规则没有在Windows平台进行过验证。
这里对第二条规则做一点说明。首先是在任何情况下,试图通过使用__attribute((aligned(n)))降低某个结构成员或者整个结构的内存对齐参数的做法都是无效的,降低结构成员或整个结构内存对齐参数的唯一办法是使用预编译指令#pragma pack (N)。另外,__attribute((aligned(n)))可以提高结构成员或者结构的内存对齐参数,但是最大不能超过通过预编译指令定义的全局内存对齐参数;但是如果没有使用该预编译指令或者该预编译指令无效(如Linux,gcc编译环境下,N超过4),则没有这个限制。这就意味着,此时尽管默认全局内存对齐参数为4,但是可通过__attribute((aligned(n)))强制定义某个结构成员(比如某个double变量)的内存对其参数为8。
4. 示例
Linux,gcc编译环境下,有如下定义的结构体:
#pragma pack(2)
struct Pool
{
char c;
char s;
double i __attribute((aligned (8)));
char d,e,f;
};
struct s2
{
char c;
struct Pool s1 __attribute((aligned(1)));
int d;
};
由于通过预编译指令设置了全局内存对齐参数为2,所以struct Pool中double i设置内存对齐参数为8无效,整个Pool结构内存对齐参数为2,结构所占存储空间大小为14;struct s2中成员s1设置内存对齐参数为1也无效,整个s2结构所占存储空间为20。
还是上面的数据结构,如果将#pragma pack (2)屏蔽,那么struct Pool中double i设置内存对齐参数为8生效,整个Pool结构内存对齐参数为8,结构所占存储空间大小为24;struct s2中成员s1设置内存对齐参数为1仍然无效,整个s2结构所占存储空间为40。
5. 参考文章
主要包括但不仅限于:
1.
http://blog.csdn.net/wenddy112/articles/300583.aspx
2.
http://blog.csdn.net/sadgod/archive/2007/08/09/1733926.aspx
3.
http://topic.csdn.net/u/20080904/10/ffc6b178-8261-4d8d-b74c-aed1e497c875.html
4.
http://bbs.chinaunix.net/viewthread.php?tid=636323
5.
http://blog.chinaunix.net/u1/39518/showart_504959.html







最新评论

QQ|小黑屋|最新主题|手机版|微赢网络技术论坛 ( 苏ICP备08020429号 )

GMT+8, 2024-9-30 13:30 , Processed in 0.119891 second(s), 12 queries , Gzip On, MemCache On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

返回顶部