<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內(nèi)核網(wǎng)絡中的軟中斷KSOFTIRQD

          共 7940字,需瀏覽 16分鐘

           ·

          2022-12-18 01:05

          1. 前言

          之前分享過Linux內(nèi)核網(wǎng)絡數(shù)據(jù)包的接收過程,當執(zhí)行到網(wǎng)卡通過硬件中斷(IRQ)通知CPU,告訴它有數(shù)據(jù)來了,CPU會根據(jù)中斷表,調(diào)用已經(jīng)注冊的中斷函數(shù),這個中斷函數(shù)會調(diào)到驅(qū)動程序(NIC Driver)中相應的函數(shù)。驅(qū)動先禁用網(wǎng)卡的中斷,表示驅(qū)動程序已經(jīng)知道內(nèi)存中有數(shù)據(jù)了,告訴網(wǎng)卡下次再收到數(shù)據(jù)包直接寫內(nèi)存就可以了,不要再通知CPU了,這樣可以提高效率,避免CPU不停的被中斷。

          由于硬中斷處理程序執(zhí)行的過程中不能被中斷,所以如果它執(zhí)行時間過長,會導致CPU沒法響應其它硬件的中斷,于是內(nèi)核引入軟中斷,這樣可以將硬中斷處理函數(shù)中耗時的部分移到軟中斷處理函數(shù)里面來慢慢處理。內(nèi)核中的ksoftirqd進程專門負責軟中斷的處理,當它收到軟中斷后,就會調(diào)用相應軟中斷所對應的處理函數(shù),網(wǎng)卡驅(qū)動模塊拋出的軟中斷,ksoftirqd會調(diào)用網(wǎng)絡模塊的net_rx_action函數(shù)。

          那么接下來,我們先宏觀上回顧一下數(shù)據(jù)包接收的過程,以了解軟中斷在此過程中的位置,然后介紹一下內(nèi)核中的軟中斷。

          2. 數(shù)據(jù)包接收宏觀過程

          加載網(wǎng)卡驅(qū)動,初始化 數(shù)據(jù)包從外部網(wǎng)絡進入網(wǎng)卡 網(wǎng)卡(通過DMA)將包拷貝到內(nèi)核內(nèi)存中的ring buffer 產(chǎn)生硬件中斷,通知系統(tǒng)收到了一個包 驅(qū)動調(diào)用 NAPI ,如果輪詢(poll)還沒有開始,就開始輪詢 ksoftirqd軟中斷調(diào)用 NAPI 的poll函數(shù)從ring buffer收包(poll 函數(shù)是網(wǎng)卡驅(qū)動在初始化階段注冊的;每個cpu上都運行著一個ksoftirqd進程,在系統(tǒng)啟動期間就注冊了) ring buffer里面對應的內(nèi)存區(qū)域解除映射(unmapped) 如果 packet steering 功能打開,或者網(wǎng)卡有多隊列,網(wǎng)卡收到的數(shù)據(jù)包會被分發(fā)到多個cpu 數(shù)據(jù)包從隊列進入?yún)f(xié)議層 協(xié)議層處理數(shù)據(jù)包 數(shù)據(jù)包從協(xié)議層進入相應 socket 的接收隊列

          3. 軟中斷

          內(nèi)核的軟中斷系統(tǒng)是一種在硬中斷處理上下文(驅(qū)動中)之外執(zhí)行代碼的機制。硬中斷處理函數(shù)(handler)執(zhí)行時,會屏蔽部分或全部(新的)硬中斷。中斷被屏蔽的時間越長,丟失事件的可能性也就越大。所以,所有耗時的操作都應該從硬中斷處理邏輯中剝離出來,硬中斷因此能盡可能快地執(zhí)行,然后再重新打開硬中斷。

          內(nèi)核中也有其他機制將耗時操作轉(zhuǎn)移出去,不過對于網(wǎng)絡棧,我們接下來只看軟中斷ksoftirqd??梢园衍浿袛嘞到y(tǒng)想象成一系列內(nèi)核線程(每個 CPU 一個),這些線程執(zhí)行針對不同事件注冊的處理函數(shù)(handler),內(nèi)核子系統(tǒng)(比如網(wǎng)絡)能通過 open_softirq() 注冊軟中斷處理函數(shù)。通過 top 命令,會注意到 ksoftirqd/0 這個內(nèi)核線程,其表示這個軟中斷線程跑在 CPU 0 上,如下圖所示。

          4. ksoftirqd

          軟中斷對分擔硬中斷的工作量至關重要,因此軟中斷線程在內(nèi)核啟動的很早階段就 spawn 出來了。

          kernel/softirq.c 中對 ksoftirqd 系統(tǒng)進行了初始化:

          static struct smp_hotplug_thread softirq_threads = {
                .store              = &ksoftirqd,
                .thread_should_run  = ksoftirqd_should_run,
                .thread_fn          = run_ksoftirqd,
                .thread_comm        = "ksoftirqd/%u",
          };

          static __init int spawn_ksoftirqd(void)
          {
                register_cpu_notifier(&cpu_nfb);

                BUG_ON(smpboot_register_percpu_thread(&softirq_threads));

                return 0;
          }
          early_initcall(spawn_ksoftirqd);

          看到注冊了兩個回調(diào)函數(shù):ksoftirqd_should_run 和 run_ksoftirqd。這兩個函數(shù)都會從 kernel/smpboot.c 里調(diào)用,作為事件處理循環(huán)的一部分。

          kernel/smpboot.c 里面的代碼首先調(diào)用 ksoftirqd_should_run 判斷是否有 pending 的軟中斷,如果有,就執(zhí)行 run_ksoftirqd,后者做一些 bookeeping 工作,然后調(diào)用 __do_softirq。

          __do_softirq 做的幾件事情:

          • 判斷哪個 softirq 被 pending
          • 計算 softirq 時間,用于統(tǒng)計
          • 更新 softirq 執(zhí)行相關的統(tǒng)計數(shù)據(jù)
          • 執(zhí)行 pending softirq 的處理函數(shù)
          asmlinkage __visible void __do_softirq(void)
          {
              unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
              unsigned long old_flags = current->flags;
              int max_restart = MAX_SOFTIRQ_RESTART;
              struct softirq_action *h;
              bool in_hardirq;
              __u32 pending;
              int softirq_bit;

              /*
               * Mask out PF_MEMALLOC s current task context is borrowed for the
               * softirq. A softirq handled such as network RX might set PF_MEMALLOC
               * again if the socket is related to swap
               */

              current->flags &= ~PF_MEMALLOC;

              pending = local_softirq_pending(); //獲取當前CPU的軟中斷寄存器__softirq_pending值到局部變量pending。
              account_irq_enter_time(current);

              __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET); //增加preempt_count中的softirq域計數(shù),表明當前在軟中斷上下文中。
              in_hardirq = lockdep_softirq_start();

          restart:
              /* Reset the pending bitmask before enabling irqs */
              set_softirq_pending(0); //清除軟中斷寄存器__softirq_pending。

              local_irq_enable(); //打開本地中斷

              h = softirq_vec; //指向softirq_vec第一個元素,即軟中斷HI_SOFTIRQ對應的處理函數(shù)。

              while ((softirq_bit = ffs(pending))) { //ffs()找到pending中第一個置位的比特位,返回值是第一個為1的位序號。這里的位是從低位開始,這也和優(yōu)先級相吻合,低位優(yōu)先得到執(zhí)行。如果沒有則返回0,退出循環(huán)。
                  unsigned int vec_nr;
                  int prev_count;

                  h += softirq_bit - 1//根據(jù)sofrirq_bit找到對應的軟中斷描述符,即軟中斷處理函數(shù)。

                  vec_nr = h - softirq_vec; //軟中斷序號
                  prev_count = preempt_count();

                  kstat_incr_softirqs_this_cpu(vec_nr);

                  trace_softirq_entry(vec_nr);
                  h->action(h); //執(zhí)行對應軟中斷函數(shù)
                  trace_softirq_exit(vec_nr);
                  if (unlikely(prev_count != preempt_count())) {
                      pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n",
                             vec_nr, softirq_to_name[vec_nr], h->action,
                             prev_count, preempt_count());
                      preempt_count_set(prev_count);
                  }
                  h++; //h遞增,指向下一個軟中斷
                  pending >>= softirq_bit; //pending右移softirq_bit位
              }

              rcu_bh_qs();
              local_irq_disable(); //關閉本地中斷

              pending = local_softirq_pending(); //再次檢查是否有軟中斷產(chǎn)生,在上一次檢查至此這段時間有新軟中斷產(chǎn)生。
              if (pending) {
                  if (time_before(jiffies, end) && !need_resched() && max_restart) //再次觸發(fā)軟中斷執(zhí)行的三個條件:1.軟中斷處理時間不超過2jiffies,200Hz的系統(tǒng)對應10ms;2.當前沒有有進程需要調(diào)度,即!need_resched();3.這種循環(huán)不超過10次。
                      goto restart;

                  wakeup_softirqd(); //如果上面的條件不滿足,則喚醒ksoftirq內(nèi)核線程來處理軟中斷。
              }

              lockdep_softirq_end(in_hardirq);
              account_irq_exit_time(current);
              __local_bh_enable(SOFTIRQ_OFFSET); //減少preempt_count的softirq域計數(shù),和前面增加計數(shù)呼應。表示這段代碼處于軟中斷上下文。
              WARN_ON_ONCE(in_interrupt());
              tsk_restore_flags(current, old_flags, PF_MEMALLOC);
          }

          查看 CPU 利用率時,si 字段對應的就是 softirq,度量(從硬中斷轉(zhuǎn)移過來的)軟中斷的 CPU 使用量。

          5. 監(jiān)測

          軟中斷的信息可以從 /proc/softirqs 讀?。?/p>

          6. 總結(jié)

          中斷是一種異步的事件處理機制,用來提高系統(tǒng)的并發(fā)處理能力。中斷事件發(fā)生,會觸發(fā)執(zhí)行中斷處理程序,而中斷處理程序被分為上半部和下半部這兩個部分。上半部對應硬中斷,用來快速處理中斷;下半部對應軟中斷,用來異步處理上半部未完成的工作。Linux 中的軟中斷包括網(wǎng)絡收發(fā)、定時、調(diào)度、RCU 鎖等各種類型,我們可以查看 proc 文件系統(tǒng)中的 /proc/softirqs ,觀察軟中斷的運行情況。在 Linux 中,每個 CPU 都對應一個軟中斷內(nèi)核線程,名字是 ksoftirqd/CPU 編號。當軟中斷事件的頻率過高時,內(nèi)核線程也會因為 CPU 使用率過高而導致軟中斷處理不及時,進而引發(fā)網(wǎng)絡收發(fā)延遲、調(diào)度緩慢等性能問題。

          參考資料:

          https://blog.packagecloud.io/eng/2016/06/22/monitoring-tuning-linux-networking-stack-receiving-data/

          https://www.cnblogs.com/luoahong/p/10815283.html

          鏈接:http://kerneltravel.net/blog/2020/ksoftirqd_ljr/

          (版權(quán)歸原作者所有,侵刪)


          瀏覽 85
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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爱爱 | 日韩欧美在中文 | 91超碰在线免费观看 | 日日夜夜综合 |