2011-06-29 41 views
26

我想了解如何在Linux內核的計劃進程實際工作。我的問題不是關於調度算法。它關於功能schedule()switch_to()是如何工作的。Linux內核的schedule()+ switch_to()函數是如何工作的?

我會盡力解釋。我看到:

當進程用完時間片時,標記need_reschedscheduler_tick()設置。內核檢查標誌,看到它已設置,並調用schedule()(與問題1相關)切換到新進程。該標誌是應該儘快調用調度的消息,因爲另一個進程值得運行。 返回到用戶空間或從中斷返回時,將檢查need_resched標誌。如果已設置,則內核在繼續之前調用調度程序。看看內核源代碼(linux-2.6.10-「Linux Kernel Development,第二版」所基於的版本),我還看到有些代碼可以自動調用schedule()函數,給出另一個進程運行權。 我看到函數switch_to()是實際進行上下文切換的函數。我研究了一些依賴於架構的代碼,試圖瞭解switch_to()實際上在做什麼。

這種行爲提出了一些問題,我無法找到答案:

  1. switch_to()結束,什麼是當前正在運行的進程?這個過程叫做schedule()?或者下一個過程,即被選中運行的過程?

  2. schedule()被中斷調用時,當中斷處理完成時(在某種RTE之後),選擇的進程運行開始運行?或之前?

  3. 如果schedule()函數不能從中斷中調用,何時設置了flag-need_resched

  4. 當定時器中斷處理程序正在工作時,正在使用什麼堆棧?

我不知道我是否可以說清楚。如果我不能,我希望我能在回答(或問題)後做到這一點。 我已經看了幾個試圖瞭解這個過程的來源。我有這本書「Linux Kernel Development,sec ed」,我也在使用它。 我知道一些關於MIP和H8300架構,如果有幫助解釋。

回答

24
  1. 調用switch_to()後,內核堆棧被切換到在next命名的任務。更改地址空間等在context_switch().中處理
  2. schedule()不能在原子上下文中調用,包括來自中斷(請參閱schedule_debug()中的檢查)。如果需要重新計劃,則設置TIF_NEED_RESCHED任務標誌,在interrupt return path中檢查該標誌。
  3. 請參閱2.
  4. 我相信,使用默認的8K堆棧,中斷將使用當前正在執行的任何內核堆棧進行處理。如果使用4K堆棧,我相信有一個單獨的中斷堆棧(自動加載感謝一些x86魔法),但我不完全確定這一點。

更詳細一點,這裏有一個實際的例子:

  1. 中斷髮生。 CPU切換到一箇中斷例行蹦牀,其推動中斷號壓入堆棧,然後JMPS到common_interrupt
  2. common_interrupt調用do_IRQ,其disables preemption然後handles the IRQ
  3. 在某一時刻,作出決定以切換任務。這可能來自定時器中斷或來自喚醒呼叫。無論哪種情況,都會調用set_task_need_resched,並設置TIF_NEED_RESCHED任務標誌。
  4. 最後,從do_IRQ原中斷的CPU的回報,並前進到IRQ exit path.如果該IRQ從內核中調用,它checks whether TIF_NEED_RESCHED is set,如果有來電preempt_schedule_irq,其中簡要能中斷在執行schedule()
  5. 如果從用戶空間調用IRQ,我們首先在check whether there's anything that needs doing之前返回。如果是這樣,我們去retint_careful,它檢查兩個待處理的重新計劃(如果需要,直接調用schedule())以及檢查待處理的信號,然後返回到retint_check的另一輪,直到沒有更重要的標誌設置爲止。
  6. 最後,我們restore GS and return from the interrupt handler

至於switch_to();什麼switch_to()(上X86-32)的作用是:

  1. 保存EIP(指令指針)和ESP(堆棧指針)的因爲當我們在某個時候回到這個任務後的當前值。
  2. 切換值current_task。此時,current現在指向新任務。
  3. 切換到新堆棧,然後將由我們切換的任務保存的EIP推入堆棧。之後,將使用此EIP作爲退貨地址執行退貨;這是它如何跳回到之前稱爲的舊代碼switch_to()
  4. 請致電__switch_to()。此時,current指向新任務,並且我們處於新任務的堆棧中,但其他各種CPU狀態尚未更新。 __switch_to()處理切換像FPU,段描述符,調試寄存器等事物的狀態。
  5. __switch_to()返回後,switch_to()手動推入堆棧的返回地址返回,將執行放回到之前的位置switch_to()在新任務中。切換任務現在已完全恢復執行。

x86-64非常相似,但由於ABI不同,必須稍微進行更多的狀態保存/恢復。

+0

對不起,我還是不明白。 例如: 假設我們有一個任務'A'正在運行。 1 - 發生定時器中斷。 2 - 定時器中斷處理程序啓動。 3 - 現在是時間調用schedule()並且我們來做。(),switch_to()已經完成,任務'B'是當前任務(現在我們正在使用任務'B'的堆棧,並且我們仍在運行中斷代碼) 。 5 - 定時器中斷結束,並恢復執行任務'B'。 這個例子正確嗎?如果不是,過程如何發生? – derf

+0

謝謝你的時間和耐心,但我想我錯過了一些觀點。你提供的信息比我現在可以處理的更多(當然,我對此非常感興趣)。你能以更一般的方式解釋嗎? 我真的不明白的是:switch_to()完成後會發生什麼? 在中斷代碼返回之前,所選任務(下一個)是否開始運行?哪個堆棧是當前的? – derf

+0

我想我明白了。爲了保持直線,我們沒有中斷,讓我們忽略所有其他細節,並且只使用switch_to()。假設我有兩個任務A和B,他們都有10個指令要做。任務A正在執行指令3,任務B正在執行指令6.任務A是當前任務。因此,任務A調用switch_to(),在指令3中保留任務A,並在指令6中恢復任務B.任務B跳到7,調用switch_to(),在7保留B,並在3恢復A.任務A去到4,再次調用switch_to(),並且進程繼續進行。那是對的嗎? – derf