套接字缓冲区用结构体struct sk_buff表示,它用于在网络子系统中的各层之间传递数据,处于一个核心地位,非常之重要。它包含了一组成员数据用于承载网络数据,同时,也定义了在这些数据上操作的一组函数。下面是其完整的定义:
struct sk_buff {
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;
char cb[48];
unsigned int len,
data_len,
mac_len,
csum;
__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
__u32 nfmark;
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;
#ifdef CONFIG_NET_CLS_ACT
__u16 tc_verd;
#endif
#endif
unsigned int truesize;
atomic_t users;
unsigned char *head,
*data,
*tail,
*end;
};
这是一个比较宠大的结构体,为了便于理解,我们分成多块进行分析。
为了使用套接字缓冲区,内核创建了两个后备高速缓存(looaside
cache),它们分别是skbuff_head_cache和skbuff_fclone_cache,协议栈中所使用到的所有的sk_buff结构都
是从这两个后备高速缓存中分配出来的。两者的区别在于skbuff_head_cache在创建时指定的单位内存区域的大小是sizeof(struct
sk_buff),可以容纳任意数目的struct
sk_buff,而skbuff_fclone_cache在创建时指定的单位内存区域大小是2*sizeof(struct
sk_buff)+sizeof(atomic_t),它的最小区域单位是一对strcut
sk_buff和一个引用计数,这一对sk_buff是克隆的,即它们指向同一个数据缓冲区,引用计数值是0,1或2,表示这一对中有几个sk_buff
已被使用。
创建一个套接字缓冲区,最常用的操作是alloc_skb,它在skbuff_head_cache中创建一个struct sk_buff,如果要在skbuff_fclone_cache中创建,可以调用__alloc_skb,通过特定参数进行。
struct
sk_buff的成员head指向一个已分配的空间的头部,该空间用于承载网络数据,end指向该空间的尾部,这两个成员指针从空间创建之后,就不能被修
改。data指向分配空间中数据的头部,tail指向数据的尾部,这两个值随着网络数据在各层之间的传递、修改,会被不断改动。所以,这四个指针指向共同
的一块内存区域的不同位置,该内存区域由__alloc_skb在创建缓冲区时创建,四个指针间存在如下关系:
head <= data <= tail < end
那指向的这块内存区域有多大呢?一般由外部根据需要传入。外部设定这个大小时,会根据实际数据量加上各层协议的首部,再加15(为了处理对齐)传入,在
__alloc_skb中根据各平台不同进行长度向上对齐。但是,我们另外还要加上一个存放结构体struct
skb_shared_info的空间,也就是说end并不真正指向内存区域的尾部,在end后面还有一个结构体struct
skb_shared_info,下面是其定义:
struct skb_shared_info{
atomic_t dataref; //引用计数。
unsigned short nr_frags; //数据片段的数量。
unsigned short tso_size;
unsigned short tso_segs;
unsigned short ufo_size;
unsigned int ip6_frag_id;
struct sk_buff *frag_list; //数据片段的链表。
skb_frag_t frags[MAX_SKB_FRAGS]; //每一个数据片段的长度。
};
这个结构体存放分隔存储的数据片段,将数据分解为多个数据片段是为了使用分散/聚集I/O。
如果是在skbuff_fclone_cache中创建,则创建一个struct sk_buff后,还要把紧邻它的一个struct
sk_buff的fclone成员置标志SKB_FCLONE_UNAVAILABLE,表示该缓冲区还没有被创建出来,同时置自己的fclone为
SKB_FCLONE_ORIG,表示自己可以被克隆。最后置引用计数为1。
最后,truesize表示缓存区的整体长度,置为sizeof(struct sk_buff)+传入的长度,不包括结构struct skb_shared_info的长度。
阅读(186) | 评论(0) | 转发(0) |