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

2021-08-07 12:30:55 字數 4248 閱讀 8754

五、佇列層

1、軟中斷與下半部

當用中斷處理的時候,為了減少中斷處理的工作量,比如,一般中斷處理時,需要遮蔽其它中斷,如果中斷處理時間過長,那麼其它中斷

有可能得不到及時處理,也以,有一種機制,就是把「不必馬上處理」的工作,推遲一點,讓它在中斷處理後的某乙個時刻得到處理。這就

是下半部。

下半部只是乙個機制,它在linux中,有多種實現方式,其中一種對時間要求最嚴格的實現方式,叫「軟中斷」,可以使用:

open_softirq()

來向核心註冊乙個軟中斷,

然後,在合適的時候,呼叫

raise_softirq_irqoff()

觸發它。

如果採用中斷方式接收資料(這一節就是在說中斷方式接收,後面,就不用這種假設了),同樣也需要軟中斷,可以呼叫

open_softirq(net_rx_softirq, net_rx_action, null);

向核心註冊乙個名為net_rx_softir的軟中斷,net_rx_action是軟中斷的處理函式。

然後,在驅動中斷處理完後的某乙個時刻,呼叫

raise_softirq_irqoff(net_rx_softirq);

觸發它,這樣net_rx_action將得到執行。

2、佇列層

什麼是佇列層?通常,在網絡卡收發資料的時候,需要維護乙個緩衝區佇列,來快取可能存在的突發資料,類似於前面的dma環形緩衝區。

佇列層中,包含了乙個叫做struct softnet_#:

[cpp]view plain

copy

struct softnet_#  

;  

核心使用了乙個同名的變數softnet_#,它是乙個per-cpu變數,每個cpu都有乙個。

net/core/dev.c

code:

declare_per_cpu(struct softnet_#,softnet_#);

[cpp]view plain

copy

static

int __init net_dev_init(void)  

#ifdef offline_sample

samp_timer.expires = jiffies + (10 * hz);  

add_timer(&samp_timer);  

#endif

dev_boot_phase = 0;  

open_softirq(net_tx_softirq, net_tx_action, null);  

open_softirq(net_rx_softirq, net_rx_action, null);  

hotcpu_notifier(dev_cpu_callback, 0);  

dst_init();  

dev_mcast_init();  

rc = 0;  

out:  

return rc;  

}  

這樣,初始化完成後,在驅動程式中,在中斷處理函式中,會呼叫netif_rx將資料交上來,這與採用輪詢技術,有本質的不同:

[cpp]view plain

copy

int netif_rx(struct sk_buff *skb)  

if (queue->throttle)  

queue->throttle = 0;  

netif_rx_schedule(&queue->backlog_dev);  

goto enqueue;  

}  if (!queue->throttle)   

drop:  

__get_cpu_var(netdev_rx_stat).dropped++;  

local_irq_restore(flags);  

kfree_skb(skb);  

return net_rx_drop;  

}  

從這段**的分析中,我們可以看到,當資料被接收後,netif_rx的工作,就是取得當前cpu的佇列,然後入隊,然後返回,然後中斷函式

現呼叫它,它再把資料報入隊……

當佇列接收完成後,netif_rx就呼叫netif_rx_schedule進一步處理資料報,我們注意到:

1、前面討論過,採用輪詢技術時,同樣地,也是呼叫netif_rx_schedule,把裝置自己傳遞了過去;

2、這裡,採用中斷方式,傳遞的是佇列中的乙個「偽裝置」,並且,這個偽裝置的poll函式指標,指向了乙個叫做process_backlog的函式;

netif_rx_schedule函式完成兩件重要的工作:

1、將bakclog_dev裝置加入「處理資料報的裝置」的鍊錶當中;

2、觸發軟中斷函式,進行資料報接收處理;

這樣,我們可以猜想,在軟中斷函式中,不論是偽裝置bakclog_dev,還是真實的裝置(如前面討論過的e100),都會被軟中斷函式以:

dev->poll()

的形式呼叫,對於e100來說,poll函式的接收過程已經分析了,而對於其它所有沒有採用輪詢技術的網路裝置來說,它們將統統呼叫

process_backlog函式(我覺得把它改名為pseudo-poll是否更合適一些^o^)。

ok,我想分析到這裡,關於中斷處理與輪詢技術的差異,已經基本分析開了……

繼續來看,netif_rx_schedule進一步呼叫__netif_rx_schedule:

[cpp]view plain

copy

static

inline

void netif_rx_schedule(struct net_device *dev)    

[cpp]view plain

copy

static

inline

void __netif_rx_schedule(struct net_device *dev)    

軟中斷被觸發,註冊的net_rx_action函式將被呼叫

[cpp]view plain

copy

static

void net_rx_action(struct softirq_action *h)  

else   

}  out:  

local_irq_enable();  

return;  

softnet_break:  

__get_cpu_var(netdev_rx_stat).time_squeeze++;  

__raise_softirq_irqoff(net_rx_softirq);  

goto out;  

}  

對於dev->poll(dev,&budget)的呼叫,乙個真實的poll函式的例子,我們已經分析過了,現在來看process_backlog

[cpp]view plain

copy

static

int process_backlog(struct net_device *backlog_dev, int *budget)  

backlog_dev->quota -= work;  

*budget -= work;  

return -1;  

job_done:  

backlog_dev->quota -= work;  

*budget -= work;  

list_del(&backlog_dev->poll_list);  

smp_mb__before_clear_bit();  

netif_poll_enable(backlog_dev);  

if (queue->throttle)  

queue->throttle = 0;  

local_irq_enable();  

return 0;  

}  

這個函式重要的工作,就是出隊,然後呼叫netif_receive_skb()將資料報交給上層,這與上一節討論的poll是一樣的。這也是為什麼,

在網絡卡驅動的編寫中,採用中斷技術,要呼叫netif_rx,而採用輪詢技術,要呼叫netif_receive_skb啦!

到了這裡,就處理完資料報與裝置相關的部分了,資料報將進入上層協議棧……

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

四 網絡卡的資料接收 核心如何從網絡卡接受資料,傳統的經典過程 1 資料到達網絡卡 2 網絡卡產生乙個中斷給核心 3 核心使用i o指令,從網絡卡i o區域中去讀取資料 我們在許多網絡卡驅動中,都可以在網絡卡的中斷函式中見到這一過程。但是,這一種方法,有一種重要的問題,就是大流量的資料來到,網絡卡會...

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核心設計的理念,以...