TCP/IP网络结构分为物理层,数据链路层,网络层,传输层,应用层。每一层都有各自独有的数据报文结构,而且数据长度不固定,并且会频繁在头部尾部插入数据包头,校验码等数据信息,从计算机资源效率角度来看,应该尽量避免数据的复制——综上可知,网络协议对系统的存储还是有很高的要求。
在LINUX网络子系统中套接口缓存(SOCKET BUFFER)对应的数据结构为sk_buff,简称为SKB。成员大致分为以下几类:
- 与SKB组织相关的成员变量
- 通用成员变量
- 标志性变量
- 与特征相关的成员变量
在缓冲区头部查入数据意味着改变修改指向缓冲区的指针,内核提供skb_reserve()来完成此功能,做法是会现在缓冲区头部预留部分空间,在上层向下层传递SKB会使用这部分的空间。
struct sk_buff { /* These two members must be first. */ struct sk_buff *next; struct sk_buff *prev; struct sock *sk; struct skb_timeval tstamp; struct net_device *dev; struct net_device *input_dev; union { struct tcphdr *th; struct udphdr *uh; struct icmphdr *icmph; struct igmphdr *igmph; struct iphdr *ipiph; struct ipv6hdr *ipv6h; unsigned char *raw; } h; union { struct iphdr *iph; struct ipv6hdr *ipv6h; struct arphdr *arph; unsigned char *raw; } nh; union { unsigned char *raw; } mac; struct dst_entry *dst; struct sec_path *sp; /* * This is the control buffer. It is free to use for every * layer. Please put your private variables there. If you * want to keep them across layers you have to do a skb_clone() * first. This is owned by whoever has the skb queued ATM. */ char cb[48]; unsigned int len, data_len, mac_len; union { __wsum csum; __u32 csum_offset; }; __u32 priority; __u8 local_df:1, cloned:1, ip_summed:2, nohdr:1, nfctinfo:3; __u8 pkt_type:3, fclone:2, ipvs_property:1; __be16 protocol; void (*destructor)(struct sk_buff *skb);#ifdef CONFIG_NETFILTER struct nf_conntrack *nfct;#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) struct sk_buff *nfct_reasm;#endif#ifdef CONFIG_BRIDGE_NETFILTER struct nf_bridge_info *nf_bridge;#endif#endif /* CONFIG_NETFILTER */#ifdef CONFIG_NET_SCHED __u16 tc_index; /* traffic control index */#ifdef CONFIG_NET_CLS_ACT __u16 tc_verd; /* traffic control verdict */#endif#endif#ifdef CONFIG_NET_DMA dma_cookie_t dma_cookie;#endif#ifdef CONFIG_NETWORK_SECMARK __u32 secmark;#endif __u32 mark; /* These elements must be at the end, see alloc_skb() for details. */ unsigned int truesize; atomic_t users; unsigned char *head, *data, *tail, *end;};以上是sk_buffer的结构,可以看到有许多#ifdef预编译指令,表示只有在编译时定义宏后,特定的成员变量才会起作用,SKB有两部分——SKB结构体,数据缓冲区(head)。
unsigned char *head, *data, *tail, *end;四个指针用来指向线性数据缓冲区及数据部分边界。head和end指向缓存区头与尾,data和tail指向数据区头与尾,每一层协议会在head和tail之间插入协议首部。这片空间便是预留给头部协议的。也可以在tail和end之间添加数据。
那么SKB如何相互组织在一起——通过双向链表。
struct sk_buff *next; struct sk_buff *prev;并且会在sk_buff之前加一个sk_buff_head的结构头节点
struct sk_buff_head { /* These two members must be first. */ struct sk_buff *next; struct sk_buff *prev; __u32 qlen; spinlock_t lock;};__u32 qlen是32位无符号整形的数值表示队列长度,也就是SKB个数,lock是自旋锁。
未完待续........