Linux中iphdr、tcphdr结构体的__LITTLE_ENDIAN_BITFIELD和__BIG_ENDIAN_BITFIELD

本文探讨了在Linux内核头文件中,为何要在`iphdr`和`tcphdr`结构体中考虑字节序差异。通过示例代码,解释了在大端和小端字节序环境下,直接使用memcpy操作位域可能会导致不同结果,从而强调了预处理指令在确保跨平台兼容性方面的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代码

在/usr/include/uapi/linux/ip.h和/usr/include/uapi/linux/tcp.h中分别有如下代码定义了ip以及tcp头部
iphdr:

struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	__u8	ihl:4,
			version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
	__u8	version:4,
  			ihl:4;
#else
#error	"Please fix <asm/byteorder.h>"
#endif
	__u8	tos;
	__be16	tot_len;
	__be16	id;
	__be16	frag_off;
	__u8	ttl;
	__u8	protocol;
	__sum16	check;
	__be32	saddr;
	__be32	daddr;
	/*The options start here. */
};

tcphdr:

struct tcphdr {
        __u16        source;
        __u16        dest;
        __u32        seq;
        __u32        ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)
        __u16   res1:4,
                doff:4,
                fin:1,
                syn:1,
                rst:1,
                psh:1,
                ack:1,
                urg:1,
                ece:1,
                cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
        __u16   doff:4,
                res1:4,
                cwr:1,
                ece:1,
                urg:1,
                ack:1,
                psh:1,
                rst:1,
                syn:1,
                fin:1;
#else
#error        "Adjust your <asm/byteorder.h> defines"
#endif       
        __u16        window;
        __u16        check;
        __u16        urg_ptr;
};

这其中:4等是C语言中的位域,表示取二进制中的低四位(在大端序中这个低四位是存储在高地址的)。

疑问

初读这些代码,我还并不理解为何有大小端的区别,"big endian"和"little endian"的区别是在按字节之间的存储顺序上。比如0x12345678
在"little endian"上表示为(假设基址为0x100):

0x100 0x78 (01111000)
0x101 0x56
0x102 0x34
0x103 0x12

在“big endian"上表示为:

0x100 0x12
0x101 0x34
0x102 0x56
0x103 0x78 (01111000)

显然对单个字节来说,其中的位存储还是相同的,所以我并不理解为何在Linux的代码中,要把它们一些字节的位域定义成相反的顺序。

解答

经过我搜集资料后终于找到了答案:

譬如ip头部ip_hdr,假设仅仅是直接对ihl或者version进行操作,确实无需分辨大小端字节序,Linux代码中那样写反而有些多此一举。

但使用者可能使用memcpy来直接对这开头的8位进行赋值操作,而这在大端序和小端序的机器上会产生不同的情况。

举个经典的例子,比如说下述代码:

u_int16_t t = 0x1;
u_int8_t x[2];
memcpy(x, t);

在小端序的机器上结果应该是

x[0] x[1]
10 … 00

而在大端序的机器上执行结果会变为:

x[0] x[1]
00 … 01

注:x[1]的地址都是比x[0]高的。

因此为了提高兼容性,使程序能够被小端序和大端序的机器共用。须要预先推断是大端序还是小端序。并调换ihr和version在内存中的位置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值