全面总结:进程与线程
首先,進(jìn)程和線程的區(qū)別:
根本區(qū)別:進(jìn)程是操作系統(tǒng)資源分配的基本單位,而線程是任務(wù)調(diào)度和執(zhí)行的基本單位
在開銷方面:每個(gè)進(jìn)程都有獨(dú)立的地址空間,進(jìn)程之間的切換會(huì)有較大的開銷;線程可以看做輕量級(jí)的進(jìn)程,同一進(jìn)程內(nèi)的多個(gè)線程共享代碼和數(shù)據(jù)空間,但每個(gè)線程都有自己獨(dú)立的運(yùn)行棧和程序計(jì)數(shù)器(PC),且線程的創(chuàng)建、銷毀以及線程之間切換的開銷較小。
線程之間的通信更方便,同一進(jìn)程下的線程共享全局變量、靜態(tài)變量等數(shù)據(jù),而進(jìn)程之間的通信需要以通信的方式(IPC)進(jìn)行。
多進(jìn)程程序更健壯,多線程程序只要有一個(gè)線程死掉,整個(gè)進(jìn)程也死掉了,而一個(gè)進(jìn)程死掉并不會(huì)對(duì)另外一個(gè)進(jìn)程造成影響,因?yàn)檫M(jìn)程有自己獨(dú)立的地址空間。
?
進(jìn)程三態(tài)狀態(tài)裝換圖
?
?
?
就緒態(tài)只需要等待處理機(jī)(CPU)
阻塞態(tài)可能在等待輸入輸出,即使分配給處理機(jī)也是徒勞
調(diào)度進(jìn)程,只需要等待就緒隊(duì)列里的進(jìn)程,因?yàn)樽枞麪顟B(tài)可以轉(zhuǎn)換到就緒隊(duì)列里去
?
就緒原因:
??? 缺少cpu
?
阻塞原因
?1.內(nèi)存資源緊張
2.? 無(wú)就緒隊(duì)列,處理機(jī)空閑
3 .I/O速度比處理機(jī)速度慢的多,可能出現(xiàn)全部進(jìn)程阻塞等待I/O。
解決方法
l? 交換技術(shù):換出一部分暫時(shí)不能運(yùn)行的進(jìn)程(阻塞進(jìn)程)到外存(只換出程序和數(shù)據(jù),PCB不換出去),以騰出內(nèi)存空間,可以調(diào)用新進(jìn)程來(lái)執(zhí)行。
l? 虛擬存儲(chǔ)技術(shù):每個(gè)進(jìn)程只能裝入一部分程序和數(shù)據(jù)
?
?
掛起原因:
????? ?掛起:進(jìn)程被交換到外存,狀態(tài)變?yōu)閽炱馉顟B(tài)
?進(jìn)程掛起的原因
1.進(jìn)程全部阻塞,處理機(jī)空閑。
2.系統(tǒng)負(fù)荷過(guò)重,內(nèi)存空間緊張。(讓其他進(jìn)程先執(zhí)行完)
3.操作系統(tǒng)的需要。操作系統(tǒng)可能需要掛起后臺(tái)進(jìn)程或一些服務(wù)進(jìn)程,或者某些可能導(dǎo)致故障的進(jìn)程。
?4.終端用戶請(qǐng)求。
5父進(jìn)程的需求。
?掛起進(jìn)程的特征
l? 不能立即執(zhí)行
l? 可能是等待某事件發(fā)生,若是,則阻塞條件獨(dú)立于掛起條件,即使阻塞事件發(fā)生,該進(jìn)程也不能執(zhí)行。
n? 阻塞和掛起沒(méi)有聯(lián)系。
n? 如果A進(jìn)程現(xiàn)在要求輸入數(shù)據(jù),此時(shí)A進(jìn)程屬于阻塞狀態(tài),在選擇掛起進(jìn)程的時(shí)候,可能先選擇阻塞進(jìn)程(A進(jìn)程),此時(shí)A進(jìn)程掛起,正在輸入數(shù)據(jù),輸入到內(nèi)存緩沖區(qū)內(nèi)。當(dāng)數(shù)據(jù)輸完了,向處理機(jī)發(fā)送命令數(shù)據(jù)已經(jīng)輸入完成,阻塞事件解除,但實(shí)際上還是掛起,所以仍是掛起態(tài)。
l? 使之掛起的進(jìn)程為:自身、父進(jìn)程、OS。
l? 只有掛起它的進(jìn)程才能使之由掛起狀態(tài)轉(zhuǎn)換為其他狀態(tài)。
?
? 阻塞與掛起
l? 進(jìn)程是否等待時(shí)間:阻塞與否。
l? 進(jìn)程是否被換出內(nèi)存:掛起與否。
?
4.4? 四種狀態(tài)組合
l? 就緒:進(jìn)程在內(nèi)存,準(zhǔn)備執(zhí)行。
l? 阻塞:進(jìn)程在內(nèi)存,等待事件。
l? 就緒/掛起:進(jìn)程在外存,只要調(diào)入內(nèi)存即可執(zhí)行。
l? 阻塞/掛起:進(jìn)程在外存,等待事件。
?
? 處理機(jī)可調(diào)度執(zhí)行的進(jìn)程有兩種
l? 新創(chuàng)建的進(jìn)程
l? 或換入一個(gè)以前掛起的進(jìn)程
通常為避免增加系統(tǒng)負(fù)載,系統(tǒng)會(huì)換入一個(gè)以前掛起的進(jìn)程執(zhí)行。
?
4.6? 具有掛起狀態(tài)的進(jìn)程狀態(tài)轉(zhuǎn)換
?
l? 阻塞 → 阻塞/掛起:OS通常將阻塞進(jìn)程換出,以騰出內(nèi)存空間
l? 阻塞/掛起→ 就緒/掛起:當(dāng)阻塞/掛起進(jìn)程等待的事件發(fā)生時(shí),可以將其轉(zhuǎn)換為就緒/掛起。
l? 就緒/掛起→ 就緒:OS需要調(diào)入一個(gè)進(jìn)程執(zhí)行。
l? 就緒 → 就緒/掛起:一般,OS掛起阻塞進(jìn)程。但是有時(shí)也會(huì)掛起就緒進(jìn)程,釋放足夠的內(nèi)存空間。
l? 新 → 就緒/掛起(新→ 就緒):新進(jìn)程創(chuàng)建后,可以插入到就緒隊(duì)列或就緒,掛起隊(duì)列,若無(wú)足夠的內(nèi)存分配給新進(jìn)程,則需要新→ 就緒/掛起。
----------------------------------------------------------------------------------------------------------------------------------------------------------
所以,我們必須知道,進(jìn)程為何掛起?因?yàn)閮?nèi)存空間資源緊張,所以要把阻塞或者就緒的進(jìn)程從內(nèi)存切換至外存,釋放內(nèi)存空間;所以說(shuō)阻塞或者就緒的進(jìn)程是在內(nèi)存中的,只是沒(méi)有占用CPU的資源;
還有一個(gè)問(wèn)題就是,我們知道,進(jìn)程一般是自己阻塞自己,等待某個(gè)時(shí)間的發(fā)生;當(dāng)某個(gè)事件發(fā)生時(shí),進(jìn)程只能由其他進(jìn)程或者OS喚醒,這是為什么?顯然,進(jìn)程阻塞它就停止運(yùn)行,釋放處理機(jī)資源了,就是說(shuō)它自己和睡著了差不多,你怎么讓一個(gè)睡著的人自己叫醒自己呢?很好理解。
----------------------------------------------------------------------------------------------------------------------------------------------------------
接下來(lái),我們來(lái)看一下進(jìn)程的創(chuàng)建、執(zhí)行、等待、終止過(guò)程
具體的流程如下。
我們知道,操作系統(tǒng)通過(guò)fork()調(diào)用來(lái)創(chuàng)建子進(jìn)程。系統(tǒng)最初的初始進(jìn)程(或者稱為root進(jìn)程)為init進(jìn)程。
當(dāng)我們創(chuàng)建子進(jìn)程之后涉及到父子進(jìn)程之間的進(jìn)程地址空間的復(fù)制,一般來(lái)說(shuō),我們會(huì)在子進(jìn)程創(chuàng)建的時(shí)候?qū)⒏高M(jìn)程的地址空間包括地址空間里的代碼、數(shù)據(jù)和堆棧等等資源全部拷貝一份給子進(jìn)程,添加修改頁(yè)表項(xiàng)。當(dāng)子進(jìn)程在通過(guò)exec()系統(tǒng)調(diào)用執(zhí)行的時(shí)候,會(huì)去打開新的代碼文件,將原始的父進(jìn)程拷貝過(guò)來(lái)的內(nèi)容覆蓋掉,執(zhí)行新的內(nèi)容。這就造成一個(gè)問(wèn)題,什么問(wèn)題?既然子進(jìn)程對(duì)于父進(jìn)程的內(nèi)容沒(méi)有執(zhí)行意義,而且拷貝會(huì)占用系統(tǒng)資源,那何必要進(jìn)行創(chuàng)建時(shí)的拷貝工作呢?
所以,我們引入一種COPY ON WRITE 技術(shù)(寫時(shí)覆蓋),就是在創(chuàng)建的時(shí)候僅僅修改頁(yè)表項(xiàng),拷貝所需的原始數(shù)據(jù)資源。當(dāng)我們的子進(jìn)程在執(zhí)行的時(shí)候需要進(jìn)行寫操作的時(shí)候,才進(jìn)行相應(yīng)的拷貝和覆蓋。因?yàn)槿绻舆M(jìn)程僅僅只是讀取操作的話,完全沒(méi)有拷貝的需要,直接讀取父進(jìn)程地址空間的內(nèi)容即可。
當(dāng)進(jìn)程調(diào)用exec()的時(shí)候不一定是立刻運(yùn)行的,理論上是進(jìn)行running。但是由于會(huì)進(jìn)行磁盤文件數(shù)據(jù)的換入操作,所以當(dāng)前進(jìn)程還是會(huì)阻塞,等待換入完成則就緒---》執(zhí)行。
進(jìn)程的等待:當(dāng)子進(jìn)程通過(guò)exit()退出的時(shí)候,系統(tǒng)會(huì)釋放掉其用戶空間的資源,但是其存在于內(nèi)核中的資源(例如PCB)卻沒(méi)有回收(因?yàn)橄到y(tǒng)內(nèi)核通過(guò)進(jìn)程的PCB來(lái)對(duì)相應(yīng)的進(jìn)程做出處理,PCB存在于內(nèi)核空間的相應(yīng)的進(jìn)程執(zhí)行隊(duì)列)。
那么怎么辦呢?那肯定是通過(guò)其父進(jìn)程來(lái)進(jìn)行回收。因此父進(jìn)程在其子進(jìn)程通過(guò)exit()退出后,通過(guò)調(diào)用wait()來(lái)得到子進(jìn)程的終止?fàn)顟B(tài)信息,然后進(jìn)行子進(jìn)程PCB等資源的相應(yīng)回收處理。
在子進(jìn)程調(diào)用exit()到父進(jìn)程調(diào)用wait()之間的狀態(tài),稱作“”僵尸狀態(tài)“”。
還有一種情況就是,當(dāng)子進(jìn)程終止時(shí),其父進(jìn)程也終止了,那么由于系統(tǒng)的init進(jìn)程會(huì)定期掃描系統(tǒng)的進(jìn)程表來(lái)篩選出僵尸進(jìn)程,并做相應(yīng)的回收處理。
考慮當(dāng)父進(jìn)程終止,但其子進(jìn)程還未終止的情況。此時(shí)子進(jìn)程稱為“”孤兒進(jìn)程“”,由init進(jìn)程收養(yǎng)。
-------------------------------------------------------------------進(jìn)程同步互斥--------------------------------------------------------------------
? ?補(bǔ)充一個(gè)坑爹坑爹坑爹坑爹的問(wèn)題:系統(tǒng)如何將一個(gè)信號(hào)通知到進(jìn)程?
? ? ?前面講過(guò)信號(hào)了,為什么又問(wèn)一遍?這個(gè)問(wèn)題有什么特殊的地方么??
? ? ?內(nèi)核給進(jìn)程發(fā)送信號(hào),是在進(jìn)程所在的進(jìn)程表項(xiàng)的信號(hào)域設(shè)置對(duì)應(yīng)的信號(hào)的位。
? ? ? ? 更多:http://blog.csdn.net/joejames/article/details/37960873?
?
-------------------------------------------------------------------線程同步互斥--------------------------------------------------------------------
linux系統(tǒng)的通訊機(jī)制,主要是指進(jìn)程間通訊,其實(shí)通訊就是進(jìn)程同步的手段。
????? 這里要說(shuō)的linux系統(tǒng)的同步機(jī)制是講線程間的同步。?
互斥量?
首先是最基礎(chǔ)的加鎖原語(yǔ),互斥量。既確保同一時(shí)間只有一個(gè)線程訪問(wèn)數(shù)據(jù),通過(guò)在訪問(wèn)共享資源前對(duì)互斥量加鎖,阻塞其他試圖再次加鎖的線程知道互斥鎖被釋放。互斥的具體實(shí)現(xiàn)有多種方法,例如開關(guān)中斷,使用原子的機(jī)器指令。?
讀寫鎖?
與互斥量類似,不過(guò)允許更高的并行性。讀寫鎖有三種狀態(tài),讀模式的加鎖,寫模式的加鎖,不加鎖狀態(tài)。一次只有一個(gè)線程可以占有寫模式的讀寫鎖,但是可以多個(gè)線程可以同時(shí)占用讀模式的讀寫鎖。既讀模式下可以共享,寫模式下互斥。一般一個(gè)線程試圖以讀模式獲取鎖時(shí),讀寫鎖通常會(huì)阻塞隨后的讀模式鎖請(qǐng)求。?
條件變量?
互斥量是加鎖原語(yǔ),條件變量屬于等待原語(yǔ),用于等待某個(gè)條件成立后喚醒阻塞的線程。條件變量與互斥量一起使用,條件本身由互斥量保護(hù)。Java Object內(nèi)置了條件變量wait(),notify(),notifyAll()。 ?
pthread_cond_wait(),pthread_cond_signal(),pthread_cond_broadcast(Unix),從函數(shù)的命名就可以看出其大致作用。?
根據(jù)陳碩的總結(jié),條件變量的正確使用方式:?
對(duì)于wait端:?
1.必須與mutex一起使用。?
2.在mutex已上鎖時(shí)才能調(diào)用wait()。?
3.把判斷布爾條件和wait()放到while循環(huán)中。?
第三個(gè)條件主要是為了防止spurious wakeup,既虛假喚醒。因?yàn)閜thread_conf_wait能被除了pthread_cond_signal(),pthread_cond_broadcast外的其他信號(hào)喚醒。需要再wait后再次檢查,同時(shí)也是為了避免錯(cuò)過(guò)一次條件變量后永遠(yuǎn)的等待下去。?
對(duì)于signal端:?
1.一定不要在mutex已經(jīng)上鎖的情況下調(diào)用signal。?
2.在signal之前一般要修改布爾表達(dá)式。?
3.修改布爾表達(dá)式通常用mutex保護(hù)。?
4.注意區(qū)分signal和broadcast:“broadcast通常用于表明狀態(tài)變化,signal通常用于表示資源可用”。?
自旋鎖?
自旋鎖與互斥量類似,但它不是通過(guò)休眠使進(jìn)程阻塞,二是在獲取鎖之前一直處于忙等。既一直占用CPU資源直到鎖被釋放。?
屏障?
屏障主要用于多個(gè)線程之間的并行工作的協(xié)調(diào)。屏障允許每個(gè)線程等待,直到所有的合作線程都達(dá)到某個(gè)點(diǎn),然后從該點(diǎn)繼續(xù)執(zhí)行。?
信號(hào)量?
這個(gè)在《unix環(huán)境高級(jí)編程》中沒(méi)有提及,在《操作系統(tǒng)》中有論述。?
信號(hào)量可作用與進(jìn)程間合作,以及多線程的同步。?
一個(gè)進(jìn)程可以被迫在某一個(gè)位置停止,直到接收到某一個(gè)信號(hào)。為了發(fā)信號(hào),需要使用一個(gè)稱為信號(hào)量的特殊變量,可以看做一個(gè)具有整數(shù)值得變量。其中只允許信號(hào)量取0和1的稱為二元信號(hào)量。非二元信號(hào)量常稱為計(jì)數(shù)信號(hào)量或一般信號(hào)量。?
一般在信號(hào)量上定義三個(gè)操作:?
1.一個(gè)信號(hào)量可以初始化成非負(fù)數(shù)。?
2.semWait操作使信號(hào)量減1。如果值變?yōu)樨?fù)數(shù),則執(zhí)行semWait的進(jìn)程或線程被阻塞,否則繼續(xù)執(zhí)行。?
3.semSignal操作使信號(hào)量加1。如果值<=0,則被semWait阻塞的進(jìn)程被解除阻塞。?
信號(hào)量需要隊(duì)列保存阻塞在信號(hào)量上等待的進(jìn)程。至于進(jìn)程按什么順序移除,最公平的是先進(jìn)先出,采用此策略的為強(qiáng)信號(hào)量。沒(méi)有規(guī)定順序的為弱信號(hào)量。?
互斥量和二元信號(hào)量的主要區(qū)別在于互斥量的加鎖和解鎖必須由同一線程分別對(duì)應(yīng)使用,信號(hào)量可以由一個(gè)線程釋放,另一個(gè)線程得到。至于用于互斥和用于同步的說(shuō)法,十分牽強(qiáng)。?
陳碩關(guān)于信號(hào)量的建議是不用。?
因?yàn)榭捎脳l件變量加互斥量完全代替,另外還需要擔(dān)心計(jì)數(shù)值需要和自己的數(shù)據(jù)長(zhǎng)度常常保持一致的問(wèn)題。?
死鎖?
死鎖大概已經(jīng)被講爛了,我也不想再搬運(yùn)了。堅(jiān)持使用Scoped Locking,死鎖的時(shí)候好檢測(cè)。?
---------------------------------------------------------------多線程和多進(jìn)程的區(qū)別---------------------------------------------------------------------
多線程和多進(jìn)程的區(qū)別(重點(diǎn) 必須從cpu調(diào)度,上下文切換,數(shù)據(jù)共享,多核cup利用率,資源占用,等等各方面回答,然后有一個(gè)問(wèn)題必須會(huì)被問(wèn)到:哪些東西是一個(gè)線程私有的?答案中必須包含寄存器,否則悲催)
區(qū)別的意思是優(yōu)缺點(diǎn)吧。?
?
多線程:
- 高效的內(nèi)存共享,數(shù)據(jù)共享
- 較輕的上下文切換開銷,不用切換地址空間,不用更改CR3寄存器,不用清空TLB。
-
創(chuàng)建銷毀切換比較簡(jiǎn)單
多進(jìn)程:
-
更強(qiáng)的容錯(cuò)性,不會(huì)一阻全阻,一個(gè)進(jìn)程崩潰不會(huì)整個(gè)系統(tǒng)崩潰。
-
更好的多核伸縮性,進(jìn)程的使用將許多內(nèi)核資源(如地址空間,頁(yè)表,打開的文件)隔離,在多核系統(tǒng)上的可伸縮性強(qiáng)于多線程程序
在多核利用率上,多進(jìn)程和多線程同樣可以提高多核利用率。?
其實(shí)對(duì)于創(chuàng)建和銷毀,上下文切換,其實(shí)在Linux系統(tǒng)下差別不大,Window下有較大差別。?
綜上,多進(jìn)程和多線程的最主要的區(qū)別就在資源共享,隔離問(wèn)題。如果工作使用的內(nèi)存較大,使用多線程可以避免CPU cache的換入換出,影響性能。線程私有
-
ID,每個(gè)線程都有自己的ID作為進(jìn)程中唯一的表述。
- 一組寄存器值,用來(lái)保存狀態(tài)
- 各自的堆棧
- 錯(cuò)誤返回碼,防止錯(cuò)誤還未被處理就被其他線程修改。
- 信號(hào)屏蔽碼,每個(gè)線程感興趣的信號(hào)不同。
- 優(yōu)先級(jí)
- 共享的:進(jìn)程的代碼段,公有數(shù)據(jù),進(jìn)程打開的文件描述符,全局內(nèi)存,進(jìn)程的棧,堆內(nèi)存等。
總結(jié)
以上是生活随笔為你收集整理的全面总结:进程与线程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 3.MYSQL函数创建和使用
- 下一篇: 请求分页系统中页面分配策略与页面置换策略