http://www.aygfsteel.com/Files/jinfeng_wang/Endian.rar
Endian 介紹
1. Endian簡(jiǎn)介
Endian可以看作是系統(tǒng)的一種屬性, 它指示多字節(jié)整數(shù)是從左向右放, 還是從右向左放. 它有兩種形式:
? Big Endian
? Little Endian
BE把多字節(jié)整數(shù)的MSB(Most Significant Byte)存儲(chǔ)在最低的地址上, 把LSB(Least Significant Byte)順序存放在最高的地址上, 而LE正好相反.
如4-bytes數(shù)據(jù)0x01020304以?xún)煞N不同的方式存儲(chǔ)如下:
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. 有些處理器甚至設(shè)置了一個(gè)標(biāo)志位可以選擇所需要的Endian.
2. 出現(xiàn)的問(wèn)題
如果我們不了解Endian在數(shù)據(jù)存儲(chǔ)上的差異, 使用的時(shí)候就有可能出現(xiàn)問(wèn)題, 比如我們想要的是0x01020304,但是在little endian的情況下, 就有可能得到0x04030201.
如何避免錯(cuò)誤的數(shù)據(jù)呢? 首先先看一下系統(tǒng)是如何存取數(shù)據(jù)的.
下面是同一組數(shù)據(jù)在兩種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
|
上圖表示了數(shù)據(jù)的存放方式, 雖然s, l在兩種endina下的存儲(chǔ)方式不同, 但取出s, l的時(shí)候系統(tǒng)還是會(huì)還原成原來(lái)的值, 數(shù)據(jù)是不會(huì)改變的. 就是說(shuō)平常使用的過(guò)程中, 我們不必關(guān)心這種存取的過(guò)程.
那么在什么情況下會(huì)造成數(shù)值的改變呢? 我們從存數(shù)據(jù)和取數(shù)據(jù)兩個(gè)方面進(jìn)行說(shuō)明
2.1. 存數(shù)據(jù)
有一些接口和規(guī)范規(guī)定了必須以某種Endian的格式進(jìn)行通訊, 大多數(shù)都規(guī)定以Big Endian的格式. 比如SCSI command數(shù)據(jù)的傳輸, TCP/IP網(wǎng)絡(luò)協(xié)議等.
下面是10 字節(jié)的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
|
我們定義如下的結(jié)構(gòu)體填充.
typedef struct cdb1tag
{
uchar_t opcode;
uchar_t lun;
uint_t lba;
uchar_t rsv1;
ushort_t block;
uchar_t cntl;
} CDB1;
假設(shè)要發(fā)行一個(gè)Read操作, LBA是0x01020304. Transfer Length是255(0x00ff).
我們需要對(duì)CDB進(jìn)行填充. 賦值:
lba=0x01020304; block=0x00ff; …(其他參數(shù)不討論)
下面我們看看賦值后Big Endian和Little Endian是怎樣存儲(chǔ)這段數(shù)據(jù)的:
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. 取數(shù)據(jù)
同上面講到的, 如果規(guī)定了必須以某種Endian通訊, 則必須按照規(guī)定的順序把數(shù)據(jù)取出來(lái), 這種情況同存數(shù)據(jù)的例子, 只不過(guò)一個(gè)是存, 一個(gè)是取.
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)
|
另外, 不按照系統(tǒng)存儲(chǔ)的方式取數(shù)據(jù), 有時(shí)會(huì)發(fā)生意外. 比如說(shuō)我們是以一個(gè)字節(jié)一個(gè)字節(jié)存儲(chǔ)的數(shù)據(jù), 卻以4個(gè)字節(jié)為一組取出.
不過(guò)有的數(shù)據(jù)比較特殊, 即使顛倒字節(jié)順序, 數(shù)值也不發(fā)生改變
例如: 0 0x1111 0x01020201 …
3. 解決方法
? 多字節(jié)數(shù)據(jù)以單字節(jié)寫(xiě)入/讀出
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()
? 交換字節(jié) byte_swap()