分类: LINUX
2009-11-20 10:47:29
**********The Socket Buffer:sk_buff Structure
sk_buff
layout Fields:
sk_buff位于双向链表之中,指向一个sk_buff_head结构。(一个socket拥有一个这样的链表是吗?)
struct sk_buff_head { struct sk_buff *next; struct sk_buff *prev; __u32 qlen; ......};
next指向第一个sk_buff元素;prev指向最后一个sk_buff元素;qlen记录链表中sk_buff元素数量
next指针:指向下一个sk_buff元素,最后一个则指向sk_buff_head;
prev指针:指向前一个sk_buff元素,第一个则指向sk_buff_head;(指针类型的统一是依靠void*实现的吗?)
list指针:指向sk_buff_head
struct sock *sk:指向拥有该buffer的socket结构,若该buffer仅仅用于forward,则NULL;
unsigned int len:数据域的长度,包括main buffer和fragments中的总和,还包括协议首部长度
unsigned int data_len:在fragments中的data size
unsigned int mac_len:MAC 首部大小
atomic_t users:使用该sk_buff buffer的实体计数器,在free的时候须其为0,否则减值而不free;须为原子操作;
unsigned int truesize:sk_buff的实际大小,即len+sizeof(sk_buff),在len发生变化时,它也要随之更新;
unsigned char *head;unsigned char *end;unsigned char *data;unsigned char *tail;
数据域系列指针,在真实数据的首尾之外还有一段headroom(首部在其中)和tailroom。data和tail指向真实数据域的始末地址,而head和end指向两个room之外的始末地址。
void (*desructor)(...):函数指针,当buffer移除的时候进行的操作(sock_rfree or sock_wfree)。
General Fields:
struct timeval stamp:收包时戳
struct net_device *dev:记录着收包设备,或者发包设备,取决于该包是要发还是刚收
struct net_device *input_dev:记录刚刚所收包从哪个设备来,若该包是本地产生的,则NULL
struct net_device *real_dev:虚拟设备所用,记录其依附的真实设备
union {...} h; union {...} nh; union{...} mac;
分别指向着传输层,路由层,MAC层的包首部地址,这些首部信息之后便是data指针所指地址;在层次之间传递时,这些指针会随着首部的增减而发生变化。
struct dst_entry dst:用于routing subsystem,以后介绍
char cb[40]:“control buffer”和private information记录
unsigned int csum, unsigned char ip_summed:checksum和associated status flag,以后介绍
unsigned char cloned:boolean flag,打开表明本结构是另一个sk_buff的clone
unsigned char pkt_type:根据MAC目标地址的类别信息,可取值定义在
__u32 priority:用于QoS;包产生时,由socket层来定义该值;包传递时,由rt_tos2priority函数来确定该值;
unsigned short protocol:MAC层的上一层的协议类型(IP,IPv6,ARP,
Management Functions
(do_someting 是 __do_something 的wrapper,添加了一些sanity check或 locking mechanisms;内层函数通常是不直接调用的)
alloc_skb:allocation of buffers的主函数,它的工作包括data buffer和sk_buff struct二者的allocation;
它通过kem_cache_alloc(原型?)函数从cache中获得一个sk_buff结构,再通过kmalloc获得一个data buffer
skb = kmem_cache_alloc(skbuff_head_cache, gfp_mask & ~_ _GFP_DMA);
... ... ...
size = SKB_DATA_ALIGN(size);
data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
skb_shared_info结构用于处理IP fragments,以后介绍
dev_alloc_skb:提供给device drivers在interrupt mode下使用,它里面会调用alloc_skb。
kfree_skb & dev_kfree_skb: 用于free sk_buff,仅当skb->users为1时,才会真正释放cache,否则users--;
sk_buff 可以具有一个dst_entry结构,利用dst_release函数来free之;另外destructor函数指针初始化的话也在此调用。
skb_put(a), skb_push(b), skb_pull(c),skb_reserve(d)
TCP会在headroom预留足够的空间用于存放所有层的首部(TCP,IP,link layer),MAX_TCP_HEADER表示最大的所有层首部大小(考虑最坏情况);TCP会将payload拷贝到自己的buffer中并加上自己的header,然后向下发并让各层加上首部;以下各层会更新skb->data和skb->len,如图:
skb_shared_info & skb_shinfo:位于data block的尾部之后
struct skb_shared_info {
atomic_t dataref; //表示该datablock的user数量,与cloning有关
unsigned int nr_frags; //与IP fragments有关
unsigned short tso_size;
unsigned short tso_seqs;
struct sk_buff *frag_list; //与IP fragments有关
skb_frag_t frags[MAX_SKB_FRAGS]; //与IP fragments有关
};
#define skb_shinfo(SKB) ((struct skb_shared_info *)((SKB)->end))
该函数用于引用skb_shared_info结构
skb_clone函数:当一个data buffer需要供给多个对象使用时,使用该函数可以产生一个sk_buff结构拷贝,它们均指向同一个data域(包括head,data,sk_shared_info,其他data fragments);需要增加sk_shared_info->dataref;skb->cloned在两者中均设置为1,skb->users在副本中设置为1;该操作之后data块不能被写(即无需锁,但之前data块可写么?应该是可写的)
pskb_copy & skb_copy:因为clone操作造成无法写data,这两个操作实现data块的拷贝并且可写;
当仅需修改skb->start到skb->end之间内容时,使用pskb_copy;
当还需要修改其他data分片的内容时,使用skb_copy;
list management functions:用于处理sk_buff_head和相应的sk_buff队列的一系列函数,它们都必须是原子操作。
skb_queue_head_init: 初始化sk_buff_head,空队列;
skb_queue_head & skb_quere_tail: 把一个sk_buff加到头部或尾部;
skb_dequeue & skb_dequeue_tail: 从头部或尾部移除一个sk_buff;
skb_queue_purge: 清空队列
skb_queue_walk: 循环访问队列的每个元素