http://m.tkk7.com/Files/jinfeng_wang/Endian.rar
Endian 介紹
1. Endian簡介
Endian可以看作是系統的一種屬性, 它指示多字節整數是從左向右放, 還是從右向左放. 它有兩種形式:
? Big Endian
? Little Endian
BE把多字節整數的MSB(Most Significant Byte)存儲在最低的地址上, 把LSB(Least Significant Byte)順序存放在最高的地址上, 而LE正好相反.
如4-bytes數據0x01020304以兩種不同的方式存儲如下:
00000001 00000010 00000011 00000100
Address
|
00
|
01
|
02
|
03
|
Big-Endian
|
00000001
|
00000010
|
00000011
|
00000100
|
Little-Endian
|
00000100
|
00000011
|
00000010
|
00000001
|
所有的處理器必須指定它用Big Endian還是Little Endian. Intel's 80x86 processor
是 little endian. Sun's SPARC, Motorola's 68K, 和PowerPC系列是big endian. 有些處理器甚至設置了一個標志位可以選擇所需要的Endian.
2. 出現的問題
如果我們不了解Endian在數據存儲上的差異, 使用的時候就有可能出現問題, 比如我們想要的是0x01020304,但是在little endian的情況下, 就有可能得到0x04030201.
如何避免錯誤的數據呢? 首先先看一下系統是如何存取數據的.
下面是同一組數據在兩種endian下的memory dump:
char c1 = 1; char c2 = 2; short s = 255; // 0x00ff long l = 0x11223344;
|
Offset : Memory dump 0x0000 : 01 02 00 FF 0x0004 : 11 22 33 44
|
char c1 = 1; char c2 = 2; short s = 255; // 0x00ff long l = 0x11223344;
|
Offset : Memory dump 0x0000 : 01 02 FF 00 0x0004 : 44 33 22 11
|
A Little-Endian memory dump
|
上圖表示了數據的存放方式, 雖然s, l在兩種endina下的存儲方式不同, 但取出s, l的時候系統還是會還原成原來的值, 數據是不會改變的. 就是說平常使用的過程中, 我們不必關心這種存取的過程.
那么在什么情況下會造成數值的改變呢? 我們從存數據和取數據兩個方面進行說明
2.1. 存數據
有一些接口和規范規定了必須以某種Endian的格式進行通訊, 大多數都規定以Big Endian的格式. 比如SCSI command數據的傳輸, TCP/IP網絡協議等.
下面是10 字節的Read command, 其CDB格式如下:
Bit
Byte
|
7
|
6
|
5
|
4
|
3
|
2
|
1
|
0
|
0
|
Operation Code (28h)
|
1
|
Reserved
( 0 0 0 )b
|
DPO
|
FUA
( 0 )b
|
Reserved
( 0 0 0 )b
|
RelAdr ( 0 )b
|
2
|
(MSB)
LBA
(LSB)
|
3
|
4
|
5
|
6
|
Reserved ( 00 h)
|
7
|
(MSB)
Transfer Length (LSB)
|
8
|
9
|
Controller
|
我們定義如下的結構體填充.
typedef struct cdb1tag
{
uchar_t opcode;
uchar_t lun;
uint_t lba;
uchar_t rsv1;
ushort_t block;
uchar_t cntl;
} CDB1;
假設要發行一個Read操作, LBA是0x01020304. Transfer Length是255(0x00ff).
我們需要對CDB進行填充. 賦值:
lba=0x01020304; block=0x00ff; …(其他參數不討論)
下面我們看看賦值后Big Endian和Little Endian是怎樣存儲這段數據的:
Byte
|
(BIG_ENDIAN)
|
0
|
|
1
|
|
2
|
(MSB) 01
|
3
|
02
|
4
|
03
|
5
|
04 (LSB)
|
6
|
|
7
|
(MSB) 00
|
8
|
ff (LSB)
|
9
|
|
Byte
|
(LITTLE_ENDIAN)
|
0
|
|
1
|
|
2
|
(LSB) 04
|
3
|
03
|
4
|
02
|
5
|
01 (MSB)
|
6
|
|
7
|
(LSB) ff
|
8
|
00 (MSB)
|
9
|
|
2.2. 取數據
同上面講到的, 如果規定了必須以某種Endian通訊, 則必須按照規定的順序把數據取出來, 這種情況同存數據的例子, 只不過一個是存, 一個是取.
union{
char c_num[4]; // 01 02 03 04 int i_num;
}dev;
|
Offset : Memory dump
0x0000 : 01 02 03 04
|
i_num= 0x04030201 (LE) : 0x01020304 (BE)
|
另外, 不按照系統存儲的方式取數據, 有時會發生意外. 比如說我們是以一個字節一個字節存儲的數據, 卻以4個字節為一組取出.
不過有的數據比較特殊, 即使顛倒字節順序, 數值也不發生改變
例如: 0 0x1111 0x01020201 …
3. 解決方法
? 多字節數據以單字節寫入/讀出
IN:
(unsigned char)((lba & 0Xff000000) >> 24) à MSB
(unsigned char)((lba & 0X00ff0000) >> 16)
(unsigned char)((lba & 0X0000ff00) >> 8)
(unsigned char) (lba & 0X000000ff) à LSB
OUT:
endian_dump()
? 交換字節 byte_swap()