头结构体指针
sk_buff_data_t sk_buff_data_t
network_header; // 指向三层IPmac_header;
// 指向二层
头结构体指针 mac头的头
/* These elements must be at the end, see alloc_skb() for sk_buff_data_t sk_buff_data_t unsigned char
tail; end;
// 表示总长 // 指向数据 // 指向数据 // 指向数据 // 指向数据
details. */
区中实际数据结束的位置
区中结束的位置(非实际数据区域结束位置)
*head, *data;
truesize;
区中开始的位置(非实际数据区域开始位置) 区中实际数据开始的位置
unsigned int atomic_t
度,包括sk_buff自身长度和数据区以及分片结构体的数据区长度
users; // skb被克
隆引用的次数,在内存申请和克隆时会用到 }; //end sk_buff
第四、结构体中常用字段解释:
char cb[48];这个字段是skb信息控制块,也就是存储每层的一些协议信息,当数据包在哪一层时,存储的就是哪一层协议信息。这个字段由数据包所在层使用和维护,如果要访问本层协议信息,可以通过用一些宏来操作这个成员字段。如:#define TCP_SKB_CB(__skb) ((struct tcp_skb_cb *)&((__skb)->cb[0]))
_u8 fclone:2;这是个克隆状态标志,到sk_buff结构内存申请时会使用到。这里提前讲下:若fclone = SKB_FCLONE_UNAVAILABLE,则表明SKB未被克隆;若fclone = SKB_FCLONE_ORIG,则表明是从skbuff_fclone_cache缓存池(这个缓存池上分配内存时,每次都分配一对skb内存)中分配的父skb,可以被克隆;若
fclone = SKB_FCLONE_CLONE,则表明是在skbuff_fclone_cache分配的子SKB,从父SKB克隆得到的;
atomic_t users;这是个引用计数,表明了有多少实体引用了这个skb。其作用就是在销毁skb结构体时,先查看下users是否为零,若不为零,则调用函数递减下引用计数users即可;当某一次销毁时,users为零才真正释放内存空间。有两个操作函数:atomic_inc()引用计数增加1;atomic_dec()引用计数减去1;
第五、几个数据长度len的解析:
(1)sk_buff->data_len:只计算分片中数据的长度,即是分片结构体中page指向的数据区长度。这个在分片结构体中会再详细讲解下。
(2)sk_buff->len:表示当前缓冲区中数据块的大小的总长度。它包括主缓冲中(即是sk_buff结构中指针data指向)的数据区的实际长度(data-tail)和分片中的数据长度。这个长度在数据包在各层间传输时会改变,因为分片数据长度不变,从L2到L4时,则len要减去帧头大小和网络头大小;从L4到L2则相反,要加上帧头和网络头大小。所以:len = (data - tail) + data_len;
(3)sk_buff->truesize:这是缓冲区的总长度,包括sk_buff结构和数据部分。如果申请一个len字节的缓冲区,alloc_skb函数会把它初始化成len+sizeof(sk_buff)。当skb->len变化时,这个变量也会变化。所以:truesize = len + sizeof(sk_buff) = (data - tail) + data_len + sizeof(sk_buff);
第六、数据包在各层间传输时,data指针的变化:
在第一的时候就讲了,sk_buff在各层间传输时,只改变指针和添加协议头,不拷贝也不删除协议头。下面来看下处理的详细进过,首先要说下,每一层协议都有个自身的协议头指针,如:二层有mac,三层有nh(nethead网络头),四层有h
(head)。现在我用的内核版本把这几个协议头指针变为了:二层为mac_header,三层为network_header,四层为transport_header。下面就简单介绍下包的传输和data指针变化情况。
假设从收包开始:
(1)开始进入第二层时,这时data指针指向帧头。mac = data,然后操作mac指针已经数据包。当二层操作完后把包往三层传送时,会调用一个函数(具体什么函数后面会详细讲)让data指针指向三层的IP头;
(2)当包进入第三层时,这时data指针已经指向了IP头,让nh = data,然后操作nh指针已经数据包,当三层操作完后把包往四层传送时,同样调用一个函数把data指向四层的TCP头;同理,四层也是一样处理的,只移动指针,不删除协议头。发包时就相反了,只是变成了为每一层添加协议头了。下面是书上的图,供参考。
sk_buff_head结构体:
sk_buff结构体是双链表结构,其头结点就是sk_buff_head结构。
struct sk_buff_head {
/* These two members must be first. */ struct sk_buff*next; struct sk_buff*prev;
__u32 qlen;//代表表中skb元数的个数 spinlock_t lock;//锁,防止并发访问 };
其实如果要学习linux内核我建议还是从list.h文件开始学起,因为那是内核常用的结构(循环双链表,哈希链表),以及里面还有些操作宏。如果学懂了list.h文件,那么看内核中其他结构将会更简单些(因为里面的一些指针和内核定义的操作宏都非常类似)。先来解释下sk_buff_head结构体成员,qlen表示当前链表中skb的个数,lock成员是自旋锁这个为了构成一个原子操作,防止多条线程同时访问结构体,后面会详细解释下。这里重点解释下next和prev,这是个前驱后继指针,这里内核特意注释规定了前两个成员一定要是:next和prev指针。因为这使得sk_buff_head和sk_buff可以放到同一个链表中,尽管他们不是同一种结构体。另外,相同的函数可以同样应用于sk_buff_head和sk_buff。很多blog都这么说,但为什么sk_buff的指针(prev和next)能够指向sk_buff_head结构呢?我查了很多资料,没结果。但最后我发现原文有句话是关键:“......在表的开端额外增加一个sk_buff_head结构作为一种哑元元素。”
sk_buff_head结构体和sk_buff结构体关系:
相关推荐: