论坛风格切换切换到宽版
  • 990阅读
  • 0回复

[问题求助]网卡驱动里的SKB(sk_buff)结构体 [复制链接]

上一主题 下一主题
 
发帖
2130
C币
-60329
威望
399
贡献值
1
银元
-1
铜钱
4691
人人网人气币
0
只看楼主 倒序阅读 使用道具 楼主  发表于: 2009-05-01

.               [成人用品]
  Linux TCP/IP 协议栈的关键数据结构Soc.ket Buffe.r(sk_buff )(        游戏          )

sk_buff.结构可能是linux网络代码中最重要的数据结构,它表示接收或发送.数据包的包头信息。它在中定义,并包含很多成员变量.供网络代码中的各子系统使用。 虚拟主机
这个结构在linux内核的发展过程中改动过很.多次,或者是增加新的选项,或.者是重新组织已存在的成员变量以使得成员变量的布局更加清晰。它的成员变量可以大致分为以下.几类: 外贸


Layout 布局


General 通用


Feature-speci.fic功能相关          婚庆

Managemen.t functions管理函数 服务器

这个结构被不同的网络层(MAC或者其他二层链路协议,三层的IP,四层的TCP或UDP等)使.用.,并且其中的成员变量           鲜花
在结构从一层向另一层传递时改变。.L4向L3传递前会添加一个L4的头.部,同样,L3向L2传递前,会添加一个L3的头部。添加头部比在不同层之间拷贝数.
据的效率更高。由于在缓冲区的头部添加数据意味着要修改指向缓冲区.的指针,这是个复杂的操作,.所以内核提供了一个函数skb_reserve(在后面的章.
节中描述)来完成这个功能。协议栈中的每一层在往下一层传递缓冲区前,第一件.事就是调用skb_reserve在缓冲区的头部给协议头预.留一定的空间。           女人
skb_reserve同样被设备驱动使用来对.齐接收到包的包头。如果缓冲区.向上层协议传递,旧的协--------------彩票
议层的头部信息就没什么用.了。例如,L2的头部只有在网络驱动处理L2的协议时有用,L3是不会关心它的信息的。但是,内核并没有把.L2的头部从缓冲区中外贸
删除,而是把.有效荷载的指针指向L3的头部,.这样做,可以节省CPU时间。 (        游戏          )
1. 网络参数和.内核数据结构            女人
就像你在浏览TCP/IP规范或者配置内.核时所看到的一样,网络代码提供了很多有用的功能,但是这些功.能并不是必须的,(广告)
比如说,防.火墙.,多播,还有其他一些功能。大部分的功能都需要在内核数据结构中添加自己的成员变量。因此,sk_buff里面包含了很多像#ifdef这    外汇
样的预编译指令。例如,在sk_buff结.构的最后,.你可以找到:            鲜花
stru.ct sk_buff {    健康
    . ... ... ...           建材
#ifdef CONFIG_N.ET_SCHED<性病>
     _ _u3.2     tc_index;.
#.ifdef CONFIG_NET_CLS_ACT电脑
     ._ _u32     tc_verd;--------------彩票
.    _ ._u32     tc_classid;<性病>
#endif
#endif
}
它表明,tc_index只有在.编译时定义了CONFIG_NE.T_SCHED符号才有效。这个符号可以通过选择特定的    美容
编译选项来定义(例如:"Device. Drivers Networkin.g supportNetworking options QoS--- 印刷
and/or fair queueing")。这些编译选项可以由管理员通过make config来选择,或者通过一些自动安.装工.具来选择。 电脑
前面的例子有两个嵌.套的选项:CO.NFIG_NET_.CLS_ACT(包分类器)只有在选择支持“QoS and/or fair queueing”时才能生效。 .
顺便提一下,Qo.S选项不能被编译成.内核模块。原因就是,内核编译之后,由某个选项所.控制的数据结构是不能动态变化的。一般来说,如果某个选项会修改内核数据结构(比如说,在sk_buff.
里面.增加一个项t.c_index),那么,包含这个选项的组件就不能被编译成内核模块。 健康
你.可能经常需要查找是哪个make.
config编译选.项或者变种定义了某.个#ifdef标记,以便理解内核中包含的某段代码。在2.6内核中,最快的,查找它们之间关联关系的方法,健康
就是查找分布.在内核源代码树中的kconfig文件中是否定义了.相应的符号(每个目录都有一个这样的文件)。在 教育
2.4内核中,你需要查看Documentation/Configur..e.help文件。 .
2. Layout Fiel.ds电脑
有.些sk_buff成员变量的作.用是方便查找或者是连接数据结构.本身。内核可以把sk_buff组织成一个双向链表。当然,这个链表的结构要比常见的双向链表的结构复杂一点。     外汇
就像任何.一个双向链表一样,sk_buff中有两个指针next和prev,其中,next指向下一个节点,.而 .
prev指向上一个节点。但是,这个链表还有另一个需求:每个sk_buff结构都必须能够很快找到链.表头节点。为了满足这个.需求,在第一个节点前面会插入另一个结构sk_buff_head,这是一个辅助节点,它的定义如下.:              电子
struct sk_buff_head {.健康
   /* These two mem.bers must be first. .*/    struct sk_buff     * next;    服务器
    struct sk_buf.f     * prev; .      .
    _ ._u32         q.len;    域名
.   spinlock_t     lo.ck;        健康
};
qlen代表链表元素的个数。lock用于防止对链表.的并发.访问。 <性病>
sk_buff和sk_buff_head的前两个元素是一样的:.next和prev指针。这使得.它们可以放到同一个链表中,尽管sk_buff_head要比sk_buff小得多。另.外,相同的函数可以同样应用于sk_buff和sk_buff_head。              乙肝
为了使这个数据结构更灵活,每个sk_buff结构都包含一个指向sk_buff_hea..d的指针。这个指针的名字是list。图1.会帮助你理解它们之间的关系。             杀毒
F.igure 1. List of sk._buff elements电影
http://upload.bbs.csuboy.com/Mon_1004/126_6912_5142aee103a56a9.jpg[/img]             电子

其他有趣的成员变量如下:
s.truct sock *sk           女人

是.一个.指向拥有这个sk_buff的sock结构的指针。这个指针在网络包由本机发出或者由本机进程接收时有效,因为插口相关的信息被L4(TCP或(        游戏          )
UDP)或者用户空间程序使.用。如果sk_buff只.在转发中使用(这意味着,源地址和目的地址都不是本机地址),这个指针是NULL。 --- 印刷
unsigned in.t len           建材
这是缓冲区中数据部分的长度。它包.括主缓冲区中的数据长度(data指.针指向它)和分片中的数.据长度。它的值在缓冲区从一个层向另一个层传递时改变,因为往上层传递,旧的头部就没有用了,而往下层传递,需要添加本层的头部。len同样包含了协议头的长度。 .
unsigned int da.ta_len虚拟主机
和len.不同,data_len只计算分片中数据.的长度。[成人用品]
u.nsigned int mac_len.
这是mac头的长度。
atomic_t users

是一个引用计数,用于计算有多少实.体引用了这个sk_buff缓冲区。它的主要用途是防止释放sk_buff后,还有其.他实体引用这个sk_buff。因教育
此,每个引用这个缓冲区的实体都必须在适当的时候增.加或减小这个变量。这个计数器只保护sk_buff结构本身,而缓.冲区的数据部分由类似的计数器健康
(dataref)来保护.
有时可以用atomic_inc.和atomic_dec函数来直接增加或减小users,但是,通常还是.使用函数skb_.get和kfree_skb来操作这个变量。 服务器
unsigne.d int truesize.
这是缓冲区的总长度,包括sk_buff结.构和数据部分。如果申请一个len字.节的缓冲区,alloc_skb函数会把它初始化成.len+sizeof(sk_buff)。               乙肝
struct sk_buff *alloc_sk.b(unsigned int size,int gfp_ma.sk)电脑
{
  .    ... ... ...<性病>
      skb->trues.iz.e = size + sizeof(struct sk_buff);.
.     ... ... ...    健康
}当skb->len变化时,这个变量也会变.化。 --------------彩票
unsigned char *head.域名
unsigne.d char *end(广告)
unsigned char *dat.a          婚庆
u.nsigned char *tail学习

们表示缓冲区和数据部分的边界。在每一层申请缓冲.区时,它会.分配比协议头或协议数据大的空间。head和end指向缓冲区的头部和尾部,而data和外贸
tail指向实际数据的头部和尾部,参见图2。每一层会在head和data之间填充协议头,或.者在tail和end之间添加新的协议数据。图2中右边数.    健康
据部分会在尾.部包含一个附加的头部。              电子
Figu.re 2. head/end versus dat.a/tail pointers             汽车
http://upload.bbs.csuboy.com/Mon_1004/126_6912_1bc1566cb3a2d35.jpg[/img]             电子

void .(*destructor)(...).

个函数指针可以初始化成一个在缓冲区释放.时完成某些.动作的函数。如果缓冲区不属于一个socket,这个函数指针通常是不会被赋值的。如果缓冲区属于一个           女人
socket,
这个函数指针会被赋值为sock_rfree或sock_wfree(分别由skb_set_.owner_r或skb._set_owner_w函数初始           鲜花
化)。这两个sock_x.xx函数用于更新soc.ket的队列中的内存容量。 .
3. Gener.al Fields虚拟主机
本节描述sk_buff的主要成员变量,这些成员变量与特定的内核.功能无关: ..
struc.t timeval stamp.
这.个变量只对接收到的包有意义。它代.表包接收时的时间戳,或者有时代表包准备发出.时的时间戳。它在netif_rx里面由函数net_timestamp设置,而netif_rx是设备驱动收到一个包后调用的函数。外贸
stru.ct net_device *dev服务器

个变量的类型是net_device,net_device它代表.一个网络设备。dev的作用与这.个包是准备发出的包还是刚接收的包有关。当收到一个包电影
时,设备驱动会把sk_buff的dev指针.指向收到这个包的设备的数据结构,就像下面的vortex_rx里的一段代码.所做的一样,这个函数属于           鲜花
3c59x系列以太网卡驱动,用于接收一个帧。(d.rivers/.net/3c59x.c):             杀毒
static int. vor.tex_rx(struct net_device *dev).
{
      .      ... ... ....
   .      skb->dev = dev;.
.           ... ... ....
     .    s.kb->protocol = eth_type_trans(skb, dev);.
         netif_rx(skb); /* Pass the pack.et to the higher l.ayer */[成人用品]
            ... .... ....
}
当一个包被发送时,这个变量代表将要发送这个包.的设备。在发送网络包时设置这个值的代码要比.接收网络包时设置这个值的代服务器
码复杂。有些网络功能可以把多个网络设备组成一个虚拟的网络设备(也就是说,这些设备没有和物理.设备直接关联),并由一个虚拟网络设.备驱动管理。当虚拟设健康
备被使用时,dev指针指向虚拟设备的net_device结构。而虚拟设备驱动会在一组设备中.选择一个设备并把dev指针修改为这.个设备的              乙肝
net_device结构。因此,在某些情况下, 指向传输设备的.指针会在包处理过程中被改变.。             杀毒
str.uct net_device *input_d.ev             汽车
这是收到包的.网络设备的指针。如果包是本地生成的,这个值为NULL。对以太网设备来..说,这个值由eth_type_trans初始化,它主要被流量控制代码使用。 域名
str.uct n.et_device *real_dev.
这..个变量只对虚拟设备有意义,它代表与虚拟设备关联的真实设备。例如,Bonding和VLAN设备都使用它来指向收到包的真实设备。 教育
union {...} h
union {...} nh
union {...}. mac             汽车
这些是指向.TCP/IP各层协议头的指针:h指向L4,nh指向L3,mac指向L2。每个指.针的类型都是一个联合,包教育
含多个数据结构,每一个数据结构都表示内核在这一层可以解析的协议。例如,h.是一个包含内核所能解析的L4协议的数据结构.的联合。每一个联合都有一个.
raw变量用于初始.化,后续的访问.都是通过协议相关的变量进行的。 .
当接收一个包时,处.理n层协议头的.函数从n-1层收到一个缓冲区,它的skb->data指向n层协议的头。处理          婚庆
n层协议的函数把本层的指针(.例如,L3对应的是skb->nh指针)初始化为skb->data,因为这个指针的.值会在处理下一层协议时改             汽车
变(sk.b->data将被初始化成缓冲区里的其他地址)。在处理n层协议的函.数结束时,在把包传递给n+1层的处理函数前,它会把skb-           鲜花
>data指针指向n层协议头的末尾,这正好是n+.1层协议的协议头(参见图3.)。               乙肝
发送包的过程.与此相反,但.是由于要为每一层添加新的协议头,这个过程要比接收包的过程复杂。 电影
Figure 3. Header's poi.nter initializat.ions while. moving from layer two to layer three             汽车
http://upload.bbs.csuboy.com/Mon_1004/126_6912_27ed89e9af7417a.jpg[/img]教育

struct dst_entry .dst    健康
这个变量.在路由子系统中使用。 学习
char cb[40]

是一个“control
buffer”,或者说是一个私有信息的存储空间,由每一层自己维护并使用。它在分配sk_buff结构时分配(.它.目前的大小是40字节,已经足够为每一<性病>
层存储必要的私有信息了)。在每一层中,访问这个变量的代码通常用宏实现.以增强代码的可读性。例如,TCP用这个变量存.储tcp_skb_cb结构,这个(广告)
结构在include/net/tcp.h中定义.: .
stru.ct tcp_skb_cb {健康
     ... ... ....            杀毒
     _ _u32         seq;    .     /* Starting sequenc.e number */    美容
     _ _u32        . end_seq;     /* SEQ + FIN + S.YN + datalen*/    美容
     _ _u32         wh.en;        /* used to compute rt.t's     */    健康
     _ _u8 .         flags;       /* TCP he.ader flags.         */--------------彩票
     ... .... ....
};
下面这个宏被TCP代码用.来访问cb变量。在这个宏里面,.有一个简单的类型转换: --- 印刷
#define TCP._SKB_CB(_ _skb)     ((struct tcp._skb_cb *)&((_ _skb)->cb[0]))服务器
下面的例子是TCP子系统在收..到一个分段时填充相关数据结构的代码: .
int. tcp_v4_rcv(struct sk_.buff *skb)学习
{
         ... ... ....            杀毒
         th =. skb->h.th;投资
         .TCP_SKB_CB(skb)->.seq = ntohl(th->seq);(        游戏          )
.        TCP_SKB_CB(skb)->end_se.q = .(TCP_SKB_CB(skb)->seq + th->syn + th->fin +.
                  .           .        skb->len - th->doff * 4);--- 印刷
         TCP_SKB_CB(skb)->ack_seq .= ntohl(th->ack_seq).;             汽车
        . TCP_SKB_CB(skb)->when = 0;.[成人用品]
         TCP_SKB_CB(skb)->flags = s.k.b->nh.iph->tos;.
        . .TCP_SKB_CB(skb)->sacked = 0;.
         .... ... ....
}
如果想要了解cb中的参.数是如何被取出的,可以查看net/ip.v4/tcp_output.c中的tcp_transmit_skb函数。这.个函数被TCP用于向IP层发送一个分段。           婚庆
unsigned int cs.um.
unsig.ned char ip_summed虚拟主机
表示校验和以及相关状态标记。. 健康
uns.igned char cloned学习
一个布尔.标记,当被设置时,表示这个结构是另一个sk_buff的克隆。在“克隆和拷贝缓冲区”.一节中有描述。 (广告)
unsigned char pkt_.type.
这个变量表示帧的类型,分类是由L2的目的地址来.决定的。可能的取值都在include/.linux/if_packet.h中.定义。对以太网设备来说,这个变量由eth_type_trans函数初始化。           婚庆
类型的可能取值如下:
PACKET_HOST
包的目的.地址与收到它的网络设备的L2地址相等。换句话说,这个包是发.给本机的。            鲜花
.PACKE.T_MULTICAST<性病>
包的目的地址是一个多播地址,而这个多播地址是收到这个包的.网络设备.所注册的多播地址。 虚拟主机
PACKET_BR.OADCAST           鲜花
包的目的地址是一个广播地址,而这个.广播地址也是收到这个包的网络设备的.广播地址。 .
P.ACKET_OTHERHOST服务器
包的目的地址与收到它的网络设备的地址完全.不同(不管是单播,多播还是广播),因此,如果本机的转发功能没有启用,这.个包会被丢弃。 .
PACKET_OUTGOIN.G              乙肝
这个包将被发出。用到这个标记的功能包括D..ecnet协议,或者是为每个网络tap都复制一份发出包的函数。              汽车
PACKET_LOOPBA.CK    健康
这个包发.向loopback设备。由于有这个标记,在处理.loopback设备时,内核可以跳过一些真实设备才需要的操作。 --- 印刷
PACKET_FA.STROUTE--- 印刷
这个包由快速.路由代码查找路由。快速路由功能在2.6.内核中已经去掉了。 教育
_ _u32. priority.

个变量描述.发.送或转发包的QoS类别。如果包是本地生成的,socket层会设置priority变量。如果包是将要被转发的,             汽车
rt_tos2priority函数会根.据ip头中的Tos域来计算赋给这个变量的值。这个变量的值与D.SCP(DiffServ.
CodePoint)没有任何关系.。 .
unsigned short p.rotocol教育

个变量是高层协议从二层设备的角度所看到的协议。典型的协议包括IP,IPV6和ARP。完整的列表.在include/l.inux/if_ether.h           建材
中。由于每个协议.都有自己的协议处理函.数来处理接收到的包,因此,这个域被设备驱动用于通知上层调用哪个协议处理函数。每个网络驱动都调用           鲜花
netif_rx来通知上层网络协议.的协议处理函数,因此pro.tocol变量必须在这些协议处理函数调用之前初始化。              电子
unsigne.d short security投资
这是.包的安全.级别。这个变量最初由IPSec子系统使用,但现在已经作废了。 .
4. Feature.-Spec.ific Fields.
linux内核是模块化的,你可以选择包含或者删除.某些功能。因此,sk_buff结构里面的一些成员变量只有.在内核选择支持某些功能时才有效,比如防火墙(netfilter)或者qos.: 域名
unsigned .long nfmark           鲜花
_ _u32 nfcache
_ _u32 n.fctinfo教育
struct nf_con.ntrack *nfct服务器
unsigned. int nfdebug(        游戏          )
struc.t nf_bridge_info *.nf_bridge学习

些变量被ne.tfilt.er使用(防火墙代码),内核编译选项是“Device Drivers->Networking电脑
support-> Netw.orking. options-> Network packet<性病>
filtering”和两个子选项“Network packet filtering debugging”和“Bridge..d IP/ARP.
packets fil.tering” 投资
union .{...} private(        游戏          )

个联合结构被高性能并行接口(HIPP.I)使用。相应的内核编译选项是“Device->Drivers ->Net.working[成人用品]
suppo.rt ->N.etwork device support ->HIPPI driver support” (广告)
_ _u32 t.c_index           鲜花
_ _u32 tc_verd
_ _u32 tc_cla.ssid.

些变量.被流量控制代码使用,内核编译选项是“Device Driv.ers ->Networking->support学习
-.>Networking options ->QoS and/or fai.r             电子
queueing”和它的子选项“Packet.c.lassifier API” 服务器
struct sec._path *sp    美容
这个变.量被IPSec协议用于跟踪传输的信息。     外汇
5. Managemen.t Functions(        游戏          )
有很多函数,.通常都比较短小而且简单,内核.用这些函数操作sk_buff的成员变量或者sk_buff              乙肝
链.表。图4会帮助我们.理解其中几个重要的函数。我们首先来看分配和释放缓冲区的函数,然后是一些通过移动指针在缓冲区的头部或尾部预留空间的函数。 .
如果你看过include/linux/skbuff..h和net/core/s.kbuff.c中的函数,你会发现,基.
本上每个函数都有两个版.本,名字分别是do_something.和__do_something。通常第一种函数是一个包装函数,它会在第二种函数的基础.
上增加合法性检查或者锁。一般来说,类似__do_someth.ing的函数不能被直接调用(除非满足特定的条件,比如说锁)。那些违反这条规则.而直接引学习
用这些函数的.不良代码会最终被更正。 电脑
Figure 4. Before an.d after: (a)skb_put, (b)skb_push, (c.)skb_pull., and (d)skb_reserve          婚庆
http://upload.bbs.csuboy.com/Mon_1004/126_6912_ca94fce3843296e.jpg[/img]--- 印刷

5.1. Allocating memory: a.lloc._skb and dev_alloc_skb电脑
alloc_skb是net/core/.skb.uff.c里面定义的,用于分配缓冲区的函数。我们已经知道,数据缓冲区.
和缓冲.区的描述结构(sk_buff结构)是两种不同的实体,这就意味着,在分配一个缓冲区时,需要分配两块内存(.一个是缓冲区,一个是缓冲区的描述结构[成人用品]
sk_buff)。
alloc_skb调用函数kmem_cache_alloc从缓存中获取一个sk_buff结构,并调.用kmalloc分配缓冲区(如果有缓存.的话,它同样从缓存中获取内存).。简化后的代码如下:              汽车
     skb = kmem_cache_alloc(skbuff_he..ad_cache, gfp_mask & ~_ _GFP_DMA);.
     ... .... ...             汽车
   .  size = SKB._DATA_ALIGN(size);    外汇
     data = kmalloc(size + sizeof(struct skb_shared_info.), gf.p_mask);外贸
在调用kmalloc前,size参数通过SKB_DATA_ALIGN宏强制对齐。在函数返回前.,它会初始化结构中的一些变量,最后的结构如图5所示。在图5右.边所示的内存块的底部,你能看到对.齐操作所带来的填充区域。 .
F.igure 5. alloc_.skb function.
http://upload.bbs.csuboy.com/Mon_1004/126_6912_b230358a365852a.jpg[/img]    外汇

dev_alloc_skb也是一个缓冲区分配函数,它主要被设备驱.动使用,通常用在中断上下文中。这是一.个--------------彩票
allo.c_skb函数的包装函数,它会在请求分.配的大小上增加16字节的空间以优化缓冲区的读写效率,它的分配要求使用原子操作(        游戏          )
(GFP_ATO.MIC),这是因为它是.在中断处理函数中被调用的。               乙肝
stat.ic inline struct sk_buff *dev_alloc_skb(unsigned int .length)健康
{
     return _ ._.dev_alloc_skb(length, GFP_ATOMIC);学习
}
static inline
stru.ct sk_buff *_ _dev_alloc_skb(unsigned in.t length, int gfp_mask)    健康
{
     struct sk._buff *skb = alloc_skb(length + 16, gfp_.mask);健康
     if. (likely(skb))             电子
             skb_re..serve(skb, 16);.
     return. skb;             电子
}
如果没有体系架构相关的实现,缺省.使用__dev_alloc_skb的.实现。               乙肝
5.2. Free.ing memory: kfree_skb and dev_.kfree_skb           女人
这两个函数释放缓冲区,并把它返回给缓冲池(缓存)。kf.ree_skb可以直接调用,也可以通过.包装函数电脑
dev_.kfree_skb调用。后面这个函数一般被设备驱动使用,与之功能相反的函数是dev_alloc_skb。dev_kfree._skb仅是一    美容
个简单的宏,它什么都不做,只简单地调用kfree_skb。这些函数只有在skb->users为1地情况下才释放内存(没有人引用这.个结构).。    外汇
否则,它只是简单地减小
skb->users。如果缓冲区有三个引用者,那么只有第三次.调用.dev_kfree_skb或kfree_skb时才释放内存。             杀毒
图6中的流程图显示了分配一个缓冲区所需要的.步骤。当sk_buff释放后,dst_release同样会被调用以减小相关d.st_.entry数据结构的引用计数。              电子
如果destructor被初.始化过,相应的函数会在此时.被调用..
在图5中,我们看到,一个简单的.场景是:一个sk_buff结构与另一个内存块.相关,这个内存块里存储的是真正的数据。.
当然,内存块底部的skb_shared_info数据结构可以包含指向其他.分片的指针(参见图5)。如果存在分片,kfree_skb同样会释放这.些分电脑
片.所占用的内存。最后,kfree_skb 把sk_buff结构返回给skbuff_head_c.ache缓存。              汽车
5.3. Dat.a reservation and alignment: skb_r.eserve, skb_put, skb_push, and. skb_pull--------------彩票
skb_r.eserve可以在缓冲区的头.部预留一定的空间,它通常被用来在缓冲区中插入协议头或者在某个边界上对齐。这.
个函数改变data和tail指针,而data和tail指针分别指向负载的开头和结.尾,图4(d)展示了调用skb_reserv.e(skb,n)的结          婚庆
果。这个函数通常在分配缓冲区之后就调用,.此时的               乙肝
data.和tail指针还是指向同一个地方。 --- 印刷
如果你查看某个以太网.设备驱动的收包函数(例如,drivers/net/3c59x.c中的vortex_rx), 你就会发现它在分配缓冲区之后,在向缓冲区中填充数据之前,会调用下面.的函数: .             汽车
skb_reserve(skb,. 2);     /* Align IP on. 16 byte boundaries */外贸
Figure 6. kfree_skb func..tion             电子
http://upload.bbs.csuboy.com/Mon_1004/126_6912_3a913c63209b135.jpg[/img]           建材

由于.以太网帧的头部长度是14个八位组,这个函数把缓冲区的头部指针向后移.动了2个字节。这样,紧跟在以太网头部之后的I.P头部在缓冲区中存储时就可以在16字节的边界上对齐。如图7所示。 虚拟主机
Fi.gure 7. (a) before skb_reserve, (b) after skb_reserve, and (c) after copying the frame o.n the bu.ffer.
http://upload.bbs.csuboy.com/Mon_1004/126_6912_591eb4c1d2e4dd7.jpg[/img]虚拟主机

图8.展示了一个在发送过程中使用skb._reserve的例子。            建材
Figure 8. Buffer .that is filled in while trave.rsing the stack .from the TCP layer down to the link layer              乙肝
http://upload.bbs.csuboy.com/Mon_1004/126_6912_ea4fe0e084cbdb9.jpg[/img].



当TC..P发送数据时,它根据一些条件分配一个缓冲区(比如,TCP的最大分段长度(mss),是否支持散读散写I/O等--------------彩票


TCP.在缓冲区的.头部预留足够的空间(用skb_reserve)用于填充各层的头部(如TCP,IP,链路层服务器
等.)。MAX_TCP_HEADER参数是各层头.部长度的总和,它考虑了最坏的情况:由于tcp层不知道将要用哪个接口发送包,它为每一层预留了最大的头健康
部长度。它甚至考虑了出现多.个IP头的可能性(如果内.核编译支持IP over IP, 我们就会遇到多个IP头的情况)。            建材


把TCP的负载拷贝到缓冲区。需要注意的是:图8只是一个例子。TCP的负载可能会被组织成其他形式。例如它可.以.存储到分片中。学习


TCP层添加自己的头部。


TCP层把缓冲区.传递给IP层,IP层同样添加自己的头部。 .          婚庆


I.P层把缓冲区传递给邻居.层,邻居层添加链路层头部。 (        游戏          )
当缓冲区在协议栈中向下层传递时,每一层都把sk.b->data指针向下移动,然后拷贝自己的头部,同时更新.skb->len。这.些操作都使用图4中所展示的函数完成。            女人
skb_reserve函数并没有把数.据移出或.移入缓冲区,它只是简单地更新了缓冲区的两个指针,这两个指针在图4(d)中有描述。            鲜花
static inl.ine void skb_.reserve(struct sk_buff *skb, unsigned int len)电影
{
     s.kb->data+=len;电脑
     skb->tai.l+=len;学习
}
skb_push在缓冲区的开头加入.一块数据,而skb_put在缓冲区的末尾加入一块数据。与skb_re.serve.
类似,这些函数都不会真的往缓冲区中添加数据,相反,它们只是移动缓冲区的头指针和.尾指针。数据由其他函数拷贝到缓冲区中。skb_pull通过把head指针往前移来在缓冲区的头部删除一块数据。图4展示了这些函数是.如何工作.的。            女人
2.1.5.4. Th.e skb_shared._info structure and the skb_shinfo function<性病>
如图5所示,在缓.冲区数据的末尾,有一个数据结构skb_shar.ed_info,.它保存了数据块的附加信息。这个数据结构紧跟在end指针所指的地址之后(end指针指示数据的末尾)。下面是这个结构的定义: 域名
struct skb_sh.ared_info {
     atomic_t      .   dat.aref;投资
     uns.ig.ned int     nr_frags;投资
  .   unsigned. short   tso_size;(广告)
     .unsigned short   tso_.seqs;.
     struct sk_buff   *.fra.g_list;--- 印刷
     skb_frag_t       .fr.ags[MAX_SKB_FRAGS];(        游戏          )
};
dataref表示数据块的“用户”数,..这个值在下一节(克隆和拷贝缓冲区)中有描述。nf_frags,.
frag_list和frags用于存储IP分片。.skb_is_nonlinear函数.用于测试一个缓冲区是否是分片的,而skb_linearize[成人用品]
可.以把分片组合成一个单一的缓冲区。组合分片涉及到数据拷贝,它将严重影响系统性.能。 .
有些网卡硬件可以完成一些传统上由CPU完成的任务。最常见的例子就是计算L3和L4校验和。有些.网卡.甚至可以维护L4电影
协议的状态机。在下面的例子中,.我们主要讨论TCP段卸载T.CP segmentation.
offload,这些网卡实现了TCP层的一部分功能。tso_size和tso_s.eqs就.在这种情况下使用。 (广告)
需要注意的是.:sk_buff中没有指向skb_shared_info结构的指针。如果要访问这个结构, 就需要使用skb_info宏.,这个宏简单地.返回end指针: .
#define skb_shinfo(SKB)     .((struct skb_shared_info *)((SKB)->e.nd))学习
下面的语句.展示了如何使用这个宏来增加结构中的某个成员变量的.值: 外贸
skb_.shinfo(skb)->dataref++.;    健康
2.1.5.5.. Cloning and copying. buffers服务器
如果一个缓冲区需要被不同的用.户独立地操作,而这些用户可能会修改sk_buff中某些变.量的值(比如h和nh值),内            杀毒
核没有必要为每个.用户复制一份完.整的sk_buff以及相应的缓冲区。相反,为提高性能,内核克隆一个缓冲区。克隆过程只复制sk_buff结构,服务器
同时修改缓冲区的引用计数以避.免共享的数据被提前释放。克隆缓冲区.使用skb_clone函数。 --------------彩票
一个使用包克隆的场景是:.一个.接收包的过程需要把这个包传递给多个接收者,例如包处理函数或者一个或多个网络模块。 电脑
被克隆的sk_buff不.会放在任何链表中,同时也不会有到socket的引用。原始的.和克隆的sk_buff中的--------------彩票
skb->cloned值都被置为1。克隆..包的skb->users值被置为1,这样,在释放时,可以先释放sk_buff结构。同时,缓冲域名
区的引用计数(dataref)增加1(因为有多个sk_buff结构指向它)。图9展示了克隆.缓冲区的例子。.
F.igu.re 9. skb_clone function    美容
http://upload.bbs.csuboy.com/Mon_1004/126_6912_49816fe9e9cc9a1.jpg[/img]电影

skb_.cloned函数可以用来测试sk.b的克隆状态。              汽车
图9展示了一个分片.缓冲区的例子,这.个缓冲区的一些数据保存在分片结构数组frags中。 .
skb_share_check用于检查引用计数skb->users,如果users变.量表明skb是被共享的, 则克隆一个新的sk_buff。. 电影
如果一个缓冲区被克隆了,这个缓冲.区的内容就不能被修改。这.就意味着,访问数据的函数没有必要加锁。因此,当一个函数不    健康
仅要修.改sk_buff,而且要修改缓冲区内容时,<性病>
就需要同时复制缓冲区.。在这种情况下,程序员有两个选择。如果他知道所修改的数据在skb->start和s.kb->end           建材
之间,他可以使用pskb_copy来复制这部分数据。如果他同时.需.要修改分片中的数据,.他就必须使用skb_copy。图10展示了pskb_copy和skb_copy的结果。skb_shared_info结构也可以包含一个 .
sk_bu.ff的链表(链表名称是frag_list)。这个链表在pskb_copy和sk.b_copy中的处理方式与frags数组的处理方式相同(图10忽.略了frag_list)。 .
Figure 10. (a) .pskb_copy function and (b) skb_copy. function             汽车
http://upload.bbs.csuboy.com/Mon_1004/126_6912_4dac0cdfb3d694c.jpg[/img]投资

在决定克隆或复制一个缓冲区时,子系统的程序员不能预测其他内核组件(其.他子系统)是否需要使用缓冲区里的原始数据。.内.
核是模块化的,它的状态变化是不可预测的,因此,每个子系统都不知道其他子系统是如何操作缓冲区的。因此,内核程序员.需要记录它们对缓冲区的修改,并.且在学习
修改缓冲.区前,复制一个新的缓冲区.,以避免其他子系统在使用缓冲区的原始数据时出现错误。 .
2..1.5.6. L.ist management functions学习
这些函数管理sk_buff的链表(也被称作队.列).。在和中有函数完整列表。以下是一些经常使用的函数: [成人用品]
skb_q.ueue_head_init.
初始.化sk_buff_he.ad结构,创建一个空队列。 健康
skb_queue_head, skb_q.ueue._tail--- 印刷
把一个缓冲区加.入队列的头部或尾部。 投资
skb_d.equeue, skb_deq.ueue_tail(广告)
从队列的头部或尾部取下一个缓冲区。第一个函数的名字应该是.skb_dequeue_head,以保持和其他函.数的名字风格一致。 电脑
skb_que.ue_purge    外汇
清空一个队列。
skb_queue_walk
Runs a loop on e.ach element of a queue in. turn.教育
这些函数都是.原子操作,它.们必须先获取sk_.buff_head中的自旋锁,然后才能访问队列中的元素。否则,它们有可能被其他异步的添加或删除操作打断,比如在定时器中调用的函数,这将导致链表出现错误而使得系统崩溃。 (        游戏          )
因此,每个函数的实现都采用下面这.种形式:     外汇
sta.tic. inline function_name ( parameter_list )[成人用品]
{
         un.signed long fl.ags;域名
         spin_.lock_irqsave(....);虚拟主机
        . _ _ _function_name ( .parameter_list )电脑
.        spin_unlock_irqrestore(....);.
}
这些函数先获取锁,然后调用一个以两.个下划线开头的同名函.数(这个函数做具体的操作,而且不需要锁),然后释放锁。<性病>
引用:http://hi.baidu.com/zshidong/blog/item/47c5bb10f15f1d00213f2eb0.html虚拟主机
摘自 http://www.52rd.com/Blog/Detail_RD.Blog_zjhfqq_7183.html--- 印刷
          .                 建材
       .         .
      .                     女人
         .       外贸
         .       <性病>

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/69947/showart_1092353.html             电子

评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

无聊

灌水
快速回复
限100 字节
如果您在写长篇帖子又不马上发表,建议存为草稿
 
上一个 下一个