報文的接收方式(linux網絡子系統學習 第二節 )

報文的接收是整個協議棧的入口,負責從網卡中把報文接收並送往內核協議棧相應協議處理模塊處理。

報文的接收方法主要分爲兩種

一種是網卡產生中斷,通知內核進行接收報文。一次中斷接收一個報文。在中斷處理程序中把報文從硬件緩存中拷貝到內存中,並把報文加入到協議棧中對應的入口隊列中,中斷退出時調用收包軟中斷來從相應隊列來讀取報文進行處理。這種方式優點是內核對報文響應較快,在網卡上 有少量報文時效果較好。這樣如果網卡有大量報文的話,會產生大量中斷。中斷會不斷打斷處理報文的軟中斷,這樣先前的報文來不及處理,總是被打斷,隊列裏的報文大量堆積導致不能被及時處理,導致報文性能下降。

另一種是中斷加輪詢的方式進行接收報文的。當一個報文到來時,產生中斷通知內核,註冊的中斷處理程序首先禁止網卡的中斷,內核開始輪詢的從網卡硬件緩存或網卡硬件管理的內存隊列中讀取報文並處理,直到報文讀完或超出了設置的允許讀取最大報文個數,輪詢函數停止處理報文。如果這次輪詢處理完了全部報文,並接着使能網卡中斷。如果輪詢沒有處理完全部報文,這時就重新調度軟中斷,等下次軟中斷被調度到後繼續輪詢處理報文。這樣是爲了防止輪詢獨佔CPU時間。以後重複這樣的接收報文步驟。這樣內核處理一次中斷可以處理多個報文,這樣就避免了產生大量中斷造成的系統開銷。但支持這樣的操作的網卡必須支持硬件能自動把報文存入內存中,或硬件緩存足夠大。否則當一次輪詢結束後並沒有處理完全部的報文時,到下次軟中斷被調度到開始輪詢的這段時間裏,當網卡硬件內部的緩存滿後會產生丟包。

Linux 提供對兩種報文接收方式的支持,供不同網卡驅動來用。因爲對處理網絡流量不是很大的網卡沒必要支持自動把報文存入內存或硬件緩存很大,而網絡設備上對網卡在這方面的功能要求較高。因此第一種方式還是有存在的必要的。在本文章把第一種稱爲舊的方式(舊是相對於NAPI來說的),第二種方法linux內核中稱爲NAPI方式。

報文的接收步驟預覽:

一、舊的接收過程

1、當網卡中斷產生後,內核的中斷處理部分會根據網卡驅動註冊的中斷號找到相應網卡 驅動中的中斷處理函數。

2、 中斷處理函數首先禁用網卡的接收報文中斷。

3、接着負責在內存中申請一個skb,把網卡硬件緩存中的報文拷貝到skb中。初始化skb中的一些字段,根據報文內容給skb->protocol字段賦值。例如ip報文會賦爲0x0800

4、接着調用netif_rx把skb 放入本地cpu的接收隊列。

5、 接着使能網卡收包中斷。中斷處理函數退出。在中斷處理函數退出時會調度報文接收軟中斷進行處理cpu上的接收隊列中的報文。

從網卡硬件緩存中把報文拷貝到內存放在硬件中斷處理中做,把協議棧處理報文的工作放在軟中斷做,這樣提高了對硬件中斷的響應速度。

二 、NAPI方式接收:

1、當網卡中斷產生時,找到網卡驅動中中斷處理函數。

2、中斷處理函數禁用網卡接收報文中斷。

3、把內核中代表網卡的net_dev 掛到本地cpu的pool隊列上。表示網卡上有報文要處理。

4、接着調度報文接收的軟中斷來處理該cpu上掛着的dev.

5、在軟中斷中會找到本地cpu上pool隊列上的dev,調用相應網卡驅動的輪詢函數來處理報文。

6、網卡驅動的輪詢函數會讀取報文,初始化skb,調用netif_receive_skb把報文直接送到協議棧進行處理。每次輪詢處理的報文個數有一個限制,防止輪詢函數長時間佔用cpu。如果讀取完全部報文,就使能網卡收報文的中斷。中斷返回。如果處理的報文到了限制個數,但沒處理完全部的報文,就把dev再次掛到本地cpu的pool鏈表上,不用使能中斷。調度報文接收軟中斷接着處理。一個報文會在輪詢函數中走完協議棧並被處理完成,不用再被緩存到相應的隊列中。


NAPI的另一個優點是設備處理更爲公平,因爲軟中斷順序執行每個需要處理的設備的輪詢函數,每個輪詢函數執行時間是有限制的,允許執行時間到了後就會執行下一個設備的輪詢函數。這樣就保證負載低的和負載高的設備被處理的機會一樣。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章