在查看NAPI機制的時候發現一篇介紹NAPI引入初衷的文章寫的很好,通俗易懂,就想要分享下,重要的是博主還作了能夠在他基礎上任意修改,而並不用註明出處的聲明,着實令我敬佩,不過仍是附上原文連接!linux
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上,這多是一個設計的缺陷吧。api