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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/568883.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

禅道的基本使用(创建项目、维护部门、用户、产品、提出需求、创建测试用例等)

禅道的基本使用一、创建项目二、创建维护部门三、添加用户四、创建产品五、提出需求六、创建测试用例禅道作为一个缺陷的管理工具&#xff0c;对于测试者来说其必不可少&#xff0c;下面将介绍禅道的基本使用 一、创建项目 登录禅道&#xff0c;点击项目&#xff0c;创建一个…

C# 延迟初始化 LazyT

概念&#xff1a;延时初始化重点是延时&#xff0c;用时加载&#xff0c;意思是对象在使用的时候创建而不是在实例化的的时候才创建。延时加载主要应用的场景&#xff1a;数据层&#xff08;ADO.NET或Entity Framework等ORM&#xff0c;Java里面的Hibernate也用到了这种技术&am…

简述get 和 post 的主要区别——计算机网络

get是从服务器获取数据&#xff0c;post是向服务器传输数据post相比get更加安全。使用get&#xff0c;在传输过程中&#xff0c;数据被放在URL中&#xff0c;而post对于所有用户来说都是不可见的。受URL的控制&#xff0c;get方式提交的数据最多只能有1024字节&#xff0c;而po…

洛谷 P1816 忠诚题解

题目描述 老管家是一个聪明能干的人。他为财主工作了整整10年&#xff0c;财主为了让自已账目更加清楚。要求管家每天记k次账&#xff0c;由于管家聪明能干&#xff0c;因而管家总是让财主十分满意。但是由于一些人的挑拨&#xff0c;财主还是对管家产生了怀疑。于是他决定用一…

禅道的使用技巧

https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 让这个可爱的宝藏女孩在努力的道路上与你一起同行&#xff01; 如有转载&#xff0c;请注明出处&#xff08;如不注明&#xff0c;盗者必究&#xf…

java删除xml文件中尖括号之外的内容

s s.replaceAll(">[\\s].*<","><").trim();//替换\t 空格 \n s s.replaceAll("\r|\n", "");//替换掉\r\n或者\n\r 或者\r或者\n转载于:https://www.cnblogs.com/CryOnMyShoulder/p/11573841.html

mysql创建表时,表的类型与java的数据类型映射(常用)

在mysql中&#xff0c;列的类型,主要是用来约束这一列的数据类型。mysql中常用列的类型与java中的数据类型对照如下 javamysqlintINTlongBIGINTBigDecimalDECIMALBooleanBITStringVARCHARjava.util.DateDATE/DATETIMEFloatFLOATDoubleDOUBLEhttps://blog.csdn.net/hanhanwangh…

mysql用代码建表基础语法

https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 如有转载&#xff0c;请注明出处&#xff08;如不注明&#xff0c;盗者必究&#xff09; 创建表一、基本创建语法二、常用约束三、建表举例一、基本…

利用可视化软件navicat查看表的sql语句

1.双击你的数据表&#xff0c;右键点击最后一个对象信息 2.再点击DDL&#xff0c;我们就可以看到我们的sql语句啦

[Err] 1064 - You have an error in your SQL syntax check the manual that corresponds to 之Mysql报错

当利用navicat用sql语句建表时候&#xff0c;出现如下报错 从下图我们可以看到&#xff0c;是我们最后一行的末尾多加了一个逗号。 当我们将最后一行的逗号取消掉就正常运行了 特别注意&#xff1a;最后一行的末尾没有逗号。 https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢…

mysql在可视化软件navicat中如何解决中文乱码问题

报错情况 https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 如有转载&#xff0c;请注明出处&#xff08;如不注明&#xff0c;盗者必究&#xff09; sql语句写好点击运行之后出现下列这样的情况&…

mysql中教如何拼接字段(列)值、加入运算、设置别名(非常实用)

https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 如有转载&#xff0c;请注明出处&#xff08;如不注明&#xff0c;盗者必究&#xff09; 在mysql中&#xff0c;在解决实际问题的时候&#xff0c;遇…

mysql模糊查询LIKE、REGEXP(正则)的详解(在可视化工具navicat下)

https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 如有转载&#xff0c;请注明出处&#xff08;如不注明&#xff0c;盗者必究&#xff09; 目录一、总结二、语法三、LIKE举例3.1"%"的用法…

Mysql在可视化工具navicat中如何解决输入小数变整数的问题,(改变小数位数)

方法一 1.点击你选中的表&#xff0c;单击设计表 2.单击字段名&#xff0c;将类型改为double&#xff0c;小数点改为你想保留的几位小数&#xff0c;最后再保存 注&#xff1a;上图中的“填充0”可勾选&#xff0c;若选中&#xff0c;有一个数字输入的时候有两位小数&#x…

Mysql常用分组聚合函数(统计行的数量、最大值、最小值、平均值、求和)

目录一、概念二、举例2.1 COUNT函数2.1.1 语法2.1.2运行结果2.2 AVG函数2.2.1语法2.2.2运行结果2.3 SUM函数2.3.1语法2.3.2运行结果2.4 MAX函数2.4.1语法2.4.2运行结果2.5 MIN函数2.5.1语法2.5.2运行结果一、概念 聚合函数&#xff1a;又称分组函数、统计函数、聚集函数。在my…

mysql使用navicat(建表前与建表后)添加时间戳(创建时间、自动更新时间)

为了知道我们插入更新数据的时间&#xff0c;mysql建表增加以下代码 目录一、建表时添加1.1代码1.2举例二、建表后添加2.1举例2.2代码2.3运行结果一、建表时添加 1.1代码 create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间,update_time TIMESTAMP DEFAULT CU…

❤️520要来啦,快去给心仪的她写表白代码趴!(python)❤️

安装库代码 pip install turtle报了个错&#xff0c;气死我了&#xff0c;不要pip了&#xff0c;气得我直接下了个Anaconda&#xff0c;Anaconda已经把所有的库下好了&#xff0c;特别方便&#xff01; Anaconda的下载地址&#xff1a;https://www.anaconda.com/products/indi…

Mysql中的IFNULL函数的详解(嵌套查询等重点)如果第一个为空就执行第二个

https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 如有转载&#xff0c;请注明出处&#xff08;如不注明&#xff0c;盗者必究&#xff09; 目录一、概念二、语法三、举例创建表加入数据3.1举例一3.2…

求1到100的数字累加和(python)

https://blog.csdn.net/hanhanwanghaha宝藏女孩 欢迎您的关注&#xff01; 欢迎关注微信公众号&#xff1a;宝藏女孩的成长日记 如有转载&#xff0c;请注明出处&#xff08;如不注明&#xff0c;盗者必究&#xff09; 代码 #codingutf-8#定义结果变量 result 0#定义一个循环…

mysql中怎么根据生日(brithday)获取年龄

语法 YEAR(from_days(datediff(now(), sbirthday)));举例 首先先创建一个表 create table studentone( sname varchar(20) not null, sbirthday datetime )charsetutf8;再插入数据 insert into studentone values(陈奇,2000-11-02); insert into studentone values(陈飞,19…