<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)絡(luò)中斷下半部處理

          共 4475字,需瀏覽 9分鐘

           ·

          2021-03-23 17:30

          上一篇文章 中,我們介紹了網(wǎng)卡接收和發(fā)過(guò)數(shù)據(jù)在 Linux 內(nèi)核中的處理過(guò)程,我們先來(lái)回顧一下網(wǎng)卡接收和發(fā)送數(shù)據(jù)的過(guò)程,如 圖1 所示:

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

          如上圖所示,當(dāng)網(wǎng)卡接收到從網(wǎng)絡(luò)中發(fā)送過(guò)來(lái)的數(shù)據(jù)后,網(wǎng)卡會(huì)向 CPU 發(fā)起一個(gè)硬件中斷。當(dāng) CPU 接收到網(wǎng)卡的硬件中斷后,便會(huì)調(diào)用網(wǎng)卡驅(qū)動(dòng)向內(nèi)核注冊(cè)的中斷處理服務(wù),如 NS8390網(wǎng)卡驅(qū)動(dòng) 會(huì)向內(nèi)核注冊(cè) ei_interrupt 中斷服務(wù)。

          由于在處理硬件中斷服務(wù)時(shí)會(huì)關(guān)閉硬件中斷,所以在處理硬件中斷服務(wù)的過(guò)程中,如果發(fā)生了其他的硬件中斷,也不能得到有效的處理,從而導(dǎo)致硬件中斷丟失的情況。

          為了避免這種情況出現(xiàn),Linux 內(nèi)核把中斷處理分為:中斷上半部中斷下半部,上半部在關(guān)閉中斷的情況下進(jìn)行,而下半部在打開(kāi)中斷的情況下進(jìn)行。

          由于中斷上半部在關(guān)閉中斷的情況下進(jìn)行,所以必須要快速完成,從而避免中斷丟失的情況。而中斷下半部處理是在打開(kāi)中斷的情況下進(jìn)行的,所以可以慢慢進(jìn)行。

          一般來(lái)說(shuō),網(wǎng)卡驅(qū)動(dòng)向內(nèi)核注冊(cè)的中斷處理服務(wù)屬于 中斷上半部,如前面介紹的 NS8390網(wǎng)卡驅(qū)動(dòng) 注冊(cè)的 ei_interrupt 中斷處理服務(wù),而本文主要分析網(wǎng)卡 中斷下半部 的處理。

          數(shù)據(jù)包上送

          在上一篇文章中,我們介紹過(guò) ei_interrupt  中斷處理服務(wù)首先會(huì)創(chuàng)建一個(gè) sk_buff 數(shù)據(jù)包對(duì)象保存從網(wǎng)卡中接收到的數(shù)據(jù),然后調(diào)用 netif_rx 函數(shù)將數(shù)據(jù)包上送給網(wǎng)絡(luò)協(xié)議棧處理。

          我們先來(lái)分析一下 netif_rx 函數(shù)的實(shí)現(xiàn): 

          int netif_rx(struct sk_buff *skb){     int this_cpu = smp_processor_id(); // 獲取當(dāng)前運(yùn)行的CPU     struct softnet_data *queue;     unsigned long flags;     ...     queue = &softnet_data[this_cpu]; // 獲取當(dāng)前CPU的待處理的數(shù)據(jù)包隊(duì)列      local_irq_save(flags); // 關(guān)閉本地硬件中斷      // 如果待處理隊(duì)列的數(shù)據(jù)包數(shù)量沒(méi)超出限制     if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {         if (queue->input_pkt_queue.qlen) {             ... enqueue:             dev_hold(skb->dev); // 增加網(wǎng)卡設(shè)備的引用計(jì)數(shù)器             __skb_queue_tail(&queue->input_pkt_queue, skb); // 將數(shù)據(jù)包添加到待處理隊(duì)列中             __cpu_raise_softirq(this_cpu, NET_RX_SOFTIRQ);  // 啟動(dòng)網(wǎng)絡(luò)中斷下半部處理             local_irq_restore(flags);              return softnet_data[this_cpu].cng_level;         }         ...         goto enqueue;     }     ... drop:     local_irq_restore(flags); // 打開(kāi)本地硬件中斷     kfree_skb(skb);           // 釋放數(shù)據(jù)包對(duì)象     return NET_RX_DROP; }

          netif_rx 函數(shù)的參數(shù)就是要上送給網(wǎng)絡(luò)協(xié)議棧的數(shù)據(jù)包,netif_rx 函數(shù)主要完成以下幾個(gè)工作:

          • 獲取當(dāng)前 CPU 的待處理的數(shù)據(jù)包隊(duì)列,在 Linux 內(nèi)核初始化時(shí),會(huì)為每個(gè) CPU 創(chuàng)建一個(gè)待處理數(shù)據(jù)包隊(duì)列,用于存放從網(wǎng)卡中讀取到網(wǎng)絡(luò)數(shù)據(jù)包。

          • 如果待處理隊(duì)列的數(shù)據(jù)包數(shù)量沒(méi)超出 netdev_max_backlog 設(shè)置的限制,那么調(diào)用 __skb_queue_tail 函數(shù)把數(shù)據(jù)包添加到待處理隊(duì)列中,并且調(diào)用 __cpu_raise_softirq 函數(shù)啟動(dòng)網(wǎng)絡(luò)中斷下半部處理。

          • 如果待處理隊(duì)列的數(shù)據(jù)包數(shù)量超出 netdev_max_backlog 設(shè)置的限制,那么就把數(shù)據(jù)包釋放。

          netif_rx 函數(shù)的處理過(guò)程如 圖2 所示:

          圖2 netif_rx 函數(shù)的處理過(guò)程

          所以,netif_rx 函數(shù)的主要工作就是把接收到的數(shù)據(jù)包添加到待處理隊(duì)列中,并且啟動(dòng)網(wǎng)絡(luò)中斷下半部處理。

          對(duì)于 Linux 內(nèi)核的中斷處理機(jī)制可以參考我們之前的文章 Linux中斷處理,這里就不詳細(xì)介紹了。在本文中,我們只需要知道網(wǎng)絡(luò)中斷下半部處理例程為 net_rx_action 函數(shù)即可。

          網(wǎng)絡(luò)中斷下半部處理

          上面說(shuō)了,網(wǎng)絡(luò)中斷下半部處理例程為 net_rx_action 函數(shù),所以我們主要分析 net_rx_action 函數(shù)的實(shí)現(xiàn):

          static void net_rx_action(struct softirq_action *h){     int this_cpu = smp_processor_id();                    // 當(dāng)前運(yùn)行的CPU     struct softnet_data *queue = &softnet_data[this_cpu]; // 當(dāng)前CPU對(duì)于的待處理數(shù)據(jù)包隊(duì)列     ...     for (;;) {         struct sk_buff *skb;          local_irq_disable();         skb = __skb_dequeue(&queue->input_pkt_queue); // 從待處理數(shù)據(jù)包隊(duì)列中獲取一個(gè)數(shù)據(jù)包         local_irq_enable();          if (skb == NULL)             break;          ...         {             struct packet_type *ptype, *pt_prev;             unsigned short type = skb->protocol; // 網(wǎng)絡(luò)層協(xié)議類(lèi)型              pt_prev = NULL;             ...             // 使用網(wǎng)絡(luò)層協(xié)議處理接口處理數(shù)據(jù)包             for (ptype = ptype_base[ntohs(type)&15]; ptype; ptype = ptype->next) {                 if (ptype->type == type                     && (!ptype->dev || ptype->dev == skb->dev))                 {                     if (pt_prev) {                         atomic_inc(&skb->users);                         // 如處理IP協(xié)議數(shù)據(jù)包的 ip_rcv() 函數(shù)                         pt_prev->func(skb, skb->dev, pt_prev);                     }                     pt_prev = ptype;                 }             }              if (pt_prev) {                 // 如處理IP協(xié)議數(shù)據(jù)包的 ip_rcv() 函數(shù)                 pt_prev->func(skb, skb->dev, pt_prev);             } else                 kfree_skb(skb);         }         ...     }     ...     return; }

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

          • 從待處理數(shù)據(jù)包隊(duì)列中獲取一個(gè)數(shù)據(jù)包,如果數(shù)據(jù)包為空,那么就退出網(wǎng)絡(luò)中斷下半部。

            如果獲取的數(shù)據(jù)包不為空,那么就從數(shù)據(jù)包的以太網(wǎng)頭部中獲取到網(wǎng)絡(luò)層協(xié)議的類(lèi)型。然后根據(jù)網(wǎng)絡(luò)層協(xié)議類(lèi)型從 ptype_base 數(shù)組中獲取數(shù)據(jù)處理接口,再通過(guò)此數(shù)據(jù)處理接口來(lái)處理數(shù)據(jù)包。

            在內(nèi)核初始化時(shí),通過(guò)調(diào)用 dev_add_pack 函數(shù)向 ptype_base 數(shù)組中注冊(cè)網(wǎng)絡(luò)層協(xié)議處理接口,如 ip_init 函數(shù):

            static struct packet_type ip_packet_type = {     __constant_htons(ETH_P_IP),     NULL,     ip_rcv,  // 處理IP協(xié)議數(shù)據(jù)包的接口     (void*)1,     NULL, };  void __init ip_init(void){     // 注冊(cè)網(wǎng)絡(luò)層協(xié)議處理接口     dev_add_pack(&ip_packet_type);     ... }

          所以,net_rx_action 函數(shù)主要從待處理隊(duì)列中獲取數(shù)據(jù)包,然后根據(jù)數(shù)據(jù)包的網(wǎng)絡(luò)層協(xié)議類(lèi)型,找到相應(yīng)的處理接口處理數(shù)據(jù)包。其過(guò)程如 圖3 所示:

          從上圖可知,net_rx_action 函數(shù)將數(shù)據(jù)包交由網(wǎng)絡(luò)層協(xié)議處理接口后就不管了,而網(wǎng)絡(luò)層協(xié)議處理接口接管數(shù)據(jù)包后,會(huì)對(duì)數(shù)據(jù)包進(jìn)行進(jìn)一步處理,如判斷數(shù)據(jù)包的合法性(數(shù)據(jù)包是否損壞、數(shù)據(jù)包是否發(fā)送給本機(jī))。如果數(shù)據(jù)包是合法的,就會(huì)交由傳輸層協(xié)議處理接口處理。

          總結(jié)

          本文主要介紹了網(wǎng)絡(luò)中斷下半部的處理,從分析可知,網(wǎng)絡(luò)中斷下半部主要工作是從待處理隊(duì)列中獲取數(shù)據(jù)包,并且根據(jù)數(shù)據(jù)包的網(wǎng)絡(luò)層協(xié)議類(lèi)型來(lái)找到相應(yīng)的處理接口,然后把數(shù)據(jù)包交由網(wǎng)絡(luò)層協(xié)議處理接口進(jìn)行處理。

          對(duì)于網(wǎng)絡(luò)層協(xié)議處理接口的相關(guān)過(guò)程,我們將會(huì)在后面的文章繼續(xù)分析。


          瀏覽 63
          點(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>
                  大鸡巴操小穴 | 手机看片91 | 神马午夜三级片 | 91av巨作在线 | 免费在线一级黄色电影网站 |