- 論壇徽章:
- 36
|
本帖最后由 Godbach 于 2010-03-27 17:58 編輯
【Godbach注】
論壇中也經(jīng)常有網(wǎng)友問到這個問題,pt_prev在ptype_all和pypte_base中的作用是什么。我在LinuxForum上發(fā)現(xiàn)了一篇對該問題解釋的精華帖。這里轉(zhuǎn)載過來,并將原帖中兩個關(guān)鍵的回帖一并貼了過來,以饗大家。原文是有ID為getmoon發(fā)表于LinuxForum上的,連接為:http://www.linuxforum.net/forum/ ... &page=&view=&sb=&o=。感謝getmoon的精彩分析。
--Mar 27,2010
看見noble_shi兄弟"關(guān)于net_rx_action函數(shù)的若干問題"貼中關(guān)于pt_prev的問題, 本來想在論壇上找到一個相關(guān)的帖子的鏈接告訴他。但是發(fā)現(xiàn)咱們論壇上關(guān)于pt_prev的討論要么沒有說明,要么理解的偏差,甚至是錯誤。而且關(guān)于pt_prev的提問很多。故寫了以下內(nèi)容。
不過本人水平有限,難免說錯。請執(zhí)教getmoon@163.com
結(jié)論:pt_prev使用的原因是為了減少一次kfree_skb的調(diào)用,提高效率。
如果有異議的請往下看。如果你對skb非常了解,那么請直接看<三>, 否則請一步一步往下看。
<一>相關(guān)知識
在講pt_prev的作用之前, 咱們先說明以下的東西。
(1)alloc_skb中初始化skb->users計數(shù)為1。- struct sk_buff( )
- {
- ....
- atomic_set(&skb->users, 1);
- ...
- }
復(fù)制代碼 (2)kfree_skb中如果計數(shù)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);
- }
復(fù)制代碼 當(dāng)引用數(shù)為1或者引用數(shù)減1等于零時, 回收包緩沖。
(3)linux內(nèi)核網(wǎng)絡(luò)協(xié)議棧中到本機的skb包是在上層協(xié)議中釋放的。
<二>實現(xiàn)ptype_base和ptype_all鏈
講了上面的東西后咱們來看ptype_base及ptype_all鏈相關(guān)的東西。這兩個鏈的作用在這里就不講了。 因為有了上面的東西, 所以涉及到一個skbuff共享的問題, 如果都用skb_clone或者skb_copy,那么性能將是很低的。 所以在linux中使用了skb共享的計數(shù),就是用skb->users計數(shù)來計算共享的地方。
許多人理解了ptype_all和ptype_base鏈的作用之后,就認(rèn)為為什么不用下面的算法實現(xiàn)。- 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);
復(fù)制代碼
我來說說為什么最后還要一個kfree_skb。在進(jìn)入這個for循環(huán)之前,skb->users的計數(shù)為1,每進(jìn)入一個ptype->fun函數(shù)之前都會加,并且在每個ptype->fun函數(shù)里面都會有kfree_skb函數(shù)(會減users),但是并沒有真正的把skb釋放掉。還記得剛開始說明中kfree_skb里面的atomic_dec_and_test(&skb->users)嗎。 所以atomic_inc(&skb->users);ptype->func(skb, skb->dev, ptype);兩句代碼執(zhí)行之后并沒有改變skb->users的計數(shù),還是1。所以這樣可以在for循環(huán)中循環(huán)好幾次, 無論幾個ptype->func都可以共享這個skb。退出for循環(huán)之后, skb->users還是1,并且之前并沒有真正的釋放掉內(nèi)存。因此你要調(diào)用kfree_skb(skb) 來釋放內(nèi)存。
再說明一下另外一個問題,如果for循環(huán)就調(diào)用了ptype->func函數(shù)一次的話,那么在整個包的流程中,是調(diào)用了kfree_skb兩次。 一次在ptype->func函數(shù)中,第二次是在for循環(huán)之后, 就是上面代碼中的kfree_skb 。
說了上面的這個例子之后, 如果你現(xiàn)在知道了為什么用pt_prev來提交效率,那么你就不用往下看了。
<三> 利用pt_prev來提高效率
理解了上面的內(nèi)容之后,咱們來看看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(當(dāng)前的)不為空,那么肯定要共享一次了,所以加1*/
- atomic_inc(&skb->users);
- pt_prev->func(skb,skb->dev, pt_prev);
- /*執(zhí)行上面的函數(shù)之后,會在里面減1。所以相對來說,上面兩句代碼執(zhí)行之后并沒有對skb->users的值進(jìn)行影響。*/
- }
- }
- pt_prev = ptype;
- }
- }
- /*現(xiàn)在skb->users的計數(shù)還是為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);函數(shù)中釋放了。
- */
- pt_prev->func(skb, skb->dev, pt_prev);
- } else{
- /*到這里,已經(jīng)沒有對skb進(jìn)行操作的了。所以必須調(diào)用kfree_skb把skb釋放掉。*/
- kfree_skb(skb);
- }
復(fù)制代碼
你看,<二>和<三>相比是否少了一次調(diào)用kfree_skb呢。
到現(xiàn)在, 你是否理解了為什么么用pt_prev了。
你是否在為那些家伙的高深之處而感慨那。
anything i can help u , please email to : getmoon@163.com
后續(xù)討論
ID:rainfall
今天我仔細(xì)看了一下linux2.2.x的net_bh,我認(rèn)為pt_prev的作用是減少一次skb_clone(當(dāng)然也少一次kfree_skb)。得出這個結(jié)論的理由是:每次在處理skbuff時,相關(guān)的處理都會復(fù)制一次skbuff的頭。如果鏈表上有n個元素,就要復(fù)制n次,然后還有釋放n次。最后還要釋放結(jié)構(gòu)本身。但是如果只復(fù)制n-1次,最后處理的就是數(shù)據(jù)本身(引用計數(shù)為1)。這樣會少復(fù)制一次。不過getmoon的說法也沒錯,只是我覺得從復(fù)制的角度看,可能更能體現(xiàn)
出高效的主題。畢竟,釋放并不花什么時間。
ID:getmoon
實際上是這樣的,2.2的net_bh里面也采用了這個pt_prev。 它的功能還是如我所言。
兄弟看見的是在調(diào)用每個pt->func之前clone了一個。 實際上這個clone在2.4里面并沒有去掉。 只是把它移動每個具體的pt->funct里面。 你可以看arp_rcv , ip_rcv等函數(shù)都有一個
- if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
- goto out_of_mem;
復(fù)制代碼
這個函數(shù)實際上也是clone一個。而2.2的每個pt->func如arp_rcv , ip_rcv里面都是沒有的。 因為在調(diào)用pt->func之前就clone了。所以2.4的做法是實際上把skb_clone往后移動了。 為什么呢。我想這個還是重效率上考慮的。
我想作者的想法是:如果在pt->func函數(shù)里面根本沒有必要skb_clone一下, 我為什么
在硬給它clone一個呢。如果呢需要新的skb頭,那么呢自己clone去。 因為可能有的人不需要。 |
|