数据包接收函数利用pt_prev减少一次skb复制

TCPIP协议 专栏收录该内容
105 篇文章 6 订阅

接收函数__netif_receive_skb_core,负责把驱动层收到的数据包传递给上层协议处理,其中包括ip层(ip_rcv)、arp层(arp_rcv)、sniffer类程序(tcpdump)等。利用pt_prev延缓执行最后一个协议处理函数,可减少一次skb复制。

所谓减少一次skb复制,首先来看复制的代码位置。在上层协议处理函数中,当需要改变skb结构体的内容时,要拷贝(skb_clone)一份协议自己的副本。以ip_rcv为例来看拷贝相关代码:

376 int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
377 {
390     if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
392         goto out;
393     }

复制代码位于skb_share_check中:

1177 static inline struct sk_buff *skb_share_check(struct sk_buff *skb, gfp_t pri)
1178 {      
1180     if (skb_shared(skb)) {
1181         struct sk_buff *nskb = skb_clone(skb, pri);    //复制代码位于此处
1187         skb = nskb;
1188     }
1189     return skb;
1190 }

拷贝与否还要看skb_shared的返回结果,这即是关键之处,通过控制skb->users的数值来控制是否复制skb:

1159 static inline int skb_shared(const struct sk_buff *skb)
1160 {
1161     return atomic_read(&skb->users) != 1;
1162 }

至此,来看__netif_receive_skb_core函数的控制逻辑,首先,调用deliver_skb处理所有注册的协议处理函数,最后一个除外,不进行处理:

3619 static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
3620 {
3665     list_for_each_entry_rcu(ptype, &ptype_all, list) {  //遍历sniffer类程序
3666         if (pt_prev)                                    //初始化为空NULL
3667             ret = deliver_skb(skb, pt_prev, orig_dev);  //调用上一个协议处理函数
3668         pt_prev = ptype;  //保存当前的协议处理结构。遍历结束时保存的是最后一个未执行的协议处理结构
3669     }

函数deliver_skb,增加skb->users的计数(等于2),导致skb_shared(skb)为真,上层协议处理函数ip_rcv复制一份skb,递减原skb的引用计数,处理完成后释放复制的skb:

1728 static inline int deliver_skb(struct sk_buff *skb,
1729                   struct packet_type *pt_prev,
1730                   struct net_device *orig_dev)
1731 {
1732     if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
1733         return -ENOMEM;
1734     atomic_inc(&skb->users);   // 增加users值,skb_shared判断为真,复制skb
1735     return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
1736 }

函数__netif_receive_skb_core结尾处,不调用deliver_skb,不增加skb->users计数,直接调用协议处理函数,由于skb->users计数等于1,skb_shared(skb)结果为假,省去了一次skb的复制(同时省去了相应的释放操作)。因为是最后一个协议处理函数,无需再复制,最后skb在此函数内完全释放。

3619 static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
3620 {
3747     if (pt_prev) {
3748         if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
3749             goto drop;
3750         else                 //不同于deliver_skb,调用处理函数之前不再增加skb->users值
3751             ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);  //省去最后这次协议处理的skb复制
3752     }
内核代码基于Linux-4.0。
  • 0
    点赞
  • 0
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值