我剛剛發現我正在寫代碼的ARM(Cortex M0),不支持未對齊的內存訪問。當我的CPU不支持未對齊的內存訪問時,這意味着什麼?
現在在我的代碼中,我使用了很多壓縮結構,並且我從來沒有得到任何警告或硬錯誤,所以當它不允許未對齊訪問時,如何訪問這些結構的成員?
我剛剛發現我正在寫代碼的ARM(Cortex M0),不支持未對齊的內存訪問。當我的CPU不支持未對齊的內存訪問時,這意味着什麼?
現在在我的代碼中,我使用了很多壓縮結構,並且我從來沒有得到任何警告或硬錯誤,所以當它不允許未對齊訪問時,如何訪問這些結構的成員?
編譯器(如gcc)瞭解對齊方式,並將發佈正確的指令來解決對齊問題。如果你有一個緊湊的結構,你會告訴編譯器它提前知道如何進行對齊。
比方說,你是在32位架構,但有一個被擠得像這樣一個struct
:
struct foo __attribute__((packed)) {
unsigned char bar;
int baz;
}
當作出baz
的訪問,它會做一個32位的邊界上的內存負載,並將所有位移到位。
在這種情況下,它可能會導致bar
地址的32位加載和bar
+ 4地址的32位加載。然後它將應用一系列邏輯運算,如shift和logical或/和最終在32位寄存器中得到正確的值baz
。
查看程序集輸出以瞭解它是如何工作的。您會注意到,未對齊的訪問效率低於這些體系結構上的對齊訪問效率。
在許多較早的8位微處理器上,有加載(和存儲)大於存儲器總線寬度的寄存器的指令。這樣的操作將通過從一個地址加載一半的寄存器並且從另一個較高的地址加載另一半來執行。即使在存儲器總線寬度超過8位(比如16位)的系統上,通常將內存視爲可尋址的字節集合也是有用的。從任何地址加載一個字節將導致處理器讀取16位內存位置的一半並忽略另一半。從偶數地址讀取16位值將導致處理器讀取整個16位內存位置並使用整個事件;該值將與讀取兩個連續字節地址並連接結果的值相同,但它將在一次操作中而不是兩次中讀取。
在某些這樣的系統上,如果有人試圖從一個奇數地址讀取一個16位值,處理器將會讀取兩個連續的地址,使用一個值的一半和另一個值的另一半,執行兩個單字節讀取併合並結果。這被稱爲未對齊的內存訪問。在其他系統上,這樣的操作將導致總線故障,這通常會觸發某種形式的中斷,從而可能會或可能無法對此做一些有用的事情。支持未對齊訪問的硬件相當複雜,設計代碼以避免未對齊訪問通常不會太困難。因此,這種硬件通常只存在於已經非常複雜的處理器上,或者處理器將運行專爲處理器設計的代碼,該處理器將從單字節讀取裝配多字節寄存器(例如在8088上,每16位讀取所需的兩個8位存儲器,並在後來的Intel處理器上運行大量8088代碼)。
未對齊在任何體系結構上效率通常較低。例如,在x86上使用未對齊的訪問會受到懲罰(如果訪問跨越自然總線/內存邊界,例如0x1F爲32位訪問) –
您應該非常小心地使用__attribute __((packed)):這很危險。如果你的代碼使用了&foo-> bar地址並將它傳遞給另一個函數會發生什麼:未對齊的內存訪問!結論,你還沒有解決你的問題。 – ydroneaud