乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      詳解Linux中斷處理中的hardirq與softirq機制

       Liucw2012 2012-04-10
      http://www./forum.php/forum.php?mod=viewthread&tid=543
      今天在ChinaUnix論壇內(nèi)核源碼版上與linuxfellow網(wǎng)友討論到hardirq和softirq的問題,雖然在《深入Linux設(shè)備驅(qū)動程序內(nèi)核機制》(以下簡稱“ILDD”)第5章“中斷處理”對此已有詳細(xì)的解讀,但是我覺得還是有必要再花點時間深入探討一下這兩者的區(qū)別。因為此前關(guān)于ARM上的中斷處理我已經(jīng)在另一篇帖子解密ARM based Linux內(nèi)核中斷處理框架 中討論過,所以下面的討論只限于x86 32位系統(tǒng)。

      首先給出一個ILDD書中的一個插圖,關(guān)于中斷處理的整體框架:




      (圖1)

      接下來的一個插圖顯示了內(nèi)核用于標(biāo)識中斷上下文(in_interrupt())的變量preempt_count的布局:


      (圖2)

      按照x86處理器在外部中斷發(fā)生時的硬件邏輯,在do_IRQ被調(diào)用時,處理器已經(jīng)屏蔽了對外部中斷的響應(yīng)。在圖中我們看到中斷的處理大體上被分成兩部分HARDIRQ和SOFTIRQ,對應(yīng)到代碼層面,do_IRQ()中調(diào)用irq_enter函數(shù)可以看做hardirq 部分的開始,而irq_exit函數(shù)的調(diào)用則標(biāo)志著softirq部分的開始:

      <arch/x86/kernel/irq.c>


      1. unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
      2. {
      3.         ...
      4.         irq_enter();

      5.         //handle external interrupt (ISR)
      6.         ...
      7.         irq_exit();

      8.         return 1;
      9. }

      irq_enter()函數(shù)的核心調(diào)用是__irq_enter(),后者的主要作用是在圖2的 preempt_count變量的HARDIRQ部分+1,即標(biāo)識一個hardirq的上下文,所以可以認(rèn)為do_IRQ()調(diào)用irq_enter函數(shù)意味著中斷處理進入hardirq階段。此時處理器響應(yīng)外部中斷的能力依然是被disable掉的(EFLAG.IF=0),因為ISR基本上屬于設(shè)備驅(qū)動程序涉足的領(lǐng)域,內(nèi)核無法保證在設(shè)備驅(qū)動程序的ISR中會否將EFLAG.IF置1(此處涉及可能的中斷嵌套以及中斷棧溢出問題,可以參考ARM中斷處理的那個帖子),所以我們會在內(nèi)核代碼中看到內(nèi)核開發(fā)者為了盡可能避免非受控的ISR部分給系統(tǒng)帶來的損害所做的努力。按照現(xiàn)在內(nèi)核的設(shè)計理念,在 hardirq部分最好不要打開中斷,所以處在hardirq上下文中的設(shè)備驅(qū)動程序的ISR應(yīng)該盡可能快地返回(所謂只完成最關(guān)鍵的任務(wù)),而將耗時的中斷處理善后工作留到softirq部分,因為在softirq部分,內(nèi)核會使EFLAG.IF=1,所以也意味著softirq部分可以隨時被外部中斷所打斷。

      下面我們重點討論一下softirq部分的實現(xiàn)原理,do_IRQ()中調(diào)用irq_exit函數(shù)標(biāo)志softirq部分的開始。 irq_exit()函數(shù)會首先在圖2的preempt_count變量的HARDIRQ部分-1,目的是清除hardirq的上下文標(biāo)記。然后它有個關(guān)鍵的調(diào)用invoke_softirq,用于實際的softirq處理工作:

      <kernel/softirq.c>


      1. void irq_exit(void)
      2. {
      3.         ...
      4.         sub_preempt_count(IRQ_EXIT_OFFSET);
      5.         if (!in_interrupt() && local_softirq_pending())
      6.                 invoke_softirq();

      7.         rcu_irq_exit();
      8.         ...
      9. }
      ILDD中對in_interrupt()函數(shù)的敘述很明確:"...其主要用意是根據(jù)當(dāng)前preempt_count變量,來判斷當(dāng)前代碼是否在一個中斷上下文中執(zhí)行。根據(jù)in_interrupt的定義來看,Linux內(nèi)核認(rèn)為HARDIRQ、SOFTIRQ以及NMI 都屬于interrupt范疇...",所以softirq部分是否被執(zhí)行,取決于:1.當(dāng)前是否在中斷上下文,2. 是否有pending的softirq需要處理。
      第一個條件,主要用來防止softirq部分的重入,因為一旦有pending的softirq需要處理,那么invoke_softirq()的調(diào)用 (實際的工作發(fā)生在__do_softirq函數(shù)中)首先會將圖2中SOFTIRQ部分+1,這樣若在當(dāng)前正在處理softirq過程中發(fā)生了外部中斷,hardirq部分標(biāo)識了一個pending softirq,那么在irq_exit函數(shù)中將直接返回,而不會調(diào)用到invoke_softirq。

      softirq部分的核心代碼段如下:

      <kernel/softirq.c>
      1. asmlinkage void __do_softirq(void)
      2. {
      3.         ...
      4.         //獲得__softirq_pending變量上保存的pending softirq數(shù)據(jù)
      5.         pending = local_softirq_pending();
      6.         //將圖2中SOFTIRQ部分+1,標(biāo)識softirq上下文,此時in_interrupt()返回true.
      7.         __local_bh_disable((unsigned long)__builtin_return_address(0), SOFTIRQ_OFFSET);
      8.         ...
      9. restart:
      10.         /* Reset the pending bitmask before enabling irqs */
      11.         set_softirq_pending(0);
      12.         //注意此處,內(nèi)核調(diào)用local_irq_enable打開處理器響應(yīng)外部中斷能力,EFLAG.IF=1,所以接下來的softirq處理時外部中斷可以進入處理器
      13.         local_irq_enable();

      14.         h = softirq_vec;
      15.         do {
      16.                 if (pending & 1) {
      17.                         ...
      18.                         //調(diào)用每個類型的softirq所對應(yīng)的處理函數(shù),見下圖3
      19.                         h->action(h);
      20.                         ...
      21.                 }
      22.                 h++;
      23.                 pending >>= 1;
      24.         } while (pending);
      25.         //softirq處理結(jié)束前,重新關(guān)閉中斷。中斷的再次打開發(fā)生在中斷的返回,也就是在圖1中處理器在執(zhí)行iret指令時的硬件邏輯,POP EFLAG, EFLAG.IF=1
      26.         local_irq_disable();
      27.         
      28.         //再次檢測是否有新的pending softirq,因為softirq執(zhí)行時可能有新的外部中斷進來,如果有,此處一并處理。
      29.         pending = local_softirq_pending();
      30.         if (pending && --max_restart)
      31.                 goto restart;
      32.         ...
      33.         //將圖2中SOFTIRQ部分-1,標(biāo)識softirq上下文的結(jié)束
      34.         __local_bh_enable(SOFTIRQ_OFFSET);
      35. }
      其中每個關(guān)鍵的節(jié)點都做了注釋,ILDD書中對此也有詳細(xì)的說明,包括其中的ksoftirqd的用法。

      對每個類型的softirq的處理發(fā)生在上面的do...while...循環(huán)中,原理其實非常簡單,為節(jié)省文字,用下面一個草圖來做說明(圖3):



      所以do...while...循環(huán)實際上是從bit 0遍歷__softirq_pending變量,目前該變量的0~9分別對應(yīng)10個不同類型的softirq,每個softirq對應(yīng)不同的處理函數(shù),比如HI_SOFTIRQ對應(yīng)的action為tasklet_hi_action,它與TASKLET_SOFTIRQ的action的原理完全一樣,也就是我們平常所說的tasklet。__softirq_pending是個per-CPU型的變量,因為SMP系統(tǒng)中每個處理器都可以獨立處理各自到來的外部中斷,也都對應(yīng)有各自的hardirq棧和softirq棧,詳見Linux內(nèi)核中的中斷棧與內(nèi)核棧的補充說明一貼。另外,在前面的帖子中我們一直說hardirq部分標(biāo)識一個softirq,何謂標(biāo)識一個softirq?其實就是調(diào)用tasklet_schedule()來把TASKLET_SOFTIRQ位置1.

      在ILDD一書中實際上將tasklet分成兩部分,分別放在第5章”中斷處理“和第6章”延遲操作“,第5章側(cè)重討論softirq的機制,而第6章則重點討論tasklet。


      (原帖首發(fā):http://www./forum.php/forum.php?mod=viewthread&tid=543)

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多