Linux核心資料報處理流程 資料報接收 2

2021-08-07 12:30:55 字數 3603 閱讀 7111

四、網絡卡的資料接收

核心如何從網絡卡接受資料,傳統的經典過程:

1、資料到達網絡卡;

2、網絡卡產生乙個中斷給核心;

3、核心使用i/o指令,從網絡卡i/o區域中去讀取資料;

我們在許多網絡卡驅動中,都可以在網絡卡的中斷函式中見到這一過程。

但是,這一種方法,有一種重要的問題,就是大流量的資料來到,網絡卡會產生大量的中斷,核心在中斷上下文中,會浪費大量的資源來處理中斷本身。所以,乙個問題是,「可不可以不使用中斷」,這就是輪詢技術,所謂napi技術,說來也不神秘,就是說,核心遮蔽中斷,然後隔一會兒就去問網絡卡,「你有沒有資料啊?」……

從這個描述本身可以看到,哪果資料量少,輪詢同樣占用大量的不必要的cpu資源,大家各有所長吧,呵呵……

ok,另乙個問題,就是從網絡卡的i/o區域,包括i/o暫存器或i/o記憶體中去讀取資料,這都要cpu去讀,也要占用cpu資源,「cpu從i/o區域讀,然後把它放到記憶體(這個記憶體指的是系統本身的物理記憶體,跟外設的記憶體不相干,也叫主記憶體)中」。於是自然地,就想到了dma技術——讓網絡卡直接從主記憶體之間讀寫它們的i/o資料,cpu,這兒不**事,自己找樂子去:

1、首先,核心在主記憶體中為收發資料建立乙個環形的緩衝佇列(通常叫dma環形緩衝區)。

2、核心將這個緩衝區通過dma對映,把這個佇列交給網絡卡;

3、網絡卡收到資料,就直接放進這個環形緩衝區了——也就是直接放進主記憶體了;然後,向系統產生乙個中斷;

4、核心收到這個中斷,就取消dma對映,這樣,核心就直接從主記憶體中讀取資料;

——呵呵,這乙個過程比傳統的過程少了不少工作,因為裝置直接把資料放進了主記憶體,不需要cpu的干預,效率是不是提高不少?

對應以上4步,來看它的具體實現:

1、分配環形dma緩衝區

linux核心中,用skb來描述乙個快取,所謂分配,就是建立一定數量的skb,然後把它們組織成乙個雙向鍊錶;

2、建立dma對映

核心通過呼叫

dma_map_single(struct device *dev,void *buffer,size_t size,enumdma_#_direction direction)

建立對映關係。

struct device *dev,描述乙個裝置;

size:快取大小;

direction:對映方向——誰傳給誰:一般來說,是「雙向」對映,資料在裝置和記憶體之間雙向流動;

對於pci裝置而言(網絡卡一般是pci的),通過另乙個包裹函式pci_map_single,這樣,就把buffer交給裝置了!裝置可以直接從裡邊讀/取資料。

3、這一步由硬體完成;

4、取消對映

dma_unmap_single,對pci而言,大多呼叫它的包裹函式pci_unmap_single,不取消的話,快取控制權還在裝置手裡,要呼叫它,把主動權掌握在cpu手裡——因為我們已經接收到資料了,應該由cpu把資料交給上層網路棧;

當然,不取消之前,通常要讀一些狀態位資訊,諸如此類,一般是呼叫

dma_sync_single_for_cpu()

讓cpu在取消對映前,就可以訪問dma緩衝區中的內容。

ok,有了這些知識,我們就可以來看e100的**了,它跟上面講的步驟基本上一樣的——繞了這麼多圈子,就是想繞到e100上面了,呵呵!

在e100_open函式中,呼叫e100_up,我們前面分析它時,略過了乙個重要的東東,就是環形緩衝區的建立,這一步,是通過

e100_rx_alloc_list函式呼叫完成的:

[cpp]view plain

copy

static

int e100_rx_alloc_list(struct nic *nic)  

}  nic->rx_to_use = nic->rx_to_clean = nic->rxs;  

nic->ru_running = ru_suspended;  

return 0;  

}  

[cpp]view plain

copy

#define rfd_buf_len (sizeof(struct rfd) + vlan_eth_frame_len)

static

inline

int e100_rx_alloc_skb(struct nic *nic, struct rx *rx)  

if(rx->prev->skb)   

return 0;  

}  

e100_rx_alloc_list函式在乙個迴圈中,建立了環形緩衝區,並呼叫e100_rx_alloc_skb為每個緩衝區分配了空間,並做了

dma對映。這樣,我們就可以來看接收資料的過程了。

前面我們講過,中斷函式中,呼叫netif_rx_schedule,表明使用輪詢技術,系統會在未來某一時刻,呼叫裝置的poll函式

[cpp]view plain

copy

static

int e100_poll(struct net_device *netdev, int *budget)  

*budget -= work_done;  

netdev->quota -= work_done;  

return 1;  

}  

目前,我們只關心rx,所以,e100_rx_clean函式就成了我們關注的對像,它用來從緩衝佇列中接收全部資料(這或許是取名為clean的原因吧!

[cpp]view plain

copy

static

inline

void e100_rx_clean(struct nic *nic, unsigned int *work_done,  

unsigned int work_to_do)  

else

if(-eno# == err)  

break;  

}  if(restart_required)  

rx_to_start = nic->rx_to_clean;  

for(rx = nic->rx_to_use; !rx->skb; rx = nic->rx_to_use = rx->next)   

if(restart_required)   

}  

[cpp]view plain

copy

static

inline

int e100_rx_indicate(struct nic *nic, struct rx *rx,  

unsigned int *work_done, unsigned int work_to_do)  

else

if(actual_size > nic->netdev->mtu + vlan_eth_hlen)  else   

rx->skb = null;  

return 0;  

}  

網絡卡驅動執行到這裡,資料接收的工作,也就處理完成了。但是,使用這一種方法的驅動,省去了網路棧中乙個重要的內容,就是

「佇列層」,讓我們來看看,傳統中斷接收資料報模式下,使用netif_rx函式呼叫,又會發生什麼

Linux核心資料報處理流程 資料報接收 3

五 佇列層 1 軟中斷與下半部 當用中斷處理的時候,為了減少中斷處理的工作量,比如,一般中斷處理時,需要遮蔽其它中斷,如果中斷處理時間過長,那麼其它中斷 有可能得不到及時處理,也以,有一種機制,就是把 不必馬上處理 的工作,推遲一點,讓它在中斷處理後的某乙個時刻得到處理。這就 是下半部。下半部只是乙...

Linux核心資料報格式

linux kernel中mac頭,ip頭,tcp頭結構體定義,核心列印方式。mac頭 核心中mac頭結構體 defein eth alen 6 struct ethhdr attribute packed define mac fmt 02x 02x 02x 02x 02x 02x define ...

Linux核心資料結構

一 概述 linux核心提供了一些常用的資料結構並建議開發者盡量重用,而不需要再去實現一些類似的資料結構。這篇部落格主要描述一下linux核心提供的鍊錶 佇列 對映及二叉樹這四種常用資料結構。當然這裡提到的每一種資料結構要說清楚都需要不少的篇幅,而這篇博文只是簡要分析一下linux核心設計的理念,以...