新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設計應用 > 對ARM處理器的內存對齊問題

對ARM處理器的內存對齊問題

作者: 時間:2016-11-09 來源:網絡 收藏
介紹

可以對齊或不對齊的內存訪問。對齊的內存訪問發(fā)生時的數據都位于其自然大小邊界。例如,如果該數據類型的大小是4個字節(jié),那么它屬于被4整除的內存地址是位于其自然大小邊界。未對齊的內存訪問發(fā)生在所有其他情況下(在上面的例子中,內存地址時,是不能被4整除)。ARM處理器的設計有效地訪問對齊的數據。在ARM處理器上試圖訪問未對齊的數據會導致不正確的數據或顯著的性能損失(這些不同的癥狀會在稍后討論)。與此相反,大多數CISC型處理器(即x86)的訪問未對齊的數據是無害的。這份文件將討論一些比較常見的方式,一個應用程序可能會執(zhí)行未對齊的內存訪問,并提供一些建議的解決方案,以避免這些問題, 。


本文引用地址:http://2s4d.com/article/201611/317700.htm

癥狀

上述問題,適用于所有ARM架構。然而,根據MMU(內存管理單元)和操作系統(tǒng)支持的可用性,應用程序可能會看到不同的行為在不同的平臺上。默認情況下,未對齊的內存訪問不會被困住了,會導致不正確的數據。與功能的MMU的平臺上,但是,OS捕獲非對齊訪問,它在運行時進行糾正。其結果將是正確的數據,但在10-20 CPU周期的成本。

常見原因


上述問題的類型轉換適用于所有ARM架構。然而,根據MMU(內存管理單元)和操作系統(tǒng)支持的可用性,應用程序可能會看到不同的行為在不同的平臺上。默認情況下,未對齊的內存訪問不會被困住了,會導致不正確的數據。與功能的MMU的平臺上,但是,OS捕獲非對齊訪問,它在運行時進行糾正。其結果將是正確的數據,但在10-20 CPU周期的成本。

代碼:

void my_func(char *a) { int *b = (int *)a; DBGPRINTF("%d", *b); }

這個簡單的例子,可能會導致未對齊的內存訪問,因為我們不能保證的char * a是一個4字節(jié)的邊界上對齊。只要有可能,應避免這種類型的施放。

使用數據緩沖區(qū)
未對齊的內存訪問的最常見的原因源于不正確地處理數據緩沖區(qū)。這些數據緩沖區(qū)可能包含任何數據從USB端口讀取,通過網絡,或從一個文件中。這個數據是很常見的包裝,有沒有插入填充,以確保數據在緩沖區(qū)內位于其自然大小邊界。在這個例子中,我們會考慮的情況下,從文件加載的Windows BMP和解析的頭。的Windows BMP文件包含一個頭的像素數據。的標頭是由兩個結構:

代碼:

typedef PACKED struct { unsigned short int type; /* Magic identifier */ unsigned int size; /* File size in bytes */ unsigned short int reserved1, reserved2; unsigned int offset; /* Offset to image data, bytes */ } HEADER; typedef PACKED struct { unsigned int size; /* Header size in bytes */ int width,height; /* Width and height of image */ unsigned short int planes; /* Number of colour planes */ unsigned short int bits; /* Bits per pixel */ unsigned int compression; /* Compression type */ unsigned int imagesize; /* Image size in bytes */ int xresolution,yresolution; /* Pixels per meter */ unsigned int ncolours; /* Number of colours */ unsigned int importantcolours; /* Important colours */ } INFOHEADER;


請注意,在的HEADER和INFOHEADER結構的大小,分別為14和40字節(jié)。讓我們假設我們要確定在運行時的圖像的寬度和高度。的代碼來訪問這些數據可能看起來像這樣:

代碼:

#define INFOHEADER_OFFSET (sizeof(HEADER)) #define WIDTH_OFFSET (INFOHEADER_OFFSET + offsetof(INFOHEADER, width)) #define HEIGHT_OFFSET (INFOHEADER_OFFSET + offsetof(INFOHEADER, height)) int imageWidth, imageHeight; void * fileBuf; pMe->mFile = IFILEMGR_OpenFile(pMe->mFileMgr, "test.bmp", _OFM_READ); if (pMe->mFile) { IFILE_GetInfo(pMe->mFile, &fileInfo); fileBuf = MALLOC(fileInfo.dwSize); if (fileBuf) { result = IFILE_Read(pMe->mFile, fileBuf, fileInfo.dwSize); if (result == fileInfo.dwSize) { imageWidth = *((uint32*)(((byte*)fileBuf) + WIDTH_OFFSET)); imageHeight = *((uint32*)(((byte*)fileBuf) + HEIGHT_OFFSET)); } } }


注意的寬度和高度的偏移量。因為他們屬于一個半字邊界上,以上述方式訪問這些值會導致未對齊的內存訪問。下面列出的一些推薦的方法來避免這個問題。

推薦的解決方案


使用memcpy

我們的第一個選項是,只需執(zhí)行MEMCPY從緩沖區(qū)中的數據到本地變量:

代碼:

if (result == fileInfo.dwSize) { MEMCPY(&imageWidth, (((byte*)fileBuf)+WIDTH_OFFSET), sizeof(uint32)); MEMCPY(&imageHeight, (((byte*)fileBuf)+HEIGHT_OFFSET), sizeof(uint32)); } 


其結果是,存儲器被字節(jié)逐字節(jié),避免任何疑問對準。


包裝的編譯器指令

或者,我們可以使用壓縮的編譯器指令允許使用指針,直接將我們需要的數據,同時迫使編譯器來處理對齊問題。在BREW環(huán)境中,PACKED被定義如下:

代碼:

#ifdef __ARMCC_VERSION #define PACKED __packed #else #define PACKED #endif


包裝形式,通過指定一個指針,ARM編譯器將生成相應的說明來正確地訪問內存,無論對齊。修改后的版本,上面的例子中,使用PACKED指針,如下:

代碼:

#define INFOHEADER_OFFSET (sizeof(HEADER)) #define WIDTH_OFFSET (INFOHEADER_OFFSET + offsetof(INFOHEADER, width)) #define HEIGHT_OFFSET (INFOHEADER_OFFSET + offsetof(INFOHEADER, height)) PACKED uint32 * pImageWidth; PACKED uint32 * pImageHeight; uint32 imageWidth, imageHeight; void * fileBuf; pMe->mFile = IFILEMGR_OpenFile(pMe->mFileMgr, "test.bmp", _OFM_READ); if (pMe->mFile) { IFILE_GetInfo(pMe->mFile, &fileInfo); fileBuf = MALLOC(fileInfo.dwSize); if (fileBuf) { result = IFILE_Read(pMe->mFile, fileBuf, fileInfo.dwSize); if (result == fileInfo.dwSize) { pImageWidth = (uint32*)(((byte*)fileBuf) + WIDTH_OFFSET); pImageHeight = (uint32*)(((byte*)fileBuf) + HEIGHT_OFFSET); imageWidth = *pImageWidth; imageHeight = *pImageHeight; } } } 




雖然程序員通常會無法控制標準化的數據格式,如BMP頭在上面的例子中,當你定義自己的數據結構應確保奠定了良好的對齊方式中的數據定義對齊的數據結構。下面的基本示例演示了這樣的原則:

代碼:

#ifdef __ARMCC_VERSION typedef PACKED struct { short a; // offsetof(a) = 0 int b; // offsetof(b) = 2 ? misalignment problem! short c; // offsetof(c) = 6 } BAD_STRUCT; typedef struct { int b; // offsetof(b) = 0 ? no problem! short a; // offsetof(a) = 4 short c; // offsetof(c) = 6 } GOOD_STRUCT; 


通過簡單地重新排列中,我們聲明的結構成員,我們可以解決一些對齊的問題。另外請注意,如果未聲明為包裝,BAD_STRUCT,編譯器通常會插入填充,每個字段對齊。然而,這通常是不希望的,因為它浪費內存和避免幾乎總是可以簡單地通過聲明為了減小尺寸的字段。

BREW模擬器測試


BREW模擬器3.1.2及以上版本提供了能夠使數據對齊檢查。BREW模擬器啟用此功能時,將顯示一個對話框,通知您的每一個未對齊的內存訪問,并為您提供的選項對這一問題視而不見,或闖入的代碼,請參閱BREW SDK用戶文檔一節(jié)揗isaligned數據異常支持更多信息,此功能。注:由于x86架構的訪問未對齊的數據不會有任何問題,你可以不編譯模擬器的DLL使用__packed指令(PACKED這就是為什么在WIN32環(huán)境下的空白被定義為)。這意味著,通過使用PACKED指針的非對齊訪問,解決依舊會觸發(fā)在模擬器的對齊檢查。



關鍵詞: ARM處理器內存對

評論


技術專區(qū)

關閉