嵌入式基礎知識硬體篇之位元組序

2021-08-09 05:12:16 字數 4429 閱讀 4915

16、大小端的介紹

little-endian就是低位位元組排放在記憶體的低位址端,高位位元組排放在記憶體的高位址端。

big-endian就是高位位元組排放在記憶體的低位址端,低位位元組排放在記憶體的高位址端。

數字0x12 34 56 78在記憶體中的表示形式為:

大端模式:

低位址 -----------------> 高位址

0x12  |  0x34  |  0x56  |  0x78

小端模式:

低位址 ------------------> 高位址

0x78  |  0x56  |  0x34  |  0x12

big-endian: 低位址存放高位,如下:

高位址---------------

buf[3] (0x78) -- 低位

buf[2] (0x56)

buf[1] (0x34)

buf[0] (0x12) -- 高位

---------------

低位址little-endian: 低位址存放低位,如下:

高位址---------------

buf[3] (0x12) -- 高位

buf[2] (0x34)

buf[1] (0x56)

buf[0] (0x78) -- 低位

--------------

低位址

c語言實現測試大小端:

#include

int main(int argc, char **argv) c;

c.a = 1;

if(c.b == 1)

else

printf("big\n");

return 0; }

常見cpu的大小端:

big endian : powerpc、ibm、sun

little endian : x86、dec

常見檔案的位元組序:

adobe ps – big endian

bmp – little endian

dxf(autocad) – variable

gif – little endian

jpeg – big endian

macpaint – big endian

rtf – little endian

大小端的轉換:

16位#define bigtolittle16(a)   (( ((uint16)(a) & 0xff00) >> 8)    | \  

(( (uint16)(a) & 0x00ff) << 8))  

32位#define bigtolittle32(a)   ((( (uint32)(a) & 0xff000000) >> 24) | \  

(( (uint32)(a) & 0x00ff0000) >> 8)   | \  

(( (uint32)(a) & 0x0000ff00) << 8)   | \  

(( (uint32)(a) & 0x000000ff) << 24))  

從軟體的角度上,不同端模式的處理器進行資料傳遞時必須要考慮端模式的不同。如進行網路資料傳遞時,必須要考慮端模式的轉換。在socket介面程式設計中,以下幾個函式用於大小端位元組序的轉換。

ntohs(n)     //16位資料型別網路位元組順序到主機位元組順序的轉換  

htons(n)     //16位資料型別主機位元組順序到網路位元組順序的轉換  

ntohl(n)      //32位資料型別網路位元組順序到主機位元組順序的轉換  

htonl(n)      //32位資料型別主機位元組順序到網路位元組順序的轉換 

很多人討厭碰到位元組序問題,跟它打交道就像走迷宮,每次都要犧牲不少腦細胞。即使這一次似乎搞清楚了,下次碰到還是要重新在大腦裡構建和模擬。這裡盡量做乙個位元組序問的完整備忘記錄。

主機位元組序

多位元組資料在記憶體中的位元組排列順序稱為主機位元組序,主機位元組序基本由cpu硬體決定,某些cpu如x86、z80等為little-endian;有些如moto6800、sparc等為big-endian;而有些cpu可通過暫存器設定支援不同位元組序,如arm、mips等,這類平台上不同os可能配置了不同位元組序。

理解位元組序需要知道兩個名詞:msb (most significant byte),代表乙個數中權值最大的位元組;lsb(least significant byte),代表乙個數中權值最小的位元組。比如十進位制數1234,1權值為千位,最大,相當於msb;4為個位,權值最小,相當於lsb。那麼真正以位元組為單位考慮, 4位元組數0x12345678裡那個是msb/lsb呢?

主機位元組序分big-endian和little-endian兩種,定義為:

a. little-endian,又稱低位元組優先,是把lsb放在記憶體低位址端,msb在記憶體高位址端。舉例,4位元組數0x12345678在little-endian體系的記憶體中存放(從位址0x1000開始)為:

b. big-endian,又稱高位元組優先,把msb放在記憶體低位址端,lsb放在記憶體高位址端。0x12345678此時存放為:

big-endian沿位址增長方向先放高權位數字的方式恰好符合一般習慣(按照千,百,十,個位來書寫數字)。這也是為什麼初學者變換endian時總覺得big-endian比較正常。

位元組序問題有時會被擴大化,有人一碰到奇怪問題就懷疑位元組序。其實它只存在於多位元組資料在記憶體中的解析,注意:1)多位元組資料;2)記憶體中的排列。

a. 多位元組資料是指諸如long int short等需要多個位元組儲存的資料型別,而象char等用乙個位元組表示的型別永遠不會有位元組序問題。

b. cpu通用暫存器都是整體操作,不存在單位元組位址以及位址增長方向的概念,因此也沒有msb/lsb在前或在後的問題,只有記憶體中的多位元組資料儲存才會有這兩種差異。從硬體角度看,cpu是直接通過資料匯流排連線記憶體和cpu暫存器,這中間沒有額外位元組序轉換的硬體開銷,只是不同匯流排連線方式導致了不同位元組序,如下圖(from wiki):

資訊交換中的位元組序

當不同型別平台通過某種媒介交換資訊時需要考慮位元組序問題,常見介質主要指檔案和網路。通過檔案或網路讀取來自其它主機的多位元組資料時,要警惕位元組序問題。如x86和moto6800主機通過檔案交換資訊時,如果不轉換,資料處理就不匹配。在x86 vc下執行如下**:

void main( )

**中a的值0x3132即'12』,由於x86為little endian體系,記憶體中多位元組lsb(這裡即0x32)在前,所以執行後開啟test.txt檔案,內容是'21'。把test.txt檔案複製到moto6800機器上,再用fread把檔案內容讀到short變數裡,得到的就不是0x3132而是0x3231了,資料就此被顛倒,後續處理完全錯誤。

假設我們制定了介質層位元組序標準(如big-endian),要通過軟體遮蔽不同endian體系的差異,需要兩步操作,一判斷endian型別:

typedef union endian;

/* judge cpu is big endian(0) or small endian(1) */

bool judge_endian(void)

想想指標怎麼做?

判斷完兩端主機的endian之後,要根據情況做相應endian交換,即傳送和接收任何一方只要不符合媒介層標準(big-endian),就要通過移位和位操作,在主機序和媒介序之間轉換。一方傳送資料前要先將記憶體資料由主機位元組序轉換為傳輸介質層的位元組序,再傳送出去,接收方收到資料後,要轉換為本地的主機位元組序,再做後續處理。

比如tcp/ip協議的socket通訊,基於socket通訊的雙方一般選擇big-endian為標準,又稱網路位元組序。socket定義了一組轉換函式,用於多位元組數在網路位元組序和主機位元組序之間的轉換。htonl,htons用於主機序轉換到網路序(如主機本身big endian,函式實際啥也不做);ntohl,ntohs把網路序轉換到本機序(同樣)。因此傻瓜式做法,無論傳送方主機位元組序是什麼,傳送前都用htons或htonl將多位元組資料轉換為網路位元組序;同樣無論接收方主機位元組序是什麼,都用ntohs或ntohl把接收的網路資料轉換為本地主機位元組序。這樣有了網路位元組序標準,socket通訊時,只需套用這幾個函式就能方便地抹平主機間的位元組序差異。

問題在於如果雙方主機都與網路位元組序相反,本來是可以直接通訊的,卻用htonl/ntohl等轉過去又轉回來,有點自找麻煩。這就要看軟體的預期執行環境,以及怎樣平衡軟體效率和多平台通用性。

補充soc內部的位元組序

soc中除主cpu外,一些外設中(如硬體**編解碼器)還包含內部mcu,它們之間一般通過高速匯流排共享外部ddr,通過低速匯流排共享外設暫存器。這時如果雙方的endian屬性不匹配,對host cpu驅動及mcu上的韌體編寫會造成一定困擾。

總結真正理解大小端的本質可能要從硬體系統角度,涉及cpu指令集、暫存器、匯流排以及硬體外設等。但對程式設計師來說,能夠了解位元組序問題的存在範圍以及如何實現移植性更高的**,這就足夠了。

tip:對於arm等可配置的cpu,其編譯工具中會有big/little endian的選項,程式移植時也需要注意,如果跟系統設定不匹配,編譯得到的目標程式就無法執行。

嵌入式基礎知識

嵌入式處理器的分類包括三種,分別是 嵌入式微控制器 嵌入式微處理器 數字訊號處理器 安裝linux系統對硬碟分割槽時,必須有兩種分割槽型別 檔案系統分割槽 和交換分割槽 在vivi狀態下,顯示系統的分割槽命令是 part show。鏈結分為 符號鏈結 和硬鏈結 當gcc僅對原始檔進行編譯而不鏈結生成...

嵌入式系統原理之基礎知識

嵌入式系統是一種以應用為中心,以計算機技術為基礎,軟體硬體可裁剪,適應應用系統對功能 可靠性 成本 體積 功耗嚴格要求的專用計算機系統。現今所說的嵌入式系統一般指嵌入式計算機系統,主要包括四部分 硬體層 中間層 系統軟體層和應用層。1.1 硬體層 硬體層是指除被控物件之外的嵌入式系統要完成其功能所具...

嵌入式系統基礎知識 了解嵌入式系統

嵌入式系統用於大量電子裝置,它們的設計涉及到硬體和軟體技術。隨著計算機技術的進步,其技術也越來越多地嵌入到越來越多的電子產品中。嵌入式系統提供的功能是使電子裝置具有比僅使用硬體技術時更大的能力。因此,嵌入式系統可用於各種電子裝置和小工具中。從電子計時器等專案中的少量處理,到遊戲控制台甚至主要工廠和其...