ARM程序由于字節(jié)對(duì)齊引起的問題深入分析
用ADS的ARMC Complier下Optimization Level可能引起問題,其中的一個(gè)問題就是字節(jié)對(duì)齊的問題。下面講講問題的現(xiàn)象及實(shí)質(zhì)。
當(dāng)時(shí)問題的現(xiàn)象是:程序使 用一公共變量Buf創(chuàng)建隊(duì)列,如果ADS編譯優(yōu)化選項(xiàng)采用Minium則軟件工作正常;源碼不變,如果采用ALL優(yōu)化,則不正常,數(shù)據(jù)紊亂且無法工作。為 了發(fā)現(xiàn)問題,我們分別用Minium和ALL編譯,在反匯編條件下單步跟蹤程序,觀察CPU寄存器和內(nèi)存變量的變化情況。發(fā)現(xiàn)在Minium模式下,編譯 器把隊(duì)列內(nèi)存塊Uart0TxBuf分配到的地址是0x400015cc,這個(gè)地址是一個(gè)4字節(jié)對(duì)齊的地址,而在ALL模式下,編譯器把Buf分配的地址 是0x400015c2,這個(gè)地址是一個(gè)非4字節(jié)對(duì)齊的地址。正是由于這個(gè)非4字節(jié)對(duì)齊的地址導(dǎo)致了問題的發(fā)生。
問題發(fā)生在QueueCreate(void *Buf, uint32 SizeOfBuf, uint8 (* ReadEmpty)(), uint8 (* WriteFull)())這個(gè)函數(shù)里,問題是如何發(fā)生的,
在了解問題發(fā)生的機(jī)理前,先了解QueueCreate這個(gè)函數(shù)的工作原理。QueueCreate工作原理是,首先把buf指向的內(nèi)存初始化為DataQueue格式的結(jié)構(gòu)體。DataQueue的結(jié)構(gòu)體格式如下:
typedef struct {
QUEUE_DATA_TYPE *Out; /* 指向數(shù)據(jù)輸出位置 */
QUEUE_DATA_TYPE *In; /* 指向數(shù)據(jù)輸入位置 */
QUEUE_DATA_TYPE *End; /* 指向Buf的結(jié)束位置 */
uint16 NData; /* 隊(duì)列中數(shù)據(jù)個(gè)數(shù) */
uint16 MaxData; /* 隊(duì)列中允許存儲(chǔ)的數(shù)據(jù)個(gè)數(shù) */
uint8 (* ReadEmpty)(); /* 讀空處理函數(shù) */
uint8 (* WriteFull)(); /* 寫滿處理函數(shù) */
QUEUE_DATA_TYPE *Buf; /* 存儲(chǔ)數(shù)據(jù)的空間 */
} DataQueue;
從結(jié)構(gòu)體可以看出,結(jié)構(gòu)體字節(jié)類型在內(nèi)存分配為: 4字節(jié)指針變量(*Out)、4字節(jié)指針變量(*In)、4字節(jié)指針變量(*End)、2字節(jié)變量NData、2字節(jié)變量MaxData、4字節(jié)函數(shù)指針 變量ReadEmpty()、4字節(jié)函數(shù)指針變量 WriteFull()。
觀察結(jié)構(gòu)體起始地址放在非對(duì)齊時(shí)會(huì)出現(xiàn)什么情況。
起始地址為0x400015c2時(shí)的由編譯器分配得到的地址 實(shí)際操作地址
*Out 0x400015c2~0x400015c5 0x40015c0~0x400015c3
*In 0x400014c6~0x400015c9 0x400014c4~0x400015c7
*End 0x400015ca~0x400015cd 0x400015c8~0x400015cb
從表中可以看出,實(shí)際操作的地址按照4字節(jié)對(duì)齊格式得到。例如,當(dāng)執(zhí)行*Out進(jìn)行操作時(shí),自動(dòng)屏蔽bit1和bit0,因此實(shí)際發(fā)生變化的是 0x40015c0~0x400015c3,而不是0x400015c2~0x400015c5,由于實(shí)際操作地址和編譯器分配地址互相覆蓋,當(dāng)對(duì)*In 操作時(shí),會(huì)導(dǎo)致*Out一起變化,對(duì)*End操作時(shí),*In也跟著變化。正是由于非對(duì)齊的原因?qū)е聞?chuàng)建隊(duì)列和對(duì)列操作完全錯(cuò)誤。
當(dāng)內(nèi)存起始地址為4字節(jié)對(duì)齊地址的情況時(shí),編譯器分配地址和實(shí)際地址一致,因此不存在上述問題。
結(jié) 論:
在ARM嵌入式系統(tǒng)中,當(dāng)把一個(gè)內(nèi)存區(qū)域初始化為某個(gè)結(jié)構(gòu)體時(shí),必須注意字節(jié)對(duì)齊的情況。如果該內(nèi)存起始地址為非對(duì)齊地址,不僅得不到預(yù)期的結(jié)果,還可能 導(dǎo)致一些很奇怪的讓人無法理解表面問題。在C層面上不太容易觀察到這些問題的實(shí)質(zhì),只有深入到匯編一層去分析程序,才可能理解這些現(xiàn)象的深層原因。
評(píng)論