核心如何從網卡接收資料,傳統的過程:
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,然後用e1000_rx_ring 環形緩沖區隊列描述符連接配接起來
2)建立DMA映射
核心通過調用
dma_map_single(struct device *dev,void *buffer,size_t size,enum dma_data_direction direction)
建立映射關系。
struct device *dev 描述一個裝置;
buffer:把哪個位址映射給裝置;也就是某一個skb——要映射全部,當然是做一個雙向連結清單的循環即可;
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緩沖區中的内容
相關引用:
接管總線,應該就是
enum_nic_normal , // 預設系統接收,作業系統協定棧
enum_nic_solarflare_efvi , // solarflare efvi接收
enum_nic_exablaze_exanic , // Exablaze exanic接收
enum_nic_x710_win_speed , // win高速接收,隻适用于windows
enum_nic_solarflare_win_speed , // win高速接收,目前隻适用于windows