計算機處於網絡中,是不知道什麼時候會發過來數據,是不可預測的。因此,目前所有的現代設備驅動程序都是使用了中斷來通知內核的有 鏈路層的以太網幀到達。
驅動程序有特定的相應的程序,因此當有數據幀到達的時候,內核就會調用該程序。將數據從網卡上傳輸到物理內存中。或者通知內核延遲處理。
下面一個圖就是數據穿過內核的路徑完整的路徑
(1)net_interrupt是設備驅動程序設置的終端處理程序。
(2)net_rx 函數是特定於網卡。首先會創建一個心的套接字緩衝區。分組的內容就會從網卡傳輸到緩衝區,也就是內核空間的內存中。然後使用各種函數來分析這個數據包,比如IP協議。
下面一個比較重要的函數
/**
* netif_rx - post buffer to the network code
* @skb: buffer to post
*
* This function receives a packet from a device driver and queues it for
* the upper (protocol) levels to process. It always succeeds. The buffer
* may be dropped during processing for congestion control or by the
* protocol layers.
*
* return values:
* NET_RX_SUCCESS (no congestion)
* NET_RX_CN_LOW (low congestion)
* NET_RX_CN_MOD (moderate congestion)
* NET_RX_CN_HIGH (high congestion)
* NET_RX_DROP (packet was dropped)
*
*/
int netif_rx(struct sk_buff *skb)
{
int this_cpu;
struct softnet_data *queue;
unsigned long flags;
if (!skb->stamp.tv_sec)
do_gettimeofday(&skb->stamp);
/*
* The code is rearranged so that the path is the most
* short when CPU is congested, but is still operating.
*/
local_irq_save(flags);
this_cpu = smp_processor_id();
queue = &__get_cpu_var(softnet_data);
__get_cpu_var(netdev_rx_stat).total++;
if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {
if (queue->input_pkt_queue.qlen) {
if (queue->throttle)
goto drop;
enqueue:
dev_hold(skb->dev);
__skb_queue_tail(&queue->input_pkt_queue, skb);
#ifndef OFFLINE_SAMPLE
get_sample_stats(this_cpu);
#endif
local_irq_restore(flags);
return queue->cng_level;
}
if (queue->throttle) {
queue->throttle = 0;
#ifdef CONFIG_NET_HW_FLOWCONTROL
if (atomic_dec_and_test(&netdev_dropping))
netdev_wakeup();
#endif
}
netif_rx_schedule(&queue->backlog_dev);
goto enqueue;
}
if (!queue->throttle) {
queue->throttle = 1;
__get_cpu_var(netdev_rx_stat).throttled++;
#ifdef CONFIG_NET_HW_FLOWCONTROL
atomic_inc(&netdev_dropping);
#endif
}
drop:
__get_cpu_var(netdev_rx_stat).dropped++;
local_irq_restore(flags);
kfree_skb(skb);
return NET_RX_DROP;
}
/*
* Incoming packets are placed on per-cpu queues so that
* no locking is needed.
*/
struct softnet_data
{
int throttle;
int cng_level;
int avg_blog;
struct sk_buff_head input_pkt_queue;
struct list_head poll_list;
struct net_device *output_queue;
struct sk_buff *completion_queue;
struct net_device backlog_dev; /* Sorry. 8) */
};
上邊那個方法就是讓所的消息按照一個隊列上上一次層協議去處理。其內部原理就是將受到的隊列分組放到特定的cpu上的等待隊列並推出中斷上下文,是cpu做別的事情。
隊列就是struct softnet_data數據結構。
接着,會分配一個新的套接字緩衝區 skb ,並調用與協議無關的、網絡設備均支持的通用網絡接收處理函數 netif_rx(skb) 。 netif_rx() 函數讓內核準備進一步處理 skb 。
- 然後, skb 會進入到達隊列以便 CPU 處理(對於多核 CPU 而言,每個 CPU 維護一個隊列)。如果 FIFO隊列已滿,就會丟棄此分組。在 skb 排隊後,調用 __cpu_raise_softirq() 標記 NET_RX_SOFTIRQ 軟中斷,等待 CPU 執行。
- 至此, netif_rx() 函數調用結束,返回調用者狀況信息(成功還是失敗等)。此時,中斷上下文進程完成任務,數據分組繼續被上層協議棧處理。
NET_RX_SOFTIRQ 網絡接收軟中斷
CPU 開始處理軟中斷 do_softirq() ,,接着 net_rx_action() 處理前面標記的 NET_RX_SOFTIRQ ,把出對列的 skb 送入相應列表處理(根據協議不同到不同的列表)。比如,IP 分組交給 ip_rcv() 處理, ARP 分組交給 arp_rcv() 處理等。
今天只能看着點,很多不明白啊,要細細研究。
更多文章,歡迎訪問 http://blog.csdn.net/wallwind