不知道原帖,我是从这里看到了,解决了迷惑我很久的疑问,抄过来。

看见noble_shi兄弟"关于net_rx_action函数的若干问题"贴中关于pt_prev的问题, 本来想在论坛上找到一个相关的帖子的链接告诉他。但是发现咱们论坛上关于pt_prev的讨论要么没有说明,要么理解的偏差,甚至是错误。而且关于pt_prev的提问很多。故写了以下内容。



    不过本人水平有限,难免说错。请执教getmoon@163.com



    结论:pt_prev使用的原因是为了减少一次kfree_skb的调用,提高效率。



    如果有异议的请往下看。如果你对skb非常了解,那么请直接看<三>, 否则请一步一步往下看。



<一>相关知识

在讲pt_prev的作用之前, 咱们先说明以下的东西。

(1)alloc_skb中初始化skb->users计数为1。

  1. struct sk_buff( )
  2. {
  3. ....
  4. atomic_set(&skb->users 1);
  5. ...
  6. }

(2)kfree_skb中如果计数skb->users不为1则不会释放skbuff 。

  1. static inline void kfree_skb(struct sk_buff *skb)
  2. {
  3. if (atomic_read(&skb->users) == 1 || atomic_dec_and_test(&skb->users))
  4. __kfree_skb(skb);
  5. }

当引用数为1或者引用数减1等于零时, 回收包缓冲。



(3)linux内核网络协议栈中到本机的skb包是在上层协议中释放的。



<二>实现ptype_base和ptype_all链


    讲了上面的东西后咱们来看ptype_base及ptype_all链相关的东西。这两个链的作用在这里就不讲了。 因为有了上面的东西,
所以涉及到一个skbuff共享的问题, 如果都用skb_clone或者skb_copy,那么性能将是很低的。
所以在linux中使用了skb共享的计数,就是用skb->users计数来计算共享的地方。

许多人理解了ptype_all和ptype_base链的作用之后,就认为为什么不用下面的算法实现。

  1. for (ptype=ptype_base[ntohs(type)&15];ptype;ptype=ptype->next) {
  2. if (ptype->type == type && (!ptype->dev || ptype->dev == skb->dev)) {
  3. atomic_inc(&skb->users);
  4. ptype->func(skb, skb->dev, ptype);
  5. }
  6. }
  7. kfree_skb(skb);

我来说说为什么最后还要一个kfree_skb。在进入这个for循环之前,skb->users的计数为1,每进入一个ptype->fun函数之前都会加,并且在每个ptype->fun函数里面都会有kfree_skb函数(会减users),但是并没有真正的把skb释放掉。还记得刚开始说明中kfree_skb里面的atomic_dec_and_test(&skb->users)吗。 所以atomic_inc(&skb->users);ptype->func(skb, skb->dev, ptype);两句代码执行之后并没有改变skb->users的计数,还是1。所以这样可以在for循环中循环好几次, 无论几个ptype->func都可以共享这个skb。退出for循环之后, skb->users还是1,并且之前并没有真正的释放掉内存。因此你要调用kfree_skb(skb) 来释放内存。

   
再说明一下另外一个问题,如果for循环就调用了ptype->func函数一次的话,那么在整个包的流程中,是调用了kfree_skb两次。
一次在ptype->func函数中,第二次是在for循环之后, 就是上面代码中的kfree_skb 。

说了上面的这个例子之后, 如果你现在知道了为什么用pt_prev来提交效率,那么你就不用往下看了。



<三> 利用pt_prev来提高效率


    理解了上面的内容之后,咱们来看看2.4中的代码。

  1. for (ptype=ptype_base[ntohs(type)&15];ptype;ptype=ptype->next) {
  2. if (ptype->type == type && (!ptype->dev || ptype->dev == skb->dev)) {
  3. if (pt_prev) {
  4. if (!pt_prev->data)
  5. deliver_to_old_ones(pt_prev, skb, 0);
  6. else {
  7. /*到这里,那么pt_prev指针不为空,ptype(当前的)不为空,那么肯定要共享一次了,所以加1*/
  8. atomic_inc(&skb->users);
  9. pt_prev->func(skbskb->dev, pt_prev);
  10. /*执行上面的函数之后,会在里面减1。所以相对来说,上面两句代码执行之后并没有对skb->users的值进行影响。*/
  11. }
  12. }
  13. pt_prev = ptype;
  14. }
  15. }
  16.  
  17. /*现在skb->users的计数还是为1*/
  18. if (pt_prev) {
  19. if (!pt_prev->data)
  20. deliver_to_old_ones(pt_prev, skb, 1);
  21. else
  22. /*在这里就没有用atomic_inc(&skb->users);因为到这里,skb->users就为1,并且这里是最后一次,所以不用加1,
  23. 那么skb就直接在下面的pt_prev->func(skb, skb->dev, pt_prev);函数中释放了。
  24. */
  25. pt_prev->func(skb skb->dev pt_prev);
  26. } else{
  27. /*到这里,已经没有对skb进行操作的了。所以必须调用kfree_skb把skb释放掉。*/
  28. kfree_skb(skb);
  29. }

你看,<二>和<三>相比是否少了一次调用kfree_skb呢。



    到现在, 你是否理解了为什么么用pt_prev了。

    你是否在为那些家伙的高深之处而感慨那。

    anything i can help u , please email to : getmoon@163.com



后续讨论

ID:rainfall

   
今天我仔细看了一下linux2.2.x的net_bh,我认为pt_prev的作用是减少一次skb_clone(当然也少一次kfree_skb)。得出这个结论的理由是:每次在处理skbuff时,相关的处理都会复制一次skbuff的头。如果链表上有n个元素,就要复制n次,然后还有释放n次。最后还要释放结构本身。但是如果只复制n-1次,最后处理的就是数据本身(引用计数为1)。这样会少复制一次。不过getmoon的说法也没错,只是我觉得从复制的角度看,可能更能体现


出高效的主题。毕竟,释放并不花什么时间。

ID:getmoon

    实际上是这样的,2.2的net_bh里面也采用了这个pt_prev。 它的功能还是如我所言。

兄弟看见的是在调用每个pt->func之前clone了一个。 实际上这个clone在2.4里面并没有去掉。 只是把它移动每个具体的pt->funct里面。 你可以看arp_rcv , ip_rcv等函数都有一个

  1. if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
  2. goto out_of_mem;

这个函数实际上也是clone一个。而2.2的每个pt->func如arp_rcv , ip_rcv里面都是没有的。
因为在调用pt->func之前就clone了。所以2.4的做法是实际上把skb_clone往后移动了。
为什么呢。我想这个还是重效率上考虑的。

    我想作者的想法是:如果在pt->func函数里面根本没有必要skb_clone一下, 我为什么

在硬给它clone一个呢。如果呢需要新的skb头,那么呢自己clone去。 因为可能有的人不需要。

关于ptype_all和pypte_base中的pt_prev的说明[转]的更多相关文章

  1. ptype_base和ptype_all学习笔记

    "linux-2.6.32/include/linux/netdevice.h" struct packet_type { __be16 type; /* This is real ...

  2. 一文彻底搞通TCP之send & recv原理

    接触过网络开发的人,大抵都知道,上层应用使用send函数发送数据,使用recv来接收数据,而send和recv的实现原理又是怎样的呢? 在前面的几篇文章中,我们有提过,TCP是个可靠的.全双工协议.其 ...

  3. Python开源框架

    info:更多Django信息url:https://www.oschina.net/p/djangodetail: Django 是 Python 编程语言驱动的一个开源模型-视图-控制器(MVC) ...

  4. Linux内核中流量控制

    linux内核中提供了流量控制的相关处理功能,相关代码在net/sched目录下:而应用层上的控制是通过iproute2软件包中的tc来实现, tc和sched的关系就好象iptables和netfi ...

  5. Alan Cox:单向链表中prev指针的妙用

    之前发过一篇二级指针操作单向链表的例子,显示了C语言指针的灵活性,这次再探讨一个指针操作链表的例子,而且是一种完全不同的用法. 这个例子是linux-1.2.13网络协议栈里的,关于链表遍历& ...

  6. ip_rcv 中使用skb_share_check

    /* * Main IP Receive routine. */ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct pack ...

  7. TCP/IP协议栈在Linux内核中的运行时序分析

    网络程序设计调研报告 TCP/IP协议栈在Linux内核中的运行时序分析 姓名:柴浩宇 学号:SA20225105 班级:软设1班 2021年1月 调研要求 在深入理解Linux内核任务调度(中断处理 ...

  8. mapreduce中一个map多个输入路径

    package duogemap; import java.io.IOException; import java.util.ArrayList; import java.util.List; imp ...

  9. Hadoop 中利用 mapreduce 读写 mysql 数据

    Hadoop 中利用 mapreduce 读写 mysql 数据   有时候我们在项目中会遇到输入结果集很大,但是输出结果很小,比如一些 pv.uv 数据,然后为了实时查询的需求,或者一些 OLAP ...

随机推荐

  1. shell小技巧(1)计算一个文件中空行数量

    方法1: grep -E "^$" 1.txt | wc -l 详解:在网上摘抄,个人觉得不使用-E参数也行,利用正则^$可帅选出空行 方法2: file="1.txt& ...

  2. Zookeeper启动流程分析

    前言 之前的Zookeeper协议篇-Paxos算法与ZAB协议通过了解Paoxs算法开始,到Zab协议的两大特性:崩溃恢复和消息广播,学习了Zookeeper是如何通过Zab协议实现高可用,本篇主要 ...

  3. nginx模型概念和配置文件结构

    一. nginx模型概念: Nginx会按需同时运行多个进程: 一个主进程(master)和几个工作进程(worker),配置了缓存时还会有缓存加载器进程(cache loader)和缓存管理器进程( ...

  4. H5游戏定制,4大优势助力企业曝光10W+

    H5游戏定制,4大优势助力企业曝光10W+ 移动互联网已成为了人们生活的一部分,普通广告形式已很难吸引用户的眼球,企业要怎样才能将广告更广泛的传播给更多用户呢?根据TOM游戏多年从业经验,为大家分享以 ...

  5. PHP的八个魔术常量

    1. 什么魔术常量 预定义常量:预定义常量就是PHP内置的常量,预先定义好的 PHP有很多预定义常量,比如:PHP_VERSION(版本号).PHP_OS(操作系统). 这些普通的预定义常量在程序中的 ...

  6. Mybatis源码如何阅读,教你一招!!!

    前言 前一篇文章简单的介绍了Mybatis的六个重要组件,这六剑客占据了Mybatis的半壁江山,和六剑客搞了基友,那么Mybatis就是囊中之物了.对六剑客感兴趣的朋友,可以看看这篇文章:Mybat ...

  7. 吴恩达《深度学习》-第一门课 (Neural Networks and Deep Learning)-第四周:深层神经网络(Deep Neural Networks)-课程笔记

    第四周:深层神经网络(Deep Neural Networks) 4.1 深层神经网络(Deep L-layer neural network) 有一些函数,只有非常深的神经网络能学会,而更浅的模型则 ...

  8. Docker学习笔记,从原理到实践

    什么是docker Docker是使用go语言基于LINUX内核的cgroup,namespace以及AUFS 类的 Union FS 等技术,对进程进行封装隔离的一种操作系统层面的虚拟化技术,由于隔 ...

  9. [Binder深入学习一]Binder驱动——基础数据结构

    具体代码路径: kernel/drivers/staging/android/binder.c kernel/drivers/staging/android/binder.h /* * binder_ ...

  10. 学习 | canvas实现图片懒加载 && 下滑底部加载

    用canvas实现图片的懒加载并且下滑到据底部60px的时候再次加载数据,模仿UC浏览器的新闻加载. 完整代码:https://github.com/dirkhe1051931999/writeBlo ...