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; Offset : Memory dump A Big-Endian memory dump char c1 = 1; Offset : Memory dump A Little-Endian memory dump
char c2 = 2;
short s = 255; // 0x00ff
long l = 0x11223344;
0x0000 : 01 02 00 FF
0x0004 : 11 22 33 44
char c2 = 2;
short s = 255; // 0x00ff
long l = 0x11223344;
0x0000 : 01 02 FF 00
0x0004 : 44 33 22 11
上圖表示了數據的存放方式, 雖然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 }dev; Offset : Memory dump 0x0000 : 01 02 03 04 i_num= 0x04030201 (LE) : 0x01020304 (BE)
int i_num;
不過有的數據比較特殊, 即使顛倒字節順序, 數值也不發生改變
例如: 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()