sk_buff结构和函数分析

在wlan驱动中,数据读取写入是通过sk_buff这个结构体,而sk_buff结构主要作用是包含接收的缓冲数据,和它的包头信息。

如下是sk_buff的主要结构成员:

  1. struct sk_buff {
  2. ...
  3. unsigned char *head;
  4. unsigned char *data;
  5. unsigned char *tail;
  6. unsigned char *end;
  7. ...
  8. };

当然,sk_buff还包含一些链表的东西,在此处并不做解析。在以上结构体中:

  • head:报文缓冲区的头;
  • data:数据的头指针;
  • tail:数据的尾指针;
  • end :报文缓冲区的尾部。

如图所示,

分别有三个空间:head roompacket datatail room。其中packet data是数据所在区包括数据的包头,head room是数据头部增长的预留空间、tail room是尾部增加的空间。预留头部使用skb_reserve(skb, header_len);函数,尾部增加使用skb_put(),头部增长使用skb_push()

以下图是调用分配空间函数,即初始化函数alloc_skb(len, GFP_KERNEL)的样子:

可以看到,headdatatail都指向了缓冲区的起始,而end指向结束。这个SKB的数据长度为0,不包含任何数据。

接着调用了skb_reserve(skb, header_len)预留了头部数据,如图所示:

3.4.4  数据预留和对齐

数据预留和对齐主要由skb_reserve()、skb_put()、skb_push()以及skb_pull()这几个函数来完成。

1.skb_reserve()

skb_reserve()在数据缓存区头部预留一定的空间,通常被用来在数据缓存区中插入协议首部或者在某个边界上对齐。它并没有把数据移出或移入数据缓存区,而只是简单地更新了数据缓存区的两个指针-分别指向负载起始和结尾的data和tail指针,图3-15 展示了调用skb_reserve()前后这两个指针的变化。

请注意:skb_reserve()只能用于空的SKB,通常会在分配SKB之后就调用该函数,此时data和tail指针还一同指向数据区的起始位置,如图3-15a所示。例如,某个以太网设备驱动的接收函数,在分配SKB之后,向数据缓存区填充数据之前,会有这样的一条语句skb_reserve(skb, 2),这是因为以太网头长度为14B,再加上2B就正好16字节边界对齐,所以大多数以太网设备都会在数据包之前保留2B。

当SKB在协议栈中向下传递时,每一层协议都把skb->data指针向上移动,然后复制本层首部,同时更新skb->len。这些操作都使用图3-15 中所示的函数完成。

 
图3-15  在接收过程中使用skb_reserve()
a) 空的SKB  b) 头部预留2个字节  c) 复制以太网帧到SKB

2.skb_push()

skb_push()在数据缓存区的前头加入一块数据,与skb_reserve()类似,也并没有真正向数据缓存区中添加数据,而只是移动数据缓存区的头指针data和尾指针tail。数据由其他函数复制到数据缓存区中。

函数执行步骤如下:

1)当TCP发送数据时,会根据一些条件,如TCP最大分段长度MSS、是否支持聚合分散I/O等,分配一个SKB。

2)TCP需在数据缓存区的头部预留足够的空间,用来填充各层首部。MAX_TCP_HEADER是各层首部长度的总和,它考虑了最坏的情况:由于TCP层不知道将要用哪个接口发送包,它为每一层预留了最大的首部长度,甚至还考虑了出现多个IP首部的可能性,因为在内核编译支持IP over IP的情况下,会遇到多个IP首部。

3)把TCP负载复制到数据缓存区。需要注意的是,图3-16 只是一个例子,TCP负载可能会被组织成其他形式,例如分片,在后续章节中将会看到一个分片的数据缓存区是什么样的。

 
图3-16  TCP层向链路层传递时数据的填充过程
a) 空的SKB  b) 在SKB的头部预留足够的空间  c) 复制TCP数据  
d) 添加TCP首部  e) 添加IP首部  f) 添加以太网帧首部

4)TCP层添加TCP首部。

5)SKB传递到IP层,IP层为数据包添加IP首部。

6)SKB传递到链路层,链路层为数据包添加链路层首部。

3.skb_put()

skb_put()修改指向数据区末尾的指针tail,使之往下移len字节,即使数据区向下扩大len字节,并更新数据区长度len。调用skb_put()前后,SKB结构变化如图3-17所示。

 
图3-17  skb_put()示意
a) 调用前  b) 调用后

4.skb_pull()

skb_pull()通过将data指针往下移动,在数据区首部忽略len字节长度的数据,通常用于接收到数据包后在各层间由下往上传递时,上层忽略下层的首部。调用skb_pull()前后,SKB结构变化如图3-18所示。

 
图3-18  skb_pull()示意
a) 调用前  b) 调用后

以上为整合内容 原文链接:http://simpleyyt.github.io/linux/2014/04/16/sk_buff_analysis/  和 http://book.51cto.com/art/201206/345043.htm


展开阅读全文

没有更多推荐了,返回首页