進程和線程
進程
進程是計算機中已運行程序的實體,一個正在運行的程序就是一個進程。
進程模型
每個進程擁有自己的程序計數(shù)器勾笆、寄存器和變量等,操作系統(tǒng)會用一張表記錄下每個進程中的信息卵渴。
就單核 CPU 而言:在某個時間點中,并不是所有進程都占用 CPU 資源,而是在 CPU 的高速切換中,每個進程都被 CPU 所運行按脚。
如果某個時間點只有一個進程運行,其他的進程的變量等信息就會被操作系統(tǒng)使用一張表來記錄敦冬,以便下一次 CPU 運算時調(diào)用辅搬,保持每次的結(jié)果相等,這張表稱為進程表匪补。
也就是說伞辛,每個進程在退出 CPU 調(diào)用的時候會把這進程表更新烂翰,以備下一次進入 CPU 時夯缺,數(shù)據(jù)的同步。
進程的狀態(tài)
進程有三種狀態(tài):
運行態(tài)(這個時刻進程實際占用 CPU 資源)
就緒態(tài)(可以運行甘耿,但是因為其他的進程正在運行而暫時停止)
阻塞態(tài)(除非某種外部事件發(fā)生踊兜,否則進程不能運行)
這三種狀態(tài)可以互相切換:
- 運行態(tài) >> 阻塞態(tài)
操作系統(tǒng)發(fā)現(xiàn)進程無法運行下去的時候就會發(fā)生這個轉(zhuǎn)換。比如:當(dāng)一個進程從 I/O 設(shè)備讀寫文數(shù)據(jù)的時候佳恬,如果一直沒有完成捏境,那么就會進入阻塞狀態(tài)于游。
- 運行態(tài) >> 就緒態(tài)
這個轉(zhuǎn)換是由于進程調(diào)度程序引起的,進程調(diào)度程序是操作系統(tǒng)的一部分垫言,但是進程感覺不到進程調(diào)度程序的存在贰剥。因為系統(tǒng)認為一個進程占用 CPU 時間過長,那么就會讓其進程退出筷频,使其他的進程使用 CPU 的時間蚌成。也就是從運行態(tài)轉(zhuǎn)換到了就緒態(tài)。
- 就緒態(tài) >> 運行態(tài)
這個轉(zhuǎn)換是上一個轉(zhuǎn)換的逆向凛捏,原理相同担忧,都是因為公平的原則,讓所有的進程公平起見坯癣,占用太久的進程需要使用 CPU 時間瓶盛,而讓就緒態(tài)進程轉(zhuǎn)換到運行態(tài)。
- 阻塞態(tài) >> 就緒態(tài)
比如:當(dāng)一個進程從 I/O 設(shè)備讀寫數(shù)據(jù)完畢之前是處于阻塞狀態(tài)示罗,而讀寫完畢之后就轉(zhuǎn)變?yōu)榱司途w態(tài)惩猫。如果這個時候 CPU 沒有其他需要運行的進程,那么就會發(fā)生從就緒態(tài)轉(zhuǎn)變?yōu)檫\行態(tài)蚜点。
線程
一個進程最少有一個線程帆锋,也可以有多個線程。線程有的時候與進程類似禽额。假如進程是管家锯厢,那么線程就是苦力,要賣力的為進程干活兒脯倒。
線程像一種輕量級的進程实辑,它比進程更容易創(chuàng)建,也更容易撤銷藻丢。有時候剪撬,創(chuàng)建一個進程比創(chuàng)建一個線程多一到兩個數(shù)量級。所以悠反,這就體現(xiàn)出了線程的必要性残黑。
假設(shè)你在一個文字處理軟件中編寫文字,當(dāng)你覺得寫的文字足夠多以至于需要保存了斋否,如果這個文字處理軟件是一個單線程進程梨水,那么在你保存的同時,你就不能做其他的事情茵臭,比如繼續(xù)編寫文字疫诽。或者發(fā)生了另外一種情況:這個文字處理軟件開發(fā)者覺得這個軟件不夠好,對其做了改進奇徒,現(xiàn)在這個軟件可以自動保存了雏亚。
這樣就不需要擔(dān)心編輯很多內(nèi)容突然斷電導(dǎo)致災(zāi)難性的后果。
第二天你還是生氣的對軟件開發(fā)者說道摩钙,在我輸入十幾個字之后電腦就動不了了罢低!
在單線程的進程中,程序只能做一件事胖笛,也就是說奕短,按照上面的例子,文字編輯器添加了自動保存的功能匀钧,在保存的同時翎碑,用戶不能夠?qū)ζ漭斎耄荒軌虻却4嫱瓿芍罄^續(xù)輸入之斯。
再舉一個例子:
老劉正在坐著清閑的看著報紙日杈,突然接到電話,說他的孫女發(fā)燒了需要照顧佑刷,正在他在趕路去孫女家的時候莉擒,朋友老李打電話讓他出去喝茶但是他不得不照顧完孫女再去喝茶。
老劉就類似于一個線程瘫絮,而他接到電話要去照顧孫女的時候涨冀,他就不能答應(yīng)老李的邀請。
要是老劉能分身的話麦萤,該多好鹿鳖!
事實上,老劉不能分身壮莹,而計算機中翅帜,可以使用多線程來完成任務(wù)。
多線程
上面所說命满,一個進程至少有一個線程涝滴,也可以有多個線程。為什么我們不使用多個進程合作而使用多個線程合作呢胶台?
試想:前面的文字處理軟件歼疮,我們可以再開啟一個進程來為軟件保存內(nèi)容,可是诈唬,這樣能做到嗎韩脏?
一個進程可以認為自己獨占了內(nèi)存,是看不見其他進程的內(nèi)存讯榕。那么上面的想法骤素,還可行嗎匙睹?
使用線程就能完美的解決這個問題了愚屁,因為多個線程是可以共享一個進程里的資源济竹。
這樣那個文字處理軟件開發(fā)者可以使用多個線程共同合作來完成任務(wù):一個線程用來更新顯示,一個線程用來保存霎槐,一個線程用來監(jiān)聽鍵盤鼠標......
我們可以稱用來保存的那個線程為守護線程送浊。
經(jīng)典的線程模型
線程的實現(xiàn)有 3 種模型:
- 多對一(M:1)的用戶級線程模型
- 一對一(1:1)的內(nèi)核級線程模型
- 多對多(M:N)的兩級線程模型
用戶級線程模型:
在這個模型中,線程的創(chuàng)建丘跌、調(diào)度袭景、同步等所有的細節(jié)全部由進程的用戶空間線程包來處理,這樣就不需要內(nèi)核來接管這些操作闭树,這種模型可以在不支持線程的操作系統(tǒng)上實現(xiàn)耸棒。
在用戶空間管理線程的時候,每個進程就需要一張線程表报辱,用來記錄與跟蹤進程中的線程与殃,這張表與進程表類似, 但是線程表僅僅記錄各個線程的屬性碍现。比如:每個線程的程序計數(shù)器幅疼、堆棧指針、寄存器昼接、狀態(tài)等等爽篷。
用戶級線程有一個優(yōu)點,就是允許每個進程有自己定制的調(diào)度算法慢睡。例如逐工,在某個應(yīng)用程序中有些垃圾收集線程的應(yīng)用就不需要擔(dān)心在不適合的時候停止導(dǎo)致食物。
用戶級線程有一個問題:如果一個線程開始執(zhí)行漂辐,那么該進程中的其他線程就不能執(zhí)行钻弄,當(dāng)這個運行的線程被阻塞了,那么操作系統(tǒng)將認為整個進程都是阻塞住了者吁,進而該進程中的其他線程無法調(diào)度窘俺。
線程與進程類似,但是也有不同之處:
a. 線程沒有時鐘中斷
b. 線程可以調(diào)用 yield 函數(shù)复凳,在執(zhí)行這個函數(shù)之后就可以調(diào)用線程調(diào)度程序來選擇另外一個需要運行的線程瘤泪。而進程不可以。不同的進程所擁有的資源都是相對獨立的育八,所以不同進程之間是競爭關(guān)系对途,而線程之間是相互合作用來完成某個任務(wù)而存在,是合作關(guān)系髓棋。所以進程沒有那么高尚实檀,將自己寶貴的時間片讓給其他進程惶洲。
內(nèi)核級線程:
在這個模型中,進程中就不需要線程表了膳犹,也沒有線程表恬吕。但是须床,內(nèi)核中有用來記錄系統(tǒng)中所有線程的線程表铐料。內(nèi)核中的線程表與上述用戶級線程表記錄的信息是一樣的。
當(dāng)一個進程的線程阻塞住了豺旬,內(nèi)核可以選擇是讓其阻塞住線程的進程中其他線程執(zhí)行或者是讓其他進程中的線程執(zhí)行。
在內(nèi)核中創(chuàng)建與銷毀線程的開銷相對來說是比較大的。
混合實現(xiàn):
用戶級線程與內(nèi)核級線程都有各自的優(yōu)點與缺點愧沟,人們研究了許多鍵這兩種優(yōu)點結(jié)合起來的方法央渣。
有一種方法就是,使用內(nèi)核級線程卜朗,然后將用戶級線程與謳歌或者全部內(nèi)核級線程多路復(fù)用起來拔第,這樣,內(nèi)核只識別內(nèi)核級線程场钉,并對其進行調(diào)度蚊俺,其中一些內(nèi)核級線程會被多個用戶級線程多路復(fù)用。這樣每個內(nèi)河集線程有一個輪流復(fù)用的用戶級線程集合逛万。