zoukankan      html  css  js  c++  java
  • 关于内存对齐

    《关于数据对齐小结》

    以下内容均摘抄于网络资源。

    .关于数据的一些简介:

    BSSbss segment):BSS段通常是指用来存放程序中未初始化,或初始化为0的全局变量,静态局部变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。

    数据段data segment):数据段通常是指用来存放程序中已初始化为非0的全局变量的一块内存区域。数据段属于静态内存分配。

    代码段code segment/text segment代码段通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

    堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)

    (stack)栈又称堆栈,是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

    highest address

    =========

    | stack |

    | vv |

    | |

    | |

    | ^^ |

    | heap |

    =========

    | bss |

    =========

    | data |

    =========

    | text |

    =========

    address 0

     

     

    .关于数据的排放

    图一:图片

    这是普通程序员心目中的内存印象,由一个个的字节组成,而CPU并不是这么看待的。

    图二:

    图片

    CPU把内存当成是一块一块的,块的大小可以是24816字节大小,因此CPU在读取内存时是一块一块进行读取的。块大小成为memory access granularity(粒度) 本人把它翻译为“内存读取粒度” 。

    假设CPU要读取一个int4字节大小的数据到寄存器中,分两种情况讨论:

    <!--[if !supportLists]-->1<!--[endif]-->数据从0字节开始

    <!--[if !supportLists]-->2<!--[endif]-->数据从1字节开始

    再次假设内存读取粒度为4

    图三:

    图片

    当该数据是从0字节开始时,很CPU只需读取内存一次即可把这4字节的数据完全读取到寄存器中。

        当该数据是从1字节开始时,问题变的有些复杂,此时该int型数据不是位于内存读取边界上,这就是一类内存未对齐的数据。

    图四:

    图片

    此时CPU先访问一次内存,读取03字节的数据进寄存器,并再次读取45字节的数据进寄存器,接着把0字节和678字节的数据剔除,最后合并1234字节的数据进寄存器。对一个内存未对齐的数据进行了这么多额外的操作,大大降低了CPU性能。

    这还属于乐观情况了,上文提到内存对齐的作用之一为平台的移植原因,因为以上操作只有有部分CPU肯干,其他一部分CPU遇到未对齐边界就直接罢工了。

    .为什么要数据对齐?不对齐的话会产生什么样的结果?

    1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。(平台移植是驱动程序开发者经常需要考虑的问题)。

    2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。(就是刚才所提到的对效率的影响)。

    .关于数据对齐细节问题:

    1.字符串数组,起始地址不一定是四字节对齐的。对于ARM体系来说,如果访问的32位整数不是4字节对齐的,是会总线错误的(相对我们用的PC,不对齐仅仅是速度变慢)!

    2.结构体或者联合体

    原则1、数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int32位机为4字节,则要从4的整数倍地址开始存储)。

    原则2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct bb里有charintdouble等元素,那b应该从8的整数倍开始存储。)

    原则3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。

    很明显按照以上原则,分析之前T1T2结构的存储方式如图所示,打X的是按照规则之后的补充位

    图片

    .先简要简要介绍一下ARM处理器是如何进行数据操作的:

       ARM32位处理器,armv4能高效的处理8,16,32位的数据,但是大多数arm处理器直接操作的是32位的数据。

     

    地址跳变基数为4字节即4.一次存取数据量为32位。(硬件角度),我们一次取到的32位数据不一定是一个完整

    的数据构,可能是两个数据结构,也可能是某个数据结构的一部分,(而编译器帮助我们将一条对数据结构操作

    C操作转化成多条对齐的汇编指令,这些从每次取到的32位数据中获得有用的值,合并重组而完成对一个数据结构的操作)

    当对一个数据结构进行操作时,如边界不对齐,编译器可以将C操作转化成多条边界对齐的汇编操作,把结果合并、

    重组来模拟对齐的操作(可见这种非对齐的存储是非常消耗效率的)。

    软件角度,在软件方面我们定义的数据排列方式是由编译器决定的,根据编译器的对称规则进行数据排列,而常用的

    数据操作指令(ARM指令中的)是以4字节为对称边界进行操作的。C中允许你干预内存对齐

         下面简要说明下内存对齐对高质量可移植代码的重要性:

         arm处理器中如果装载和存储的地址与数据类型的边界不对齐,那么可能产生异常的结果,例如:通常C编译器假定

    指针是边界对齐的。如果指针不是边界对齐,那么程序执行会产生不正确的结果。这样,把代码从允许边界不对齐的

    处理器移植到ARM处理器时就会出现问题。

     

    六.最后再提一下对齐规则:

    1)对齐规则

    1.__align(num)

       这个用于修改最高级别对象的字节边界。在汇编中使用LDRD或者STRD

       就要用到此命令__align(8)进行修饰限制。来保证数据对象是相应对齐。

       这个修饰对象的命令最大是8个字节限制,可以让2字节的对象进行4字节

       对齐,但是不能让4字节的对象2字节对齐。

      __align是存储类修改,他只修饰最高级类型对象不能用于结构或者函数对象。

      

    2.__packed

    __packed是进行一字节对齐

      1.不能对packed的对象进行对齐

      2.所有对象的读写访问都进行非对齐访问

    3.float及包含float的结构联合及未用__packed的对象将不能字节对齐

    4.__packed对局部整形变量无影响

      5.强制由unpacked对象向packed对象转化是未定义,整形指针可以合法定

      义为packed

    2)对齐规则

      每个特定平台上的编译器都有自己的默认对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n)

    n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的对齐系数

    规则:

      1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset0的地方,以后每个数据

    成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。

      2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma

    pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。

      3、结合12可推断:当#pragma packn值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果

  • 相关阅读:
    20个最佳的Java集合框架面试题目
    SSL/TLS 加密新纪元
    NServiceBus-日志
    NServiceBus-容器
    NServiceBus-网关和多站点分布
    NServiceBus-性能测试
    NServiceBus教程-持久化配置
    NServiceBus教程-持久化
    NServiceBus教程-NServiceBus和WCF
    NServiceBus教程-消息传递与处理
  • 原文地址:https://www.cnblogs.com/wwjdwy/p/3074256.html
Copyright © 2011-2022 走看看