<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Linux 網(wǎng)卡數(shù)據(jù)收發(fā)過(guò)程分析

          共 4662字,需瀏覽 10分鐘

           ·

          2021-03-19 17:39

          一般來(lái)說(shuō),網(wǎng)卡主要有兩個(gè)重要的功能:接收數(shù)據(jù)發(fā)送數(shù)據(jù)。

          所以,當(dāng)網(wǎng)卡接收到數(shù)據(jù)包后,要通知 Linux 內(nèi)核有數(shù)據(jù)需要處理。另外,網(wǎng)卡驅(qū)動(dòng)應(yīng)該提供讓 Linux 內(nèi)核把數(shù)據(jù)把發(fā)送出去的接口。

          net_device 結(jié)構(gòu)是 Linux 為了適配不同類型的網(wǎng)卡設(shè)備而抽象出來(lái)的對(duì)象,不同的網(wǎng)卡驅(qū)動(dòng)只需要按 Linux 的規(guī)范來(lái)填充 net_device 結(jié)構(gòu)的各個(gè)成員變量,Linux 內(nèi)核就能夠識(shí)別出網(wǎng)卡,并工作起來(lái)。

          下面我們將分析網(wǎng)卡設(shè)備接收和發(fā)送數(shù)據(jù)包的實(shí)現(xiàn)原理。

          net_device 結(jié)構(gòu)

          net_device 結(jié)構(gòu)是 Linux 內(nèi)核對(duì)網(wǎng)卡設(shè)備的抽象,但由于歷史原因,net_device 結(jié)構(gòu)的定義十分復(fù)雜。

          不過(guò)本文主要分析網(wǎng)卡設(shè)備收發(fā)數(shù)據(jù)的實(shí)現(xiàn),所以不會(huì)分析 net_device 結(jié)構(gòu)的所有成員。下面主要列出收發(fā)數(shù)據(jù)相關(guān)的成員,如下:

          struct net_device{    char                name[IFNAMSIZ];  // 設(shè)備名字    ...    unsigned int        irq;             // 中斷號(hào)    ...    int (*init)(struct net_device *dev); // 設(shè)備初始化設(shè)備的接口    ...    int (*open)(struct net_device *dev); // 打開(kāi)設(shè)備時(shí)調(diào)用的接口    int (*stop)(struct net_device *dev); // 關(guān)閉設(shè)備時(shí)調(diào)用的接口
          // 發(fā)送數(shù)據(jù)接口 int (*hard_start_xmit)(struct sk_buff *skb,struct net_device *dev); ...};

          下面介紹一下各個(gè)成員的作用:

          • name:設(shè)備的名字。用于在終端顯示設(shè)備的名字或者通過(guò)設(shè)備名字來(lái)搜索設(shè)備。

          • irq:中斷號(hào)。當(dāng)網(wǎng)卡從網(wǎng)絡(luò)接收到數(shù)據(jù)包后,需要產(chǎn)生一個(gè)中斷來(lái)通知 Linux 內(nèi)核有數(shù)據(jù)包需要處理,而 irq 就是網(wǎng)卡驅(qū)動(dòng)注冊(cè)到內(nèi)核中斷服務(wù)的中斷號(hào)。

          • init、openstop:分別為設(shè)備的初始化接口,打開(kāi)接口和關(guān)閉接口。

          • hard_start_xmit:當(dāng)需要通過(guò)網(wǎng)卡設(shè)備發(fā)送數(shù)據(jù)時(shí),可以調(diào)用這個(gè)接口來(lái)發(fā)送數(shù)據(jù)。

          所以,一個(gè)網(wǎng)卡驅(qū)動(dòng)必須完成以下兩個(gè)工作:

          • 通過(guò)實(shí)現(xiàn) net_device 結(jié)構(gòu)的 hard_start_xmit 方法來(lái)提供發(fā)送數(shù)據(jù)的功能。

          • 通過(guò)向內(nèi)核注冊(cè)硬件中斷服務(wù),來(lái)通知內(nèi)核處理網(wǎng)卡設(shè)備接收到的數(shù)據(jù)包。

          也就是說(shuō),發(fā)送數(shù)據(jù)的功能是由 net_device 結(jié)構(gòu)的 hard_statr_xmit 方法提供,而通知內(nèi)核處理接收到的數(shù)據(jù)包的功能是由網(wǎng)卡的硬件中斷提供的。

          圖1 展示了網(wǎng)卡接收和發(fā)送數(shù)據(jù)的過(guò)程:

          圖1 網(wǎng)卡接收和發(fā)送數(shù)據(jù)過(guò)程

          上圖展示的是 NS8390網(wǎng)卡 接收和發(fā)送數(shù)據(jù)的過(guò)程(紅色括號(hào)為接收過(guò)程,藍(lán)色括號(hào)為發(fā)送過(guò)程),從上圖可以發(fā)現(xiàn),NS8390網(wǎng)卡驅(qū)動(dòng) 完成了兩件事情:

          • net_device 結(jié)構(gòu)的 hard_start_xmit 方法設(shè)置為 ei_start_xmit。

          • 向 Linux 內(nèi)核注冊(cè)了 ei_interrupt 硬件中斷服務(wù)。

          所以,當(dāng)網(wǎng)卡接收到數(shù)據(jù)包時(shí),會(huì)觸發(fā) ei_interrupt 中斷服務(wù)來(lái)通知內(nèi)核有數(shù)據(jù)包需要處理。而當(dāng)需要通過(guò)網(wǎng)卡發(fā)送數(shù)據(jù)時(shí),將會(huì)調(diào)用 ei_start_xmit 方法把數(shù)據(jù)發(fā)送出去。

          接收數(shù)據(jù)過(guò)程

          當(dāng)網(wǎng)卡從網(wǎng)絡(luò)中接收到數(shù)據(jù)包后,會(huì)觸發(fā) ei_interrupt 中斷服務(wù),我們來(lái)看看 ei_interrupt 中斷服務(wù)的實(shí)現(xiàn):

          void ei_interrupt(int irq, void *dev_id, struct pt_regs *regs){    struct net_device *dev = dev_id;    long e8390_base;    int interrupts, nr_serviced = 0;    struct ei_device *ei_local;
          e8390_base = dev->base_addr; ei_local = (struct ei_device *)dev->priv;
          spin_lock(&ei_local->page_lock); ... // (1) 通過(guò)讀取網(wǎng)卡的中斷類型來(lái)進(jìn)行相應(yīng)的操作 while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0 && ++nr_serviced < MAX_SERVICE) { ... // (2) 如果中斷類型為接收到數(shù)據(jù)包 if (interrupts & (ENISR_RX + ENISR_RX_ERR)) { ei_receive(dev); // (3) 則從網(wǎng)卡讀取數(shù)據(jù) } ... } ... spin_unlock(&ei_local->page_lock); return;}

          上面的代碼刪除了很多硬件相關(guān)的操作,因?yàn)楸疚牟⒉皇欠治鼍W(wǎng)卡驅(qū)動(dòng)的實(shí)現(xiàn)。

          ei_interrupt 中斷服務(wù)首先讀取中斷的類型,保存到 interrupts 變量中。然后判斷中斷類型是否為接收到數(shù)據(jù)包,如果是就調(diào)用 ei_receive 函數(shù)從網(wǎng)卡處讀取數(shù)據(jù)。

          我們繼續(xù)分析 ei_receive 函數(shù)的實(shí)現(xiàn):

          static void ei_receive(struct net_device *dev){    ...    while (++rx_pkt_count < 10)     {        int pkt_len;  // 數(shù)據(jù)包的長(zhǎng)度        int pkt_stat; // 數(shù)據(jù)包的狀態(tài)        ...        if ((pkt_stat & 0x0F) == ENRSR_RXOK) { // 如果數(shù)據(jù)包狀態(tài)是合法的            struct sk_buff *skb;
          skb = dev_alloc_skb(pkt_len + 2); // 申請(qǐng)一個(gè)數(shù)據(jù)包對(duì)象 if (skb) { skb_reserve(skb, 2); skb->dev = dev; // 設(shè)置接收數(shù)據(jù)包的設(shè)備 skb_put(skb, pkt_len); // 增加數(shù)據(jù)的長(zhǎng)度
          // 從網(wǎng)卡中讀取數(shù)據(jù)(由網(wǎng)卡驅(qū)動(dòng)實(shí)現(xiàn)), 并將數(shù)據(jù)保存到skb中 ei_block_input(dev, pkt_len, skb, current_offset+sizeof(rx_frame));
          skb->protocol = eth_type_trans(skb, dev); // 從以太網(wǎng)頭部中獲取網(wǎng)絡(luò)層協(xié)議類型
          netif_rx(skb); // 將數(shù)據(jù)包上送給內(nèi)核網(wǎng)絡(luò)協(xié)議棧 ... } } ... } ... return;}

          ei_receive 函數(shù)主要完成以下幾個(gè)工作:

          • 申請(qǐng)一個(gè) sk_buff 數(shù)據(jù)包對(duì)象,并且設(shè)置其 dev 字段為接收數(shù)據(jù)包的設(shè)備。

          • 通過(guò)調(diào)用 ei_block_input 函數(shù)從網(wǎng)卡中讀取接收到的數(shù)據(jù),并保存到剛申請(qǐng)的 sk_buff 數(shù)據(jù)包對(duì)象中。ei_block_input 函數(shù)是由網(wǎng)卡驅(qū)動(dòng)實(shí)現(xiàn)的,所以這里不作詳細(xì)分析。

          • 通過(guò)調(diào)用 eth_type_trans 函數(shù)從數(shù)據(jù)包的以太網(wǎng)頭部中獲取網(wǎng)絡(luò)層協(xié)議類型。

          • 調(diào)用 netif_rx 函數(shù)將數(shù)據(jù)包上送給內(nèi)核網(wǎng)絡(luò)協(xié)議棧。

          當(dāng)把數(shù)據(jù)包上送給內(nèi)核網(wǎng)絡(luò)協(xié)議棧后,數(shù)據(jù)包的處理就由內(nèi)核接管。一般來(lái)說(shuō),內(nèi)核網(wǎng)絡(luò)協(xié)議棧會(huì)通過(guò)網(wǎng)絡(luò)層的 IP協(xié)議 和傳輸層的 TCP協(xié)議 或者 UDP協(xié)議 來(lái)對(duì)數(shù)據(jù)包進(jìn)行處理,處理完后就會(huì)把數(shù)據(jù)提交給應(yīng)用層的進(jìn)程進(jìn)行處理。

          發(fā)送數(shù)據(jù)過(guò)程

          當(dāng)網(wǎng)絡(luò)協(xié)議棧需要通過(guò)網(wǎng)卡設(shè)備發(fā)送數(shù)據(jù)時(shí),會(huì)調(diào)用 net_device 結(jié)構(gòu)的 hard_start_xmit 方法,而對(duì)于 NS8390網(wǎng)卡 來(lái)說(shuō),hard_start_xmit 方法會(huì)被設(shè)置為 ei_start_xmit 函數(shù)。

          也就是說(shuō),使用 NS8390網(wǎng)卡 發(fā)送數(shù)據(jù)時(shí),最終會(huì)調(diào)用 ei_start_xmit 函數(shù)將數(shù)據(jù)發(fā)送出去。我們來(lái)看看 ei_start_xmit 函數(shù)的實(shí)現(xiàn):

          static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev){    ...    length = skb->len; // 數(shù)據(jù)包的長(zhǎng)度    ...    disable_irq_nosync(dev->irq);      // 關(guān)閉硬件中斷    spin_lock(&ei_local->page_lock);   // 對(duì)設(shè)備進(jìn)行上鎖, 避免多核CPU對(duì)設(shè)備的使用    ...    // 使用網(wǎng)卡驅(qū)動(dòng)的發(fā)送接口把數(shù)據(jù)發(fā)送出去,skb->data 為數(shù)據(jù)包的數(shù)據(jù)部分    ei_block_output(dev, length, skb->data, ei_local->tx_start_page);    ...    spin_unlock(&ei_local->page_lock); // 對(duì)設(shè)備進(jìn)行解鎖    enable_irq(dev->irq);              // 打開(kāi)硬件中斷    ...    return 0;}

          刪減了硬件相關(guān)的操作后,ei_start_xmit 函數(shù)的實(shí)現(xiàn)就非常簡(jiǎn)單:

          • 首先關(guān)閉網(wǎng)卡的硬件中斷,防止發(fā)送過(guò)程中受到硬件中斷的干擾。

          • 調(diào)用 ei_block_output 函數(shù)把數(shù)據(jù)包的數(shù)據(jù)發(fā)送出去,此函數(shù)由網(wǎng)卡驅(qū)動(dòng)實(shí)現(xiàn),這里不作詳細(xì)分析。

          • 打開(kāi)網(wǎng)卡的硬件中斷,讓網(wǎng)卡能夠繼續(xù)通知內(nèi)核。

          總結(jié)

          本文主要簡(jiǎn)單的介紹了網(wǎng)卡設(shè)備接收和發(fā)送數(shù)據(jù)包的過(guò)程,而網(wǎng)卡設(shè)備的初始化過(guò)程并沒(méi)有涉及。當(dāng)然網(wǎng)卡設(shè)備的初始化過(guò)程也非常重要,可能會(huì)在后面的文章繼續(xù)分析。


          瀏覽 64
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  豆花视频成人网站入口免费观看 | 操B视频欧美 | 樱桃视频黄色 | av先峰网婷婷五月天 | 久久午夜鲁丝片无码免费 |