久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

linux中斷源碼分析4/4--軟中斷

 心不留意外塵 2016-05-11
http://blog./uid-26772321-id-5062163.html
2015


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

軟中斷

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

  1. enum
  2. {
  3.     HI_SOFTIRQ=0, /* 高優(yōu)先級(jí)tasklet */ /* 優(yōu)先級(jí)最高 */
  4.     TIMER_SOFTIRQ, /* 時(shí)鐘相關(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, /* 高精度計(jì)時(shí)器軟中斷 */
  12.     RCU_SOFTIRQ, /* RCU鎖軟中斷,該軟中斷總是最后一個(gè)軟中斷 */      /* 優(yōu)先級(jí)最低 */

  13.     NR_SOFTIRQS /* 軟中斷數(shù),,為10 */
  14. };
  注釋中的tasklet我們之后會(huì)說(shuō)明,,這里先無(wú)視它,。每一個(gè)優(yōu)先級(jí)的軟中斷都使用一個(gè)struct softirq_action結(jié)構(gòu)來(lái)表示,在這個(gè)結(jié)構(gòu)中,,只有一個(gè)成員變量,就是action函數(shù)指針,,因?yàn)椴煌能浿袛嗨奶幚矸绞娇赡懿煌?,從?yōu)先級(jí)表中就可以看出來(lái),有塊設(shè)備的,,也有網(wǎng)卡處理的,。系統(tǒng)將這10個(gè)軟中斷用softirq_vec[10]的數(shù)組進(jìn)行保存。
  1. /* 用于描述一個(gè)軟中斷 */
  2. struct softirq_action
  3. {
  4.     /* 此軟中斷的處理函數(shù) */
  5.     void (*action)(struct softirq_action *);
  6. };

  7. /* 10個(gè)軟中斷描述符都保存在此數(shù)組 */
  8. static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
  系統(tǒng)一般使用open_softirq()函數(shù)進(jìn)行軟中斷描述符的初始化,,主要就是將action函數(shù)指針指向該軟中斷應(yīng)該執(zhí)行的函數(shù),。在start_kernel()進(jìn)行系統(tǒng)初始化中,就調(diào)用了softirq_init()函數(shù)對(duì)HI_SOFTIRQ和TASKLET_SOFTIRQ兩個(gè)軟中斷進(jìn)行了初始化
  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)先級(jí)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ù),這兩個(gè)函數(shù)我們需要結(jié)合tasklet進(jìn)行說(shuō)明,。我們也可以看看其他的軟中斷使用了什么函數(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);

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

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

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

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

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

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

  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的硬中斷計(jì)數(shù)器 */
  10.     preempt_count_sub(HARDIRQ_OFFSET);
  11.     
  12.     /* in_interrupt()會(huì)檢查preempt_count上的軟中斷計(jì)數(shù)器和硬中斷計(jì)數(shù)器來(lái)判斷是否處于中斷嵌套中 */
  13.     /* local_softirq_pending()則會(huì)檢查該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. }
  我們?cè)龠M(jìn)入到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.         /* 如果強(qiáng)制使用軟中斷線程進(jìn)行軟中斷處理,,會(huì)通知調(diào)度器喚醒軟中斷線程ksoftirqd */
  22.         wakeup_softirqd();
  23.     }
  24. }
  重頭戲就在__do_softirq()中,我已經(jīng)注釋好了,方便大家看:
  1. asmlinkage __visible void __do_softirq(void)
  2. {
  3.     /* 為了防止軟中斷執(zhí)行時(shí)間太長(zhǎng),,設(shè)置了一個(gè)軟中斷結(jié)束時(shí)間 */
  4.     unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
  5.     /* 保存當(dāng)前進(jìn)程的標(biāo)志 */
  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)計(jì)進(jìn)程被軟中斷使用時(shí)間 */
  23.     account_irq_enter_time(current);

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

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

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

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

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

  47.         /* 增加統(tǒng)計(jì)中該軟中斷發(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并不等于當(dāng)前的preempt_count的情況處理,也是簡(jiǎn)單的把之前的復(fù)制到當(dāng)前的preempt_count上,,這樣做是防止最后軟中斷計(jì)數(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指向下一個(gè)軟中斷,,但下個(gè)軟中斷并不一定需要執(zhí)行,,這里只是配合softirq_bit做到一個(gè)處理 */
  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í)行的情況下,如果時(shí)間片沒(méi)有執(zhí)行完,,并且循環(huán)次數(shù)也沒(méi)到10次,,繼續(xù)執(zhí)行軟中斷 */
  72.         if (time_before(jiffies, end) && !need_resched() &&
  73.             --max_restart)
  74.             goto restart;
  75.         /* 這里是有軟中斷掛起,但是軟中斷時(shí)間和循環(huán)次數(shù)已經(jīng)用完,,通知調(diào)度器喚醒軟中斷線程去執(zhí)行掛起的軟中斷,,軟中斷線程是ksoftirqd,這里只起到一個(gè)通知作用,,因?yàn)樵谥袛嗌舷挛闹惺墙拐{(diào)度的 */
  76.         wakeup_softirqd();
  77.     }

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

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

 

tasklet

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

  系統(tǒng)會(huì)為每個(gè)CPU維護(hù)兩個(gè)鏈表,,用于保存HI_SOFTIRQ的tasklet和TASKLET_SOFTIRQ的tasklet,這兩個(gè)鏈表是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ù)中,會(huì)將每個(gè)CPU的tasklet_vec鏈表和tasklet_hi_vec鏈表進(jìn)行初始化,,將他們的頭尾相連,,實(shí)現(xiàn)為一個(gè)空鏈表。由于tasklet_vec和tasklet_hi_vec處理方式幾乎一樣,,只是軟中斷的優(yōu)先級(jí)別不同,,我們只需要理解系統(tǒng)如何對(duì)tasklet_vec進(jìn)行處理即可。需要注意的是,,tasklet_vec鏈表都是以順序方式執(zhí)行,,并不會(huì)出現(xiàn)后一個(gè)先執(zhí)行,再到前一個(gè)先執(zhí)行(在軟中斷期間被中斷的情況),,之后的代碼我們?cè)敿?xì)說(shuō)明,。

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

  1. struct tasklet_struct
  2. {
  3.     struct tasklet_struct *next; /* 指向鏈表下一個(gè)tasklet */
  4.     unsigned long state; /* tasklet狀態(tài) */
  5.     atomic_t count; /* 禁止計(jì)數(shù)器,,調(diào)用tasklet_disable()會(huì)增加此數(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處于某個(gè)tasklet鏈表之上(可能是tasklet_vec也可能是tasklet_hi_vec),。
  • TASKLET_STATE_RUN:表示此tasklet正在運(yùn)行中,。

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

 

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


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

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

  實(shí)際上,,為了處理這種情況,,在HI_SOFTIRQ和TASKLET_SOFTIRQ的action函數(shù)中,會(huì)先將對(duì)應(yīng)的tasklet鏈表取出來(lái),,并把對(duì)應(yīng)的tasklet鏈表的head和tail清空,,如果在執(zhí)行過(guò)程中,某個(gè)tasklet的state為TASKLET_STATE_RUN狀態(tài),,則把此tasklet加入到原先已清空的tasklet鏈表的末尾,,然后設(shè)置__softirq_pending變量,這樣,,在下次循環(huán)軟中斷的過(guò)程中,,會(huì)再次運(yùn)行這個(gè)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中拿出來(lái) */
  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í)行中會(huì)再次執(zhí)行此tasklet */
  39.         __raise_softirq_irqoff(TASKLET_SOFTIRQ);
  40.         /* 開啟中斷 */
  41.         local_irq_enable();
  42.     }
  43. }

 

軟中斷處理線程

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

  1. /* 在smpboot_thread_fun的一個(gè)死循環(huán)中被調(diào)用 */
  2. static void run_ksoftirqd(unsigned int cpu)
  3. {
  4.     /* 禁止中斷,,在__do_softirq()中會(huì)開啟 */
  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. }



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

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多