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

看见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。

struct sk_buff( )
{
....atomic_set(&skb->users, 1);
...
} 

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

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

当引用数为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链的作用之后,就认为为什么不用下面的算法实现。

for (ptype=ptype_base[ntohs(type)&15];ptype;ptype=ptype->next) {if (ptype->type == type && (!ptype->dev || ptype->dev == skb->dev)) {atomic_inc(&skb->users);ptype->func(skb, skb->dev, ptype);}
}
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中的代码。

for (ptype=ptype_base[ntohs(type)&15];ptype;ptype=ptype->next) {if (ptype->type == type && (!ptype->dev || ptype->dev == skb->dev)) {if (pt_prev) {if (!pt_prev->data)deliver_to_old_ones(pt_prev, skb, 0);else {/*到这里,那么pt_prev指针不为空,ptype(当前的)不为空,那么肯定要共享一次了,所以加1*/atomic_inc(&skb->users);pt_prev->func(skb,skb->dev, pt_prev);/*执行上面的函数之后,会在里面减1。所以相对来说,上面两句代码执行之后并没有对skb->users的值进行影响。*/}}pt_prev = ptype;
}
}/*现在skb->users的计数还是为1*/
if (pt_prev) {if (!pt_prev->data)deliver_to_old_ones(pt_prev, skb, 1);else/*在这里就没有用atomic_inc(&skb->users);因为到这里,skb->users就为1,并且这里是最后一次,所以不用加1,那么skb就直接在下面的pt_prev->func(skb, skb->dev,   pt_prev);函数中释放了。*/pt_prev->func(skb, skb->dev, pt_prev);
} else{/*到这里,已经没有对skb进行操作的了。所以必须调用kfree_skb把skb释放掉。*/kfree_skb(skb);
} 

你看,<二>和<三>相比是否少了一次调用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等函数都有一个

    if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)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去。 因为可能有的人不需要。

转载于:https://www.cnblogs.com/4a8a08f09d37b73795649038408b5f33/p/11545031.html

关于ptype_all和pypte_base中的pt_prev的说明[转]相关推荐

  1. chinaunix 内核源码分析精贴

    1. 内核学习方法,编译.调试等常见问题 1.1 关于编译升级内核到2.6.0的一些问题 作者:ommm         http://linux.chinaunix.net/bbs/thread-2 ...

  2. udp数据报从网卡驱动到用户空间流程总结

    附有相关介绍资料 NAPI驱动流程:     中断发生     -->确定中断原因是数据接收完毕(中断原因也可能是发送完毕,DMA完毕,甚至是中断通道上的其他设备中断)     -->通过 ...

  3. GNU Make 使用手册(于凤昌中译版)

    GNU Make 使用手册(中译版) 翻译:于凤昌 GNU make Version 3.79 April 2000 Richard M. Stallman and Roland McGrath 1 ...

  4. Linux内核分析 - 网络[五]:vlan协议-802.1q

    内核版本:2.6.34 802.1q 1. 注册vlan网络系统子空间, [cpp] view plaincopy err = register_pernet_subsys(&vlan_net ...

  5. sk_buff属性详解

    第一部分: skb - Linux network buffers skb--Linux网络缓存 Harald Welte laforge@gnumonks.org 1.3, 2000/10/14 2 ...

  6. 面试:第十二章:所有总结

    Java基础 java基本类型哪些,所占字节 byte :1个字节 short :2个字节 char :2个字节 int :4个字节 long :8个字节 float :4个字节 double :8个 ...

  7. linux内核分析(转自某位大哥网上的笔记)

    启动 当PC启动时,Intel系列的CPU首先进入的是实模式,并开始执行位于地址0xFFFF0处的代码,也就是ROM-BIOS起始位置的代码.BIOS先进行一系列的系统自检,然后初始化位于地址0的中断 ...

  8. 内核网络中的GRO、RFS、RPS技术介绍和调优

    内核网络中的GRO.RFS.RPS技术介绍和调优 1. 前言 2. GRO(Generic Receive Offloading) 2.1 使用 ethtool 修改 GRO 配置 2.2 napi_ ...

  9. 网卡驱动和队列层中的数据包接收

    一.从网卡说起 这并非是一个网卡驱动分析的专门文档,只是对网卡处理数据包的流程进行一个重点的分析.这里以Intel的e100驱动为例进行分析. 大多数网卡都是一个PCI设备,PCI设备都包含了一个标准 ...

最新文章

  1. oracle java rmi 漏洞,Oracle Java SE Java运行时环境RMI子组件远程漏洞(CVE-2011-3556)
  2. AI科技大本营招实习生了!
  3. 原来访问网页弹出cookie是这样的
  4. apache将请求转发到到tomcat应用
  5. ECS之Component组件
  6. 计算机无法读取exe文件,电脑打不开exe文件怎么解决
  7. Jon的Java程序员实习报告
  8. DEVC++实现街头篮球(爷青回)(1.01版)
  9. 爱快软路由设置DHCP多个LAN处于同一网段
  10. [转]嵌入式Web服务器
  11. Elasticsearch 8.x 破解x-pack-core
  12. 英飞凌SP370方案胎压监测器-入门级视频课程-朱有鹏-专题视频课程
  13. Flutter 单元测试
  14. 微信小程-截取小数点的位数
  15. 二进制文件和文本文件
  16. 二代身份证读卡器的二次开发
  17. 用Blog搭建同学录网络
  18. 为什么 dot-product attention 需要被 scaled?
  19. 计算机网络工程英语能力,新颖的计算机网络工程论文参考文献 计算机网络工程英语参考文献哪里找...
  20. OpenPose相关资料整理

热门文章

  1. 天文学专业在什么时候学计算机,南京大学在985排名第几?南京大学最牛的专业是天文系吗?...
  2. django2.1支持的mysql版本_一文解决django 2.2与mysql兼容性问题
  3. java 多态实现的jvm调用过程_多态:JVM是如何进行方法调用的
  4. bcc挖矿用什么_BCC对BTC挖矿有何影响?
  5. 域内计算机如何同步网络t时间,网络节点的同步方法
  6. 形容人的内核是什么意思_识人核心是什么?
  7. treegrid 如何获取getchanges inserted_如何避开Vue性能优化之路的荆棘?
  8. 在计算机桌面上添加小工具日历,实用桌面小工具时钟日历在win7中的添加方法...
  9. c 通过jni调用java_使用c通过jni调用java
  10. HTML+CSS+JS实现 ❤️canvas 3D云动画效果❤️