最近家里面没有了网络,所以写文章的次数也少了。所以,暂时只能利用一下公司加班的时间,补充一下最近的心得。曾经有一段时间,自己对字节序和位序不是很清楚。所以,前几天找了几本书,同时做了一些练习,算是把这个问题弄清除了。
a)字节序
所谓字节序,其实就是指cpu的大小端。大家平常接触到的cpu一般都是小端类型的,比如arm、x86、mips等等。大端的类型的cpu也有,如果有朋友是做通讯设备的,那么他所接触到的cpu一般就是powerpc,而powerpc一般都是大端类型的。当然,在cpu设计的时候,LITTLE ENDIAN还是BIG ENDIAN,其实都是可以灵活配置的,我说的也只是普通的情况。举例来说,
如果一个数据为0x12345678,那么对于大端cpu,它在ddr中的保存形式就是12 34 56 78。而对于小端的cpu来说,它的保存形式是 78 56 34 12。如果数据只是给自己用,不需要和外界交互,那么其实无所谓大端和小端。但是对于某些设备,这种情况却要分清楚一些。特别是自己的机器和外界设备交互的时候,势必会涉及到各种packet,而packet中也必然会涉及到各种control word。当然,如果不巧控制字是32位或者64位类型的,那么还是会涉及到大小端的问题。不同的packet在各个设备之间传输,甚至通过路由器传输到地球的另一端,这都是可能的。对于处理packet的设备,这中间肯定有大端cpu设备,也有小端cpu设备。我们可以假设packet都是按照小端类型存储的。所以,大家可以灵活地根据cpu类型作出调整。在linux系统上,有一个很灵活的macro,即le32_to_cpu帮助我们解决了这个问题。如果你是小端cpu,什么也不要做;然是如果你是大端cpu,那么就要做一些数据的调整了。
b) 位序
同样是packet报文,有的时候我们需要把一些控制字组成8位或者32位类型,首先,我们拿8位类型举例,
struct word {
#ifdef LITTLE_ENDIAN
u8 a:3;
u8 b:3;
u8 c:2;
#endif
#ifdef BIG_ENDIAN
u8 c:2;
u8 b:3;
u8 a:3;
#endif
};
如果是u8类型,即数据是按照8位形式保存的,那么其实这样使用就可以了。字节序本身只对byte有意义,对于位序没什么影响。当然位序也和cpu有关,小端cpu保存数据是按照从小到大排列的,而大端数据是按照从大到小排列的。所以不管是大端cpu还是小端cpu,在ddr中这个数据的形式都是唯一的。但如果是32位数据呢,
struct word {
#ifdef LITTLE_ENDIAN
u32 a:4;
u32 b:8;
u32 c:4;
u32 d:8;
u32 e:8;
#endif
#ifdef BIG_ENDIAN
u32 e:8;
u32 d:8
u32 c:4;
u32 b:8;
u32 a:4;
#endif
};
这个数据明显要比上面的数据复杂一下。但是大家只要记住基本的原则就可以了。那就是,大端cpu从大到小排列,而小端cpu是从小到大排列。所以,大家可以考虑一下,这个数据在小端cpu和大端cpu中分别是怎么排列的?我们可以4位、4位的来判断,如果是小端cpu,应该是这样的
b1、 a || c、 b2 || d2、 d1 || e2、 e1
大端cpu呢?
e2、 e1 || d2、 d1 || c、 b2 || b1、 a
不知道,大家看出什么差别来没?其实这两个数据除了字节序不同之外,在每一个byte中数据的相对位置都是一样的。所以在处理位序的时候,我们只需要在定义控制字的时候按照逆向排列数据,就可以得到符合cpu需要的数据了。当然,这个数据如果需要传输的话,以packet类型是小端为例,还是需要进行cpu_to_le32的转换的。只要灵活运用这些实例,就可以判析字节序和位序的关系了。