研究IPV4的过程中发现了几个与大小端相关的宏,好奇的我找了些资料,简单的分析一下数据根据CPU大小端序的大小端转换。
源码分析使用linux3.6.0内核代码。
本文转自:http://blog.csdn.net/jiebaoabcabc/article/details/38704175
linux网络协议栈相关代码中经常出现ntoh或hton的宏,n代表(net),h代表(host),那么ntoh就是将网络大端序数据转换成本机的大小端序的过程,hton就是将本机的大小端序数据转换成网络的大端序过程。
它们的定义在/include/linux/byteorder/generic.h中
#define htonl(x) ___htonl(x)
#define ntohl(x) ___ntohl(x)
#define htons(x) ___htons(x)
#define ntohs(x) ___ntohs(x)
#define ___htonl(x) __cpu_to_be32(x)
#define ___htons(x) __cpu_to_be16(x)
#define ___ntohl(x) __be32_to_cpu(x)
#define ___ntohs(x) __be16_to_cpu(x)
htonl的'l'代表(long),这里的long指定为32位类型,htons的's'代表(short),即16位数据类型。从宏定义中也可以清楚的辨别它们的转换方式。
我们继续展开,这里就有CPU大小端的区分了,如果是大端的CPU,__cpu_to_be32这类宏被定义在/include/linux/byteorder/big_endian.h中
#define __cpu_to_be64(x) ((__force __be64)(__u64)(x)) // CPU本来就是大端 只有简单的编译器扩展属性检查操作和强制类型转换
#define __be64_to_cpu(x) ((__force __u64)(__be64)(x))
#define __cpu_to_be32(x) ((__force __be32)(__u32)(x))
#define __be32_to_cpu(x) ((__force __u32)(__be32)(x))
#define __cpu_to_be16(x) ((__force __be16)(__u16)(x))
#define __be16_to_cpu(x) ((__force __u16)(__be16)(x))
其中__force编译器属性是提醒编译器变量可以进行强制转换,__be64属性是提醒编译器确保变量是相同的位方式,即给变量贴上大小端序的标签,编译器会在恰当的时机提醒对这些变量的操作。
在大端序的CPU中这些大端序的变量将仅仅是进行强制类型转换,不会进行端序的转换。
如果是小端的CPU,__cpu_to_be32这类宏被定义在/include/linux/byteorder/little_endian.h中
#define __cpu_to_be64(x) ((__force __be64)__swab64((x)))
#define __be64_to_cpu(x) __swab64((__force __u64)(__be64)(x))
#define __cpu_to_be32(x) ((__force __be32)__swab32((x)))
#define __be32_to_cpu(x) __swab32((__force __u32)(__be32)(x))
#define __cpu_to_be16(x) ((__force __be16)__swab16((x)))
#define __be16_to_cpu(x) __swab16((__force __u16)(__be16)(x))
在小端序的CPU中,宏定义多了一个__swab转换端序的宏,它的作用是进行大小端的转换,那么它是如果进行转换的呢,我们继续展开。
__swab64被定义在/include/linux/swab.,h中
#define __swab32(x) \
(__builtin_constant_p((__u32)(x)) ?\
___constant_swab32(x) :\
__fswab32(x))
其中由多出来几个宏和函数,__builtin_constant_p函数是Gcc内建函数(虽然我在gcc4.7.2中没找到它- -),gcc对它的描述是:
-----------------------------------------------------------------------------↓__builtin_constant_p↓---------------------------------------------------------------------------------------
int __builtin_constant_p (exp);
You can use the built-in function __builtin_constant_p
to determine if a value is known to beconstant at compile-time and hence that GCC can perform constant-folding on expressions involving that value. The argument of the function is the value to test. The function returns the integer 1 if the argument is known to be a compile-time constant and 0 if it is not known to be a compile-time constant. A return of 0 does not indicate that the value is not a constant, but merely that GCC cannot prove it is a constant with the specified value of the '-O' option.
If you have some complex calculation, you may want it to be folded if it involves constants, but need to call a function if it does not. For example:
#define Scale_Value(X) \ (__builtin_constant_p (X) \ ? ((X) * SCALE + OFFSET) : Scale (X))You may use this built-in function in either a macro or an inline function. However, if you use it in an inlined function and pass an argument of the function as the argument to the built-in, GCC will never return 1 when you call the inline function with a string constant or compound literal (see Compound Literals ) and will not return 1 when you pass a constant numeric value to the inline function unless you specify the '-O' option.
__builtin_constant_p
in initializers for static data. For instance, you can write
static const int table[] = { __builtin_constant_p (EXPRESSION) ? (EXPRESSION) : -1, /* ... */ };This is an acceptable initializer even if EXPRESSION is not a constant expression. GCC must be more conservative about evaluating the built-in in this case, because it has no opportunity to perform optimization.
-----------------------------------------------------------------------------↑__builtin_constant_p↑---------------------------------------------------------------------------------------
根据资料描述,这个函数最大的用处是判断输入值是不是一个常数,如果是则返回1,不确定是不是常数则返回0,GCC会很保守的判断输入值。
回到__swab32,这个宏先判断输入值是不是一个常数,为什么要判断是不是一个常数呢,这关系到后面执行的方式了。
我们先探究下___constant_swab32宏和__fswab32内联函数:
#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)))
___constant_swab32宏,顾名思义,预编译阶段将处理完的工作,如果输入是一个常量,它完全可以在预编译阶段完成转换,相当于是给代码的优化,而它的端序转换方式也是很简单理解的掩码移位。
static inline __attribute_const__ __u32 __fswab32(__u32 val)
{
#ifdef __arch_swab32 // 如果定义过平台交换函数 就使用平台函数
return __arch_swab32(val);// 平台交换函数
#else
return ___constant_swab32(val);// 通用交换
#endif
}
__fswab32函数则有两种处理方式,如果定义过平台级的交换函数,则使用速度更快的、使用汇编代码的平台函数,如果没有定义过这类函数,则使用通用的掩码移位宏来进行转换。
至此,整个ntoh大小端的转换流程就分析完了。