標籤:
在查看NAPI機制的時候發現一篇介紹NAPI引入初衷的文章寫的很好,通俗易懂,就想要分享下,重要的是博主還做了可以在他基礎上任意修改,而並不用註明出處的聲明,著實令我敬佩,不過還是附上原文連結!
http://blog.csdn.net/dog250/article/details/5302853
處理外來事件是cpu必須要做的事,因為cpu和外設的不平等性導致外設的事件被cpu 當作是外來事件,其實它們是平等的,只不過馮氏機器不這麼認為罷了,既然要處理外來事件,那麼就需要一定的方法,方法不止一種,大致有中斷和輪詢以及一種 混雜又複雜的方式,也就是DMA方式。中斷是cpu被動處理的一種方式,也就是說cpu不知道何時中斷,只要有了中斷就會通知cpu,而cpu此時必須停 下一切來處理,而輪詢是cpu主動查詢並處理的過程,cpu隔一會查詢一下外設看有沒有事情可做。
我們看一下這兩種方式,中斷看似很高效,但是卻會遺漏一些資料,避免遺漏的機制要麼由硬體實現要麼由上層的軟體實現,而輪詢就沒有中斷高效了,它會做很多 徒勞的操作,而且必須引入暫存機制,就是說由於cpu不可能在每次查詢硬體的時候正好有事情可做,為了不使請求遺漏,隨時到來的請求必須暫存在一個私人的 地區內,只要這些都做好了,輪詢是不會造成請求遺漏的,中斷在很多中斷高頻觸發的時候會造成大量遺漏和競爭,畢竟只有一個cpu,同一個時間點只能有一個 請求被處理,而輪詢由於是cpu分批打包處理請求的,因此不會遺漏。
以上的論述有點像我討論過的inotify和rsync實現的檔案同步,inotify的實現就是中斷,很顯然有遺漏,而rsync實現的就是輪詢,顯然 沒有遺漏,cpu主動做的事情它自己最明白了,但是它要是被動應對就不會這麼明白了,它只是按照規則應對罷了,絲毫不會存在任何策略。如果中斷過於頻繁也 是不好的,因為cpu必須處理中斷,這會導致cpu沒有時間做正經事,此時最好用輪詢,但是外設活動很緩和的時候,用輪詢就不合適了,因為詢也是白詢,此 時比較適合用中斷,可是系統怎麼知道何時外設活躍何時外設緩和呢?啊哈,可以用智能預測演算法嘛,以曆史值為依據!不,不能那樣的,因為這是在核心,核心不 是秀演算法的地方,我另外的文章強調過這一點。那麼怎麼辦?好辦,還是約定,就是將中斷和輪詢相結合,這就是linux網卡驅動中的NAPI的方式,它的設 計十分巧妙,就是在第一個包到來的時候中斷,然後關閉中斷開始輪詢,等某一次輪詢完畢後發現沒有資料了,那麼核心預設此次資料已經傳輸完畢,短時間內不會 再有資料了,那麼停止輪詢,重新開啟中斷,這樣會減少很多次的中斷,雖然某次輪詢完畢發現沒有資料並不能代表1ms以後不會再有資料,但是剛才說了,要想 使演算法簡單,必須做一個合理的約定,人性化的約定,如果說加上判定什麼情況下百分之九十五的可能不需要輪詢了並不是不可能,只是維護那個演算法的開銷太大, 它直接抵消了演算法帶來的優勢。用人的思想考慮,如果一個飯店的服務員不停的從廚房接菜然後送到餐桌,注意是不停的,10秒一趟,但是突然隔了半分鐘沒有廚 房的人吆喝接菜,如果你是服務員,難道你還會去視窗等菜嗎?反正我不會,我會蹲下來稍微休息一下,即使剛蹲下來就會有新活我也願意賭一把,雖然輸得可能性 很大很大。
如此一來,我們看一下NAPI解決了什麼問題,第一,它限制了中斷的數量,一旦有中斷過來就停掉中斷改為輪詢,這樣就不會造成cpu被頻繁中斷,第 二,cpu不會做無用功,就是所謂的無用的輪詢,因為只有在中斷來了才改為輪詢,中斷來了說明有事可做,看看NAPI將中斷和輪詢結合的是多麼巧妙啊。以 往的實現中,在硬體網卡中斷中將skb排入隊,然後在非強制中斷中出隊並交由上層處理,一切配合的看起來那麼好,可是在遇到突發快速小包傳輸的時候就會導致頻 繁中斷,因為是突發的包,因此不能用輪詢,因為是快速小包,因此不適合用中斷,最終二者巧妙結合,各取優勢,優勢互補,絕了!這個架構適合一切的網卡模 式,因此就將傳統的網卡收發機制也納入到了NAPI架構,很簡單,就是用原來的邏輯實現dev的poll回呼函數即可,至於傳統的非NAPI方案,完全可 以用一個樁子代替。
cpu利用率和頻繁的中斷問題通過NAPI機制解決了,但是這又引入了一個新的問題,就是這可能造成cpu利用率的失衡,這個怎麼理解呢?NAPI啟動之 後,網卡的中斷就會變得很少,要知道中斷balance的目前實現是基於中斷數量的均衡,它根本不管中斷數量均衡之後引起的softirq導致的cpu使 用率是否均衡,softirq的均衡也是一樣,比如一個是磁碟softirq,一個是網卡的NAPI的softirq,前者瞬間就可以完成但是來得頻繁, 而後者要輪詢網卡並且處理協議棧很耗時但是來得不頻繁,可是balancer不管這麼多,它只是覺得磁碟的softirq太多了而為了數量均衡會將更多的 softirq發布到softiqr少的cpu上,它根本不在乎這些更多的softirq是否會帶來更高的cpu負載。NAPI削弱了中斷/非強制中斷均衡的 作用,畢竟它的主導在輪詢,輪詢會佔用很多的處理器資源,而中斷和非強制中斷數量很少。中斷或者非強制中斷特別是非強制中斷數量在cpu間的均衡可能造成各個cpu負 載的嚴重不均衡,因為各個硬中斷幾乎都是瞬間完成的,硬中斷不能耽擱太久的,但是各個不同非強制中斷的任務量缺是千差萬別的,因此絕對不能按照數量來均衡軟中 斷,然而一般都是硬中斷觸發非強制中斷,它們都在同一個cpu上,因此如果想簡單的實現NAPI在多cpu上的cpu使用率均衡,那麼必須重新實現硬體的負載 均衡機制,這樣可以嗎?不!因此這樣會使得兩個部分耦合過重,因此必須讓硬中斷的均衡和cpu的均衡解耦合,其實現在的核心就是這麼做的,所以才會造成 cpu不均衡,硬體中斷的均衡和cpu均衡的解耦合帶來的好處就是我們可以對非強制中斷均衡做文章,而硬中斷的負載平衡還是用數量均衡實現,非強制中斷徹底從硬體 中斷中解放出來,不再是在處理硬中斷的cpu上觸發非強制中斷,而是可以在任何cpu上觸發非強制中斷,由於不同非強制中斷的任務量千差萬別,因此我們定義一個非強制中斷 的“權值”,然後按照不同非強制中斷這個權值和數量的積的和來均衡非強制中斷,這樣的話,我想各個cpu的負載就均衡了,現在問題是,各個不同的非強制中斷的“權值” 的計算問題,呵呵。累了,有時間再說。一個論壇上一哥們兒寫了一個patch,很有創意,比我這裡的非強制中斷均衡的粒度要小得多,這個補丁不是均衡非強制中斷, 而是將非強制中斷進一步也分成了上下兩部分,和cpu相關的上半部必須加急處理,這樣不會對cpu造成太大負載,仍然用硬體中斷均衡,因為硬體中斷的cpu觸 發軟體中斷,這部分不變,但是非強制中斷的下半部就需要均衡了,該補丁為每一個cpu創立了一個工作隊列,然後將ip_rcv 這種操作的cpu相關的工作放到非強制中斷的上半部,其實就是從一個cpu的skb隊列中抽取一個skb,然後將這個skb隨機放到這些工作隊列中進行處理, 和整個非強制中斷均衡有何不同嗎?大大不同。非強制中斷均衡針對的是一個poll_list裡面的所有的skb,而這哥們兒的補丁針對的是一個skb,粒度十分 小,但是沒有測試,是不是太小了呢?這其實也是一個模式方法,逐步的將粒度精細化,類似將中斷分成上半部和下半部的做法是放之四海而皆準的,這是一種哲 學,也是一種風格。
如果你說你沒有見過linux的方式,那麼只要你上過枯燥的電腦課或者讀過枯燥的教科書或者你是天才你就知道一個叫做生產者/消費者的模型,它其實和 linux的中斷的上半部和下半部很類似,上半部是生產者,只管將環境搭建好,資料準備好,然後觸發下半部,其實就是喚醒消費者,這個思想在多線程並發中 很著名,而並發就是為了提高系統輸送量,在SMP環境中也是為了並發,因此我們何嘗不用用生產者/消費者模型呢?它也是一種低耦合的各司其職的模型。如果 你想不到NAPI的中斷+輪詢的方式,那麼你聽說過linux下怎樣做檔案同步的嗎?rsync+inotify的方式聽說過嗎?如果沒有就趕快 google一下吧。rsync+inotify其實就是中斷+輪詢,rsync是輪詢,而inotify是中斷,這個同步方案十分高效,保證只有在 inotify監控到檔案變化的時候才開始輪詢,平時就睡覺,inotify不再需要監控到具體的檔案,因為它只負責告知事件,具體工作由rsync完 成,inotify只需要告訴一端檔案變化了即可,那豈不是要全部同步了即使你只改了一個字元,別忘了rsync的演算法,這就是另一篇文章了。所以不要再 覺得linux核心深不可測了,它的特點只有一個就是簡單,比起使用者應用那些複雜的演算法,核心的演算法一向簡單易懂,其實核心的每一個機制,都可以在使用者空 間找到原型的。
可是cpu對NAPI處理的均衡真的有意義嗎?使用者難道就不能忍受一個cpu佔用100%而另一個0%嗎?表面上看是那樣的,但是如果考慮cache或者 切換代價的話就不一樣了,效能因素不僅僅是cpu使用率還有別的,如果一件事的開銷過大,即使cpu使用率再均衡也是划不來的。這就好像初學者用free 命令看記憶體時總是嚇一大跳。特別是NAPI的網路資料包操作,比如TCP的IP包的分段重組問題,一旦亂序就要重傳,這種情況下,一個linux主機如果只是作為一台路由器的話,那 麼進入系統的一個TCP包的不同分段如果被不同的cpu處理並向一個網卡轉寄了,那麼同步問題會很麻煩的,如果你不做同步處理,那麼很可能後面的段被一個 cpu先發出去了,那麼在真正的接收方接收到亂序的包後就會請求重發,這是不希望的,因此還是一個cpu串列處理好,這也許是TCP/IP協議棧的缺陷, 但是沒有辦法,協議是那樣的,只能那樣去應對。在大流量下,由於napi的緣故,網卡的中斷被關閉了,此時那個第一次被中斷的cpu正在poll這個網卡,因此所有的流量都會聚集到這個cpu上,這可能是一個設計的缺陷吧。
LINux網路的NAPI機制詳解一