浅析Linux线程调度
?在Linux中,線程是由進程來實現,線程就是輕量級進程(?lightweight?process?),因此在Linux中,線程的調度是按照進程的調度方式來進行調度的,也就是說線程是調度單元。Linux這樣實現的線程的好處的之一是:線程調度直接使用進程調度就可以了,沒必要再搞一個進程內的線程調度器。在Linux中,調度器是基于線程的調度策略(scheduling?policy)和靜態調度優先級(static?scheduling?priority)來決定那個線程來運行。
? ? 對于下面三種調度策略SCHED_OTHER,?SCHED_IDLE,?SCHED_BATCH,其調度優先級sched_priority是不起作用的,即可以看成其調度優先級為0;調度策略SCHED_FIFO和SCHED_RR是實時策略,他們的調度值范圍是1到99,數值越大優先級越高,另外實時調度策略的線程總是比前面三種通常的調度策略優先級更高。通常,調度器會為每個可能的調度優先級(sched_priority?value)維護一個可運行的線程列表,并且是以最高靜態優先級列表頭部的線程作為下次調度的線程。所有的調度都是搶占式的:如果一個具有更高靜態優先級的線程轉換為可以運行了,那么當前運行的線程會被強制進入其等待的隊列中。下面介紹幾種常見的調度策略:
? ??SCHED_OTHER:該策略是是默認的Linux分時調度(time-sharing?scheduling)策略,它是Linux線程默認的調度策略。SCHED_OTHER策略的靜態優先級總是為0,對于該策略列表上的線程,調度器是基于動態優先級(dynamic?priority)來調度的,動態優先級是跟nice中相關(nice值可以由接口nice,?setpriority,sched_setattr來設置),該值會隨著線程的運行時間而動態改變,以確保所有具有SCHED_OTHER策略的線程公平運行。在Linux上,nice值的范圍是-20到+19,默認值為0;nice值越大則優先級越低,相比高nice值(低優先級)的進程,低nice值(高優先級)的進程可以獲得更多的處理器時間。使用命令ps?-el查看系統的進程列表,其中NI列就是進程對應的nice值;使用top命令,看到的NI列也是nice值。運行命令的時候可用nice?–n?xx?cmd來調整cmd任務的nice值,xx的范圍是-20~19之間。
? ??SCHED_FIFO:先入先出調度策略(First?in-first?out?scheduling)。該策略簡單的說就是一旦線程占用cpu則一直運行,一直運行直到有更高優先級任務到達或自己放棄。
? ??SCHED_RR:時間片輪轉調度(Round-robin?scheduling)。該策略是SCHED_FIFO基礎上改進來的,他給每個線程增加了一個時間片限制,當時間片用完后,系統將把該線程置于隊列末尾。放在隊列尾保證了所有具有相同優先級的RR任務的調度公平。使用top命令,如果PR列的值為RT,則說明該進程采用的是實時策略,即調度策略是SCHED_FIFO或者為SCHED_RR,而對于非實時調度策略(比如SCHED_OTHER)的進程,該列的值是NI+20,以供Linux內核使用。我們可以通過命令:
?
[cpp] view plain copy來查看進程對應的實時優先級(位于RTPRIO列下),如果有進程對應的列顯示“-”,則說明它不是實時進程。注意任何實時策略進程的優先級都高于普通的進程,也就說實時優先級和nice優先級處于互不相交的兩個范疇。
?
? ? 在Linux中,與調度相關的常見接口如下:
?
[cpp] view plain copy該接口獲取指定調度策略可以設置的最大優先級,類似的?sched_get_priority_min接口獲取調度策略可以設置的最小優先級。在Linux中,對于SCHED_FIFO和SCHED_RR調度策略其優先級為1到99,其他調度策略優先級為0。注意在不同系統上,這個優先級范圍可能不一樣。
?
[cpp] view plain copy該接口可以用來設置線程的調度策略,即設置線程屬性attr。參數policy可以是CHED_FIFO,?SCHED_RR和SCHED_OTHER。系統創建線程時,默認的線程調度策略是SCHED_OTHER。類似可以通過接口
?
[cpp] view plain copy獲取線程的調度策略。
?
[cpp] view plain copy該接口可以用來設置線程的調度優先級。結構sched_param定義如下:
?
[cpp] view plain copy類似的的接口,可以用來獲取線程調度的優先級:
?
[cpp] view plain copy調用該接口可以使得當前線程主動交出CPU,并把該線程放到相應調度隊列的末尾。如果當前線程是最高優先級隊列中唯一的線程,則在調用sched_yield后,該線程繼續保持運行。
?
[cpp] view plain copy該接口可以用來設置線程的CPU親和性(CPU?affinity),設置線程的親和性可以使得線程綁定到一個或多個指定的CPU上運行。在多處理器系統上,設置CPU親和性可以提高性能(主要原因是盡可能避免了cache失效和切換到其他CPU的消耗)。CPU親和性掩碼是由cpu_set_t結果來實現的,該結構體需要用預定義好的宏來操作;參數pid是指定線程的TID,可以通過gettid()來獲取,即線程在內核中對應進程id,若pid為0,則設置的是調用線程的CPU親和性,注意用getpid()獲取的是主線程的id;參數cpusetsize的值通常是mask的大小,即sizeof(mask)。除了這個接口外,設置線程親和性接口還有:
?
[cpp] view plain copy通過fork創建的子進程繼承父進程的CPU親和性,通過?execve()后,親和性仍然保持不變。我們可以下面命令來查看多核cpu的負載:
? ? I)cat?/proc/cpuinfo??查看所有cpu的信息;
? ? II)top命令,然后再輸入1,則顯示多個cpu的使用信息;
? ? III)top命令,然后按下f,進入top?Current?Fields設置頁面,然后按下j,表示要求顯示進程使用那個cpu,回車后,回到剛才界面,此時P?顯示此進程使用哪個CPU。
?
? ? 下面是測試代碼:
?
[cpp] view plain copy編譯和運行程序結果如下:
?
[cpp] view plain copy從輸出結果,我們可以看到:
? ? I)線程默認的調度策略為SCHED_OTHER,并且最大和最小調度優先級都是0。
? ? II)調度策略SCHED_FIFO和SCHED_RR的優先級范圍為1到99,并且初始設置時對應的調度優先級初始值為0。
? ??在Linux中,調度程序是一個叫schedule()的函數,該函數調用的頻率很高,由它來決定是否要執行進程的切換,如果要切換的話,切換到那個進程等。那么在Linux中,在什么情況下要執行這個調度程序呢?我們把這種情況叫作調度時機。Linux調度時機主要有:
? ?I)進程狀態轉換的時刻:進程終止、進程睡眠(比如I/O阻塞就會導致這種情況),還比如進程調用sleep()或exit()等函數進行狀態轉換。?
? ?II)當前進程的時間片用完時。
? ? ?III)設備驅動程序,當設備驅動程序執行長而重復的任務時,在每次反復循環中,驅動程序讀檢查是否需要調度,如果必要,則調用調度程序schedule()放棄CPU。
? ? ?IV)進程從中斷、異常及系統調用返回到用戶態時。
參考資料:
http://man7.org/linux/man-pages/man7/sched.7.html
http://man7.org/linux/man-pages/man2/sched_getaffinity.2.html
http://www.cnblogs.com/xiaotlili/p/3510224.html
http://www.708luo.com/?p=78 Linux
http://www.quora.com/What-is-the-difference-between-the-NI-and-PR-values-in-the-top-1-commands-output
http://blog.csdn.net/hanchaoman/article/details/6697636
http://www.ibm.com/developerworks/cn/linux/l-affinity.html
http://blog.csdn.net/chenggong2dm/article/details/6131052
《Linux內核設計與實現》(第3版)
《深入分析Linux內核源代碼》(陳莉君著)
總結
以上是生活随笔為你收集整理的浅析Linux线程调度的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 加班越久故障越多,如何跳出程序员的恶性循
- 下一篇: Spring的Java配置方式