ntoh hton __builtin_constant_p ___constant_swab __fswab简介

研究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. 

You may also use  __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. 

Previous versions of GCC did not accept this built-in in data initializers. The earliest version where it is completely safe is 3.0.1.

-----------------------------------------------------------------------------↑__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大小端的转换流程就分析完了。

你可能感兴趣的:(网络协议,gcc,ipv4,编译器,linux内核)