在kernel里面经常能看见把十六进制数用下面几个函数转换一下,比如be32_to_cpu, cpu_to_be32, cpu_to_le16,cpu_to_le32等.其实很简单.
名词解释
le叫做little endian, be叫做big endian,这是两种字节序,分别称为小段和大端.
le表示地址低为存储值的低位,地址高位存储值的高位.
be表示地址低位存储值的高位,地址高位存储值的低位.
不用cpu使用了不同的字节序,比如PowerPC系列cpu就用了大端模式,而ARM和x86就用的小端模式.
因此,对于不用的cpu,上面几个函数的执行结果也是不一样的.
但是,凡是xx_to_cpu就说明结果是给cpu使用的.反之,cpu_to_xx就说明从cpu的字节序转换成目标字节序.
如果cpu本身就是小端模式,那么cput_to_le32这类函数就会do nothing.
如何实现
下面以cpu_to_le32为例,看看内核是如何转换的.
//generic.h
#define cpu_to_le32 __cpu_to_le32
//cpu如果是小端模式,do nothing
#define __cpu_to_le32(x) ((__force __le32)(__u32)(x))
//cpu如果是大端模式,交换
#define __cpu_to_le32(x) ((__force __le32)__swab32((x)))
/**
* __swab32 - return a byteswapped 32-bit value
* @x: value to byteswap
*/
#define __swab32(x) \
(__builtin_constant_p((__u32)(x)) ? \
___constant_swab32(x) : \
__fswab32(x))
//通过与运算和位移操作实现高低位交换操作
#define ___constant_swab32(x) ((__u32)( \
(((__u32)(x) & (__u32)0x000000ffUL) << 24) | \
(((__u32)(x) & (__u32)0x0000ff00UL) << 8) | \
(((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | \
(((__u32)(x) & (__u32)0xff000000UL) >> 24)))
kernel真是博大精深,一直以来都是专心调驱动,对这些细节还从来没仔细研究过,当仔细看过之后,不得不佩服Kernel的contributor确实牛逼啊.