深入浅出大端和小端
- 格式:pdf
- 大小:138.87 KB
- 文档页数:12
⼤端序与⼩端序端序(Endianness),⼜称字节序、尾序、位序。
在计算机领域是指机器存放多字节数据的字节顺序。
在涉及到低层数据存储和⽹络数据传输研究中都会涉及端序。
⼤端序(Big-Endian,⼤尾序):⾼位字节放在内存的低地址,低位字节放在内存的⾼地址。
⼩端序(Little-Endian,⼩尾序):低位字节放在内存的低地址,⾼位字节放在内存的⾼地址。
端序是与硬件的体系结构相关⽽与所使⽤的操作系统⽆关的概念,⽬前基本上所有x86系列的PC机都是⼩端序。
在32位机器上,对于数0x12345678来说,低层表⽰的⽅法因端序⽽排列不同⼩端序: | 0x78 | 0x56 | 0x34 | 0x12 |低地址 --0----------7-8-------15-16------23-24-------31--> ⾼地址⼤端序: | 0x12 | 0x34 | 0x56 | 0x78 |低地址 --0----------7-8-------15-16------23-24-------31--> ⾼地址判定⽅法:⽅法⼀:将整形int强制转换char测试1bool isBigEndian()2 {3int a = 1;4if (((char*)&a)[sizeof(int) - 1] == 1) {5return true;6 } else {7return false;8 }9 }对于32位机器来说:⼩端序: | 0x01 | 0x00 | 0x00 | 0x00 |低地址 --0---[0]---7-8--[1]--15-16-[2]-23-24--[3]--31--> ⾼地址⼤端序: | 0x00 | 0x00 | 0x00 | 0x01 |低地址 --0---[0]---7-8--[1]--15-16-[2]-23-24--[3]--31--> ⾼地址⽅法⼆:使⽤union,其所有成员共享同⼀个最⼤的内存地址。
轻松记住⼤端⼩端的含义(附对⼤端和⼩端的解释) 或许你曾经仔细了解过什么是⼤端⼩端,也动⼿编写了测试⼿头上的机器上是⼤端还是⼩端的程序,甚⾄还编写了⼤端⼩端转换程序;但过了⼀段时间之后,当你再看到⼤端和⼩端这两个字眼,你的脑中很快浮起了⾃⼰曾经做过的⼯作,却总是想不起究竟哪种是⼤端、哪种是⼩端,然后⼜去查以前写的记录?更让⼈不快的是,这种经历反反复复,让你⼗分困扰。
如果你和以前的笔者⼀样,有过这种不快的经历,那么这篇⽂章希望能帮你彻底解决这个苦恼,让你彻底记住它们。
如果你在⼯作中经常使⽤到⼤端和⼩端以⾄于对它们⼗分熟悉,或者你的记忆⼒在保持时间的长度和精准度上都⼗分优秀,以⾄于不需要借助其他的⽅法,那么这篇⽂章不适合你。
如果你在看这篇⽂章前完全不知道什么是⼤端和⼩端,那么可以参考本⽂的附录或者其他的博⽂,相关的介绍⾮常之多,⽽附录提供了⼀个很常见解释和⼀段测试程序,然后再来看正⽂。
为了帮助记忆,理解是必要的;⽽记忆的⽬的,也就是为什么要记住它,是更重要的。
或许你会问,先了解概念,⽤的时候再查,不⾏么?其实我之前也是这么认为的。
⼤端和⼩端这两个名词,你会在很多有关⽹络编程、系统设计、甚⾄是代码写作的书上看到,⽽且它也是很多公司的笔试题、⾯试题热门内容,可见它在⼀些领域是很常⽤。
如果等到你⽤的时候再查,⼀⽅⾯要降低你的⼯作效率,另⼀⽅⾯,应试的时候也不是你想查就能查的;其实最主要的是,在掌握规律后,记住它们并不困难。
现在先来理解这对概念,⼤端和⼩端这两个令⼈迷惑的术语究竟是如何产⽣的?《程序设计实践》第9章中提到,“⼤端”和“⼩端”可以追溯到1726年的Jonathan Swift的《格列佛游记》,其中⼀篇讲到有两个国家因为吃鸡蛋究竟是先打破较⼤的⼀端还是先打破较⼩的⼀端⽽争执不休,甚⾄爆发了战争。
1981年10⽉,Danny Cohen的⽂章《论圣战以及对和平的祈祷》(On holy wars and a plea for peace)将这⼀对词语引⼊了计算机界。
字节序,大端,小端的概念大端、小端:大小端是指CPU存储数据的方式,比如一个0x01020304这个整数,在WIN、Linux下在内存中的布局如下[01][02][03][04] 注意左边是高地址,而右边是低地址在UNIX下则是[04][03][02][01] 注意左边是高地址,而右边是低地址通俗的说,和WIN下的内存布局一致的就是小端,和UNIX下一致的就是大端。
其实在以前还出现过中端的的机型,不过这些机型也太“孤僻”了,已经落伍没人生产没人用了。
网络字节序:其实是指网络传输的字节序,这个字节序可能是大端序或者小端序,这取决于软件开始时通讯双方的协议规定。
平时,如果有人说的网络字节序,那么大家就认为是大端序。
主机字节序:是指主机处理数据时采用的字节序,虽然主机字节序和网络字节序是相对的概念,但是我们说主机字节序的时候,并不默认是之大端或者小端,而是结合机型来确定大小端的。
位序:我们通常所说的字节序是指字节之间的关系,但是即使是一个字节中的某一位(bit)也是有排序的问题的。
位序也有大端序,小端序,中端序,也还有其他的乱七八糟的位序的,但是都不常见。
开发的时候,我们是不用关心位序,编译器和CPU会自己处理这些事情。
算术运算与内存操作运算:算术运算是不改变被运算数据的字节序的,此时我们不用关心所操作的数据到底是什么字节序的。
但是内存操作运算就要注意了,比若我们将一个整数指针强制转换为一个字符指针类型,然后对字符指针类型的四个字节进行算数运算,此时我们必须知道是什么字节序。
不然写出的代码要么是错误的,要么移植到其他机器的时候就不能正常运行。
常见的算术有+ - * / % & | ~ << >> = 等,请注意& | ~ << >> 这几个是算术运算而不是内存操作运算,所以他们进行混合运算的时候不用关心字节序或者位序问题。
赋值运算符仅在数据类型兼容的时候才不涉及字节序问题,才能算作算术运算。
⼤端模式和⼩端模式1.为什么存在⼤⼩端模式计算机系统中,每个地址单元对应⼀个字节(8bit),⼀种数据类型的数据可能占⽤若⼲字节。
如何安排这种数据类型中的各个字节,哪个字节在低地址哪个在⾼地址,以及⼀个字节中的各个⽐特的排列,这就牵涉到⼤⼩端模式。
也就是⼤家常说的字节序和⽐特序问题。
字节序和⽐特序⼀般是⼀致的,要么都是⼤端,要么都是⼩端。
2.什么是⼤端和⼩端⼤端模式:低位(字节/⽐特)放在⾼地址中,⾼位(字节/⽐特)放在低地址中。
⼩端模式:低位(字节/⽐特)放在低地址中,⾼位(字节/⽐特)放在⾼地址中。
⾼位和地位是对于我们正常阅读和书写来说,最开始是⾼位,例如int型数0x1234,0x12是字节的⾼位,0x34是字节的低位。
根据以上规则,我们给出在⼤、⼩端序系统中整数0x0a0b0c0d的表⽰⽅式。
对于⼤端系统:byte addr 0 1 2 3bit offset 01234567 01234567 01234567 01234567binary 00001010 00001011 00001100 00001101hex 0a 0b 0c 0d对于⼩端系统:byte addr 3 2 1 0bit offset 76543210 76543210 76543210 76543210binary 00001010 00001011 00001100 00001101hex 0a 0b 0c 0d3. 检测⼤⼩端联合体的存放顺序是所有成员都从低地址开始存放,利⽤该特性可以轻松获得当前系统采⽤⼤端还是⼩端模式BOOL IsBigEndian(){union NUM{int a;char b;}num;num.a = 0x1234;if( num.b == 0x12 ){return TRUE;}return FALSE;}4.常见的⼤⼩端⼀般操作系统都是⼩端模式;⽽通讯协议是⼤端模式;java和平台⽆关,默认是⼤端模式常见的cpu的⼤⼩端:⼤端:PowerPC、IBM、Sun⼩端:x86ARM既可以⼯作在⼤端模式,也可以⼯作在⼩端模式。
⼤端模式与⼩端模式理解字节序字节序指多字节数据在计算机内存储或者⽹络上传输时各字节的顺序。
(来源:百度百科)为了⽅便,逻辑上将字节序列⾥左边的字节称为⾼字节,右边的字节称为低字节,从左到右,由⾼到低,这样符合数学上的思维习惯,左边是⾼位,右边是地位。
⼤端模式与⼩端模式由于每个字节在内存中都是有地址的,并且内存的地址是顺序排列的,当我们在内存中保存数据时:如果,⾼字节存放在低地址,低字节存放在⾼地址,则为⼤端模式(big-endian)。
如果,低字节存放在低地址,⾼字节存放在⾼地址,则为⼩端模式(little-endian)。
数据从内存保存到⽂件(或发送到⽹络上)时,会受到内存的⼤端模式与⼩端模式的影响。
数据从⽂件读取到(或从⽹络接收到)内存时,需要知道之前是先保存的(或是先发送的)⾼字节还是低字节。
C++⽰例代码1//int 占 4 个字节,short 占 2 个字节int main(){printf("在栈上分配内存\n");int a = 0x11223344;short b = 0x5566;short c = 0x7788;unsigned char *pa = (unsigned char *)&a;unsigned char *pb = (unsigned char *)&b;unsigned char *pc = (unsigned char *)&c;printf("pa 0x%p 0x%x\n", pa, a);printf("pb 0x%p 0x%x\n", pb, b);printf("pc 0x%p 0x%x\n", pc, c);printf("按字节序打印所有字节(⾼字节->低字节)\n");printf("a0 0x%x\n", (a & 0xFF000000) >> (3 * 8));printf("a1 0x%x\n", (a & 0x00FF0000) >> (2 * 8));printf("a2 0x%x\n", (a & 0x0000FF00) >> (1 * 8));printf("a3 0x%x\n", (a & 0x000000FF));printf("b0 0x%x\n", (b & 0xFF00) >> (1 * 8));printf("b1 0x%x\n", (b & 0x00FF));printf("c0 0x%x\n", (c & 0xFF00) >> (1 * 8));printf("c1 0x%x\n", (c & 0x00FF));printf("根据地址顺序打印所有字节(低地址->⾼地址)\n");for (int i = 0; i < 4; i++) {printf("pa[%d] 0x%p 0x%02x\n", i, pa + i, pa[i]);}for (int i = 0; i < 2; i++) {printf("pb[%d] 0x%p 0x%02x\n", i, pb + i, pb[i]);}for (int i = 0; i < 2; i++) {printf("pc[%d] 0x%p 0x%02x\n", i, pc + i, pc[i]);}return 0;}⽰例代码1运⾏结果在栈上分配内存pa 0x007ffe24 0x11223344pb 0x007ffe22 0x5566pc 0x007ffe20 0x7788按字节序打印所有字节(⾼字节->低字节)a0 0x11a1 0x22a2 0x33a3 0x44b0 0x55b1 0x66c0 0x77c1 0x88根据地址顺序打印所有字节(低地址->⾼地址)pa[0] 0x007ffe24 0x44pa[1] 0x007ffe25 0x33pa[2] 0x007ffe26 0x22pb[0] 0x007ffe22 0x66pb[1] 0x007ffe23 0x55pc[0] 0x007ffe20 0x88pc[1] 0x007ffe21 0x77⽰例代码1结果分析a、b、c 在内存中的排列情况:---------------------------------------------------|低地址 -> ⾼地址|---------------------------------------------------|....|0x88|0x77|0x66|0x55|0x44|0x33|0x22|0x11|....|---------------------------------------------------a、b、c 是在栈中分配的,可以看到内存地址是连续的,且 a 的地址相对较⾼,c 的地址相对较低。
什么是⼤⼩端,程序如何判断⼤⼩端?如何判断所使⽤系统的字节序?假设计算机要存储4byte int a = 0x12345678, 通常有2种存储⽅式: ⼤端, 和⼩端. 具体是哪种, 取决于计算机体系结构(硬件)字节地址(序号)0123低->⾼⼤端12345678⼩端78563412上⾯表格都以16进制表⽰, 省略了"0x".判断依据:⼤端: 低字节存储⾼位字节数据, 如序号为0的地址存储了⾼位字节12;⼩端: 低字节存储低位字节数据, 如序号为0的地址存储了低位字节78;注: a按数值的意义, 从⾼位字节到低位字节, 分别是: 12, 34, 56, 78程序判断⽅法(3种):#include <stdio.h>#include <stdint.h>typedef union {uint16_t v;uint8_t bytes[2]; // bytes[0] 是⼩地址,bytes[1]是⼤地址:w}u16;/* ⽅法1: 利⽤联合体进⾏取不同地址对应数* 利⽤联合体, 对2byte 联合体赋值后, 分别检查⾼低地址字节数据是否为⼤端特征* */void isBigEnding() {u16 a;a.v = 0x1234;if (a.bytes[0] == 0x12 && a.bytes[1] == 0x34) {printf("big ending\n");}else if (a.bytes[0] == 0x34 && a.bytes[1] == 0x12) {printf("little ending\n");}else {printf("error value");}return;}#include <arpa/inet.h>/* ⽅法2: 利⽤已知端序的API, 进⾏⽐较* 利⽤已知的⽹络序为⼤端, ⽽htons是将2byte主机序数据转换成⽹络序, 判断转换前后是否有变化* 如果没变化, 则说明为⼤端; 如果有, 则说明为⼩端* */void isBigEnding2() {uint16_t hport = 0x8000;uint16_t nport = htons(hport); // ⽹络字节序是⼤端printf("number in net byte order is 0x%x\n", nport );if (hport == nport) {printf("big ending\n");}else {printf("little ending\n");}}/** ⽅法3:取地址直接转换*/void isBigEnding3() {uint16_t n = 0x1234;if ( *(uint8_t *)&n == 0x12) {printf("big ending\n");}else if (*(uint8_t *)&n == 0x34) {printf("little ending\n");}else {printf("error value");}}/* 写⼀个程序判断所使⽤系统的字节序 */ int main() {isBigEnding();isBigEnding2();isBigEnding3();return0;}。
结构体大小端定义大小端(Endian)是计算机存储数据的一种方式,它决定了数据在内存中的存储顺序。
在计算机系统中,数据在内存中的存储是以字节为单位的,每个字节有一个唯一的地址。
在存储多字节的数据时,计算机可以选择不同的字节存储顺序,即大小端。
大小端定义了数据的高位字节和低位字节的存储顺序。
在小端存储方式中,数据的低位字节(最小有效位)存储在低地址中,而高位字节(最高有效位)存储在高地址中。
而在大端存储方式中,数据的高位字节存储在低地址中,低位字节存储在高地址中。
为了更好地理解大小端的概念,我们可以以一个结构体的存储为例。
假设我们有一个结构体定义如下:```cstruct example {int a;char b;short c;};```在内存中,这个结构体的存储空间是连续的。
根据大小端的不同,这个结构体的存储方式也会有所区别。
在小端存储方式中,结构体的存储顺序如下:```地址内容0x1000 a的低字节0x1001 a的高字节0x1002 b0x1003 c的低字节0x1004 c的高字节```可以看到,结构体的成员变量按照从低地址到高地址的顺序依次存储。
这是因为小端存储方式将数据的低位字节存储在低地址中,高位字节存储在高地址中。
相反,在大端存储方式中,结构体的存储顺序如下:```地址内容0x1000 a的高字节0x1001 a的低字节0x1002 b0x1003 c的高字节0x1004 c的低字节```可以看到,结构体的成员变量按照从高地址到低地址的顺序依次存储。
这是因为大端存储方式将数据的高位字节存储在低地址中,低位字节存储在高地址中。
在实际应用中,大小端的选择对于数据的传输和解析非常重要。
在网络通信中,不同的计算机可能使用不同的大小端存储方式。
因此,在进行网络数据传输时,需要对数据进行大小端的转换,以保证数据的正确解析。
在编程语言中,通常提供了一些函数或宏来进行大小端的转换。
例如,在C语言中,可以使用`htonl`和`ntohl`函数来进行32位整型数据的大小端转换。
⼤端和⼩端(BigendianandLittleendian)⼀、⼤端和⼩端的问题对于整型、长整型等数据类型,Big endian 认为第⼀个字节是最⾼位字节(按照从低地址到⾼地址的顺序存放数据的⾼位字节到低位字节);⽽ Little endian 则相反,它认为第⼀个字节是最低位字节(按照从低地址到⾼地址的顺序存放据的低位字节到⾼位字节)。
例如,假设从内存地址 0x0000 开始有以下数据:0x0000 0x0001 0x0002 0x00030x12 0x34 0xab 0xcd如果我们去读取⼀个地址为 0x0000 的四个字节变量,若字节序为big-endian,则读出结果为0x1234abcd;若字节序为little-endian,则读出结果为0xcdab3412。
如果我们将0x1234abcd 写⼊到以 0x0000 开始的内存中,则Little endian 和 Big endian 模式的存放结果如下:地址 0x0000 0x0001 0x0002 0x0003big-endian 0x12 0x34 0xab 0xcdlittle-endian 0xcd 0xab 0x34 0x12⼀般来说,x86 系列 CPU 都是 little-endian 的字节序,PowerPC 通常是 big-endian,⽹络字节顺序也是 big-endian还有的CPU 能通过跳线来设置 CPU ⼯作于 Little endian 还是 Big endian 模式。
对于0x12345678的存储:⼩端模式:(从低字节到⾼字节)地位地址 0x78 0x56 0x34 0x12 ⾼位地址⼤端模式:(从⾼字节到低字节)地位地址 0x12 0x34 0x56 0x78 ⾼位地址⼆、⼤端⼩端转换⽅法htonl() htons() 从主机字节顺序转换成⽹络字节顺序ntohl() ntohs() 从⽹络字节顺序转换为主机字节顺序Big-Endian转换成Little-Endian#define BigtoLittle16(A) ((((uint16)(A) & 0xff00) >> 8) | (((uint16)(A) & 0x00ff) << 8))#define BigtoLittle32(A) ((((uint32)(A) & 0xff000000) >> 24) | (((uint32)(A) & 0x00ff0000) >> 8) | \(((uint32)(A) & 0x0000ff00) << 8) | (((uint32)(A) & 0x000000ff) << 24))三、⼤端⼩端检测⽅法如何检查处理器是big-endian还是little-endian?C程序:int i = 1;char *p = (char *)&i;if(*p == 1)printf("Little Endian");elseprintf("Big Endian");⼤⼩端存储问题,如果⼩端⽅式中(i占⾄少两个字节的长度)则i所分配的内存最⼩地址那个字节中就存着1,其他字节是0.⼤端的话则1在i的最⾼地址字节处存放,char是⼀个字节,所以强制将char型量p指向i则p指向的⼀定是i的最低地址,那么就可以判断p中的值是不是1来确定是不是⼩端。
大端和小端概念、区别、转换以及辨别方法解析
大端和小端概念、区别、转换以及辨别方法解析
学习底层编程或逆向的童鞋,肯定对这两个名词并不陌生吧?!今天就给大家介绍一下这两个概念.
科普:
大端和小端,老外叫Big-Endian 和Little-Endian,其实指的都是同一个东东在计算机界,表示数据在存储器中的存放顺序。
不同的CPU、操作系统对待数据的存储方式各有不同,但一般常见的操作系统都是小端,而通讯协议则是大端。
但并不是说系统是小端形式存储,文件就一定要采用小端的形式,不同的应用程序对于自身数据的存储方式也各有千秋(自家数据爱咋放咋放,有些还打乱了加密呢),比如:
* Adobe PS -- 大端
* BMP -- 小端
* GIF -- 小端
* JPEG -- 大端
* MacPaint -- 大端
* RTF -- 小端
那么小端和大端有什么区别呢?举个栗子大家就明了了:
比如0x12345678 这个数:
* 大端法在内存中按字节依次存放为:12 34 56 78
* 小端法在内存中按字节依次存放为:78 56 34 12
解释:
大端:较高的有效字节存放在较低的存储器地址,较低的有效字节存放在较高的存储器地址。
比如整型变量0x12345678 占 4 个字节,那么根据内存地址从小到大它们的存放方式如下:。
深入浅出大端和小端文章一:端模式(Endian)的这个词出自Jonathan Swift书写的《格列佛游记》。
这本书根据将鸡蛋敲开的方法不同将所有的人分为两类,从圆头开始将鸡蛋敲开的人被归为Big Endian,从尖头开始将鸡蛋敲开的人被归为Littile Endian。
小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开。
在计算机业Big Endian和Little Endian也几乎引起一场战争。
在计算机业界,Endian表示数据在存储器中的存放顺序。
下文举例说明在计算机中大小端模式的区别。
如果将一个32位的整数0x12345678存放到一个整型变量(int)中,这个整型变量采用大端或者小端模式在 内存中的存储由下表所示。
为简单起见,本书使用OP0表示一个32位数据的最高字节MSB(Most Significant Byte),使用OP3表示一个32位数据最低字节LSB(Least Significant Byte)。
地址偏移大端模式小端模式0x0012(OP0)78(OP3)0x0134(OP1)56(OP2)0x0256(OP2)34(OP1)0x0378(OP3)12(OP0)如果将一个16位的整数0x1234存放到一个短整型变量(short)中。
这个短整型变量在内存中的存储在大小端 模式由下表所示。
地址偏移大端模式小端模式0x0012(OP0)34(OP1)0x0134(OP1)12(OP0)由上表所知,采用大小模式对数据进行存放的主要区别在于在存放的字节顺序,大端方式将高位存放在低地址,小端方式将 高位存放在高地址。
采用大端方式进行数据存放符合人类的正常思维,而采用小端方式进行数据存放利于计算机处理。
到目前为止,采用大端或者小端进行数据存 放,其孰优孰劣也没有定论。
有的处理器系统采用了小端方式进行数据存放,如Intel的奔腾。
有的处理器系统采用了大端方式进行数据存放,如 IBM半导体和Freescale的PowerPC处理器。
不仅对于处理器,一些外设的设计中也存在着使用大端或者小端进行数据存放的选择。
因此在一个处理器系统中,有可能存在大端和小端模式同时存在的现象。
这一现象为系统的软硬件设计带来了不小的麻烦, 这要求系统设计工程师,必须深入理解大端和小端模式的差别。
大端与小端模式的差别体现在一个处理器的寄存器,指令集,系统总线等各个层次中。
1.1.1 从软件的角度理解端模式从软件的角度上,不同端模式的处理器进行数据传递时必须要考虑端模式的不同。
如进行网络数据传递时,必须要考虑端模 式的转换。
有过Socket接口编程经验的程序员一定使用过以下几个函数用于大小端字节序的转换。
¨ #define ntohs(n) //16位数据类型网络字节顺序到主机字节顺序的转换¨ #define htons(n) //16位数据类型主机字节顺序到网络字节顺序的转换¨ #define ntohl(n) //32位数据类型网络字节顺序到主机字节顺序的转换¨ #define htonl(n) //32位数据类型主机字节顺序到网络字节顺序的转换其中互联网使用的网络字节顺序采用大端模式进行编址,而主机字节顺序根据处理器的不同而不同,如PowerPC处理 器使用大端模式,而Pentuim处理器使用小端模式。
大端模式处理器的字节序到网络字节序不需要转换,此时ntohs(n)=n,ntohl = n;而小端模式处理器的字节序到网络字节必须要进行转换,此时ntohs(n) = __swab16(n),ntohl =__swab32(n)。
__swab16与__swab32函数定义如下所示。
#define ___swab16(x){__u16 __x = (x);((__u16)((((__u16)(__x) & (__u16)0x00ffU) << 8) |(((__u16)(__x) & (__u16)0xff00U) >> 8) ));}#define ___swab32(x){__u32 __x = (x);((__u32)((((__u32)(__x) & (__u32)0x000000ffUL) << 24) |(((__u32)(__x) & (__u32)0x0000ff00UL) << 8) |(((__u32)(__x) &(__u32)0x00ff0000UL) >> 8) |(((__u32)(__x) &(__u32)0xff000000UL) >> 24) ));}PowerPC处理器提供了lwbrx,lhbrx,stwbrx,sthbrx四条指令用于处理字节序的转换以优 化__swab16和__swap32这类函数。
此外PowerPC处理器中的rlwimi指令也可以用来实现__swab16和__swap32这类函 数。
在Linux PowerPC中,定义了一系列有关字节序转换的函数,其详细定义在./include/asm-powerpc/byteorder.h文件中。
程序员在对普通文件进行处理也需要考虑端模式问题。
在大端模式的处理器下对文件的32,16位读写操作所得到的结果 与小端模式的处理器不同。
读者单纯从软件的角度理解上远远不能真正理解大小端模式的区别。
事实上,真正的理解大小端模式的区别,必须要从系统的角度,从指 令集,寄存器和数据总线上深入理解,大小端模式的区别。
1.1.2 从系统的角度理解端模式除了4.2.1节中,软件上对不同端模式编程上的差异,处理器在硬件上也由于端模式问题在设计中有所不同。
从系统的 角度上看,端模式问题对软件和硬件的设计带来了不同的影响,当一个处理器系统中大小端模式同时存在时,必须要对这些不同端模式的访问进行特殊的处理。
PowerPC处理器主导网络市场,可以说绝大多数的通信设备都使用PowerPC处理器进行协议处理和其他控制信 息的处理,这也可能也是在网络上的绝大多数协议都采用大端编址方式的原因。
因此在有关网络协议的软件设计中,使用小端方式的处理器需要在软件中处理端模式 的转变。
而Pentium主导个人机市场,因此多数用于个人机的外设都采用小端模式,包括一些在网络设备中使用的PCI总线,Flash等设备,这也要求 硬件工程师在硬件设计中注意端模式的转换。
本书中的小端外设是指这种外设中的寄存器以小端方式进行存储,如PCI设备的配置空间,NOR FLASH中的寄存器等等。
对于有些设备,如DDR颗粒,没有以小端方式存储的寄存器,因此从逻辑上讲并不需要对端模式进行转换。
在设计中,只 需要将双方数据总线进行一一对应的互连,而不需要进行数据总线的转换。
如果从实际应用的角度说,采用小端模式的处理器需要在软件中处理端模式的转换,因为采用小端模式的处理器在与小端外 设互连时,不需要任何转换。
而采用大端模式的处理器需要在硬件设计时处理端模式的转换。
大端模式处理器需要在寄存器,指令集,数据总线及数据总 线与小端外设的连接等等多个方面进行处理,以解决与小端外设连接时的端模式转换问题。
在寄存器和数据总线的位序定义上,基于大小端模式的处理器有所不同。
一个采用大端模式的32位处理器,如基于E500内核的MPC8541,将其寄存器的最高位msb(most significant bit)定义为0,最低位lsb(lease significant bit)定义为31;而小端模式的32位处理器,将其寄存器的最高位定义为31,低位地址定义为0。
与此向对应,采用大端模式的32位处理器数据总线的最高位为0,最高位为31;采用小端模式的32位处理器的数据总 线的最高位为31,最低位为0。
如图4.5所示。
OP0OP1OP2OP3OP0OP1OP2OP33131图4.5大小端模式处理器的寄存器的定义大端模式处理器寄存器位序定义小端模式处理器寄存器位序定义大小端模式处理器外部总线的位序也遵循着同样的规律,根据所采用的数据总线是32位,16位和8位,大小端处理器外 部总线的位序有所不同。
¨ 大端模式下32位数据总线的msb是第0位,MSB是数据总线的第0~7的字段;而lsb是第31位,LSB是第24~31字段。
小端模式下32位总线的 msb是第31位,MSB是数据总线的第31~24位,lsb是第0位,LSB是7~0字段。
¨ 大端模式下16位数据总线的msb是第0位,MSB是数据总线的第0~7的字段;而lsb是第15位,LSB是第8~15字段。
小端模式下16位总线的 msb是第15位,MSB是数据总线的第15~7位,lsb是第0位,LSB是7~0字段。
¨ 大端模式下8位数据总线的msb是第0位,MSB是数据总线的第0~7的字段;而lsb是第7位,LSB是第0~7字段。
小端模式下8位总线的msb是第 7位,MSB是数据总线的第7~0位,lsb是第0位,LSB是7~0字段。
由上分析,我们可以得知对于8位,16位和32位宽度的数据总线,采用大端模式时数据总线的msb和MSB的位置都 不会发生变化,而采用小端模式时数据总线的lsb和LSB位置也不会发生变化。
为此,大端模式的处理器对8位,16位和32位的内存访问(包括外设的访问)一般都包含第0~7字段,即MSB。
小 端模式的处理器对8位,16位和32位的内存访问都包含第7~0位,小端方式的第7~0字段,即LSB。
由于大小端处理器的数据总线其8位,16位和32位宽度的数据总线的定义不同,因此需要分别进行讨论在系统级别上如 何处理端模式转换。
在一个大端处理器系统中,需要处理大端处理器对小端外设的访问。
1.1.2.1 大端处理器对32位小端外设进行访问大端处理器采用32位总线与小端外设进行访问时,大端处理器的32位数据总线的第0~7位用来处理OP0,第 8~15位用来处理OP1,第16~23位用来处理OP2,第24~31位用来处理OP3。
而32位的小端设备使用数据总线的第31~24位用来处理 OP0,第23~16位用来处理OP1,第15~8位用来处理OP2,第7~0位用来处理OP3。
大端处理器,如MPC8541,使用stw,sth,stb和lwz,lhz,lbz指令对32位的外部设备进行访 问。
在这些指令结束后,存放在外部设备的数据将被读入MPC8541的通用寄存器中。
为保证软件的一致性,当访问结束后,存放在通用寄存器的字节序,即 OP0,OP1,OP2和OP3必须要和存放在小端外设的字节序一致。