Linux網絡協議棧(三)——網絡設備(2)

2.一、網絡設備的註冊與註銷
註冊網絡設備發生在下列情形: 
(1)加載網卡驅動程序 
  網卡驅動程序若是被編譯進內核,則它在啓動時被初始化,在運行時被做爲模塊加載。不管初始化是否發生,因此由驅動程序控制的網卡都被註冊。 
(2)插入可熱拔插網絡設備 
  當用戶插入一塊熱拔插網卡,內核通知其對應的驅動程序以註冊設備。(爲了簡單化,咱們假定設備驅動程序已經被加載)。

兩個主要的情形會致使設備註銷:
(1)卸載網卡驅動程序 
  這隻適用與驅動程序做爲模塊被加載的情形,固然不適於編譯進內核的狀況。當管理員卸載網卡設備驅動程序時,全部相關網卡的驅動程序都被註銷。
(2)移除熱拔插網絡設備 
  當用戶從正在運行且內核支持熱拔插功能的系統中移除熱拔插網卡時,網絡設備被註銷。

2.1.一、分配 net_device結構空間
在內核中,網絡設備由結構net_device表示,在註冊網絡設備以前,必須爲之分配內存空間,這一任務由net/core/dev.c中定義的alloc_netdev函數來完成:網絡

Code

內核也提供了一些封裝alloc_netdev功能的函數,以下:

alloc_etherdev函數用於以太網設備,因此它建立以字符串eth後跟惟一數字形式的設備名;第二點,它指派ether_setup做爲配置函數,對於全部以太網卡來講,配置函數均把net_device結構的部分域初始化爲公用值。數據結構

Code

2.1.二、註冊網絡設備
網絡設備註冊(a)與註銷模型(b):

爲了簡單,來看看回環設備的註冊:socket

Code

注:在這裏,設備驅動初始化函數loopback_init並無調用alloc_netdev來分配內存,而是直接調用kmalloc,實際上alloc_netdev只是對kmalloc的封裝而已。

2.二、網絡設備的方法
2.2.一、打開與關閉設備
打開網絡設備
一旦設備註冊便可使用,但它必須在用戶(或用戶空間應用程序)使能後才能夠收發數據。dev_open 處理設備使能的請求,它定義在net/core/dev.c,定義以下:函數

Code

打開設備由如下幾部組成:
(1)若是 dev->open 被定義則調用它。並不是全部的驅動程序都初始化這個函數。 
(2)設置 dev->state 的__LINK_STATE_START 標誌位,標記設備打開並在運行。
(3)設置 dev->flags 的 IFF_UP 標誌位標記設備啓動。 
(4)調用dev_activate所指函數初始化流量控制用的排隊規則,並啓動監視定時器。如
果用戶沒有配置流量控制,則指定缺省的先進先出(FIFO)隊列。 
(5)發送 NETDEV_UP 通知給 netdev_chain 通知鏈以通知對設備使能有興趣的內核組件。

設備驅動的open方法
來看看3com網卡的驅動drivers/net/3c59x.c中的vortex_open,它是在vortex_init()中(即驅動程序初始化的過程當中)賦給dev->open函數指針:oop

Code


關閉網絡設備post

Code

它由如下幾步組成:
(1)發送 NETDEV_GOING_DOWN 通知到 netdev_chain 通知鏈以通知對設備禁止有興趣的內核組件。 
(2)調用 dev_deactivate 函數禁止出口隊列規則,這樣確保設備再也不用於傳輸,並中止
再也不須要的監控定時器。 
(3)清除 dev->state 標誌的__LINK_STATE_START 標誌位,標記設備卸載。 
(4)若是輪詢動做被調度在讀設備入隊列數據包,則等待此動做完成。這是因爲__LINK_STATE_START 標誌位被清除,再也不接受其它輪詢在設備上調度,但在標誌被清除前已有一個輪詢正被調度。 
(5)若是 dev->stop 指針不空則調用它,並不是全部的設備驅動都初始化此函數指針。 
(6)清除 dev->flags 的 IFF_UP 標誌位標識設備關閉。
(7)發送 NETDEV_DOWN 通知給 netdev_chain 通知鏈,通知對設備禁止感興趣的內核組件。

2.2.二、傳輸數據與接收數據
傳輸數據
網絡子系統中,數據最後由鏈路層協議調用dev_queue_xmit(),位於net/core/dev.c,完成傳輸,而dev_queue_xmit又會調用具體網絡適配器的驅動程序方法dev->hard_start_xmit(),從而驅動網絡適配器最終完成數據傳輸,參見vortex_start_xmit()。

接收數據
當網絡適配器接收一個數據幀時,就會觸發一箇中斷,在中斷處理程序(位於設備驅動)中,會分配sk_buff數據結構保存數據幀,最後會調用netif_rx(),將套接字緩衝區放入網絡設備的輸入隊列。對於3c39x.c,其過程以下:
vortex_interrupt( )---> vortex_rx( )--->netif_rx( )。

來看看回環設備的發送與接收過程:spa

複製代碼
//derivers/net/loopback.c
/*首先調用skb_orphan把skb孤立,使它跟發送socket和協議棧再也不有任何聯繫,也即對本機來講,
**這個skb的數據內容已經發送出去了,而skb至關於已經被釋放掉了。對於環回設備接口來講,
**數據的發送工做至此已經所有完成,接下來,只要把這個實際上還未被釋放的skb傳回給協議棧
**的接收函數便可。
*/
static int loopback_xmit(struct sk_buff *skb, struct net_device *dev)
{
    struct net_device_stats *lb_stats;
    /////////至關於發送過程////////////////////
    skb_orphan(skb);

    //////////如下至關於接收過程////////////////
    skb->protocol=eth_type_trans(skb,dev);
    skb->dev=dev;
#ifndef LOOPBACK_MUST_CHECKSUM
    skb->ip_summed = CHECKSUM_UNNECESSARY;
#endif

    if (skb_shinfo(skb)->tso_size) {
        BUG_ON(skb->protocol != htons(ETH_P_IP));
        BUG_ON(skb->nh.iph->protocol != IPPROTO_TCP);

        emulate_large_send_offload(skb);
        return 0;
    }

    dev->last_rx = jiffies; //接收到數據的時間
    
    //統計信息
    lb_stats = &per_cpu(loopback_stats, get_cpu());
    lb_stats->rx_bytes += skb->len;
    lb_stats->tx_bytes += skb->len;
    lb_stats->rx_packets++;
    lb_stats->tx_packets++;
    put_cpu();

    netif_rx(skb);

    return(0);
}
複製代碼
相關文章
相關標籤/搜索