(轉)內核中斷,異常,搶占總結篇


一、基本概念

中斷分為同步中斷和異步中斷。同步中斷是由CPU控制單元產生的,“同步”是指只有在一條指令執行完畢后,CPU才會發出中斷,而不是發生在代碼指令執行期間,比如系統調用。而異步中斷是由其他硬件設備依照CPU時鍾信號產生的,即意味着中斷能夠在指令之間發生,例如鍵盤中斷

按照Intel的微處理器手冊,同步中斷和異步中斷也分別稱為異常(或者軟件中斷)和中斷。中斷大家都比較熟悉,是由硬件設備產生的。異常的產生源有兩種:一種是由程序的錯誤產生的,內核通過發送一個Unix程序員都熟悉的信號來處理異常;第二種時內核必須處理的異常條件產生的,此時內核執行恢復異常需要的所有步驟,例如缺頁,或對內核服務的一個請求(系統調用,通過一條int指令)。有一個知識點值得了解:內核態能夠觸發的唯一異常就是缺頁異常,其他的都是用戶態觸發的。

二、硬中斷、軟中斷、異常之間的搶占關系

硬中斷可以被另一個優先級比自己高的硬中斷“中斷”,不能被同級(同一種硬中斷)或低級的硬中斷“中斷”,更不能被軟中斷“中斷”。

軟中斷可以被硬中斷“中斷”,但是不會被另一個軟中斷“中斷”。在一個CPU上,軟中斷總是串行執行。所以在單處理器上,對軟中斷的數據結構進行訪問不需要加任何同步原語。

關於這一點,我對《深入理解linux內核》第三版P223頁中保護可延遲函數所訪問的數據結構有疑問,書上說保護可延遲函數(軟中斷)所訪問的數據結構應采取的措施:對於單處理器的情況,在單處理器上不存在競爭條件,這是因為可延遲函數(軟中斷)的執行總是在一個CPU上串行執行--也就是說,一個可延遲函數不會被另一個可延遲函數中斷。因此,根本不需要同步原語。我認為:一個軟中斷雖然不會被另一個軟中斷“中斷”,但是可能被硬中斷“中斷”,而硬中斷最后還是要執行到軟中斷,因此還是會形成對資源的臨界區訪問。我覺得在保護軟中斷時,應該關閉本地軟中斷,比如用local_bh_disable

還沒寫完這篇博客,就知道我在這個問題上錯了。附上在Linux 內核開發中文郵件列表上某位仁兄提供的解答。

開始處理軟中斷的情況主要是
1、中斷退出執行的irq_exit
2、內核線程ksoftirqd
3、local_bh_enable


asmlinkage void do_softirq(void)
{
       unsigned long flags;
       struct thread_info *curctx;
       union irq_ctx *irqctx;
       u32 *isp;

       if (in_interrupt())
               return;
....
}
可以看到,in_interrupt 判斷,當前若是從硬件中斷退出后執行的irq_exit進入的do_softirq,則立即返回,可以避免你說的情況

本文最后還將附上一篇軟中斷源碼的分析,很詳細地說明了這個問題。

硬中斷和軟中斷都可以搶占(或者稱為中斷)異常(最典型的是系統調用),但是異常不能搶占硬中斷和軟中斷。

硬中斷和軟中斷(只要是中斷上下文)執行的時候都不允許內核搶占,換句話說,中斷上下文中永遠不允許進程切換。(個人理解,由於中斷處理程序都需要較快地完成,而且中斷處理程序可以嵌套,因此中斷處理程序必須不能阻塞,否則性能就非常不能保證了。)

 

三、用戶搶占和內核搶占

搶占分兩種情況:用戶搶占和內核搶占,其中內核搶占在Linux2.5.4版本發布時被並入內核的,通SMP一樣作為內核的一項標准可選配置。

1、用戶搶占:內核即將返回用戶空間的時候,如果need resched標志被設置,會導致schedule()被調用,此時就會發生用戶搶占。在內核返回用戶空間的時候,它知道自己是安全的。所以,內核無論是在從中斷處理程序還是在系統調用后返回,都會檢查need resched標志。如果它被設置了,那么,內核會選擇一個其他(更合適的)進程投入運行。在內核搶占還沒有出現的時候,內核所有的搶占情況都是用戶搶占。

2、內核搶占:內核搶占是指,一個在內核態運行的進程,可能在執行內核函數期間被另一個進程取代。不是在內核的任何一個地方都可以發生內核搶占的。

內核不能被搶占的情況如下:

1)內核正進行中斷處理。在Linux內核中進程不能搶占中斷(中斷只能被其他中斷中止、搶占,進程不能中止、搶占中斷),在中斷例程中不允許進行進程調度。進程調度函數schedule()會對此作出判斷,如果是在中斷中調用,會打印出錯信息。

2)內核正在進行中斷上下文的Bottom Half(中斷的底半部)處理。硬件中斷返回前會執行軟中斷,此時仍然處於中斷上下文中。

3)內核的代碼段正持有spinlock自旋鎖、writelock/readlock讀寫鎖等鎖,處干這些鎖的保護狀態中。內核中的這些鎖是為了在SMP系統中短時間內保證不同CPU上運行的進程並發執行的正確性。當持有這些鎖時,內核不應該被搶占。

4)內核正在執行調度程序Scheduler。搶占的原因就是為了進行新的調度,沒有理由將調度程序搶占掉再運行調度程序。

5)內核正在對每個CPU“私有”的數據結構操作(Per-CPU date structures)。在SMP中,對於per-CPU數據結構未用spinlocks保護,因為這些數據結構隱含地被保護了(不同的CPU有不一樣的per-CPU數據,其他CPU上運行的進程不會用到另一個CPU的per-CPU數據)。但是如果允許搶占,但一個進程被搶占后重新調度,有可能調度到其他的CPU上去,這時定義的Per-CPU變量就會有問題,這時應禁搶占。

除了上述情況,在內核的任何地方都可能發生內核搶占,內核搶占發生的時機一般在:

1)當從中斷處理程序正在執行,且返回內核空間之前。

2)當內核代碼再一次具有可搶占性的時候,如解鎖(spin_unlock_bh)及使能軟中斷(local_bh_enable)等。

3)如果內核中的任務顯式的調用schedule()。

4)如果內核中的任務阻塞(這同樣也會導致調用schedule())。

內核搶占主要是為實時系統來設計的,但也不是在所有情況下都是最優的,因為搶占也需要調度和同步開銷,在某些情況下甚至要關閉內核搶占。以下是一篇關於開啟和關閉內核搶占性能測試的文章。http://www.ibm.com/developerworks/cn/linux/l-nptl/index.html

 

四、怎么對內核臨界區進行保護

在進程內核數據結構的互斥同步訪問時,我們最常用的辦法是:信號量(睡眠等待),自旋鎖(自旋等待),中斷禁止和軟中斷禁止。往往需要幾種方法配合使用才能達到我們想要的結果。

1、保護異常(最典型的是系統調用)所訪問的數據結構

此時最常選用的是信號量,因為信號量原語允許進程睡眠到資源變為可用,對大部分系統調用而言,這是所期望的行為。信號量的工作方式在單處理器系統和多處理器系統上完全相同。只有在訪問每CPU變量的情況下,必須顯式地禁用內核搶占,其他情況下內核搶占不會出現問題。

2、保護中斷所訪問的數據結構

1)單處理器情況下:假如數據結構只被這一種中斷訪問,則完全可以不加同步原語,因為中斷不能被同一種中斷“中斷”;假如數據結構被多個中斷處理程序訪問,則必須通過禁用本地中斷來保護臨界區。

2)多處理器情況下:除了必須禁用本地中斷,還必須使用自旋鎖來避免來自其他CPU的干擾。可以使用如spin_lock_irq()來完成這兩件事情。

3、保護可延遲函數(軟中斷和tasklet)所訪問的數據結構

1)單處理器情況下:在單處理器系統上不存在競爭條件,因為可延遲函數的執行在一個CPU上是串行的,一個可延遲函數不會被另一個可延遲函數所中斷。因此無需同步原語。

2)多處理器情況下:需要自旋鎖來加以保護。由於軟中斷和tasklet並發程度不同,加鎖情況也不同。同一軟中斷可以在不同CPU上運行,因此無論一個或多個軟中斷,都必須用如spin_lock加以保護。同一tasklet不能在不同CPU上運行,因此無需加鎖;不同tasklet可以在不同CPU上運行,因此也需要如spin_lock的鎖加以保護。

4、保護由異常和中斷訪問的數據結構

單處理器情況下:

1)對中斷而言:中斷不能被異常“中斷”,無需考慮異常的干擾。第1條一樣,如果此數據結構只被一種中斷訪問,則可不加同步原語;否則要禁用本地中斷。

2)對異常而言:異常的優先級低,如需訪問共享數據結構,必須先禁用本地中斷。

多處理器情況下:

1)對中斷而言:除了單處理器考慮的情況外,還必須用自旋鎖排除其他CPU的干擾。

2)對異常而言:除了單處理器考慮的情況外,還必須用自旋鎖排除其他CPU的干擾。

5、保護由異常和可延遲函數訪問的數據結構

 

單處理器情況下:

1)對可延遲函數而言:可延遲函數不能被異常“中斷”,無需考慮異常的干擾。在每個CPU上可延遲函數串行執行,不存在競爭條件,因此不用同步原語。

2)對異常而言:異常的優先級低,如需訪問共享數據結構,必須先禁用本地軟中斷。

多處理器情況下:

1)對中斷而言:除了單處理器考慮的情況外,還必須用自旋鎖排除其他CPU的干擾。

2)對異常而言:除了單處理器考慮的情況外,還必須用自旋鎖排除其他CPU的干擾。

6、保護由中斷和可延遲函數訪問的數據機構

單處理器情況下:

1)對中斷而言:中斷不能被可延遲函數“中斷”,無需考慮可延遲函數的干擾。第1條一樣,如果此數據結構只被一種中斷訪問,則可不加同步原語;否則要禁用本地中斷。

2)對可延遲函數而言:可延遲函數的優先級低,如需訪問共享數據結構,必須先禁用本地中斷。

多處理器情況下:

1)對中斷而言:除了單處理器上考慮的外,還必須用自旋鎖排除其他CPU的干擾。

2)對可延遲函數而言:除了單處理器上考慮的外,還必須用自旋鎖排除其他CPU的干擾。

7、保護由異常、中斷和可延遲函數訪問的數據結構

單處理器情況下:

1)對中斷而言:優先級最高,無需考慮其他兩種的影響。第1條一樣,如果此數據結構只被一種中斷訪問,則可不加同步原語;否則要禁用本地中斷。

2)對可延遲函數而言:可延遲函數的優先級低,如需訪問共享數據結構,必須先禁用本地中斷。

3)對異常而言:可延遲函數的優先級低,如需訪問共享數據結構,必須先禁用本地中斷。禁用了本地中斷,也就相當於禁用了本地軟中斷。

多處理器情況下:

 

1)對中斷而言:除了單處理器上考慮的外,還必須用自旋鎖排除其他CPU的干擾。

2)對可延遲函數而言:除了單處理器上考慮的外,還必須用自旋鎖排除其他CPU的干擾。

3)對異常而言:除了單處理器上考慮的外,還必須用自旋鎖排除其他CPU的干擾。

 

五、軟中斷源碼分析

之所以說軟中斷的執行時是串行的,是因為在軟中斷執行時,對於從硬中斷進來的即將要執行的新的軟中斷會采取屏蔽措施,不讓他們立即運行,而是保存起來,延遲一會,等自身的軟中斷執行完畢后,再執行那些保存起來的軟中斷,從而達到串行的目的。

  1 //
  2 // do_IRQ 函數執行完硬件 ISR 后退出時調用此函數。
  3 //
  4 void irq_exit(void)
  5 {
  6         account_system_vtime(current);
  7         trace_hardirq_exit();
  8         sub_preempt_count(IRQ_EXIT_OFFSET);
  9         //
 10         // 判斷當前是否有硬件中斷嵌套,並且是否有軟中斷在
 11         // pending 狀態,注意:這里只有兩個條件同時滿足
 12         // 時,才有可能調用 do_softirq() 進入軟中斷。也就是
 13         // 說確認當前所有硬件中斷處理完成,且有硬件中斷安裝了
 14         // 軟中斷處理時理時才會進入。
 15         // 
 16         if (!in_interrupt() && local_softirq_pending())
 17                 //
 18                 // 其實這里就是調用 do_softirq() 執行
 19                 //
 20                 invoke_softirq();
 21         preempt_enable_no_resched();
 22 }
 23 #ifndef __ARCH_HAS_DO_SOFTIRQ
 24 asmlinkage void do_softirq(void)
 25 {
 26         __u32 pending;
 27         unsigned long flags;
 28         //
 29         // 這個函數判斷,如果當前有硬件中斷嵌套,或
 30         // 有軟中斷正在執行時候,則馬上返回。在這個
 31         // 入口判斷主要是為了和 ksoftirqd 互斥。
 32         //
 33         if (in_interrupt())
 34                 return;
 35         //
 36         // 關中斷執行以下代碼
 37         //
 38         local_irq_save(flags);
 39         //
 40         // 判斷是否有 pending 的軟中斷需要處理。
 41         //
 42         pending = local_softirq_pending();
 43         //
 44         // 如果有則調用 __do_softirq() 進行實際處理
 45         //
 46         if (pending)
 47                 __do_softirq();
 48         //
 49         // 開中斷繼續執行
 50         //
 51         local_irq_restore(flags);
 52 }
 53 //
 54 // 最大軟中斷調用次數為 10 次。
 55 //
 56 #define MAX_SOFTIRQ_RESTART 10
 57 asmlinkage void __do_softirq(void)
 58 {
 59         //
 60         // 軟件中斷處理結構,此結構中包括了 ISR 中
 61         // 注冊的回調函數。
 62         //
 63         struct softirq_action *h;
 64         __u32 pending;
 65         int max_restart = MAX_SOFTIRQ_RESTART;
 66         int cpu;
 67         //
 68         // 得到當前所有 pending 的軟中斷。
 69         // 
 70         pending = local_softirq_pending();
 71         account_system_vtime(current);
 72         //
 73         // 執行到這里要屏蔽其他軟中斷,這里也就證實了
 74         // 每個 CPU 上同時運行的軟中斷只能有一個。
 75         //
 76         __local_bh_disable((unsigned long)__builtin_return_address(0));
 77         trace_softirq_enter();
 78         //
 79         // 針對 SMP 得到當前正在處理的 CPU
 80         //
 81         cpu = smp_processor_id();
 82 //
 83 // 循環標志
 84 //
 85 restart:
 86         //
 87         // 每次循環在允許硬件 ISR 強占前,首先重置軟中斷
 88         // 的標志位。
 89         //
 90         /* Reset the pending bitmask before enabling irqs */
 91         set_softirq_pending(0);
 92         //
 93         // 到這里才開中斷運行,注意:以前運行狀態一直是關中斷
 94         // 運行,這時當前處理軟中斷才可能被硬件中斷搶占。也就
 95         // 是說在進入軟中斷時不是一開始就會被硬件中斷搶占。只有
 96         // 在這里以后的代碼才可能被硬件中斷搶占。
 97         //
 98         local_irq_enable();
 99         //
100         // 這里要注意,以下代碼運行時能被硬件中斷搶占,但
101         // 這個硬件 ISR 執行完成后,他的所注冊的軟中斷無法馬上運行,
102         // 別忘了,目前雖是開硬件中斷執行,但前面的 __local_bh_disable()
103         // 函數屏蔽了軟中斷。所以這種環境下只能被硬件中斷搶占,但這
104         // 個硬中斷注冊的軟中斷回調函數無法運行。要問為什么,那是因為
105         // __local_bh_disable() 函數設置了一個標志當作互斥量,而這個
106         // 標志正是上面的 irq_exit() 和 do_softirq() 函數中的
107         // in_interrupt() 函數判斷的條件之一,也就是說 in_interrupt() 
108         // 函數不僅檢測硬中斷而且還判斷了軟中斷。所以在這個環境下觸發
109         // 硬中斷時注冊的軟中斷,根本無法重新進入到這個函數中來,只能
110         // 是做一個標志,等待下面的重復循環(最大 MAX_SOFTIRQ_RESTART)
111         // 才可能處理到這個時候觸發的硬件中斷所注冊的軟中斷。
112         //
113         //
114         // 得到軟中斷向量表。
115         //
116         h = softirq_vec;
117         //
118         // 循環處理所有 softirq 軟中斷注冊函數。
119         // 
120         do {
121                 //
122                 // 如果對應的軟中斷設置 pending 標志則表明
123                 // 需要進一步處理他所注冊的函數。
124                 //
125                 if (pending & 1) {
126                         //
127                         // 在這里執行了這個軟中斷所注冊的回調函數。
128                         //
129                         h->action(h);
130                         rcu_bh_qsctr_inc(cpu);
131                 }
132         //
133         // 繼續找,直到把軟中斷向量表中所有 pending 的軟
134         // 中斷處理完成。
135         //
136                 h++;
137                 //
138                 // 從代碼里能看出按位操作,表明一次循環只
139                 // 處理 32 個軟中斷的回調函數。
140                 //
141                 pending >>= 1; 
142         } while (pending);
143         //
144         // 關中斷執行以下代碼。注意:這里又關中斷了,下面的
145         // 代碼執行過程中硬件中斷無法搶占。
146         //
147         local_irq_disable();
148         //
149         // 前面提到過,在剛才開硬件中斷執行環境時只能被硬件中斷
150         // 搶占,在這個時候是無法處理軟中斷的,因為剛才開中
151         // 斷執行過程中可能多次被硬件中斷搶占,每搶占一次就有可
152         // 能注冊一個軟中斷,所以要再重新取一次所有的軟中斷。
153         // 以便下面的代碼進行處理后跳回到 restart 處重復執行。
154         //
155         pending = local_softirq_pending();
156         //
157         // 如果在上面的開中斷執行環境中觸發了硬件中斷,且每個都
158         // 注冊了一個軟中斷的話,這個軟中斷會設置 pending 位,
159         // 但在當前一直屏蔽軟中斷的環境下無法得到執行,前面提
160         // 到過,因為 irq_exit() 和 do_softirq() 根本無法進入到
161         // 這個處理過程中來。這個在上面周詳的記錄過了。那么在
162         // 這里又有了一個執行的機會。注意:雖然當前環境一直是
163         // 處於屏蔽軟中斷執行的環境中,但在這里又給出了一個執行
164         // 剛才在開中斷環境過程中觸發硬件中斷時所注冊的軟中斷的
165         // 機會,其實只要理解了軟中斷機制就會知道,無非是在一些特
166         // 定環境下調用 ISR 注冊到軟中斷向量表里的函數而已。
167         //
168         //
169         // 如果剛才觸發的硬件中斷注冊了軟中斷,並且重復執行次數
170         // 沒有到 10 次的話,那么則跳轉到 restart 標志處重復以上
171         // 所介紹的所有步驟:設置軟中斷標志位,重新開中斷執行...
172         // 注意:這里是要兩個條件都滿足的情況下才可能重復以上步驟。 
173         //
174         if (pending && --max_restart)
175                 goto restart;
176         //
177         // 如果以上步驟重復了 10 次后更有 pending 的軟中斷的話,
178         // 那么系統在一定時間內可能達到了一個峰值,為了平衡這點。
179         // 系統專門建立了一個 ksoftirqd 線程來處理,這樣避免在一
180         // 定時間內負荷太大。這個 ksoftirqd 線程本身是個大循環,
181         // 在某些條件下為了不負載過重,他是能被其他進程搶占的,
182         // 但注意,他是顯示的調用了 preempt_xxx() 和 schedule()
183         // 才會被搶占和轉換的。這么做的原因是因為在他一旦調用 
184         // local_softirq_pending() 函數檢測到有 pending 的軟中斷
185         // 需要處理的時候,則會顯示的調用 do_softirq() 來處理軟中
186         // 斷。也就是說,下面代碼喚醒的 ksoftirqd 線程有可能會回
187         // 到這個函數當中來,尤其是在系統需要響應非常多軟中斷的情況
188         // 下,他的調用入口是 do_softirq(),這也就是為什么在 do_softirq()
189         // 的入口處也會用 in_interrupt()  函數來判斷是否有軟中斷
190         // 正在處理的原因了,目的還是為了防止重入。ksoftirqd 實現
191         // 看下面對 ksoftirqd() 函數的分析。
192         //
193         if (pending)
194                //
195                // 此函數實際是調用 wake_up_process() 來喚醒 ksoftirqd
196                // 
197                 wakeup_softirqd();
198         trace_softirq_exit();
199         account_system_vtime(current);
200         //
201         // 到最后才開軟中斷執行環境,允許軟中斷執行。注意:這里
202         // 使用的不是 local_bh_enable(),不會再次觸發 do_softirq()
203         // 的調用。
204         // 
205         _local_bh_enable();
206 }
207 static int ksoftirqd(void * __bind_cpu)
208 {
209         //
210         // 顯示調用此函數設置當前進程的靜態優先級。當然,
211         // 這個優先級會隨調度器策略而變化。
212         //
213         set_user_nice(current, 19);
214         //
215         // 設置當前進程不允許被掛啟
216         //
217         current->flags |= PF_NOFREEZE;
218         //
219         // 設置當前進程狀態為可中斷的狀態,這種睡眠狀
220         // 態可響應信號處理等。
221         // 
222         set_current_state(TASK_INTERRUPTIBLE);
223         //
224         // 下面是個大循環,循環判斷當前進程是否會停止,
225         // 不會則繼續判斷當前是否有 pending 的軟中斷需
226         // 要處理。
227         //
228         while (!kthread_should_stop()) {
229                 //
230                 // 如果能進行處理,那么在此處理期間內禁止
231                 // 當前進程被搶占。
232                 //
233                 preempt_disable();
234                 //
235                 // 首先判斷系統當前沒有需要處理的 pending 狀態的
236                 // 軟中斷
237                 //
238                 if (!local_softirq_pending()) {
239                         //
240                         // 沒有的話在主動放棄 CPU 前先要允許搶占,因為
241                         // 一直是在不允許搶占狀態下執行的代碼。
242                         //
243                         preempt_enable_no_resched();
244                         //
245                         // 顯示調用此函數主動放棄 CPU 將當前進程放入睡眠隊列,
246                         // 並轉換新的進程執行(調度器相關不記錄在此)
247                         //
248                         schedule();
249                         //
250                         // 注意:如果當前顯示調用 schedule() 函數主動轉換的進
251                         // 程再次被調度執行的話,那么將從調用這個函數的下一條
252                         // 語句開始執行。也就是說,在這里當前進程再次被執行的
253                         // 話,將會執行下面的 preempt_disable() 函數。
254                         //
255                         //
256                         // 當進程再度被調度時,在以下處理期間內禁止當前進程
257                         // 被搶占。
258                         //
259                         preempt_disable();
260                 }
261                 //
262                 // 設置當前進程為運行狀態。注意:已設置了當前進程不可搶占
263                 // 在進入循環后,以上兩個分支不論走哪個都會執行到這里。一是
264                 // 進入循環時就有 pending 的軟中斷需要執行時。二是進入循環時
265                 // 沒有 pending 的軟中斷,當前進程再次被調度獲得 CPU 時繼續
266                 // 執行時。
267                 //
268                 __set_current_state(TASK_RUNNING);
269                 //
270                 // 循環判斷是否有 pending 的軟中斷,如果有則調用 do_softirq()
271                 // 來做具體處理。注意:這里又是個 do_softirq() 的入口點,
272                 // 那么在 __do_softirq() 當中循環處理 10 次軟中斷的回調函數
273                 // 后,如果更有 pending 的話,會又調用到這里。那么在這里則
274                 // 又會有可能去調用 __do_softirq() 來處理軟中斷回調函數。在前
275                 // 面介紹 __do_softirq() 時已提到過,處理 10 次還處理不完的
276                 // 話說明系統正處於繁忙狀態。根據以上分析,我們能試想如果在
277                 // 系統非常繁忙時,這個進程將會和 do_softirq() 相互交替執行,
278                 // 這時此進程占用 CPU 應該會非常高,雖然下面的 cond_resched() 
279                 // 函數做了一些處理,他在處理完一輪軟中斷后當前處理進程可能會
280                 // 因被調度而減少 CPU 負荷,不過在非常繁忙時這個進程仍然有可
281                 // 能大量占用 CPU。
282                 //
283                 while (local_softirq_pending()) {
284                         /* Preempt disable stops cpu going offline.
285                            If already offline, we’ll be on wrong CPU:
286                            don’t process */
287                         if (cpu_is_offline((long)__bind_cpu))
288                                 //
289                                 // 如果當前被關聯的 CPU 無法繼續處理則跳轉
290                                 // 到 wait_to_die 標記出,等待結束並退出。
291                                 // 
292                                 goto wait_to_die;
293                         //
294                         // 執行 do_softirq() 來處理具體的軟中斷回調函數。注
295                         // 意:如果此時有一個正在處理的軟中斷的話,則會馬上
296                         // 返回,還記得前面介紹的 in_interrupt() 函數么。
297                         //
298                         do_softirq();
299                         //
300                         // 允許當前進程被搶占。
301                         //
302                         preempt_enable_no_resched();
303                         
304                         //
305                         // 這個函數有可能間接的調用 schedule() 來轉換當前
306                         // 進程,而且上面已允許當前進程可被搶占。也就是
307                         // 說在處理完一輪軟中斷回調函數時,有可能會轉換到
308                         // 其他進程。我認為這樣做的目的一是為了在某些負載
309                         // 超標的情況下不至於讓這個進程長時間大量的占用 CPU,
310                         // 二是讓在有非常多軟中斷需要處理時不至於讓其他進程
311                         // 得不到響應。
312                         //
313                         cond_resched();
314                         //
315                         // 禁止當前進程被搶占。
316                         //
317                         preempt_disable();
318                         //
319                         // 處理完所有軟中斷了嗎?沒有的話繼續循環以上步驟
320                         //
321                 }
322                 //
323                 // 待一切都處理完成后,允許當前進程被搶占,並設置
324                 // 當前進程狀態為可中斷狀態,繼續循環以上所有過程。
325                 //
326                 preempt_enable();
327                 set_current_state(TASK_INTERRUPTIBLE);
328         }
329    
330         //
331         // 如果將會停止則設置當前進程為運行狀態后直接返回。
332         // 調度器會根據優先級來使當前進程運行。
333         //
334         __set_current_state(TASK_RUNNING);
335         return 0;
336 //
337 // 一直等待到當前進程被停止
338 //
339 wait_to_die:
340         //
341         // 允許當前進程被搶占。
342         //
343         preempt_enable();
344         /* Wait for kthread_stop */
345         //
346         // 設置當前進程狀態為可中斷的狀態,這種睡眠狀
347         // 態可響應信號處理等。
348         // 
349         set_current_state(TASK_INTERRUPTIBLE);
350         //
351         // 判斷當前進程是否會被停止,如果不是的話
352         // 則設置進程狀態為可中斷狀態並放棄當前 CPU
353         // 主動轉換。也就是說這里將一直等待當前進程
354         // 將被停止時候才結束。
355         //
356         while (!kthread_should_stop()) {
357                 schedule();
358                 set_current_state(TASK_INTERRUPTIBLE);
359         }
360         //
361         // 如果將會停止則設置當前進程為運行狀態后直接返回。
362         // 調度器會根據優先級來使當前進程運行。
363         //
364         __set_current_state(TASK_RUNNING);
365         return 0;
366 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM