Linux網路子系統中報文的接收及NAPI的實現

2021-10-03 08:52:34 字數 3697 閱讀 4353

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

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

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

linux 提供對兩種報文接收方式的支援,供不同網絡卡驅動來用。在本文中把第一種稱為舊的方式(舊是相對於napi來說的),第二種方法linux核心中稱為napi方式。

napi的另乙個優點是裝置處理更為公平,因為軟中斷順序執行每個需要處理的裝置的輪詢函式,每個輪詢函式執行時間是有限制的,超過最大的執行時間後就會執行下乙個裝置的輪詢函式。這樣就保證負載低的和負載高的裝置被處理的機會是一樣。

linux 核心協議棧中報文接收的設計思路分析如下:

napi介面和舊介面兩者有一下相同點:

(1)、對報文的處理都是放在軟中斷中處理。

(2)、兩者都有儲存報文的佇列,napi的佇列是由網絡卡驅動來管理的(一般是有網絡卡dma到記憶體中,網絡卡驅動管理著儲存報文的佇列),而舊介面的佇列是由linux核心管理的,由網絡卡驅動負責往佇列裡加報文。

每個napi裝置都有乙個輪詢函式來由收包軟中斷呼叫,來進行輪詢處理報文。我們可以建立乙個虛擬的napi裝置(backlog),讓她的輪詢函式來輪詢的處理舊介面的報文佇列。收包軟中斷處理函式只是進行呼叫相應napi的輪詢函式進行處理,並不關心是虛擬napi還是非虛擬napi。具體輪詢細節由相關napi的輪詢函式自己實現。這樣舊介面和napi就可以很好的融合在一起來實現了。

如上所述,我們現在可以知道每個cpu上需要有如下幾個元素:

1、乙個報文的接收佇列,由舊介面來使用。

2、乙個虛擬的napi裝置,來有linux協議棧自己建立並處理舊介面使用的佇列中的報文。

3、乙個napi的鍊錶,上面掛著有報文需要處理的napi裝置,由收包軟中斷來遍歷該鍊錶,順序執行每個napi的輪詢函式。

linux核心具體實現

結構體定義如下

structsoftnet_data

;定義乙個per_cpu變數 softnet_data

declare_per_cpu(struct softnet_data,softnet_data);

softnet_data 的初始化如下

static int __initnet_dev_init(void)

}napi的資料結構

每個napi需要如下幾個元素:

1、乙個輪詢函式,來輪詢處理報文。

2、需要有乙個狀態,來標識napi的排程狀態,是正在被排程還是沒有被排程。

3、每個napi的一次輪詢能處理的報文個數應該有乙個上限,不能無限制的處理下去,防止獨佔cpu資源導致其他程序被餓死。

4、每個napi應該和乙個網路裝置進行關聯。

5、napi設計時考慮了多佇列的情況。乙個網路裝置可以有多個報文佇列,這樣乙個網路裝置可以關聯多個napi,每個napi只處理特定的佇列的報文。這樣在多核情況下,多個cpu核可以並行的進行報文的處理。一般專用的網路多核處理器存在這種情況。

6、每個napi在有報文的情況下應該掛到softnet_data的pool_list上去。

如上所述,napi的結構體定義如下

struct napi_struct

;napi的排程狀態

napi_state_sched設定時表示該napi有報文 需要接收。即把napi掛到softnet_data 時要設定該狀態,處理完從softnet_data 上摘除該napi時要清除該狀態。

一些napi的函式詳解

1、netif_napi_add(),把網路裝置net_device 和napi結構相繫結。

voidnetif_napi_add(struct net_device *dev,

struct napi_struct *napi,

int (*poll)(struct napi_struct *, int), int weight)

2、使能和禁用napi

static inline voidnapi_disable(struct napi_struct *n)

3、排程napi

void__napi_schedule(struct napi_struct *n)

4、napi執行完畢,如果napi一次輪詢處理完佇列的所以報文,呼叫該函式。

void__napi_complete(struct napi_struct *n)

一般網絡卡驅動的napi的步驟:

1、不同的網絡卡驅動都會為網路裝置定義自己的結構體。有的是自己的結構體中即包括net_device和napi,有的是把自己的結構體放到net_device的priv部分。

2、實現napi的輪詢函式 rx_pool(struct napi_struct *napi,int weigh);

intrx_pool_***(struct napi_struct *napi,int weigh)

if(rx_cnt < weigh)

else

return rx_cnt;

}3、把網路裝置跟napi相關聯,netif_napi_add(netdev,napi,rx_pool_***,weigh);

4、註冊網絡卡的收包中斷:例:

request_irq(irq,&rx_irq_handle, 0, netdev->name, netdev);

在中斷處理函式中,把對應的napi加入到核心管理的鍊錶中。

static irqreturn_t rx_irq_handle(int irq, void *data)

網絡卡使用舊介面的方法:

在網絡卡收包中斷處理程式中,把報文從網絡卡硬體快取中拷貝到記憶體後,初始化好skb結構體,直接呼叫

netif_rx(skb)把報文加入到核心管理的佇列中即可。

Linux 網路子系統底層機制分析 1

linux 網路子系統底層機制分析 1 網路子系統在linux中的地位非常重要。在如今這個嚴重依賴網際網路,強調協同工作的時代,乙個高效,穩定的網路處理系統是留住使用者群的基本手段。前段時間花了一部分時間學習了一下linux的網路子系統的源 以及一些處理機制。這部分是由於工作的原因,另一部分原因是想...

Linux網路子系統中鏈路層中GRO的處理

根據上篇博文的介紹,gro需要支援gro的每種協議都要實現自己的報文匹配合併函式和合併完成函式。這裡我們先來看看鏈路層上 實現的自己的gro函式。鏈路層的接收匹配函式 napi gro receive napi,skb 該函式對報文進行匹配,並不合併報文。匹配規則 必須同時滿足以下兩個條件 1 兩個...

Linux網路子系統中協議棧的入口處理

網路驅動接收到報文後,會初始化skb protocol 字段。鏈路層的接收函式netif receive skb會根據該字段來確定把報文送給那個協議模組進一步處理。乙太網的裝置呼叫eth type trans 來給skb protocol賦值。be16eth type trans struct sk...