進(jìn)程與線程
- 概念
- 進(jìn)程:
對運行時程序的封裝,是系統(tǒng)進(jìn)行資源調(diào)度和分配的的基本單位,實現(xiàn)了操作系統(tǒng)的并發(fā);- 線程:
進(jìn)程的子任務(wù),是CPU調(diào)度和分派的基本單位,實現(xiàn)進(jìn)程內(nèi)部的并發(fā);
線程是操作系統(tǒng)可識別的最小執(zhí)行和調(diào)度單位。
每個線程都獨自占用一個虛擬處理器:獨自的寄存器組,指令計數(shù)器和處理器狀態(tài)。每個線程完成不同的任務(wù),但是共享同一地址空間(也就是同樣的動態(tài)內(nèi)存,映射文件,目標(biāo)代碼等等),打開的文件隊列和其他內(nèi)核資源。
- 區(qū)別
- 擁有資源
進(jìn)程是資源分配的基本單位,但是線程不擁有資源,線程可以訪問隸屬進(jìn)程的資源。- 調(diào)度
線程是獨立調(diào)度的基本單位,在同一進(jìn)程中,線程的切換不會引起進(jìn)程切換,從一個進(jìn)程中的線程切換到另一個進(jìn)程中的線程時,會引起進(jìn)程切換。- 系統(tǒng)開銷
由于創(chuàng)建或撤銷進(jìn)程時,系統(tǒng)都要為之分配或回收資源,如內(nèi)存空間、I/O 設(shè)備等,所付出的開銷遠(yuǎn)大于創(chuàng)建或撤銷線程時的開銷。類似地,在進(jìn)行進(jìn)程切換時,涉及當(dāng)前執(zhí)行進(jìn)程 CPU 環(huán)境的保存及新調(diào)度進(jìn)程 CPU 環(huán)境的設(shè)置,而線程切換時只需保存和設(shè)置少量寄存器內(nèi)容,開銷很小。- 通信方面
線程間可以通過直接讀寫同一進(jìn)程中的數(shù)據(jù)進(jìn)行通信,但是進(jìn)程通信需要借助 IPC。
- 進(jìn)程間通信IPC
- 管道
- 消息隊列
- 信號量
- 信號
- 共享內(nèi)存
- 套接字SOCKET
- 線程間的同步方式(線程鎖)
- 信號量
它只取自然數(shù)值,并且只支持兩種操作:
P(SV): 如果信號量SV大于0,將它減一;如果SV值為0,則掛起該線程。
V(SV): 如果有其他進(jìn)程因為等待SV而掛起,則喚醒,然后將SV+1;否則直接將SV+1。- 互斥量
互斥量又稱互斥鎖,主要用于線程互斥,不能保證按序訪問,可以和條件鎖一起實現(xiàn)同步。當(dāng)進(jìn)入臨界區(qū) 時,需要獲得互斥鎖并且加鎖;當(dāng)離開臨界區(qū)時,需要對互斥鎖解鎖,以喚醒其他等待該互斥鎖的線程。- 條件變量
條件變量,又稱條件鎖,用于在線程之間同步共享數(shù)據(jù)的值。條件變量提供一種線程間通信機(jī)制:當(dāng)某個共享數(shù)據(jù)達(dá)到某個值時,喚醒等待這個共享數(shù)據(jù)的一個/多個線程。
- 線程間通信
- 使用全局變量
主要由于多個線程可能更改全局變量,因此全局變量最好聲明為volatile- 使用消息實現(xiàn)通信
在Windows程序設(shè)計中,每一個線程都可以擁有自己的消息隊列(UI線程默認(rèn)自帶消息隊列和消息循環(huán),工作線程需要手動實現(xiàn)消息循環(huán)),因此可以采用消息進(jìn)行線程間通信sendMessage,postMessage。- 使用事件CEvent類實現(xiàn)線程間通信
Event對象有兩種狀態(tài):有信號和無信號,線程可以監(jiān)視處于有信號狀態(tài)的事件,以便在適當(dāng)?shù)臅r候執(zhí)行對事件的操作。
- 并發(fā)與并行
- 并發(fā):
指宏觀上看起來兩個程序在同時運行,比如說在單核cpu上的多任務(wù)。但是從微觀上看兩個程序的指令是交織著運行的,你的指令之間穿插著我的指令,我的指令之間穿插著你的,在單個周期內(nèi)只運行了一個指令。這種并發(fā)并不能提高計算機(jī)的性能,只能提高效率。- 并行:
指嚴(yán)格物理意義上的同時運行,比如多核cpu,兩個程序分別運行在兩個核上,兩者之間互不影響,單個周期內(nèi)每個程序都運行了自己的指令,也就是運行了兩條指令。這樣說來并行的確提高了計算機(jī)的效率。所以現(xiàn)在的cpu都是往多核方面發(fā)展。
- 進(jìn)程狀態(tài)轉(zhuǎn)換圖
- 5個基本狀態(tài)
創(chuàng)建狀態(tài):進(jìn)程正在被創(chuàng)建。
就緒狀態(tài):進(jìn)程被加入到就緒隊列中等待CPU調(diào)度運行。
執(zhí)行狀態(tài):進(jìn)程正在被運行。
等待阻塞狀態(tài):進(jìn)程因為某種原因,比如等待I/O,等待設(shè)備,而暫時不能運行。
終止?fàn)顟B(tài):進(jìn)程運行完畢。- 交換技術(shù)
當(dāng)多個進(jìn)程競爭內(nèi)存資源時,會造成內(nèi)存資源緊張,并且,如果此時沒有就緒進(jìn)程,處理機(jī)會空閑,I/0速度比處理機(jī)速度慢得多,可能出現(xiàn)全部進(jìn)程阻塞等待I/O。
- 交換技術(shù):換出一部分進(jìn)程到外存,騰出內(nèi)存空間。
在交換技術(shù)上,將內(nèi)存暫時不能運行的進(jìn)程,或者暫時不用的數(shù)據(jù)和程序,換出到外存,來騰出足夠的內(nèi)存空間,把已經(jīng)具備運行條件的進(jìn)程,或進(jìn)程所需的數(shù)據(jù)和程序換入到內(nèi)存。從而出現(xiàn)了進(jìn)程的掛起狀態(tài):進(jìn)程被交換到外存,進(jìn)程狀態(tài)就成為了掛起狀態(tài)。
- 虛擬存儲技術(shù):每個進(jìn)程只能裝入一部分程序和數(shù)據(jù)。
- 進(jìn)程調(diào)度
- 批處理系統(tǒng)
保證吞吐量和周轉(zhuǎn)時間(從提交到終止的時間)
- 先來先服務(wù) first-come first-serverd(FCFS)
非搶占式的調(diào)度算法,按照請求的順序進(jìn)行調(diào)度。
有利于長作業(yè),但不利于短作業(yè),因為短作業(yè)必須一直等待前面的長作業(yè)執(zhí)行完畢才能執(zhí)行,而長作業(yè)又需要執(zhí)行很長時間,造成了短作業(yè)等待時間過長。- 短作業(yè)優(yōu)先 shortest job first(SJF)
非搶占式的調(diào)度算法,按估計運行時間最短的順序進(jìn)行調(diào)度。
長作業(yè)有可能會餓死,處于一直等待短作業(yè)執(zhí)行完畢的狀態(tài)。因為如果一直有短作業(yè)到來,那么長作業(yè)永遠(yuǎn)得不到調(diào)度。- 最短剩余時間優(yōu)先 shortest remaining time next(SRTN)
最短作業(yè)優(yōu)先的搶占式版本,按剩余運行時間的順序進(jìn)行調(diào)度。 當(dāng)一個新的作業(yè)到達(dá)時,其整個運行時間與當(dāng)前進(jìn)程的剩余時間作比較。如果新的進(jìn)程需要的時間更少,則掛起當(dāng)前進(jìn)程,運行新的進(jìn)程。否則新的進(jìn)程等待。
- 交互式系統(tǒng)
快速地進(jìn)行響應(yīng)
- 時間片輪轉(zhuǎn)
將所有就緒進(jìn)程按 FCFS 的原則排成一個隊列,每次調(diào)度時,把 CPU 時間分配給隊首進(jìn)程,該進(jìn)程可以執(zhí)行一個時間片。當(dāng)時間片用完時,由計時器發(fā)出時鐘中斷,調(diào)度程序便停止該進(jìn)程的執(zhí)行,并將它送往就緒隊列的末尾,同時繼續(xù)把 CPU 時間分配給隊首的進(jìn)程。- 優(yōu)先級調(diào)度
為每個進(jìn)程分配一個優(yōu)先級,按優(yōu)先級進(jìn)行調(diào)度。
為了防止低優(yōu)先級的進(jìn)程永遠(yuǎn)等不到調(diào)度,可以隨著時間的推移增加等待進(jìn)程的優(yōu)先級。- 多級反饋隊列
多級隊列是為這種需要連續(xù)執(zhí)行多個時間片的進(jìn)程考慮,它設(shè)置了多個隊列,每個隊列時間片大小都不同,例如 1,2,4,8,..。進(jìn)程在第一個隊列沒執(zhí)行完,就會被移到下一個隊列。這種方式下,之前的進(jìn)程只需要交換 7 次。
每個隊列優(yōu)先權(quán)也不同,最上面的優(yōu)先權(quán)最高。因此只有上一個隊列沒有進(jìn)程在排隊,才能調(diào)度當(dāng)前隊列上的進(jìn)程。
- 進(jìn)程的內(nèi)存空間
進(jìn)程的內(nèi)存模型
進(jìn)程的內(nèi)存模型- 堆和棧的區(qū)別
- 大小限制:
棧底的地址和棧的最大容量是系統(tǒng)預(yù)先規(guī)定好的(2M/1M,總之是一個編譯時就確定的常數(shù)),如果申請的空間超過棧的剩余空間, stack overflow。因此,能從棧獲得的空間較小。堆是用鏈表來存儲的不連續(xù)內(nèi)存區(qū)域,大小受限于計算機(jī)系統(tǒng)中有效的虛擬內(nèi)存。由此可見,堆獲得的空間比較靈活,也比較大。- 申請效率:
棧由系統(tǒng)自動分配,速度較快。但程序員是無法控制的。堆是由new分配的內(nèi)存,一般速度比較慢,而且容易產(chǎn)生內(nèi)存碎片,不過用起來最方便.- 存儲內(nèi)容:
棧存儲返回地址,參數(shù),局部變量。堆在這塊內(nèi)存空間中的首地址處記錄本次分配的大小,具體內(nèi)容由程序員安排。- 數(shù)據(jù)訪問:
存儲在堆中的對象是全局可以被訪問的,然而棧內(nèi)存不能被其他線程所訪問,且遵循LIFO原則。
- 生命周期:
棧內(nèi)存的生命周期很短,而堆內(nèi)存的生命周期從程序的運行開始到運行結(jié)束。
- 進(jìn)程關(guān)系
進(jìn)程通常不是憑空獨立的出現(xiàn)的,在類Unix系統(tǒng)中,所有的其他進(jìn)程都是從 進(jìn)程0 fork 出來的,每個進(jìn)程都會擁有多個子進(jìn)程。
- fork()
- fork給父進(jìn)程返回子進(jìn)程pid,給其拷貝出來的子進(jìn)程返回0,
- 這也是他的特點之一,一次調(diào)用,兩次返回。
- 實質(zhì)是在子進(jìn)程的棧中構(gòu)造好數(shù)據(jù)后,子進(jìn)程從棧中獲取到的返回值。
由于現(xiàn)代操作系統(tǒng)的寫時復(fù)制機(jī)制,即使我們知道每個進(jìn)程都擁有自己獨立的地址空間,其實子進(jìn)程指向的物理內(nèi)存是和父進(jìn)程相同的(代碼段,數(shù)據(jù)段,堆棧都指向父親的物理空間),只有子進(jìn)程修改了其中的某個值時(通常會先調(diào)度運行子進(jìn)程),才會給子進(jìn)程分配新的物理內(nèi)存,并根據(jù)情況把新的值或原來的值復(fù)制給子進(jìn)程的內(nèi)存。
- 孤兒進(jìn)程
- 一個父進(jìn)程退出,而它的一個或多個子進(jìn)程還在運行,那么這些子進(jìn)程將成為孤兒進(jìn)程。
- 孤兒進(jìn)程將被 init 進(jìn)程(進(jìn)程號為 1)所收養(yǎng),并由 init 進(jìn)程對它們完成狀態(tài)收集工作。
- 由于孤兒進(jìn)程會被 init 進(jìn)程收養(yǎng),所以孤兒進(jìn)程不會對系統(tǒng)造成危害。
- 僵尸進(jìn)程
- 一個子進(jìn)程的進(jìn)程描述符在子進(jìn)程退出時不會釋放,只有當(dāng)父進(jìn)程通過 wait() 或 waitpid() 獲取了子進(jìn)程信息后才會釋放。如果子進(jìn)程退出,而父進(jìn)程并沒有調(diào)用 wait() 或 waitpid(),那么子進(jìn)程的進(jìn)程描述符仍然保存在系統(tǒng)中,這種進(jìn)程稱之為僵尸進(jìn)程。
- 僵尸進(jìn)程通過 ps 命令顯示出來的狀態(tài)為 Z(zombie)。
- 系統(tǒng)所能使用的進(jìn)程號是有限的,如果產(chǎn)生大量僵尸進(jìn)程,將因為沒有可用的進(jìn)程號而導(dǎo)致系統(tǒng)不能產(chǎn)生新的進(jìn)程。
- 要消滅系統(tǒng)中大量的僵尸進(jìn)程,只需要將其父進(jìn)程殺死,此時僵尸進(jìn)程就會變成孤兒進(jìn)程,從而被 init 進(jìn)程所收養(yǎng),這樣 init 進(jìn)程就會釋放所有的僵尸進(jìn)程所占有的資源,從而結(jié)束僵尸進(jìn)程。
