Linux Kernel里的cpu_to_le32是干啥的?

Posted by Jason on February 7, 2018

在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确实牛逼啊.