Linux网络协议栈之套接字缓冲区(1)
来源:互联网 发布:serv u linux 破解 编辑:程序博客网 时间:2024/05/21 18:00
转自:http://blog.csdn.net/bullbat/article/details/7635314
作者:bullbat
Linux网络协议栈是内核中最大的组件之一,由于网络部分应用的范围很广,也相对较热,该部分现有的资料很多,学起来也比较容易。首先,我们看看贯穿网络协议栈各层的一个最关键数据结构——套接字缓冲区(sk_buff结构)。
一个封包就存储在这个数据结构中。所有网络分层都会使用这个结构来存储其报头、有关数据的信息,以及用来协调工作的其他内部信息。在内核的进化历程中,这个结构经历多次变动,本文及后面的文章都是基于2.6.20版本,在2.6.32中该结构又变化了很多。该结构字段可粗略划分为集中类型:布局、通用、专用、可选(可用宏开关)。
SKB在不同网络层之间传递,可用于不同的网络协议。协议栈中的每一层往下一层传递SKB之前,首先就是调用skb_reserve函数在数据缓存区头部预留出的一定空间以保证每一层都能把本层的协议首部添加到数据缓冲区中。如果是向上层协议传递skb,则下层协议层的首部信息就没有用了,内核实现上用指针改变指向来实现。
下面看看该结构体中的字段,大部分都给了注释,后面的方法与实现中我们将看到他的各个字段的应用与实际意义。
struct sk_buff {
/* These two members must be first. */
/*链表,放在结构头,用于强制转化得到,链表头为skb_buff_head*/
struct sk_buff *next;
struct sk_buff *prev;
/*指向拥有此缓冲区的sock数据结构,当数据在本地
产生或者正由本地进程接收时,就需要这个指针
因为该数据以及套接字相关的信息会由L4
以及用户应用程序使用。当缓冲去只是被
转发时,该指针为NULL*/
struct sock *sk;
/*通常只对一个以及接收的封包才有意义
这是一个时间戳记,用于表示封包何时被
接收,或者有时用于表示封包预定传输时间
*/
struct skb_timeval tstamp;
/*此字段描述一个网络设备,该字段的作用与该SKB是发送包还是
接受包有关*/
struct net_device *dev;
/*接收报文的原始网络设备,主要用于流量控制*/
struct net_device *input_dev;
union {
struct tcphdr *th;
struct udphdr *uh;
struct icmphdr *icmph;
struct igmphdr *igmph;
struct iphdr *ipiph;
struct ipv6hdr *ipv6h;
unsigned char *raw;
} h;/*四层协议首部*/
union {
struct iphdr *iph;
struct ipv6hdr *ipv6h;
struct arphdr *arph;
unsigned char *raw;
} nh;/*三层协议首部*/
union {
unsigned char *raw;
} mac;/*二层协议首部*/
/*目的路由缓存项*/
struct dst_entry *dst;
/*IPSec协议用来跟踪传输的信息*/
struct sec_path *sp;
/*
* This is the control buffer. It is free to use for every
* layer. Please put your private variables there. If you
* want to keep them across layers you have to do a skb_clone()
* first. This is owned by whoever has the skb queued ATM.
*/
/*SKB信息控制块,是每层协议的私有信息存储空间,由每层协议
自己使用并维护,并只有在本层有效*/
char cb[48];
/*数据总长度,包括线性缓冲区数据长度(data指向),SG类型的聚合分散I/O的数据
以及FRAGLIST类型的聚合分散I/O的数据长度,也包括协议首部的长度*/
unsigned int len,
/*SG类型和FRAGLIST类型聚合分散I/O存储区中的数据长度*/
data_len,
/*二层首部长度。实际长度与网络介质有关,在以太网中为以太网桢首部的长度*/
mac_len;
union {
__wsum csum;
__u32 csum_offset;
};
/*发送或转发QOS类别。*/
__u32 priority;
/*表示此skb在本地允许分片*/
__u8 local_df:1,
/*标记所属SKB是否已经克隆*/
cloned:1,
/*标记传输层校验和的状态*/
ip_summed:2,
/*标识payload是否被单独引用,不存在协议首部*/
nohdr:1,
/*防火墙使用*/
nfctinfo:3;
/*桢类型,分类是由二层目的地址来决定的,对于以太网设备
来说,该字段由eth_type_trans()初始化*/
__u8 pkt_type:3,
/*当前克隆状态*/
fclone:2,
ipvs_property:1;
/*从二层设备角度看到的上层协议,即链路层承载的三层协议类型*/
__be16 protocol;
/*skb析构函数指针,在释放skb时被调用,完成某些必要的工作*/
void (*destructor)(struct sk_buff *skb);
/*在数据结构中定义的宏不能编译成模块;
原因在于内核编译之后,开启该选项所得
的多数结果为不可逆的,一般而言,任何
引起内核数据结构改变的选项,都不适合
编译成一个模块,编译选项和特定的#ifdef
符号相配,以了解一个代码区块什么时候
会包含到内核中,这种关联在源码树的
Kconfig文件中*/
#ifdef CONFIG_NETFILTER
/*防火墙使用*/
struct nf_conntrack *nfct;
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct sk_buff *nfct_reasm;
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
/*防火墙使用*/
struct nf_bridge_info *nf_bridge;
#endif
#endif /* CONFIG_NETFILTER */
#ifdef CONFIG_NET_SCHED
/*用于流量控制*/
__u16 tc_index; /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
/*用于流量控制*/
__u16 tc_verd; /* traffic control verdict */
#endif
#endif
#ifdef CONFIG_NET_DMA
dma_cookie_t dma_cookie;
#endif
#ifdef CONFIG_NETWORK_SECMARK
__u32 secmark;
#endif
__u32 mark;
/* These elements must be at the end, see alloc_skb() for details. */
/*全部数据的长度+该结构的长度,如果申请了一个len字节的缓冲区
该字段为len+sizeof(sk_buff)*/
unsigned int truesize;
/*引用计数,使用这个缓冲区的实例的数目*/
atomic_t users;
/*head和end指向数据在内存中的起始和结束位置,即
已经分配的缓冲区空间的开端和尾端
data和tail指向数据区域的起始和结束位置,即
实际数据的开端和尾端*/
unsigned char *head,
*data,
*tail,
*end;
};
可选功能字段
在网络模块中同时也提供了很多有用的功能,虽然这些功能都不是必须的,但对现在的应用来讲是不可缺少的一部分,例如,防火墙、组播等。为了支持这些功能,一般都需要在内核数据结构sk_buff中添加相应的成员变量。因此,sk_buff结构中包含很多想#ifdef这样的预编译指令。如下面的两个宏定义。
struct sk_buff {
……
/*在数据结构中定义的宏不能编译成模块;
原因在于内核编译之后,开启该选项所得
的多数结果为不可逆的,一般而言,任何
引起内核数据结构改变的选项,都不适合
编译成一个模块,编译选项和特定的#ifdef
符号相配,以了解一个代码区块什么时候
会包含到内核中,这种关联在源码树的
Kconfig文件中*/
#ifdef CONFIG_NET_SCHED
__u16 tc_index; /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
__u16 tc_verd; /* traffic control verdict */
#endif
……
}
我们打开内核文件夹net->sched下面的Kconfig文件,发现有下面文字:
menu "QoS and/or fair queueing"
config NET_SCHED
bool "QoS and/or fair queueing"
……
config NET_CLS_ACT
bool "Actions"
select NET_ESTIMATOR
---help---
……
endif # NET_SCHED
endmenu
与上面数据结构中的宏对应就显然了,如果需要了解内核配置选项与对应的宏,查看对应的Kconfig文件就可以了。需要指出的是,内核编译之后,由某些选项所控制的数据结构是固定的而不是动态变化的。一般来说,如果某些选项修改了内核数据结构,则包含该选项的组件就不能被编译成内核模块。
- Linux网络协议栈之套接字缓冲区(1)
- Linux网络协议栈之套接字缓冲区(2)
- Linux网络协议栈之套接字缓冲区
- Linux网络编程之原始套接字-ping协议实现
- Linux网络编程之原始套接字-ping协议实现
- Linux网络编程之原始套接字-ping协议实现
- linux内核学习之网络篇——套接字缓冲区
- linux网络协议栈分析——套接字创建
- Linux网络协议栈(二) -- 套接字缓存(socket buffer)
- Linux网络协议栈(二) -- 套接字缓存(socket buffer)
- linux网络协议栈(四)链路层 (4)原始套接字
- LINUX网络编程之套接字
- Linux 网络IPC 之套接字
- Linux 网络编程之原始套接字
- Linux 网络编程之原始套接字
- Linux 网络编程之原始套接字
- Linux网络编程之套接字基础
- Linux网络编程之TCP套接字
- ASP.NET MVC中使用Unity Ioc Container
- Android中汉字转换成拼音报错的处理解决.....
- 介绍一个很不错的电影网站
- android.view.InflateException: Binary XML file line #95: Error inflating class(out of memory)
- 01背包变形--求方案数
- Linux网络协议栈之套接字缓冲区(1)
- linux 下swftools0.9.2的安装
- 文本文件与二进制文件
- Android应用防内存泄露小结
- 将Eclipse代码导入到AndroidStudio的两种方式...
- 用proguard混淆java web项目
- 手机QQ内置浏览器position:fixed 属性支持不好的解决方案
- windows服务和进程的区别和联系
- App Manifest:<application> android:largeHeap