生活随笔
收集整理的這篇文章主要介紹了
kernel笔记——进程调度
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
調度器完成以下任務:
?
時鐘中斷(或類似的定時器)時間內刷新進程的時間片,設置進程調度標志 系統調用返回或中斷完成時檢查調度標志 ?
?
schedule函數
內核代碼中完成進程調度的函數為schedule(),該函數中包含以下調用:
?
put_prev_task(rq, prev); next = pick_next_task(rq); context_switch(rq, prev, next); ?
schedule首先將當前執行函數放入運行隊列,然后選擇下一個要運行的進程(怎么選擇下一個進程,這部分就是調度算法的工作),最后完成進程上下文切換。
?
?
由A進程進入schedule函數,從函數出來將變成B進程,A進程調用schedule相當于放棄cpu資源,進程從R狀態變成S等其他狀態都會經過schedule。在vmcore中,我們可以看到S狀態進程進入S狀態前最后一個調用的函數為schedule:
?
crash> ps | grep sshd 1300 1 0 ffff81024fba1810?IN?0.0 26088 1380 sshd crash> bt 1300 PID: 1300 TASK: ffff81024fba1810 CPU: 0 COMMAND: "sshd" #0 [ffff81026a2e9cd8]?schedule?at ffffffff802d9025 #1 [ffff81026a2e9da0] schedule_timeout at ffffffff802d9a6b #2 [ffff81026a2e9df0] do_select at ffffffff801935f2 #3 [ffff81026a2e9ed0] sys_select at ffffffff80193880 #4 [ffff81026a2e9f80] system_call at ffffffff8010ad3e …… 進程可以是“被調度”,而有的進程或線程主動調用schedule,放棄cpu,例如ksoftirqd內核線程。
?
進程優先級
早期的調度算法根據進程優先級計算進程運行的時間片、選擇被調度的進程。對于普通進程(normal process)而言,nice表示其優先級,nice的取值范圍為-20~19,nice值越大優先級越低;對于實時進程(real time process)而言,優先級的取值范圍為0~99,同樣值越大優先級越低。
?
內核代碼中nice取值范圍對應100~139,MAX_RT_PRIO宏定義了實時進程優先級與nice取值的分界點,實時進程的優先級比普通進程優先級高。
?
通過top、ps等命令我們可以查到進程的優先級,以下為top命令輸出的下半部分,進程信息的查詢結果:
?
? ?PID USER ??PR? NI ? ?VIRT RES ?SHR ? S ?%CPU %MEM ? ? ?TIME+ ?COMMAND ? ? ?22 ?root ? 20 ? ?0 ? ? ? ?0 ? ? 0 ? ? ?0 ? R ? ? ?1.9 ? ? ?0.0 ?60:23.34 ?ksoftirqd/9 ? 8689 ?root ? 20 ? ?0 ?273m 67m ?11m ?S ? ? ? 0.0 ? ? ?0.4 ? 2:04.01 ?java 11058 ?root ? 39 ? 19 ? ? ? 0 ? ? 0 ? ? ?0 ? S ? ? ? 0.0 ? ? ?0.0 ? 1:45.68 ?kipmi0 11771 ?root ?-98 ? ?0 20388 19m 7256 ? S ? ? ? 0.0 ? ? ?0.1 ? 0:16.06 ?had ? ? ? 3 ?root ? RT ? ?0 ? ? ? ?0 ? ? 0 ? ? ?0 ? S ? ? ? 0.0 ? ? ?0.0 ? 0:00.00 ?migration/0 ?
其中(PR列值+100)指示進程優先級,以上輸出中,java進程優先級為120,為普通進程;had進程優先級為2,為實時進程。RT表示該進程為實時進程,并且優先級為0。
?
可以使用renice命令設置普通進程的優先級:
?
linux #?renice?10 -p 21691 21691: old priority -20, new priority 10 ?
?
調度算法
從最初的遍歷O(n)算法,到2.5版kernel的O(1)算法,再到2.6版kernel的CFS(completely fair scheduler),調度算法被不斷地改進。
?
O(1)調度算法使得調度器選擇下一個執行進程的時間變為常量,不隨可運行進程數量變化,但其有以下缺點:
?
動態調整進程優先級的算法很復雜,不易于代碼維護 不“偏袒”交互型程序,假如響應鼠標移動的進程與某后臺進程有同一優先級,在鼠標被移動時,另一進程可能更先獲得調度,這使得鼠標移動響應慢或有重影 ?
?
因而應用于服務器,O(1)調度算法表現尚可,在交互性更強的桌面應用方面就強差人意。
?
CFS解決了以上O(1)算法問題,它的核心數據結構為紅黑樹,該紅黑樹的key為進程的虛擬運行時間(vruntime)。每次調度時選擇具有最小vruntime的進程執行,即紅黑樹最左邊的結點相應的進程。
?
CFS調度算法中,交互型的進程將獲得更大的權重、更多被執行的機會。假如現在有一個vruntime為1ms的交互型進程,另有一個vruntime為10ms的cpu消耗型進程,調度器連續調度交互型進程10次,再調度vruntime為10ms的cpu消耗型進程。Android中使用的調度方法亦為CFS。
?
之前的調度算法中,nice優先級決定進程的時間片大小,在CFS中,nice與進程權重關聯,權重大的進程獲得的運行時間就長。prio_to_weight數組定義了nice與權重的對應關系,該數組在中定義。
?
調度策略
kernel提供了5種調度策略,針對普通進程,有以下3種:
?
SCHED_OTHER SCHED_BATCH SCHED_IDLE ?
這3種調度策略對應的進程,按CFS方式調度,優先級依次降低。
?
針對實時進程,有以下2種:
?
SCHED_FIFO SCHED_RR ?
FIFO即先到先服務,具備該調度策略的進程將一直運行,直至其被I/O請求掛起或被具有更高優先級的實時進程搶占。
RR即輪叫調度(Round Robin),除設定了進程運行的最大時間片外,其他調度方式與FIFO相同。
?
因實時進程的優先級比普通進程的優先級都要高,具備以上調度策略的進程變為可運行狀態時,都將搶占以CFS方式調度的普通進程。
?
CFS調度算法相關的代碼在sched_fair.c中,實時進程相關的調度算法在sched_rt.c中定義。
?
與進程調度策略相關的函數調用有以下兩個,分別用于獲取、設置進程調度策略:
?
sched_getscheduler sched_setscheduler 以下程序示例使用sched_setscheduler調用,將程序自身的優先級設置為最高的實時優先級:
// sched_test.c #include #include #include int main() { ? ? struct sched_param param; ? ? param.sched_priority = sched_get_priority_max(SCHED_FIFO); ? ??sched_setscheduler(getpid(), SCHED_FIFO, ?m); ? ? printf("running...\n"); ? ? while(1); ? ? return 0; } ?
執行程序之后,我們在top中可以看到
?
? ? PID USER ?PR? NI ? VIRT ? RES ?SHR ?S ?%CPU ?%MEM ? ?TIME+ ? ?COMMAND 24484 ? root ?RT? ? 0 ? 3704 ? 400 ? 316 ?R ? ? 100 ? ? ?0.0 ? 1:32.51 ? ?sched_test ?
?
設置實時進程的優先級只能通過函數調用,不像設置nice值,可使用renice命令。
?
負載均衡
對于多核cpu,kernel的調度器還需要解決另一個問題:如何在多核間做到負載平衡。若一個核忙于跑程序,而其他核是空閑的,那么就失去了多核存在的意義。
?
對于對稱多處理(symmetric multiprocessing,SMP)架構的cpu,同一物理cpu中,多核間可共享cache,物理cpu之間則不能。因而在同一物理cpu多核間移動進程,相比物理cpu間移動進程,開銷要小。
?
migration內核線程完成平衡多核工作負載的工作,migration以以下方式拉起:
?
時鐘中斷處理函數timer_interrupt調用scheduler_tick函數 在scheduler_tick中,trigger_load_balance函數被調用 若滿足調整cpu負載條件,trigger_load_balance通過raise_softirq調用產生一個軟中斷 后續軟中斷得到處理,?migration線程被拉起,完成負載平衡工作 另外我們可以通過taskset命令設置某進程在某個特定cpu上執行,并且不參與負載平衡調度:
?
linux # taskset -cp 0 ./sched_test ?
?
以上命令設置sched_test在0號cpu上運行,使用top我們可以看到執行的效果:
?
linux # ps -elf | grep test | grep -v grep 4 R root?24502?24053 99 -40 - - 633 - 12:49 pts/7 00:00:15 ./sched_test linux # top -p?24502 top - 12:50:19 up 5 days, 3:04, 7 users, load average: 1.55, 1.86, 2.16 Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie Cpu0 :100.0%us, 0.0%sy, 0.0%ni, 0.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Cpu1 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Cpu2 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Cpu3 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Mem: 23980M total, 375M used, 23604M free, 37M buffers Swap: 2055M total, 0M used, 2055M free, 221M cached ? PID USER PR NI VIRT RES SHR S?%CPU?%MEM ? TIME+ COMMAND 24502 root RT ?0 2532 364 292 R ?100??0.0 0:40.73 sched_test ?
Reference: Chapter 4 - Process Scheduling, ?Linux kernel development.3rd.Edition
總結
以上是生活随笔 為你收集整理的kernel笔记——进程调度 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。