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
|
上图表示了数据的存放方式, 虽然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) |
不过有的数据比较特殊, 即使颠倒字节顺序, 数值也不发生改变
例如: 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()