sk_buff 有些字段只是为了方便搜寻以及组织数据结构本身,内核在一个双向链表中维护所有的sk_buff结构,但是该表的组织比传统的双向链表更为复杂。
像任何双向链表一样,通过每个sk_buff 结构中的next和prev字段实现联系,next字段只想前,prev指向后,但是,这个表还有另一项必要需求,每个sk_buff 结构必须能够快速找到整个表的头,为了实现这个,在表的开端额外增加一个sk_buff_head 结构作为一种哑元元素,sk_buff_head结构是,
struct sk_buff_head {
struct sk_buff *next;
struct sk_buff *prev;
__u32 qlen;
spinlock_t lock;
}
qlen 代表表中的元素的数目,lock是用于防止对表的并发访问,在本章稍后的表管理函数,一节中会予以描述。
sk_buff和sk_buff_head 的前两个元素是相同的,next和prev指针,尽管sk_buff_head与sk_buff 相比实在太小,但是还是允许两个结构共同存在一个表中。
另外,同样的函数也可以用于操作sk_buff和sk_buff_head二者。
每个sk_buff 结构都包涵一个指针,指向单一的sk_buff_head 结构,增加了其复杂性,这个指针的字段名称为list, 图2-1 有助于你了解这些数据结构的关系。
sk_buff其他感兴趣的字段如下:
struct sock *sk
这是一个指针,指向拥有此缓冲区的套接字的sock数据结构,当数据在本地产生或者正由本地的进程接收时,就需要这个指针。因为该数据以及套接字相关的信息会由L4(TCP或者UDP)以及用户应用程序使用,当缓冲区只是被转发时,该指针就是NULL。
unsigned int len
这是指缓冲区中数据区块的大小。这个长度包括主要缓冲区的数据以及一些片段的数据,当缓冲区从一个网络分层移往下一个网络分层的时候,值就会变化,因为在协议栈重往上移动时报头就会被丢弃,但是往下移动的时候,就会添加进来,len也会吧协议报头算在内,如数据预留和对齐,skb_reserve,skb_put, skb_push 以及skb_pull 一节中国呢的图2-8所示。
unsigned int data_len
与len不同的是,data_len 只计算片段中的数据大小。
unsigned int mac_len
MAC报头的大小。
atomic_t user
这是引用计数,或者使用这个sk_buff缓冲区的实例的树木,这个参数的主要用途是避免在某人依然使用sk_buff结构时,把这个结构给释放,因此,此缓冲区的每个用户在必要时,都要递增和递减此字段,此计数器只计算sk_buff数据结构的用户,此缓冲区所包含的实际数据由一个相似的字段dataref 所包含,本章稍后的skb_shared_info 结构和skb_shinfo 函数,一节将予以介绍。
users 有时会直接用atomic_inc和atomic_dec函数递增和递减,但是大多数情况下,采用skb_get和kfree_skb 进行处理。
unsigned int truesize
此字段代表此缓冲区总的大小,包括sk_buff结构本身,当此缓冲区得到所分配的len个字符的数据请求空间时,此字段的初始化由alloc_skb函数设置成len + sizeof(sk_buff)
struct sk_buff *alloc_skb(unsigned int size, int gfp_mask)
{
skb->truesize = size + sizeof(struct sk_buff);
}
每当skb->len的值增加时,此字段就会得到更新。
unsigned char *head;
unsigned char *end;
unsigned char *data;
unsigned char *tail
这些字段代表缓冲区边界一起其中的数据,每当一个分层为其工作而准备缓冲区时,可能会为一个报头或者更多的数据分配更多的空间。head和end指向已经分配缓冲区空间的开端和尾端,而data和tail则指向实际数据的开端和尾端,参见图2-2所示,然后,该分层可以吧head和data之间的空隙填上一个协议报头,或者以新数据填入tail和head之间的空隙。在稍后的分配内存,alloc_skb 和dev_alloc_skb 一节中就会知道,图2-2右边的缓冲区在底端包含一个附加报头。
void (*destructor)(...)
此函数可以被初始化为一个函数,当此缓冲区被删除时,可完成某些工作,当此缓冲区不属于一个套接字时,destructor 通常不湖北初始化,此缓冲区属于一个套接字时,通常吧诶设置成sock_kfree或者sock_wfree ,这两个sock_xxx函数可用于更新套接字队列中所持有的内存。