国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
linux中斷源碼分析4/4--軟中斷
http://blog.chinaunix.net/uid-26772321-id-5062163.html
2015


  在上一篇文章中,我們看到中斷實際分為了兩個部分,俗稱就是一部分是硬中斷,一部分是軟中斷。軟中斷是專門用于處理中斷過程中費時費力的操作,而為什么系統(tǒng)要分硬中斷和軟中斷呢?問得明白點就是為什么需要軟中斷。我們可以試著想想,如果只有硬中斷的情況下,我們需要在中斷處理過程中執(zhí)行一些耗時的操作,比如浮點數(shù)運算,復(fù)雜算法的運算時,其他的外部中斷就不能得到及時的響應(yīng),因為在硬中斷過程中中斷是關(guān)閉著的,甚至一些很緊急的中斷會得不到響應(yīng),系統(tǒng)穩(wěn)定性和及時性都受到很大影響。所以linux為了解決上述這種情況,將中斷處理分為了兩個部分,硬中斷和軟中斷。首先一個外部中斷得到響應(yīng)時,會先關(guān)中斷,并進入到硬中斷完成較為緊急的操作,然后開中斷,并在軟中斷執(zhí)行那些非緊急、可延時執(zhí)行的操作;在這種情況下,緊急操作可以立即執(zhí)行,而其他的外部中斷也可以獲得一個較為快速的響應(yīng)。這也是軟中斷存在的必要性。在軟中斷過程中是不可以被搶占也不能被阻塞的,也不能在一個給定的CPU上交錯執(zhí)行。

軟中斷

  軟中斷是在中斷框架中專門用于處理非緊急操作的,在SMP系統(tǒng)中,軟中斷可以并發(fā)地運行在多個CPU上,但在一些路徑在需要使用自旋鎖進行保護。在系統(tǒng)中,很多東西都分優(yōu)先級,軟中斷也不例外,有些軟中斷要求更快速的響應(yīng)運行,在內(nèi)核中軟中斷一共分為10個,同時也代表著10種不同的優(yōu)先級,系統(tǒng)用一個枚舉變量表示:

  1. enum
  2. {
  3.     HI_SOFTIRQ=0, /* 高優(yōu)先級tasklet */ /* 優(yōu)先級最高 */
  4.     TIMER_SOFTIRQ, /* 時鐘相關(guān)的軟中斷 */
  5.     NET_TX_SOFTIRQ, /* 將數(shù)據(jù)包傳送到網(wǎng)卡 */
  6.     NET_RX_SOFTIRQ, /* 從網(wǎng)卡接收數(shù)據(jù)包 */
  7.     BLOCK_SOFTIRQ, /* 塊設(shè)備的軟中斷 */
  8.     BLOCK_IOPOLL_SOFTIRQ, /* 支持IO輪詢的塊設(shè)備軟中斷 */
  9.     TASKLET_SOFTIRQ, /* 常規(guī)tasklet */
  10.     SCHED_SOFTIRQ, /* 調(diào)度程序軟中斷 */
  11.     HRTIMER_SOFTIRQ, /* 高精度計時器軟中斷 */
  12.     RCU_SOFTIRQ, /* RCU鎖軟中斷,該軟中斷總是最后一個軟中斷 */      /* 優(yōu)先級最低 */

  13.     NR_SOFTIRQS /* 軟中斷數(shù),為10 */
  14. };
  注釋中的tasklet我們之后會說明,這里先無視它。每一個優(yōu)先級的軟中斷都使用一個struct softirq_action結(jié)構(gòu)來表示,在這個結(jié)構(gòu)中,只有一個成員變量,就是action函數(shù)指針,因為不同的軟中斷它的處理方式可能不同,從優(yōu)先級表中就可以看出來,有塊設(shè)備的,也有網(wǎng)卡處理的。系統(tǒng)將這10個軟中斷用softirq_vec[10]的數(shù)組進行保存。
  1. /* 用于描述一個軟中斷 */
  2. struct softirq_action
  3. {
  4.     /* 此軟中斷的處理函數(shù) */
  5.     void (*action)(struct softirq_action *);
  6. };

  7. /* 10個軟中斷描述符都保存在此數(shù)組 */
  8. static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
  系統(tǒng)一般使用open_softirq()函數(shù)進行軟中斷描述符的初始化,主要就是將action函數(shù)指針指向該軟中斷應(yīng)該執(zhí)行的函數(shù)。在start_kernel()進行系統(tǒng)初始化中,就調(diào)用了softirq_init()函數(shù)對HI_SOFTIRQ和TASKLET_SOFTIRQ兩個軟中斷進行了初始化
  1. void __init softirq_init(void)
  2. {
  3.     int cpu;

  4.     for_each_possible_cpu(cpu) {
  5.         per_cpu(tasklet_vec, cpu).tail =
  6.             &per_cpu(tasklet_vec, cpu).head;
  7.         per_cpu(tasklet_hi_vec, cpu).tail =
  8.             &per_cpu(tasklet_hi_vec, cpu).head;
  9.     }

  10.     /* 開啟常規(guī)tasklet */
  11.     open_softirq(TASKLET_SOFTIRQ, tasklet_action);
  12.     /* 開啟高優(yōu)先級tasklet */
  13.     open_softirq(HI_SOFTIRQ, tasklet_hi_action);
  14. }


  15. /* 開啟軟中斷 */
  16. void open_softirq(int nr, void (*action)(struct softirq_action *))
  17. {
  18.     softirq_vec[nr].action = action;
  19. }
   可以看到,TASKLET_SOFTIRQ的action操作使用了tasklet_action()函數(shù),HI_SOFTIRQ的action操作使用了tasklet_hi_action()函數(shù),這兩個函數(shù)我們需要結(jié)合tasklet進行說明。我們也可以看看其他的軟中斷使用了什么函數(shù):
  1. open_softirq(TIMER_SOFTIRQ, run_timer_softirq);
  2.     open_softirq(NET_TX_SOFTIRQ, net_tx_action);
  3.     open_softirq(NET_RX_SOFTIRQ, net_rx_action);
  4.     open_softirq(BLOCK_SOFTIRQ, blk_done_softirq);
  5.     open_softirq(BLOCK_IOPOLL_SOFTIRQ, blk_iopoll_softirq);
  6.     open_softirq(SCHED_SOFTIRQ, run_rebalance_domains);
  7.     open_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq);
  8.     open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);

   其實很明顯可以看出,除了TASKLET_SOFTIRQ和HI_SOFTIRQ,其他的軟中斷更多地是用于特定的設(shè)備和環(huán)境,對于我們普通的IO驅(qū)動和設(shè)備而已,使用的軟中斷幾乎都是TASKLET_SOFTIRQ和HI_SOFTIRQ,而系統(tǒng)為了對這些不同IO設(shè)備進行統(tǒng)一的處理,就在TASKLET_SOFTIRQ和HI_SOFTIRQ的action函數(shù)中使用到了tasklet。

  對于每個CPU,都有一個irq_cpustat_t的數(shù)據(jù)結(jié)構(gòu),里面有一個__softirq_pending變量,這個變量很重要,用于表示該CPU的哪個軟中斷處于掛起狀態(tài),在軟中斷處理時可以根據(jù)此值跳過不需要處理的軟中斷,直接處理需要處理的軟中斷。內(nèi)核使用local_softirq_pending()獲取此CPU的__softirq_pending的值。

  當使用open_softirq設(shè)置好某個軟中斷的action指針后,該軟中斷就會開始可以使用了,其實更明了地說,從中斷初始化完成開始,即使所有的軟中斷都沒有使用open_softirq()進行初始化,軟中斷都已經(jīng)開始使用了,只是所有軟中斷的action都為空,系統(tǒng)每次執(zhí)行到軟中斷都沒有軟中斷需要執(zhí)行罷了。

  在每個CPU上一次軟中斷處理的一個典型流程是:

  1. 硬中斷執(zhí)行完畢,開中斷。
  2. 檢查該CPU是否處于嵌套中斷的情況,如果處于嵌套中,則不執(zhí)行軟中斷,也就是在最外層中斷才執(zhí)行軟中斷。
  3. 執(zhí)行軟中斷,設(shè)置一個軟中斷執(zhí)行最多使用時間和循環(huán)次數(shù)(10次)。
  4. 進入循環(huán),獲取CPU的__softirq_pending的副本。
  5. 執(zhí)行此__softirq_pending副本中所有需要執(zhí)行的軟中斷。
  6. 如果軟中斷執(zhí)行完畢,退出中斷上下文。
  7. 如果還有軟中斷需要執(zhí)行(在軟中斷期間又發(fā)發(fā)生了中斷,產(chǎn)生了新的軟中斷,新的軟中斷記錄在CPU的__softirq_pending上,而我們的__softirq_pending只是個副本)。
  8. 檢查此次軟中斷總共使用的時間和循環(huán)次數(shù),條件允許繼續(xù)執(zhí)行軟中斷,循環(huán)次數(shù)減一,并跳轉(zhuǎn)到第4步。

  我們具體看一下代碼,首先在irq_exit()中會檢查是否需要進行軟中斷處理:

  1. void irq_exit(void)
  2. {
  3. #ifndef __ARCH_IRQ_EXIT_IRQS_DISABLED
  4.     local_irq_disable();
  5. #else
  6.     WARN_ON_ONCE(!irqs_disabled());
  7. #endif

  8.     account_irq_exit_time(current);
  9.     /* 減少preempt_count的硬中斷計數(shù)器 */
  10.     preempt_count_sub(HARDIRQ_OFFSET);
  11.     
  12.     /* in_interrupt()會檢查preempt_count上的軟中斷計數(shù)器和硬中斷計數(shù)器來判斷是否處于中斷嵌套中 */
  13.     /* local_softirq_pending()則會檢查該CPU的__softirq_pending變量,是否有軟中斷掛起 */
  14.     if (!in_interrupt() && local_softirq_pending())
  15.         invoke_softirq();

  16.     tick_irq_exit();
  17.     rcu_irq_exit();
  18.     trace_hardirq_exit(); /* must be */
  19. }
  我們再進入到invoke_softirq():
  1. static inline void invoke_softirq(void)
  2. {

  3.     if (!force_irqthreads) {
  4. #ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK
  5.         /*
  6.          * We can safely execute softirq on the current stack if
  7.          * it is the irq stack, because it should be near empty
  8.          * at this stage.
  9.          */
  10.         /* 軟中斷處理函數(shù) */
  11.         __do_softirq();
  12. #else
  13.         /*
  14.          * Otherwise, irq_exit() is called on the task stack that can
  15.          * be potentially deep already. So call softirq in its own stack
  16.          * to prevent from any overrun.
  17.          */
  18.         do_softirq_own_stack();
  19. #endif
  20.     } else {
  21.         /* 如果強制使用軟中斷線程進行軟中斷處理,會通知調(diào)度器喚醒軟中斷線程ksoftirqd */
  22.         wakeup_softirqd();
  23.     }
  24. }
  重頭戲就在__do_softirq()中,我已經(jīng)注釋好了,方便大家看:
  1. asmlinkage __visible void __do_softirq(void)
  2. {
  3.     /* 為了防止軟中斷執(zhí)行時間太長,設(shè)置了一個軟中斷結(jié)束時間 */
  4.     unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
  5.     /* 保存當前進程的標志 */
  6.     unsigned long old_flags = current->flags;
  7.     /* 軟中斷循環(huán)執(zhí)行次數(shù): 10次 */
  8.     int max_restart = MAX_SOFTIRQ_RESTART;
  9.     /* 軟中斷的action指針 */
  10.     struct softirq_action *h;
  11.     bool in_hardirq;
  12.     __u32 pending;
  13.     int softirq_bit;

  14.     /*
  15.      * Mask out PF_MEMALLOC s current task context is borrowed for the
  16.      * softirq. A softirq handled such as network RX might set PF_MEMALLOC
  17.      * again if the socket is related to swap
  18.      */
  19.     current->flags &= ~PF_MEMALLOC;

  20.     /* 獲取此CPU的__softirq_pengding變量值 */
  21.     pending = local_softirq_pending();
  22.     /* 用于統(tǒng)計進程被軟中斷使用時間 */
  23.     account_irq_enter_time(current);

  24.     /* 增加preempt_count軟中斷計數(shù)器,也表明禁止了調(diào)度 */
  25.     __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);
  26.     in_hardirq = lockdep_softirq_start();

  27. /* 循環(huán)10次的入口,每次循環(huán)都會把所有掛起需要執(zhí)行的軟中斷執(zhí)行一遍 */
  28. restart:
  29.     /* 該CPU的__softirq_pending清零,當前的__softirq_pending保存在pending變量中 */
  30.     /* 這樣做就保證了新的軟中斷會在下次循環(huán)中執(zhí)行 */
  31.     set_softirq_pending(0);

  32.     /* 開中斷 */
  33.     local_irq_enable();

  34.     /* h指向軟中斷數(shù)組頭 */
  35.     h = softirq_vec;

  36.     /* 每次獲取最高優(yōu)先級的已掛起軟中斷 */
  37.     while ((softirq_bit = ffs(pending))) {
  38.         unsigned int vec_nr;
  39.         int prev_count;
  40.         /* 獲取此軟中斷描述符地址 */
  41.         h += softirq_bit - 1;
  42.         
  43.         /* 減去軟中斷描述符數(shù)組首地址,獲得軟中斷號 */
  44.         vec_nr = h - softirq_vec;
  45.         /* 獲取preempt_count的值 */
  46.         prev_count = preempt_count();

  47.         /* 增加統(tǒng)計中該軟中斷發(fā)生次數(shù) */
  48.         kstat_incr_softirqs_this_cpu(vec_nr);

  49.         trace_softirq_entry(vec_nr);
  50.         /* 執(zhí)行該軟中斷的action操作 */
  51.         h->action(h);
  52.         trace_softirq_exit(vec_nr);

  53.         /* 之前保存的preempt_count并不等于當前的preempt_count的情況處理,也是簡單的把之前的復(fù)制到當前的preempt_count上,這樣做是防止最后軟中斷計數(shù)不為0導(dǎo)致系統(tǒng)不能夠執(zhí)行調(diào)度 */
  54.         if (unlikely(prev_count != preempt_count())) {
  55.             pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n",
  56.                    vec_nr, softirq_to_name[vec_nr], h->action,
  57.                    prev_count, preempt_count());
  58.             preempt_count_set(prev_count);
  59.         }
  60.         /* h指向下一個軟中斷,但下個軟中斷并不一定需要執(zhí)行,這里只是配合softirq_bit做到一個處理 */
  61.         h++;
  62.         pending >>= softirq_bit;
  63.     }

  64.     rcu_bh_qs();
  65.     /* 關(guān)中斷 */
  66.     local_irq_disable();

  67.     /* 循環(huán)結(jié)束后再次獲取CPU的__softirq_pending變量,為了檢查是否還有軟中斷未執(zhí)行 */
  68.     pending = local_softirq_pending();
  69.     /* 還有軟中斷需要執(zhí)行 */
  70.     if (pending) {
  71.         /* 在還有軟中斷需要執(zhí)行的情況下,如果時間片沒有執(zhí)行完,并且循環(huán)次數(shù)也沒到10次,繼續(xù)執(zhí)行軟中斷 */
  72.         if (time_before(jiffies, end) && !need_resched() &&
  73.             --max_restart)
  74.             goto restart;
  75.         /* 這里是有軟中斷掛起,但是軟中斷時間和循環(huán)次數(shù)已經(jīng)用完,通知調(diào)度器喚醒軟中斷線程去執(zhí)行掛起的軟中斷,軟中斷線程是ksoftirqd,這里只起到一個通知作用,因為在中斷上下文中是禁止調(diào)度的 */
  76.         wakeup_softirqd();
  77.     }

  78.     lockdep_softirq_end(in_hardirq);
  79.     /* 用于統(tǒng)計進程被軟中斷使用時間 */
  80.     account_irq_exit_time(current);
  81.     /* 減少preempt_count中的軟中斷計數(shù)器 */
  82.     __local_bh_enable(SOFTIRQ_OFFSET);
  83.     WARN_ON_ONCE(in_interrupt());
  84.     /* 還原進程標志 */
  85.     tsk_restore_flags(current, old_flags, PF_MEMALLOC);
  86. }

  流程就和上面所說的一致,如果還有不懂,可以去內(nèi)核代碼目錄/kernel/softirq.c查看源碼。

 

tasklet

  軟中斷有多種,部分種類有自己特殊的處理,如從NET_TX_SOFTIRQ和NET_RT_SOFTIRQ、BLOCK_SOFTIRQ等,而如HI_SOFTIRQ和TASKLET_SOFTIRQ則是專門使用tasklet。它是在I/O驅(qū)動程序中實現(xiàn)可延遲函數(shù)的首選方法,如上一句所說,它建立在HI_SOFTIRQ和TASKLET_SOFTIRQ這兩種軟中斷之上,多個tasklet可以與同一個軟中斷相關(guān)聯(lián),系統(tǒng)會使用一個鏈表組織他們,而每個tasklet執(zhí)行自己的函數(shù)處理。而HI_SOFTIRQ和TASKLET_SOFTIRQ這兩個軟中斷并沒有什么區(qū)別,他們只是優(yōu)先級上的不同而已,系統(tǒng)會先執(zhí)行HI_SOFTIRQ的tasklet,再執(zhí)行TASKLET_SOFTIRQ的tasklet。同一個tasklet不能同時在幾個CPU上執(zhí)行,一個tasklet在一個時間上只能在一個CPU的軟中斷鏈上,不能同時在多個CPU的軟中斷鏈上,并且當這個tasklet正在執(zhí)行時,其他CPU不能夠執(zhí)行這個tasklet。也就是說,tasklet不必要編寫成可重入的函數(shù)。

  系統(tǒng)會為每個CPU維護兩個鏈表,用于保存HI_SOFTIRQ的tasklet和TASKLET_SOFTIRQ的tasklet,這兩個鏈表是tasklet_vec和tasklet_hi_vec,它們都是雙向鏈表,如下:

  1. struct tasklet_head {
  2.     struct tasklet_struct *head;
  3.     struct tasklet_struct **tail;
  4. };

  5. static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);
  6. static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);

   在softirq_init()函數(shù)中,會將每個CPU的tasklet_vec鏈表和tasklet_hi_vec鏈表進行初始化,將他們的頭尾相連,實現(xiàn)為一個空鏈表。由于tasklet_vec和tasklet_hi_vec處理方式幾乎一樣,只是軟中斷的優(yōu)先級別不同,我們只需要理解系統(tǒng)如何對tasklet_vec進行處理即可。需要注意的是,tasklet_vec鏈表都是以順序方式執(zhí)行,并不會出現(xiàn)后一個先執(zhí)行,再到前一個先執(zhí)行(在軟中斷期間被中斷的情況),之后的代碼我們詳細說明。

  介紹完tasklet_vec和tasklet_hi_vec鏈表,我們來看看tasklet,tasklet簡單來說,就是一個處理函數(shù)的封裝,類似于硬中斷中的irqaction結(jié)構(gòu)。一般來說,在一個驅(qū)動中如果需要使用tasklet進行軟中斷的處理,只需要一個中斷對應(yīng)初始化一個tasklet,它可以在每次中斷產(chǎn)生時重復(fù)使用。系統(tǒng)使用tasklet_struct結(jié)構(gòu)進行描述一個tasklet,而且對于同一個tasklet_struct你可以選擇放在tasklet_hi_vec鏈表或者tasklet_vec鏈表上。我們來看看:

  1. struct tasklet_struct
  2. {
  3.     struct tasklet_struct *next; /* 指向鏈表下一個tasklet */
  4.     unsigned long state; /* tasklet狀態(tài) */
  5.     atomic_t count; /* 禁止計數(shù)器,調(diào)用tasklet_disable()會增加此數(shù),tasklet_enable()減少此數(shù) */
  6.     void (*func)(unsigned long); /* 處理函數(shù) */
  7.     unsigned long data; /* 處理函數(shù)使用的數(shù)據(jù) */
  8. };

  tasklet狀態(tài)主要分為以下兩種:

  • TASKLET_STATE_SCHED:這種狀態(tài)表示此tasklet處于某個tasklet鏈表之上(可能是tasklet_vec也可能是tasklet_hi_vec)。
  • TASKLET_STATE_RUN:表示此tasklet正在運行中。

  這兩個狀態(tài)主要就是用于防止tasklet同時在幾個CPU上運行和在同一個CPU上交錯執(zhí)行。

 

  而func指針就是指向相應(yīng)的處理函數(shù)。在編寫驅(qū)動時,我們可以使用tasklet_init()函數(shù)或者DECLARE_TASKLET宏進行一個task_struct結(jié)構(gòu)的初始化,之后可以使用tasklet_schedule()或者tasklet_hi_schedule()將其放到相應(yīng)鏈表上等待CPU運行。我們使用一張圖描述一下軟中斷和tasklet結(jié)合運行的情況:


  我們知道,每個軟中斷都有自己的action函數(shù),在HI_SOFTIRQ和TASKLET_SOFTIRQ的action函數(shù)中,就用到了它們對應(yīng)的TASKLET_HI_VEC鏈表和TASKLET_VEC鏈表,并依次順序執(zhí)行鏈表中的每個tasklet結(jié)點。

  在SMP系統(tǒng)中,我們會遇到一個問題:兩個CPU都需要執(zhí)行同一個tasklet的情況,雖然一個tasklet只能放在一個CPU的tasklet_vec鏈表或者tasklet_hi_vec鏈表上,但是這種情況是有可能發(fā)生的,我們設(shè)想一下,中斷在CPU1上得到了響應(yīng),并且它的tasklet放到了CPU1的tasklet_vec上進行執(zhí)行,而當中斷的tasklet上正在執(zhí)行時,此中斷再次發(fā)生,并在CPU2上進行了響應(yīng),此時CPU2將此中斷的tasklet放到CPU2的tasklet_vec上,并執(zhí)行到此中斷的tasklet。

  實際上,為了處理這種情況,在HI_SOFTIRQ和TASKLET_SOFTIRQ的action函數(shù)中,會先將對應(yīng)的tasklet鏈表取出來,并把對應(yīng)的tasklet鏈表的head和tail清空,如果在執(zhí)行過程中,某個tasklet的state為TASKLET_STATE_RUN狀態(tài),則把此tasklet加入到原先已清空的tasklet鏈表的末尾,然后設(shè)置__softirq_pending變量,這樣,在下次循環(huán)軟中斷的過程中,會再次運行這個tasklet。

  我們可以看看TASKLET_SOFTIRQ的action處理:

  1. static void tasklet_action(struct softirq_action *a)
  2. {
  3.     struct tasklet_struct *list;

  4.     local_irq_disable();
  5.     /* 將tasklet鏈表從該CPU中拿出來 */
  6.     list = __this_cpu_read(tasklet_vec.head);
  7.     /* 將該CPU的此軟中斷的tasklet鏈表清空 */
  8.     __this_cpu_write(tasklet_vec.head, NULL);
  9.     __this_cpu_write(tasklet_vec.tail, this_cpu_ptr(&tasklet_vec.head));
  10.     local_irq_enable();

  11.     /* 鏈表已經(jīng)處于list中,并且該CPU的tasklet_vec鏈表為空 */
  12.     while (list) {
  13.         struct tasklet_struct *t = list;

  14.         list = list->next;

  15.         /* 檢查并設(shè)置該tasklet為TASKLET_STATE_RUN狀態(tài) */
  16.         if (tasklet_trylock(t)) {
  17.             /* 檢查是否被禁止 */
  18.             if (!atomic_read(&t->count)) {
  19.                 /* 清除其TASKLET_STATE_SCHED狀態(tài) */
  20.                 if (!test_and_clear_bit(TASKLET_STATE_SCHED,
  21.                             &t->state))
  22.                     BUG();
  23.                 /* 執(zhí)行該tasklet的func處理函數(shù) */
  24.                 t->func(t->data);
  25.                 /* 清除該tasklet的TASKLET_STATE_RUN狀態(tài) */
  26.                 tasklet_unlock(t);
  27.                 continue;
  28.             }
  29.             tasklet_unlock(t);
  30.         }

  31.         /* 以下為tasklet為TASKLET_STATE_RUN狀態(tài)下的處理 */
  32.         /* 禁止中斷 */
  33.         local_irq_disable();
  34.         /* 將此tasklet添加的該CPU的tasklet_vec鏈表尾部 */
  35.         t->next = NULL;
  36.         *__this_cpu_read(tasklet_vec.tail) = t;
  37.         __this_cpu_write(tasklet_vec.tail, &(t->next));
  38.         /* 設(shè)置該CPU的此軟中斷處于掛起狀態(tài),設(shè)置irq_cpustat_t的__sofirq_pending變量,這樣在軟中斷的下次執(zhí)行中會再次執(zhí)行此tasklet */
  39.         __raise_softirq_irqoff(TASKLET_SOFTIRQ);
  40.         /* 開啟中斷 */
  41.         local_irq_enable();
  42.     }
  43. }

 

軟中斷處理線程

  當有過多軟中斷需要處理時,為了保證進程能夠得到一個滿意的響應(yīng)時間,設(shè)計時給定軟中斷一個時間片和循環(huán)次數(shù),當時間片和循環(huán)次數(shù)到達但軟中斷又沒有處理完時,就會把剩下的軟中斷交給軟中斷處理線程進行處理,這個線程是一個內(nèi)核線程,其作為一個普通進程,優(yōu)先級是120。其核心處理函數(shù)是run_ksoftirqd(),其實此線程的處理也很簡單,就是調(diào)用了上面的__do_softirq()函數(shù),我們可以具體看看:

  1. /* 在smpboot_thread_fun的一個死循環(huán)中被調(diào)用 */
  2. static void run_ksoftirqd(unsigned int cpu)
  3. {
  4.     /* 禁止中斷,在__do_softirq()中會開啟 */
  5.     local_irq_disable();
  6.     /* 檢查該CPU的__softirq_pending是否有軟中斷被掛起 */
  7.     if (local_softirq_pending()) {
  8.         /*
  9.          * We can safely run softirq on inline stack, as we are not deep
  10.          * in the task stack here.
  11.          */
  12.         /* 執(zhí)行軟中斷 */
  13.         __do_softirq();
  14.         rcu_note_context_switch(cpu);
  15.         /* 開中斷 */
  16.         local_irq_enable();
  17.         /* 檢查是否需要調(diào)度 */
  18.         cond_resched();
  19.         return;
  20.     }
  21.     /* 開中斷 */
  22.     local_irq_enable();
  23. }



本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
內(nèi)核隨記(一)——理解中斷(3)
【原創(chuàng)】Linux中斷子系統(tǒng)(三)-softirq和tasklet
softirq和tasklet
中斷下半部分析_softirq
linux內(nèi)核中的軟中斷的實現(xiàn)
硬中斷和軟中斷
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服