sk_buff详细介绍

struct sk_buff可能是linux网络代码中最重要的数据结构,它表示接收或发送数据包的包头信息,并包含很多成员变量供网络代码中的各子系统使用。 
// 这个结构被网络的不同层(MAC或者其他二层链路协议,三层的IP,四层的TCP或UDP等)使用,并且其中的成员变量在结构从一层向另一层传递时改变。
// L4向L3传递前会添加一个L4的头部,同样,L3向L2传递前,会添加一个L3的头部。 添加头部比在不同层之间拷贝数据的效率更高。
//由于在缓冲区的头部 添加数据意味着要修改指向缓冲区的指针,这是个复杂的操作,所以内核提供了一个函数skb_reserve来完成这个功能。
//协议栈中的每一层在往下一层传 递缓冲区前,第一件事就是调用skb_reserve在缓冲区的头部给协议头预留一定的空间
skb_reserve同样被设备驱动使用来对齐接收到包的包头。如果缓冲区向上层协议传递,旧的协议层的头部信息就没什么用了。
例如,L2的头部只有在 网络驱动处理L2的协议时有用,L3是不会关心它的信息的。
但是,内核并没有把L2的头部从缓冲区中删除,而是把有效荷载的指针指向L3的头部,这样做, 可以节省CPU时间。

有些sk_buff成员变量的作用是方便查找或者是连接数据结构本身。内核可以把sk_buff组织成一个双向链表。
当然,这个链表的结构要比常见的双向 链表的结构复杂一点。
就像任何一个双向链表一样,sk_buff中有两个指针next和prev,其中,next指向下一个节点,而prev指向上一个节 点。
在第一个节点前面会插入另一个结构sk_buff_head,这是一个辅助节点(作为sk_buff双向链表的头),它的定义如下:
struct sk_buff_head { 
struct sk_buff -*next; 
struct sk_buff -*prev; 
__u32 qlen; //qlen代表链表元素的个数 
spinlock_t lock; //lock用于防止对链表的并发访问 

};

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
//next和prev指针:协议栈中经常用到sk_buff的队列,队列通过sk_buff中的指针相连接。这两个指针用来连接相关的skb的(例如有分片)

ktime_t	tstamp; //报文到达或者离开的时间戳; Time we arrived 表示这个skb被接收的时间

struct sock	*sk; //指向报文所属的套接字指针;Socket we are owned by 表示从属于那个socket,主要是被4层用到。 
/*
这是一个指向拥有这个sk_buff的sock结构的指针。
这个指针在网络包由本机发出或者由本机进程接收时有效,因为插口相关的信息被L4(TCP或 UDP)或者用户空间程序使用。
如果sk_buff只在转发中使用(这意味着,源地址和目的地址都不是本机地址),这个指针是NULL
*/
struct net_device	*dev; //表示一个接收或者发送报文的网络设备; Device we arrived on/are leaving by
//这个表示一个网络设备,当skb为输出时它表示skb将要输出的设备,当接收时,它表示输入设备。要注意,这个设备有可能会是虚拟设备(在3层以上看来)
char	cb[48] __aligned(8); //保存每一层的控制信息,以及私有信息;Control buffer. Free for use by every layer. Put private vars here
/*
cb 这个域很重要,我们下面会详细说明。这里只需要知道这个域是保存每层的控制信息的就够
中的48个字节是控制字段,配合各层协议工作,为每层存储必要的控制信息。 ) 
*/
unsigned long	_skb_refdst; // destination entry (with norefcount bit)
///这里其实应该是dst_entry类型,不知道为什么内核要改为ul。这个域主要用于路由子系统。这个数据结构保存了一些路由相关信息 
#ifdef CONFIG_XFRM
struct	sec_path	*sp; 安全路径,用于xfrm
#endif
unsigned int	len, // Length of actual data //当前协议数据包的长度,包括主缓冲区中数据长度和分片中数据长度
//(个人理解为当前协议层包头和数据区域长度)。skb的组成是sk_buff控制+线性数据+非线性数据(skb_shared_info)组成!
// 表示当前协议数据包的长度。它包括主缓冲区中的数据长度(data指针指向它)和分片中的数据长度
//这个长度表示当前的skb中的数据的长度,这个长度即包括buf中的数据也包括切片的数据,也就是保存在skb_shared_info中的数据。
//这个值是会随着从一层到另一层而改变的。下面我们会对比这几个长度的。
data_len; //Data length ;//分片中数据长度 和len不同,data_len只计算分片中数据的长度 // 这个长度只表示切片数据的长度,也就是skb_shared_info中的长度。 

__u16	mac_len, // Length of link layer header 这是mac头的长度 //这个长度表示mac头的长度(2层的头的长度) 
hdr_len; //writable header length of cloned skb 用于clone时,表示clone的skb的头长度;//这个主要用于clone的时候,它表示clone的skb的头的长度。
///接下来是校验相关的域。 
union {
__wsum	csum; // Checksum (must include start/offset pair)
struct {
__u16	csum_start; //skb->head即校验和计算起始点 Offset from skb->head where checksumming should start
__u16	csum_offset; //Offset from csum_start where checksum should be stored 校验和存储位置偏移(以csum_start处起始偏移csum_offset大小)

};
};
///优先级,主要用于QOS。
__u32	priority; //Packet queueing priority 报文排队优先级,取决于ip中的tos域

kmemcheck_bitfield_begin(flags1);

///接下来是一些标志位。 


__u8	local_df:1, //是否可以本地切片的标志。 
cloned:1, ///为1说明头可能被clone。 
ip_summed:2, ///这个表示校验相关的一个标记,表示硬件驱动是否为我们已经进行了校验(前面的blog有介绍) 
nohdr:1, ///这个域如果为1,则说明这个skb的头域指针已经分配完毕,因此这个时候计算头的长度只需要head和data的差就可以了。 

nfctinfo:3;
__u8	pkt_type:3, ///pkt_type主要是表示数据包的类型,比如多播,单播,回环等等。
fclone:2, ///这个域是一个clone标记。主要是在fast clone中被设置,我们后面讲到fast clone时会详细介绍这个域。
ipvs_property:1, ///ipvs拥有的域。 
peeked:1, ///这个域应该是udp使用的一个域。表示只是查看数据。 
nf_trace:1; ///netfilter使用的域。是一个trace 标记 

kmemcheck_bitfield_end(flags1);
__be16	protocol; ///这个表示L3层的协议。比如IP,IPV6等等。


void	(*destructor)(struct sk_buff *skb); //skb的析构函数,一般都是设置为sock_rfree或者sock_wfree.
///netfilter相关的域。 
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct nf_conntrack	*nfct;
#endif
#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED
struct sk_buff	*nfct_reasm;
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
struct nf_bridge_info	*nf_bridge;
#endif

int	skb_iif; //接收设备的index。 

///流量控制的相关域。 
#ifdef CONFIG_NET_SCHED
__u16	tc_index;	/* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
__u16	tc_verd;	/* traffic control verdict */
#endif
#endif

__u32	rxhash;

kmemcheck_bitfield_begin(flags2);
///多队列设备的映射,也就是说映射到那个队列。 
__u16	queue_mapping:16;
#ifdef CONFIG_IPV6_NDISC_NODETYPE
__u8	ndisc_nodetype:2;
#endif
__u8	ooo_okay:1;
kmemcheck_bitfield_end(flags2);

/* 0/13 bit hole */

#ifdef CONFIG_NET_DMA
dma_cookie_t	dma_cookie;
#endif
#ifdef CONFIG_NETWORK_SECMARK
__u32	secmark;
#endif
union {
__u32	mark; // skb的标记。
__u32	dropcount;
};
__u16	vlan_tci; //vlan标签控制信息;

sk_buff_data_t	transport_header; //传输层头
sk_buff_data_t	network_header; // 网络层头指针
sk_buff_data_t	mac_header; //链路层头
/* These elements must be at the end, see alloc_skb() for details. */
sk_buff_data_t	tail; //保存数据被容的结尾;
sk_buff_data_t	end; //分配的内存块的结尾;
unsigned char	*head, // 分配的内存块的起始位置;指向数据区中开始的位置(非实际数据区域开始位置)
*data; //保存数据内容的首地址;(实际数据区域开始位置)

/*
sk_buff->head sk_buff->data sk_buff->tail sk_buff->end 它们表示缓冲区和数据部分的边界。
在每一层申请缓冲区时,它会分配比协议头或协议数据大的空间。head和end指向缓冲区的头部和尾部,而data和 tail指向实际数据的头部和尾部。
每一层会在head和data之间填充协议头,或者在tail和end之间添加新的协议数据。数据部分会在尾部包含一 个附加的头部。	
*/
unsigned int	truesize; //该缓冲区分配的所有总的内存,包括:skb_buff + 所有数据大小;//这个表示整个skb的大小,包括skb本身,以及数据。 
/*这是缓冲区的总长度,包括sk_buff结构和数据部分。如果申请一个len字节的缓冲区,alloc_skb函数会把它初始化成len+sizeof(sk_buff)。当skb->len变化时,这个变量也会变化
*/
atomic_t	users; //保存引用skb_buff的数量
/*
这是一个引用计数,用于计算有多少实体引用了这个sk_buff缓冲区。
它的主要用途是防止释放sk_buff后,还有其他实体引用这个sk_buff。
因此,每个引用这个缓冲区的实体都必须在适当的时候增加或减小这个变量。
这个计数器只保护sk_buff结构本身,而缓冲区的数据部分由类似的计数器 (dataref)来保护.有时可以用atomic_inc和atomic_dec函数来直接增加或减小users,
但是,通常还是使用函数 skb_get和kfree_skb来操作这个变量。
*/

};

1: http://www.cnblogs.com/image-eye/archive/2012/01/17/2324866.html
http://blog.csdn.net/ysu108/article/details/7764461
http://blog.csdn.net/geekcome/article/details/7488828
http://blog.csdn.net/ysu108/article/details/7764461
http://blog.csdn.net/yuzhihui_no1/article/details/38666589 

http://www.cnblogs.com/zengkefu/p/5583618.html

http://www.cnblogs.com/sammyliu/p/5225623.html
http://www.cnblogs.com/sammyliu/p/5225623.html
http://simohayha.iteye.com/blog/556168 //IMPORENT 

http://www.cnblogs.com/zengkefu/p/5583618.html

http://blog.csdn.net/lmjjw/article/details/9992235

http://www.cnblogs.com/zengkefu/p/5602396.html //important

http://www.cnblogs.com/zengkefu/p/5583618.html

http://www.cnblogs.com/zengkefu/p/5602396.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Farmwang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值