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