]linux內核學習之網絡篇——接收分組

計算機處於網絡中,是不知道什麼時候會發過來數據,是不可預測的。因此,目前所有的現代設備驅動程序都是使用了中斷來通知內核的有 鏈路層的以太網幀到達。

驅動程序有特定的相應的程序,因此當有數據幀到達的時候,內核就會調用該程序。將數據從網卡上傳輸到物理內存中。或者通知內核延遲處理。

下面一個圖就是數據穿過內核的路徑完整的路徑

數據包在Linux內核鏈路層路徑

 

(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

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章