Linux網路程式設計 之 大小端初探
首先解釋一下大小端的概念。
大端(Big Endian),同時也是網路序,是資料在網路上傳輸的一種資料組織格式,其儲存的方式比較符合人們讀寫的習慣。
小端(Little Endian),這裡不能說其是主機序,因為主機可能採用的是大端cpu也可能採用的是小端cpu,小端與大端相對。
通過一個例子深入瞭解它們之間的區別:
用一臺Big Endian 和 另一臺 Little Endian 分別儲存32bit的數值,為:0x12345678,資料在記憶體當中的儲存順序如下表所示。
Addr | Big Endian | Little Endian |
0xa | 0x12 | 0x78 |
0xb | 0x34 | 0x56 |
0xc | 0x56 | 0x34 |
0xd | 0x78 | 0x21 |
由表1可以清晰看到,在大端計算機當中,儲存方式可以總結為八個字:大端(高位元組)存在起始地址;在小端計算機當中,可以總結為八個字:小端(低位元組)存在起始地址。我們深入瞭解位元組在計算機當中的儲存方式,仍以0x12345678為例。
32 bit representation in memory:
- Big Endian:
Hex: 0x12345678
Binary: 00010010 00110100 01010110 01111000
- Little Endian
Hex: 0x78563412
Binary: 01111000 01010110 00110100 00010010
Decimal: 305419896
從上面的資料分佈,可以發現一個規律,不論大小端
現在我們去找一下htonl 的linux實現程式碼,由於linux的標頭檔案基本都在/usr/include下,因此我們直接利用linux提供的find工具去進行搜尋:在終端鍵入
find /usr/include/ -type f | xargs grep -n htonl
得到結果如下:
紅色的兩句對我們而言是比較有用的兩句,從上面可以看到,htonl其實是__int32_identify 和 __bsway_32的巨集替換,我們鍵入命令開啟檔案 /usr/include/netinet/in.h檢視並定位到行397(vim 下鍵入":"並輸入行號即可定位到相應行)。
vim /usr/include/netinet/in.h
得到如下結果:
得知一個是用於大端的轉換(其實大端已經沒有必要轉,網路流進主機的資料,就是大端的)我們可以猜想,這個__int32_identify是一個類似 #define__int32_identify(x) x空的巨集定義或者一個直接將傳入引數返回的函式,事實證明ubuntu中採用的的是後者,並且使用了內連函式。我們繼續查詢__bsway_32,因為這個函式的實現才是我們所關心的,在終端鍵入:
find /usr/include/ -type f | xargs grep -n __bswap_32
得到如下結果:
紅色框起來的部分資訊就是我們所尋找的資訊,開啟檔案byteswap.h並定位到45行
vim /usr/include/x86_64-linux-gnu/bits/byteswap.h
從程式碼當中可以看到,針對不同的編譯器版本,提供了不同的位元組轉換,有使用編譯器內建的函式,也有使用匯編實現的位元組序的轉換,同時也有程式碼自己實現的__bswap_constant_32(x),這也正是我們所關注的,可以看到,這個巨集定義的實現,正是採用移位運算和位運算完成位元組序的轉換。
#define __bswap_constant_32(x) \
((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
(((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
我們分析一下這個巨集定義,例如小端上儲存的為0x78563412,由上面的分析得知網路序其實是0x12345678,我們用這個函式證實一下:
0x78563412 & 0xff000000 值為 0x78000000 ,左移24bit即 0x00000078
0x78563412 & 0x00ff0000 值為 0x00560000 ,左移 8bit 即 0x00005600
0x78563412 & 0x0000ff00 值為 0x00003400 ,右移 8bit 即 0x00340000
0x78563412 & 0x000000ff 值為 0x00000012 ,右移24bit即 0x12000000
最後將右側的結果按位與得到結果0x12345678,結果符合我們的猜想。
從網路序轉換為主機序的分析思路與此完全一致,有興趣的盆友可自行動手試探一番。
好啦,位元組序轉換初步寫到這裡,這裡僅僅介紹了位元組間的轉換,並沒有分析跨位元組位域怎麼儲存和我們怎麼轉換和存值,欲知後事,請聽下回分析~