<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > ARM 內(nèi)存對(duì)齊總結(jié)

          ARM 內(nèi)存對(duì)齊總結(jié)

          作者: 時(shí)間:2016-11-21 來(lái)源:網(wǎng)絡(luò) 收藏
          一、啥是內(nèi)存對(duì)齊?為啥要內(nèi)存對(duì)齊?

          現(xiàn)代計(jì)算機(jī)中內(nèi)存空間都是按照byte劃分的,從理論上講似乎對(duì)任何類型的變量的訪問(wèn)都可以從任何地址開始,但實(shí)際情況是在訪問(wèn)特定類型變量的時(shí)候經(jīng)常在特定的內(nèi)存地址訪問(wèn),這就是對(duì)齊。
          字節(jié)對(duì)齊的原因大致是如下兩條:
          1、平臺(tái)原因(移植原因):不是所有的硬件平臺(tái)都能訪問(wèn)任意地址上的任意數(shù)據(jù)的;某些硬件平臺(tái)只能在某些地址處取某些特定類型的數(shù)據(jù),否則拋出硬件異常。
          2、性能原因:數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應(yīng)該盡可能地在自然邊界上對(duì)齊。原因在于,為了訪問(wèn)未對(duì)齊的內(nèi)存,處理器需要作兩次內(nèi)存訪問(wèn);而對(duì)齊的內(nèi)存訪問(wèn)僅需要一次訪問(wèn)。

          本文引用地址:http://www.ex-cimer.com/article/201611/319535.htm

          二、對(duì)齊規(guī)則
          每個(gè)特定平臺(tái)上的編譯器都有自己的默認(rèn)“對(duì)齊系數(shù)”(也叫對(duì)齊模數(shù))。程序員可以通過(guò)預(yù)編譯命令#pragma pack(n),n=1,2,4,8,16來(lái)改變這一系數(shù),其中的n就是你要指定的“對(duì)齊系數(shù)”。規(guī)則:
          1. 數(shù)據(jù)成員對(duì)齊規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員,第一個(gè)數(shù)據(jù)成員放在偏移量為0的地方,以后每個(gè)數(shù)據(jù)成員的對(duì)齊按照#pragma pack指定的數(shù)值和這個(gè)數(shù)據(jù)成員自身長(zhǎng)度中,比較小的那個(gè)進(jìn)行,

          即各成員變量存放的起始地址相對(duì)于結(jié)構(gòu)的起始地址的偏移量必須為該變量的類型所占用的字節(jié)數(shù)的整數(shù)倍。
          2. 結(jié)構(gòu)(或聯(lián)合)的整體對(duì)齊規(guī)則:在數(shù)據(jù)成員完成各自對(duì)齊之后,結(jié)構(gòu)(或聯(lián)合)本身也要進(jìn)行對(duì)齊,對(duì)齊將按照#pragma pack指定的數(shù)值和結(jié)構(gòu)(或聯(lián)合)最大數(shù)據(jù)成員長(zhǎng)度中,比較小的那個(gè)進(jìn)行。
          3. 結(jié)合1、2可推斷:第一、如果n大于等于該變量所占用的字節(jié)數(shù),那么偏移量必須滿足默認(rèn)的對(duì)齊方式,第二、如果n小于該變量的類型所占用的字節(jié)數(shù),那么偏移量為n的倍數(shù),不用滿足默認(rèn)的對(duì)齊方式。

          三、X86對(duì)齊實(shí)驗(yàn)
          下面再簡(jiǎn)要回顧解釋一下上述的對(duì)齊規(guī)則,結(jié)合實(shí)例進(jìn)行分析:
          1. 數(shù)據(jù)類型自身的對(duì)齊值:對(duì)于char型數(shù)據(jù),其自身對(duì)齊值為1字節(jié),對(duì)于short型為2字節(jié),對(duì)于

          int,float,double類型,其自身對(duì)齊值為4字節(jié)。
          2. 結(jié)構(gòu)體的自身對(duì)齊值:其成員中自身對(duì)齊值最大的那個(gè)值。
          3. 指定對(duì)齊值: #pragma pack(n)來(lái)設(shè)定變量以n字節(jié)對(duì)齊方式。n字節(jié)對(duì)齊就是說(shuō)變量存放的起始

          地址的偏移量有兩種情況,第一、如果n大于等于該變量所占用的字節(jié)數(shù),那么偏移量必須滿足默認(rèn)的對(duì)齊方式,第二、如果n小于該變量的類型所占用的字節(jié)數(shù),那么偏移量為n的倍數(shù),不用滿足默認(rèn)的對(duì)齊方式。
          4. 數(shù)據(jù)成員和結(jié)構(gòu)體的有效對(duì)齊值:數(shù)據(jù)成員(數(shù)據(jù)類型)和數(shù)據(jù)結(jié)構(gòu)的自身對(duì)齊值和指定對(duì)齊值中小的那個(gè)值,數(shù)據(jù)成員對(duì)齊了數(shù)據(jù)結(jié)構(gòu)自然也就對(duì)齊了。
          了解上述四個(gè)基本概念,我們開始討論具體數(shù)據(jù)結(jié)構(gòu)的成員和其自身的對(duì)齊方式。有效對(duì)齊值N是最終用來(lái)決定數(shù)據(jù)存放地址方式的值。有效對(duì)齊N,就是表示“對(duì)齊在N上”,也就是說(shuō)該數(shù)據(jù)的"存放起始地址%N=0"。而數(shù)據(jù)結(jié)構(gòu)中的數(shù)據(jù)變量都是按定義的先后順序來(lái)排放的。第一個(gè)數(shù)據(jù)變量的起始地址就是數(shù)據(jù)結(jié)構(gòu)的起始地址。結(jié)構(gòu)體的成員變量要對(duì)齊排放,結(jié)構(gòu)體本身也要根據(jù)自身的有效對(duì)齊值圓整(結(jié)構(gòu)體成員變量占用總長(zhǎng)度需要是對(duì)結(jié)構(gòu)體有效對(duì)齊值的整數(shù)倍)。下面結(jié)合VS2005中編譯環(huán)境的例子進(jìn)行深入了解:
          例子B分析:
          struct B
          {
          char b;
          int a;
          short c;
          };
          假設(shè)B從地址空間0x0000開始排放。該例中沒有顯式指定對(duì)齊值N,VS2005默認(rèn)值為4。
          成員變量b自身對(duì)齊值是1,比指定或默認(rèn)指定對(duì)齊值4小,故有效對(duì)齊值為1,其存放地址0x0000符合0x0000%1=0,滿足字節(jié)對(duì)齊原則。
          成員變量a自身對(duì)齊值為4,和指定或默認(rèn)指定對(duì)齊值4相等,故有效對(duì)齊值也為4,為了保證字節(jié)對(duì)齊,成員變量a只能存放在起始地址為0x0004到0x0007這四個(gè)連續(xù)的字節(jié)空間中,復(fù)核0x0004%4=0。
          成員變量c自身對(duì)齊值為2,比指定或默認(rèn)指定對(duì)齊值4小,故有效對(duì)齊值為2,可順序存放在0x0008至0x0009兩個(gè)字節(jié)空間中,符合0x0008%2=0。
          至此滿足了數(shù)據(jù)成員的字節(jié)對(duì)齊,接著看數(shù)據(jù)結(jié)構(gòu)B的對(duì)齊。數(shù)據(jù)結(jié)構(gòu)B的自身對(duì)齊值為其變量中最大對(duì)齊值(也就是成員變量b)4,故結(jié)構(gòu)體B的有效對(duì)齊值也是4。根據(jù)結(jié)構(gòu)體圓整的要求, 0x0009到0x0000=10字節(jié),(10+2)%4=0。所以0x0000A到0x000B也為結(jié)構(gòu)體B所占用。故B從0x0000到0x000B 共有12個(gè)字節(jié),sizeof(struct B)=12。
          之所以在變量C補(bǔ)充2字節(jié),是因?yàn)橐獙?shí)現(xiàn)編譯器快速有效的存取結(jié)構(gòu)數(shù)組,試想如果定義B結(jié)構(gòu)數(shù)組,第一個(gè)結(jié)構(gòu)起始地址是0沒有問(wèn)題,但是第二個(gè)結(jié)構(gòu)呢?按照數(shù)組的定義,數(shù)組中所有元素都是緊挨著的,如果不把結(jié)構(gòu)的大小補(bǔ)充為對(duì)齊值(4)的整數(shù)倍,那下一個(gè)結(jié)構(gòu)的起始地址將是0x0000A,這顯然不能滿足結(jié)構(gòu)的地址對(duì)齊了。
          例子C分析:

          __align(2) struct C
          {
          char b;
          int a;
          short c;
          };
          同理,例子C中成員變量b自身對(duì)齊值為1,指定對(duì)齊值為2,故效對(duì)齊值為1,假設(shè)C從0x0000開始,那么b存放在0x0000,符合0x0000%1= 0,滿足字節(jié)對(duì)齊原則。
          成員變量a自身對(duì)齊值為4,指定對(duì)齊值為2,故有效對(duì)齊值為2,順序存放在0x0002、0x0003、0x0004、0x0005四個(gè)連續(xù)字節(jié)中,符合0x0002%2=0,滿足字節(jié)對(duì)齊原則。
          成員變量c的自身對(duì)齊值為2,與指定對(duì)齊值相等,故有效對(duì)齊值為2,順序存放在0x0006、0x0007中,符合 0x0006%2=0,滿足字節(jié)對(duì)齊原則。
          從0x0000到0x00007共八字節(jié)存放的是結(jié)構(gòu)體C的變量。結(jié)構(gòu)體C自身對(duì)齊值為4,比指定對(duì)齊值2大,故C的有效對(duì)齊值為2,因8%2=0,C只占用0x0000到0x0007的八個(gè)字節(jié)。所以sizeof(struct C)=8,完全滿足字節(jié)對(duì)齊原則。除了指定的對(duì)齊值不同能導(dǎo)致數(shù)據(jù)結(jié)構(gòu)的地址存放不同外, 編譯器不同存放結(jié)構(gòu)體方式也可能不同。

          四、ARM平臺(tái)的對(duì)齊問(wèn)題
          在ARM中,有ARM和Thumb兩種指令。
          ARM指令:每執(zhí)行一條指令,PC的值加4個(gè)字節(jié)(32bits).一次訪問(wèn)4字節(jié)內(nèi)容,該字節(jié)的起始地址必須是4字節(jié)對(duì)齊的位置上,即地址的低兩位為bits[0b00],也就是說(shuō)地址必須是4的倍數(shù)。
          Thumb指令:每執(zhí)行一條指令,PC的值加2個(gè)字節(jié)(16bits).).一次訪問(wèn)2字節(jié)內(nèi)容,該字節(jié)的起始地址必須是2字節(jié)對(duì)齊的位置上,即地址的低兩位為bits[0b0],也就是說(shuō)地址必須是2的倍數(shù)。
          遵循以上方式叫對(duì)齊(aligned)方式,不遵守這樣方式稱為非對(duì)齊(unaligned)的存儲(chǔ)訪問(wèn)操作。

          五、ARM平臺(tái)字節(jié)對(duì)齊關(guān)鍵字
          1. __align(num)用于修改最高級(jí)別對(duì)象的字節(jié)邊界。
          A、在匯編中使用LDRD或者STRD時(shí),就用到此命令__align(8)進(jìn)行修飾限制。來(lái)保證數(shù)據(jù)對(duì)象是相應(yīng)對(duì)齊。
          B、該修飾對(duì)象的命令最大是8個(gè)字節(jié)限制,可讓2字節(jié)的對(duì)象按4字節(jié)對(duì)齊,但不能讓4字節(jié)的對(duì)象2字節(jié)對(duì)齊。
          C、 __align是存儲(chǔ)類修改,他只修飾最高級(jí)類型對(duì)象不能用于結(jié)構(gòu)或者函數(shù)對(duì)象。
          2. __packed 是進(jìn)行一字節(jié)對(duì)齊。
          A、不能對(duì)packed的對(duì)象進(jìn)行對(duì)齊;
          B、所有對(duì)象的讀寫訪問(wèn)都進(jìn)行非對(duì)齊訪問(wèn);
          C、float及包含float的結(jié)構(gòu)聯(lián)合及未用__packed的對(duì)象將不能字節(jié)對(duì)齊;
          D、__packed對(duì)局部整形變量無(wú)影響;
          E、強(qiáng)制由unpacked對(duì)象向packed對(duì)象轉(zhuǎn)化是未定義,整形指針可以合法定義為:

          packed __packed int* p; //__packed int 則沒有意義。

          3. __unaligned 用于修飾該變量可按照非對(duì)齊訪問(wèn)。

          六、如何查找與字節(jié)對(duì)齊方面的問(wèn)題,如果出現(xiàn)對(duì)齊或者賦值問(wèn)題首先查看:
          1. 編譯器的big little端設(shè)置;
          2. 看這種體系本身是否支持非對(duì)齊訪問(wèn);
          3. 如果支持看設(shè)置了對(duì)齊與否,如果沒有則看訪問(wèn)時(shí)需要加某些特殊的修飾來(lái)標(biāo)志其特殊訪問(wèn)操作。

          七、結(jié)論
          針對(duì)于32位處理器對(duì)于本地使用的數(shù)據(jù)結(jié)構(gòu),為提高內(nèi)存訪問(wèn)效率,采用四字節(jié)對(duì)齊方式;同時(shí)為了減少內(nèi)存的開銷,合理安排結(jié)構(gòu)成員的位置,減少四字節(jié)對(duì)齊導(dǎo)致的成員之間的空隙,降低內(nèi)存開銷。
          對(duì)于處理器之間的數(shù)據(jù)結(jié)構(gòu),需要保證消息的長(zhǎng)度不因?yàn)樵诓煌幾g平臺(tái)和不同處理器導(dǎo)致消息結(jié)構(gòu)的長(zhǎng)度發(fā)生變化,使用一字節(jié)對(duì)齊方式對(duì)消息結(jié)構(gòu)進(jìn)行緊縮;為保證處理器之間的消息的數(shù)據(jù)結(jié)構(gòu)的內(nèi)存訪問(wèn)效率,采用字節(jié)填充的方式自己對(duì)消息中成員進(jìn)行四字節(jié)對(duì)齊。
          數(shù)據(jù)結(jié)構(gòu)的成員位置要兼顧成員之間的關(guān)系、數(shù)據(jù)訪問(wèn)效率和空間利用率。順序安排的原則是:四字節(jié)的放在最前面,兩字節(jié)的緊接最后一個(gè)四字節(jié)成員,一字節(jié)緊接最后一個(gè)兩字節(jié)成員,填充字節(jié)放在最后。舉例如下:
          typedef struct tag_T_MSG{
          long ParaA;
          long ParaB;
          short ParaC;
          char ParaD;
          char Pad;
          } T_MSG;




          關(guān)鍵詞: ARM內(nèi)存對(duì)

          評(píng)論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();