Linux進程調度
一、調度策略類型
Linux核心的進程是通過雙鏈表的方式將進程的struct task_struct結構串連在一起, task_struct結構裡麵包含了與一個進程相關的全部資訊(比如進程的狀態、優先順序、進程的地址空間等);每個進程都有一個唯一的進程描述符,linux就是通過進程描述符來感知一個進程的存在或者消亡,進程的結構定義在核心的<linux/sched.h>中。
下面是linux核心進程調度策略的定義
Linux進程調度
1、SCHED_NORMAL(SCHED_OTHER):普通的分時進程,這是普通的使用者進程,進程的預設類型,採用動態優先調度策略,選擇進程的依據主要是根據進程goodness值的大小。這種進程在運行時,可以被高goodness值的進程搶先。
2、SCHED_FIFO:先入先出的即時進程,旦佔用cpu則一直運行。一直運行直到有更高優先順序任務到達或自己放棄。
3、SCHED_RR:時間片輪轉的即時進程,每次調度,把CPU分配隊首進程,並令其執行一個時間片。當執行的時間片用完時,由一個記時器發出一個時鐘插斷要求,該進程被停止,並被送往就緒隊列末尾;依次迴圈。
4、SCHED_BATCH:批處理進程
5、SCHED_EDLE:只有系統空閑事才能夠被調度執行的進程
二、調度分類
1、CFS(Completely Fair Scheduler,CFS)調度類:SCHED_NORMAL、SCHED_BATH、SCHED_IDLE
2、即時調度類:SCHED_RR、SCHED_FIFO
三、調度時機
1、主動式:在核心中直接調用schedule()。當進程需要等待資源等而暫時停止運動時,會把狀態置於掛起(睡眠),並主動請求調度,讓出CPU。
2、被動式(搶佔)
使用者搶佔:Linux2.4、Linux2.6
核心搶佔:Linux2.6
(1)使用者搶佔
使用者搶佔發生在:從系統調用返回使用者空間、從中斷處理常式返回到使用者空間。
(2)核心搶佔:
在不支援核心搶佔的系統中,進程/線程一旦運行於核心空間,就一直執行,知道它主動放棄或時間片耗盡為止。這樣一些非常緊急的進程或線程將長時間得不到運行。
在支援核心搶佔的系統中,更高優先順序的進程/線程可以搶佔正在核心空間啟動並執行低優先順序進程/線程。
在支援核心搶佔的系統中,默寫特例下不允許核心搶佔:
a)核心正進行中斷處理。進程調度函數schedule()會對此作出判斷,如果是在中斷中調用,會列印錯誤資訊。
b)核心進行中中斷內容相關的Bottom Half(中斷的底半部)處理。硬體中斷返回前會和執行非強制中斷,此時仍然處於中斷上下文中。
c)進程掙持有spinlock自旋鎖、writelock/readlock讀寫鎖等,當持有這些鎖時,不應該被搶佔,否則由於搶佔將導致其他CPU長期不能獲得鎖而死等。
d)核心正在執行發送器Scheduler。搶佔的原因就是為了新的調度,沒有理由將發送器搶佔掉再運行發送器。
為了保證LInux核心在以上情況下不會被搶佔,搶佔式核心使用了一個變數preempt_count,稱為核心搶佔計數。這一變數被設定在進程的thread_info結構中。每當核心要進入以上幾種狀態時,變數preempt_count就加1,指示核心不允許搶佔。每當核心從以上幾種狀態退出時,變數preempt_count就減1,同時進行可搶佔的判斷與調度。
核心搶佔可能發生在:
中斷處理常式完成,返回核心空間之前。
當核心代碼再一次具有可搶佔性的時候,如解鎖及使能非強制中斷等。
四、Schedule函數工作流程:
1)清理當前啟動並執行進程
2)選擇下一個要啟動並執行進程(pick_next_task)
3)設定新進場的工作環境。
4)進程環境切換。
五、進程描述符的分配:
Linux通過一slab分配器來建立task_struct進程結構,各個task_struct放在核心棧的尾端,只要通過一個棧指標就可以找到task_struct的位置,由於進程結構通過slab分配器動態地建立,所以在棧尾建立了一個新的結構struct thread_info,thread_info結構根據所選用的處理器架構不同會有小的差別,thread_info被定義在arch\xxx\Include\asm中(xxx表示所選用的處理器架構),如MIPS架構的thread_info定義如下:
進程中的核心堆棧與進程描述符的關係如下圖: