五、佇列層
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核心設計的理念,以...