签到

05月05日
尚未签到

共有回帖数 0

    忘记过去

    等级:
    在最近的项目中,我们涉及到了“内存对齐”技术。对于大部分程序员来说,“内存对齐”对他们来说都应该是“透明的”。“内存对齐”应该是编译器的“管辖范围”。编译器为程序中的每个“数据单元”安排在适当的位置上。但是C语言的一个特点就是太灵活,太强大,它允许你干预“内存对齐”。如果你想了解更加底层的秘密,“内存对齐”对你就不应该再透明了。

    一、内存对齐的原因
    大部分的参考资料都是如是说的:
    1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
    2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

    二、对齐规则
    每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。

    规则:
    1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
    2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
    3、结合1、2颗推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。

    三、试验
    我们通过一系列例子的详细说明来证明这个规则吧!
    我试验用的编译器包括GCC 3.4.2和VC6.0的C编译器,平台为Windows XP + Sp2。

    我们将用典型的struct对齐来说明。首先我们定义一个struct:
    #pragma pack(n) /* n = 1, 2, 4, 8, 16 */
    struct test_t {
    int a;
    char b;
    short c;
    char d;
    };
    #pragma pack(n)
    首先我们首先确认在试验平台上的各个类型的size,经验证两个编译器的输出均为:
    sizeof(char) = 1
    sizeof(short) = 2
    sizeof(int) = 4

    我们的试验过程如下:通过#pragma pack(n)改变“对齐系数”,然后察看sizeof(struct test_t)的值。

    1、1字节对齐(#pragma pack(1))
    输出结果:sizeof(struct test_t) = 8 [两个编译器输出一致]
    分析过程:
    1) 成员数据对齐
    #pragma pack(1)
    struct test_t {
    int a;  /* 长度4  1 按1对齐;起始offset=0 0%1=0;存放位置区间[0,3] */
    char b;  /* 长度1 = 1 按1对齐;起始offset=4 4%1=0;存放位置区间[4] */
    short c; /* 长度2  1 按1对齐;起始offset=5 5%1=0;存放位置区间[5,6] */
    char d;  /* 长度1 = 1 按1对齐;起始offset=7 7%1=0;存放位置区间[7] */
    };
    #pragma pack()
    成员总大小=8

    2) 整体对齐
    整体对齐系数 = min((max(int,short,char), 1) = 1
    整体大小(size)=$(成员总大小) 按 $(整体对齐系数) 圆整 = 8 /* 8%1=0 */ [注1]

    2、2字节对齐(#pragma pack(2))
    输出结果:sizeof(struct test_t) = 10 [两个编译器输出一致]
    分析过程:
    1) 成员数据对齐
    #pragma pack(2)
    struct test_t {
    int a;  /* 长度4  2 按2对齐;起始offset=0 0%2=0;存放位置区间[0,3] */
    char b;  /* 长度1  2 按1对齐;起始offset=4 4%1=0;存放位置区间[4] */
    short c; /* 长度2 = 2 按2对齐;起始offset=6 6%2=0;存放位置区间[6,7] */
    char d;  /* 长度1  2 按1对齐;起始offset=8 8%1=0;存放位置区间[8] */
    };
    #pragma pack()
    成员总大小=9

    2) 整体对齐
    整体对齐系数 = min((max(int,short,char), 2) = 2
    整体大小(size)=$(成员总大小) 按 $(整体对齐系数) 圆整 = 10 /* 10%2=0 */
    3、4字节对齐(#pragma pack(4))
    输出结果:sizeof(struct test_t) = 12 [两个编译器输出一致]
    分析过程:
    1) 成员数据对齐
    #pragma pack(4)
    struct test_t {
    int a;  /* 长度4 = 4 按4对齐;起始offset=0 0%4=0;存放位置区间[0,3] */
    char b;  /* 长度1  4 按1对齐;起始offset=4 4%1=0;存放位置区间[4] */
    short c; /* 长度2  4 按2对齐;起始offset=6 6%2=0;存放位置区间[6,7] */
    char d;  /* 长度1  4 按1对齐;起始offset=8 8%1=0;存放位置区间[8] */
    };
    #pragma pack()
    成员总大小=9

    2) 整体对齐
    整体对齐系数 = min((max(int,short,char), 4) = 4
    整体大小(size)=$(成员总大小) 按 $(整体对齐系数) 圆整 = 12 /* 12%4=0 */

    4、8字节对齐(#pragma pack(8))
    输出结果:sizeof(struct test_t) = 12 [两个编译器输出一致]
    分析过程:
    1) 成员数据对齐
    #pragma pack(8)
    struct test_t {
    int a;  /* 长度4  8 按4对齐;起始offset=0 0%4=0;存放位置区间[0,3] */
    char b;  /* 长度1  8 按1对齐;起始offset=4 4%1=0;存放位置区间[4] */
    short c; /* 长度2  8 按2对齐;起始offset=6 6%2=0;存放位置区间[6,7] */
    char d;  /* 长度1  8 按1对齐;起始offset=8 8%1=0;存放位置区间[8] */
    };
    #pragma pack()
    成员总大小=9

    2) 整体对齐
    整体对齐系数 = min((max(int,short,char), 8) = 4
    整体大小(size)=$(成员总大小) 按 $(整体对齐系数) 圆整 = 12 /* 12%4=0 */


    5、16字节对齐(#pragma pack(16))
    输出结果:sizeof(struct test_t) = 12 [两个编译器输出一致]
    分析过程:
    1) 成员数据对齐
    #pragma pack(16)
    struct test_t {
    int a;  /* 长度4  16 按4对齐;起始offset=0 0%4=0;存放位置区间[0,3] */
    char b;  /* 长度1  16 按1对齐;起始offset=4 4%1=0;存放位置区间[4] */
    short c; /* 长度2  16 按2对齐;起始offset=6 6%2=0;存放位置区间[6,7] */
    char d;  /* 长度1  16 按1对齐;起始offset=8 8%1=0;存放位置区间[8] */
    };
    #pragma pack()
    成员总大小=9

    2) 整体对齐
    整体对齐系数 = min((max(int,short,char), 16) = 4
    整体大小(size)=$(成员总大小) 按 $(整体对齐系数) 圆整 = 12 /* 12%4=0 */

    四、结论
    8字节和16字节对齐试验证明了“规则”的第3点:“当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果”。另外内存对齐是个很复杂的东西,上面所说的在有些时候也可能不正确。呵呵^_^

    [注1]
    什么是“圆整”?
    举例说明:如上面的8字节对齐中的“整体对齐”,整体大小=9 按 4 圆整 = 12
    圆整的过程:从9开始每次加一,看是否能被4整除,这里9,10,11均不能被4整除,到12时可以,则圆整结束。
    1 明就尚观Linux内核与驱动开发!  
    1 程序设计  
    1.1 C语言程序设计:基础和强化  
    1.2 汇编语言零基础入门  
    1.3 C语言编译原理  
    1.4 数据结构与算法  

    2 Linux开发工具及使用技巧  
    2.1 脚本编程  
    2.2 手工编译GNU Toolchain  
    2.3 深入理解GNU Toolchain——各组件详解  
    2.4 gcc用法及对标准C的扩展  
    2.5 gcc内嵌汇编(inline assemly)  
    2.6 GDB与调试技巧  
    2.7 大型项目源代码阅读:工具和技巧  
    2.8 make与Makefile  
    2.9 SVN源代码管理  

    3 嵌入式Linux系统移植  
    3.1 嵌入式软件开发模式和流程  
    3.2 搭建完整的嵌入式Linux开发环境  
    3.3 bootloader的移植和使用  
    3.4 移植Linux内核  
    3.5 移植、创建rootfs  
    3.6 移植、构建嵌入式GUI环境  
    3.7 移植应用程序  

    4 文件和目录开发  
    4.1 文件基本操作  
    4.2 高级I/O操作  
    4.3 文件信息和控制  
    4.4 目录操作  

    5 进程与线程开发  
    5.1 进程创建和退出  
    5.2 进程间通信  
    5.3 signal机制  
    5.4 daemon进程  
    5.5 NPTL分析  
    5.6 线程创建和退出  
    5.7 线程同步与互斥  

    6 网络开发  
    6.1 TCP/IP网络参考模型  
    6.2 TCP/IP协议栈详解  
    6.3 Socket编程  
    6.4 应用层协议开发  

    7 嵌入式GUI开发  
    7.1 GTK+  
    7.2 其他GUI开发库介绍  

    8 ARM体系结构  
    8.1 ARM体系结构概述  
    8.2 ARM汇编指令与高级编程  
    8.3 ARM异常和中断处理  

    9 Bootloader(g-bios)开发  
    9.1 Bootloader介绍  
    9.2 g-bios特点及设计思想  
    9.3 g-bios top-half详解  
    9.4 中断和异常  
    9.5 软硬件分级(multi-level)初始化  
    9.6 stdio与串口驱动  
    9.7 命令行解析  
    9.8 软件浮点问题  
    9.9 Flash子系统及Nand驱动  
    9.10 各种image文件的智能烧录算法  
    9.11 网络协议栈的设计与实现  
    9.12 网卡驱动开发  
    9.13 Linux内核参数的智能设置与内核启动  
    9.14 其他bootloader分析和移植  

    10 Linux内核开发基础  
    10.1 Linux内核源代码目录树介绍  
    10.2 Linux内核编译选项逐条详解  
    10.3 Linux系统调用  
    10.4 Linux中断  
    10.5 Linux进程调度  
    10.6 Linux内核中的同步与互斥  
    10.7 Linux存储管理  
    10.8 Linux文件系统  

    11 Linux设备驱动  
    11.1 ldm1——“Hello, LDM!”  
    11.2 ldm2——“总线-设备-驱动”模型及常见总线介绍  
    11.3 ldm3——我的第一个设备驱动  
    11.4 ldm4——device class、sysfs和udev机制  
    11.5 ldm5——子系统的设计和应用  
    11.6 ldm6——驱动中的的同步/互斥问题  
    11.7 ldm7——访问硬件资源  
    11.8 ldm8——中断和DMA  
    11.9 Linux显卡驱动开发  
    http://www.maxwit.com/content/view/4453/  
     
    www.uplooking.com

    楼主 2015-06-27 22:17 回复

共有回帖数 0
  • 回 帖
  • 表情 图片 视频
  • 发表

登录直线网账号

Copyright © 2010~2015 直线网 版权所有,All Rights Reserved.沪ICP备10039589号 意见反馈 | 关于直线 | 版权声明 | 会员须知