天天看點

Linux網絡協定棧(三)——網絡裝置(2)

2.1、網絡裝置的注冊與登出

注冊網絡裝置發生在下列情形: 

(1)加載網卡驅動程式 

  網卡驅動程式如果被編譯進核心,則它在啟動時被初始化,在運作時被作為子產品加載。無論初始化是否發生,是以由驅動程式控制的網卡都被注冊。 

(2)插入可熱拔插網絡裝置 

  當使用者插入一塊熱拔插網卡,核心通知其對應的驅動程式以注冊裝置。(為了簡單化,我們假定裝置驅動程式已經被加載)。

兩個主要的情形會導緻裝置登出:

(1)解除安裝網卡驅動程式 

  這隻适用與驅動程式作為子產品被加載的情形,當然不适于編譯進核心的情況。當管理者解除安裝網卡裝置驅動程式時,所有相關網卡的驅動程式都被登出。

(2)移除熱拔插網絡裝置 

  當使用者從正在運作且核心支援熱拔插功能的系統中移除熱拔插網卡時,網絡裝置被登出。

2.1.1、配置設定 net_device結構空間

在核心中,網絡裝置由結構net_device表示,在注冊網絡裝置之前,必須為之配置設定記憶體空間,這一任務由net/core/dev.c中定義的alloc_netdev函數來完成:

Linux網絡協定棧(三)——網絡裝置(2)

Code

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

alloc_etherdev函數用于以太網裝置,是以它建立以字元串eth後跟唯一數字形式的裝置名;第二點,它指派ether_setup作為配置函數,對于所有以太網卡來說,配置函數均把net_device結構的部分域初始化為公用值。

Linux網絡協定棧(三)——網絡裝置(2)

2.1.2、注冊網絡裝置

網絡裝置注冊(a)與登出模型(b):

為了簡單,來看看回環裝置的注冊:

Linux網絡協定棧(三)——網絡裝置(2)

注:在這裡,裝置驅動初始化函數loopback_init并沒有調用alloc_netdev來配置設定記憶體,而是直接調用kmalloc,實際上alloc_netdev隻是對kmalloc的封裝而已。

2.2、網絡裝置的方法

2.2.1、打開與關閉裝置

打開網絡裝置

一旦裝置注冊即可使用,但它必須在使用者(或使用者空間應用程式)使能後才可以收發資料。dev_open 處理裝置使能的請求,它定義在net/core/dev.c,定義如下:

Linux網絡協定棧(三)——網絡裝置(2)

打開裝置由以下幾部組成:

(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函數指針:

Linux網絡協定棧(三)——網絡裝置(2)

關閉網絡裝置

Linux網絡協定棧(三)——網絡裝置(2)

它由以下幾步組成:

(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.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( )。

來看看回環裝置的發送與接收過程:

Linux網絡協定棧(三)——網絡裝置(2)

//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);

}

Linux網絡協定棧(三)——網絡裝置(2)

<a> </a>

繼續閱讀