sk_buff学习总结

sk_buff 源文件:linux-2.6.37/ include/ linux/ skbuff.h

                           linux-2.6.37/ include/ linux/ skbuff.c

sk_buff数据结构:

/* struct sk_buff - socket buffer */
struct sk_buff {
        /* These two members must be first */
        struct sk_buff *next ; /* Next buffer in list */
        struct sk_buff *prev ; /* Previous buffer in list */

        ktime_t tstamp ; /* Time we arrived,记录接收或发送报文的时间戳*/

        struct sock *sk ; /* Socket we are owned by */

        /* Device we arrived on / are leaving by
         * 通过该设备接收或发送,记录网络接口的信息和完成操作
          */
        struct net_device *dev ;

        /* This is the control buffer. It is free to use for every
         * layer. Please put your private variables there.
         */
        char cb[48] __aligned (8) ;
        ...
        /* data_len为分页数据所包含的全部报文长度
          * len为某时刻的报文总长度
          * 那么,线性数据的长度为:skb->len - skb->data_len
        */
        unsigned int len , data_len ;

        /* 保存了下一个协议层的信息,在处理报文时由当前协议层设置 */
        __be16 protocol ; 
        ...
        /* head指向线性数据区的开始
         * data指向驻留线性数据区中数据的起始位置
         */
        unsigned char *head , *data ;
        ...
        /* 协议头表示 */
        sk_buff_data_t  transport_header ; /* 传输层协议头 */
        sk_buff_data_t  network_header ; /* 网络层协议头 */
        sk_buff_data_t  mac_header ; /* 链路层协议头 */

        sk_buff_data_t tail ; /* 指向驻留在线性数据区的最后一字节数据*/
        sk_buff_data_end ; /* 指向线性数据区的结尾,确保不超出可用存储缓冲区 */
        atomic_t users ; /* 引用该sk_buff的数量*/

        /* 该缓冲区所分配的总内存,包括sk_buff结构大小 + 数据块大小 (应该不包括分页大小?)*/
        unsigned int truesize ;
}

/* This data is invariant across clones and lives at 
 * the end of the header data, ie. at skb->end.
 */
struct skb_shared_info {
        /* number of fragments belonged to this sk_buff 
         * 此sk_buff分页段的数目,它表示frags[]数组的元素数量,该数组包含sk_buff的分页数据
          */
        unsigned short nr_frags; 

        ...

        /* 指向其分段列表,此sk_buff的总长度为frag_list链表中每个分段长度(skb->len)的和,
          * 再加上原始的sk_buff的长度
          * 通过此域可进行报文分段!!
          */
        struct sk_buff *frag_list ;

        /* 
         * Warning : all fields before dataref are cleared in __alloc_skb()
         * 此sk_buff被引用的次数
          */
        atomic_t dataref ;

        /* 
         * must be last field
         * 分段的数组,包含sk_buff的分页数据
          */
         skb_frag_t frags[MAX_SKB_FRAGS] ;
}

/* To allow 64K frame to be packed as single skb without frag_list 
 * 允许小于64K的数据不用分段,即不适用frag_list
 */
#define MAX_SKB_FRAGS (65536 / PAGE_SIZE + 2 )

typedef struct skb_frag_struct skb_frag_t ;
struct skb_frag_struct {
        struct page *page ; /* 该页的虚拟地可用page_address()得到*/

#if (BITS_PER_LONG > 32) || (PAGE_SIZE >= 65536)
        __u32 page_offset ;
        __u32 size;
#else
        __u16 page_offset ;
        __u16 size ;
#endif
};

buff 结构体非常重要,它的含义为“套接字缓冲区”,用于在Linux 网络子系统中的各层之间传递数据,是Linux网络子系统数据传递的“中枢神经”。当发送数据包时,Linux 内核的网络处理模块必须建立一个包含要传输的数据包的sk_buff,然后将sk_buff 递交给下层,各层在sk_buff 中添加不同的协议头直至交给网络设备发送。同样地,当网络设备从网络媒介上接收到数据包后,它必须将接收到的数据转换为sk_buff 数据结构并传递给上层,各层剥去相应的协议头直至交给用户。


1.套接字缓冲区成员
参看 linux/skbuff.h中的源代码,sk_buff结构体包含的主要成员如下
(1)各层协议头h、nh和mac。
sk_buff结构体中定义了3个协议头以对应于网络协议的不同层次,这3 个协议头为传输层TCP/UDP(及ICMP和IGMP)协议头h、网络层协议头nh和链路层协议头mac。这3个协议头数据结构都被定义为联合体。

 union{
   struct tcphdr *th; /* TCP头部*/
   struct udphdr *uh; /* UDP头部*/
   struct icmphdr *icmph; /* ICMP头部*/
   struct igmphdr *igmph; /* IGMP头部*/
   struct iphdr *ipiph; /* IP头部*/
   struct ipv6hdr *ipv6h; /* IPv6头部*/
   unsigned char *raw; /* 数据链路层头部*/
 }h;

 union{
   struct iphdr *iph; /* IP头部*/
   struct ipv6hdr *ipv6h; /* IPv6头部*/
   struct arphdr *arph; /*ARP头部*/
   unsigned char *raw; /* 数据链路层头部*/
 }nh;

 union{
   unsigned char *raw; /* 数据链路层头部*/
 } mac;

(2)数据缓冲区指针head、data、tail和end。

Linux 内核必须分配用于容纳数据包的缓冲区,sk_buff结构体定义了4 个指向这片缓冲区不同位置的指针head、data、tail和end。head 指针指向内存中已分配的用于承载网络数据的缓冲区的起始地址,sk_buff和相关数据块在分配之后,该指针的值就被固定了。data 指针则指向对应当前协议层有效数据的起始地址。每个协议层的有效数据含义并不相同,各层的有效数据信息包含的内容如下。
l 对于传输层而言,用户数据和传输层协议头属于有效数据。
l 对于网络层而言,用户数据、传输层协议头和网络层协议头是其有效数据。
l 对于数据链路层而言,用户数据、传输层协议头、网络层协议头和链路层头部都属于有效数据。
因此,data 指针的值需随着当前拥有sk_buff的协议层的变化进行相应的移动。tail指针则指向对应当前协议层有效数据负载的结尾地址,与data指针对应。end 指针指向内存中分配的数据缓冲区的结尾,与head 指针对应。和head 指针一样,sk_buff被分配之后,end指针的值也就固定不变了。很显然,有效数据必须位于分配的数据缓冲区内,即(data,tail)区间位于(head,end)区间内。因此,head、data、tail和end 这4 个指针间存在如下关系:
head <- data <- tail <- end。


2.套接字缓冲区操作
下面我们来分析套接字缓冲区涉及到的操作函数,Linux 套接字缓冲区支持分配、
释放、指针移动等功能函数。
(1)分配。
Linux 内核用于分配套接字缓冲区的函数有:

struct sk_buff *alloc_skb(unsigned int len,int priority);
struct sk_buff *dev_alloc_skb(unsigned int len);

alloc_skb()函数分配一个套接字缓冲区和一个数据缓冲区,参数len为数据缓冲区的空间大小,以16 字节对齐,参数priority为内存分配的优先级。

dev_alloc_skb()函数只是以GFP_ATOMIC优先级(代表分配过程不能被中断)调用上面的alloc_skb()函数,并保存skb->head和skb->data 之间的16个字节。分配成功之后,因为还没有存放具体的网络数据包,所以sk_buff的data、tail指针都指向存储空间的起始地址head,而len的大小则为0。


(2)释放。
Linux 内核用于释放套接字缓冲区的函数有:

void kfree_skb(struct sk_buff *skb);
void dev_kfree_skb(struct sk_buff *skb);
void dev_kfree_skb_irq(struct sk_buff *skb);
void dev_kfree_skb_any(struct sk_buff *skb);
上述函数用于释放被alloc_skb()函数分配的套接字缓冲区和数据缓冲区。Linux 内核内部使用kree_skb()函数,而网络设备驱动程序中则必须用
dev_kfree_skb()、dev_kfree_skb_irq()或dev_kfree_skb_any()函数进行套接字缓冲区的释放。其中,dev_kfree_skb()函数用于非中断上下文,dev_kfree_skb_irq()函数用于中断上下文,而dev_kfree_skb_any()函数则在中断和非中断上下文中皆可采用。


(3)指针移动。
Linux 套接字缓冲区中的数据缓冲区指针移动操作包括put(放置)、push(推)、pull(拉)、reserve(保留)等。
① put 操作

数据缓冲区指针put 操作以下列函数完成:

unsigned char *skb_put(struct sk_buff *skb, unsigned int len);
unsigned char *_ _skb_put(struct sk_buff *skb, unsigned int len);
上述函数将tail 指针下移,增加sk_buff 的len 值,并返回skb->tail 的当前值。skb_put()和__ skb_put()的区别在于前者会检测放入缓冲区的数据,而后者不会检查。这两个函数主要用于在缓冲区尾部添加数据。

② push操作

数据缓冲区指针put 操作以下列函数完成:

unsigned char *skb_push(struct sk_buff *skb, unsigned int len);
unsigned char *_ _skb_push(struct sk_buff *skb, unsigned int len);
与skb_put()和_ _skb_put()不同,skb_push()和_ _skb_push()会将data 指针上移,因此也要增加sk_buff 的len 值。push 操作在存储空间的头部增加一段可以存储网络数据包的空间,而put 操作则在存储空间的尾部增加一段可以存储网络数据包的空间,因此主要用于在数据包发送时添加头部skb_push()与_ _skb_push()的区别和skb_put()和_ _skb_put()的区别类似。
③ pull操作
数据缓冲区指针pull操作以下列函数完成:

unsigned char * skb_pull(struct sk_buff *skb, unsigned int len);
skb_pull()函数将data 指针下移,并减小skb 的len值。这个操作一般用于下层协议向上层协议移交数据包,使data 指针指向上一层协议的协议头。
④ reserve操作
数据缓冲区指针reserve操作以下列函数完成:

void skb_reserve(struct sk_buff *skb, unsigned int len);
skb_reserve()函数将data 指针和tail指针同时下移,这个操作主要用于在存储空间的头部预留len长度的空隙。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值