一洲鸠、一個(gè)系統(tǒng)中線程ID是否唯一堂淡?
就像每個(gè)進(jìn)程有一個(gè)進(jìn)程ID一樣馋缅,每個(gè)線程也有一個(gè)線程ID。進(jìn)程ID在整個(gè)系統(tǒng)中是唯一的绢淀,但線程ID不同萤悴,線程ID只在它所屬的進(jìn)程環(huán)境中有效。
這段話選自《UNIX環(huán)境高級編程第二版》第11.3節(jié)皆的。我第一次看到這段話時(shí)覆履,自然而然的認(rèn)為線程ID既然只在其所述的進(jìn)程有效,那自然在不同的進(jìn)程中可以重復(fù)才對费薄,不然多浪費(fèi)資源對吧硝全。但是工作之后我發(fā)現(xiàn)事實(shí)并不是如此。比如我們在程序設(shè)計(jì)時(shí)經(jīng)常需要在一定的條件發(fā)生時(shí)終止某個(gè)特定線程义锥,而這時(shí)我們的做法是直接給這個(gè)線程發(fā)終止信號柳沙。試想,如果不同的進(jìn)程擁有相同線程ID的線程拌倍,那是不是會出現(xiàn)錯殺的情況呢赂鲤?所以線程的ID在系統(tǒng)中絕對是唯一的。
我們也可以從另外一方面去理解這個(gè)問題柱恤。我們都知道Linux的線程叫輕量級進(jìn)程(LWP数初,Light Weight Process),每個(gè)進(jìn)程有一個(gè)或者多個(gè)線程(LWPs)梗顺。系統(tǒng)運(yùn)行時(shí)以線程為執(zhí)行單元泡孩,那如果一個(gè)進(jìn)程沒有起其他線程會怎樣呢?那這個(gè)進(jìn)程其實(shí)就是自己的主線程寺谤,所以單線程進(jìn)程的線程ID就等于該進(jìn)程ID仑鸥。因?yàn)檫M(jìn)程ID系統(tǒng)唯一,所以線程ID也應(yīng)該系統(tǒng)唯一变屁。
二眼俊、進(jìn)程優(yōu)先級是數(shù)值越大優(yōu)先級越高嗎?還是數(shù)值越低優(yōu)先級越高粟关?
這個(gè)問題我開始也搞糊涂了疮胖,因?yàn)槲野l(fā)現(xiàn)用ps
和top
兩個(gè)命令看到同一個(gè)進(jìn)程的優(yōu)先級不一樣,用ps
看時(shí)闷板,數(shù)值越大進(jìn)程優(yōu)先級越高澎灸,用top
看時(shí)則剛好相反。那到底是怎么回事呢遮晚?
首先根據(jù)調(diào)度策略的不同性昭,我們將進(jìn)程分為兩類:
1. 實(shí)時(shí)進(jìn)程
實(shí)時(shí)進(jìn)程有SCHE_FIFO
和SCHE_RR
兩種調(diào)度策略。SCHE_FIFO
的進(jìn)程只有等當(dāng)前進(jìn)程執(zhí)行完才能調(diào)度下一個(gè)鹏漆,而SCHE_RR
采用時(shí)間片輪轉(zhuǎn)的方式進(jìn)行調(diào)度巩梢。實(shí)時(shí)進(jìn)程的優(yōu)先級范圍為[0,MAX_RT_PRIO-1](MAX_RT_PRIO=100)创泄,稱為實(shí)時(shí)優(yōu)先級,并且只有實(shí)時(shí)進(jìn)程有實(shí)時(shí)優(yōu)先級括蝠,這也是實(shí)時(shí)進(jìn)程的靜態(tài)優(yōu)先級鞠抑。Linux會根據(jù)實(shí)時(shí)進(jìn)程的靜態(tài)優(yōu)先級計(jì)算優(yōu)先級權(quán)重從而實(shí)現(xiàn)調(diào)度。
2. 普通進(jìn)程
普通進(jìn)程的調(diào)度策略為SCHE_OTHER
忌警。普通進(jìn)程沒有實(shí)時(shí)優(yōu)先級搁拙,僅根據(jù)nice值獲得它的靜態(tài)優(yōu)先級。并且nice只對普通進(jìn)程有效法绵,實(shí)時(shí)進(jìn)程沒有nice值箕速。
static_prio=MAX_RT_PRIO + 20 + nice
nice的范圍為[-20,19],所以普通進(jìn)程靜態(tài)優(yōu)先級的范圍為[100,139]朋譬。
由上可知進(jìn)程進(jìn)程優(yōu)先級(靜態(tài)優(yōu)先級)是數(shù)值越大優(yōu)先級越低盐茎,之所以ps
和top
看到的優(yōu)先級不一樣是因?yàn)轱@示的算法不一樣。
- 如下圖所示徙赢,使用
ps -lA
看到的進(jìn)程優(yōu)先級ps_prio = static_prio - 40字柠。所以圖片中看到的watchdog進(jìn)程實(shí)際的(靜態(tài))優(yōu)先級應(yīng)為0,而khelper進(jìn)程優(yōu)先級應(yīng)為 100(已減去nice值20)狡赐;
- 使用
ps -eo pid,cmd,class,pri,rtprio,ni
看到的進(jìn)程優(yōu)先級ps_prio = 139-static_prio窑业。此時(shí)列出的優(yōu)先級符合一般的思維,就是顯示的數(shù)值越大枕屉,優(yōu)先級越高常柄。
- 使用
top
看到的進(jìn)程優(yōu)先級top_prio = static_prio-MAX_RT_PRIO。
三搀擂、一個(gè)線程所在的進(jìn)程被殺后西潘,該線程會被終止嗎?
當(dāng)然會終止哨颂。線程并不能獨(dú)立運(yùn)行秸架,線程只是進(jìn)程的一個(gè)執(zhí)行實(shí)體。所以當(dāng)進(jìn)程結(jié)束時(shí)咆蒿,該進(jìn)程下的所有線程也就都結(jié)束了。但是此時(shí)線程退出有正常退出和非正常退出兩種情況蚂子。Linux創(chuàng)建一個(gè)線程的默認(rèn)狀態(tài)是joinable沃测,那么這些線程必須由其創(chuàng)建者主動調(diào)用pthread_join
來回收資源和獲取線程退出狀態(tài),如果沒有調(diào)用pthread_join
的食茎,就會類似于僵尸進(jìn)程一樣蒂破,造成資源浪費(fèi)。如果不想主動回收别渔,可以在線程中調(diào)用pthread_detach
來分離線程附迷。這樣線程終止時(shí)資源就會被系統(tǒng)自動回收惧互。
四、向進(jìn)程中的一個(gè)線程發(fā)送終止信號喇伯,該進(jìn)程會退出嗎喊儡?
如果線程中的任一線程調(diào)用了
exit
,_Exit
或者_exit
,那么整個(gè)進(jìn)程機(jī)會終止稻据。與此類似艾猜,如果信號的默認(rèn)動作是終止進(jìn)程,那么捻悯,把該信號發(fā)給線程會終止整個(gè)進(jìn)程匆赃。
單個(gè)線程可以通過下列三種方式退出,在不終止整個(gè)進(jìn)程的情況下停止它的控制流今缚。
(1)線程只是從啟動例程中返回算柳,返回值是線程退出碼;
(2)線程被同一進(jìn)程中其他線程取消姓言;
(3)線程調(diào)用pthread_exit
瞬项;
五、什么叫優(yōu)先級反轉(zhuǎn)和優(yōu)先級繼承事期?
大家都知道多進(jìn)程使用共享資源時(shí)都需要用到鎖滥壕,以此達(dá)到互斥效果,保證共享數(shù)據(jù)的安全兽泣。我們假設(shè)此時(shí)系統(tǒng)中有A,B,C三個(gè)進(jìn)程绎橘,其中A進(jìn)程和C進(jìn)程在運(yùn)行過程中都需要訪問同一塊共享內(nèi)存,用一個(gè)信號量控制唠倦。三個(gè)進(jìn)程的優(yōu)先級關(guān)系為Prio(A) > Prio(B) > Prio(C)称鳞。如下圖所示,剛開始C進(jìn)程在運(yùn)行時(shí)獲得了信號量稠鼻,可是還沒有執(zhí)行完時(shí)(沒有釋放信號量)冈止,進(jìn)程A在T1時(shí)刻搶占了Cpu并運(yùn)行。進(jìn)程A運(yùn)行時(shí)也需要訪問共享內(nèi)存候齿,可是信號量此時(shí)在進(jìn)程C手里熙暴,在T2時(shí)刻進(jìn)程A調(diào)用sem_wait()
讓出Cpu』哦ⅲ可是這個(gè)時(shí)候進(jìn)程C并沒有馬上得到調(diào)度周霉,優(yōu)先級比C高的進(jìn)程B獲得調(diào)度并開始執(zhí)行。T3時(shí)刻亚皂,進(jìn)程B運(yùn)行完之后才輪到進(jìn)程C運(yùn)行俱箱。T4時(shí)刻,進(jìn)程C使用完共享資源灭必,用sem_post()
讓出Cpu狞谱,進(jìn)程A最終得到信號量才繼續(xù)運(yùn)行乃摹。由此發(fā)現(xiàn),高優(yōu)先級的進(jìn)程A的調(diào)度與否反而被掌握在了控制了信號量而優(yōu)先級低它很多的進(jìn)程C手里跟衅,這種情況就叫優(yōu)先級反轉(zhuǎn)孵睬。
優(yōu)先級繼承就是為了解決優(yōu)先級反轉(zhuǎn)的問題。如下圖所示与斤,在T2時(shí)刻肪康,Linux使進(jìn)程C臨時(shí)獲得進(jìn)程A的高優(yōu)先級以保證得到調(diào)度,等進(jìn)程C使用完共享數(shù)據(jù)并釋放信號量撩穿,恢復(fù)進(jìn)程C的優(yōu)先級磷支。