【操作系統】第七章:進程管理(Part4:進程管理)

上下文切換

在這裏插入圖片描述
當運行不同程序,程序以進程的形式在OS中運行。各個進程之間共享CPU資源,不同時間需要切換不同進程去CPU執行。上下文Context切換switch。
進程的上下文切換就切換進程所用到寄存器,寄存器和CPU緊密相連,也就意味着如果在OS裏實現進程切換的話,需要去關注OS所在的CPU到底具有哪些寄存器,以及這些寄存器哪些是被我們進程使用的、在進程運行的時候我們需要哪一些寄存器。比如棧指針知道調用關係和相應局部變量的位置,程序寄存器進程執行在什麼地方。
這些信息在進程切換時需要保存在PCB中的某個位置,這樣當運行另一個進程時,我們這個把寄存器從另一個進程的PCB中取出,然後恢復到CPU裏去,把寄存器資源重新賦好。使得接下來的進程可以繼續在CPU裏執行,這就是一個大致的CPU上下文切換。
在這裏插入圖片描述
進程切換涉及到進程A和進程B兩個進程,進程A執行到一定階段後OS決定讓B繼續執行。這個時候進程A需要把它的Context各種寄存器信息保存着PCB中,然後把另一個進程的PCB中保存的上下文信息恢復到CPU中去,這個來回過程就完成了上下文切換。這些過程和我們硬件緊密相連,所以在實際實現中基本上都是用匯編代碼來寫的。上下文切換的開銷是很關鍵的,我們希望它的開銷越小越好,這樣進程把更多的時間用在執行而不是切換。
那麼我們就需要知道哪些進程能夠去做這個切換。【就緒態進程、阻塞態進程和殭屍隊列】
在這裏插入圖片描述
這些能夠在CPU中執行的進程都放在就緒隊列裏,我們需要去放置處於就緒態的進程的鏈表,來便於OS去選擇。這個鏈表我們稱之爲就緒隊列Run Queue。那麼另一些不能馬上佔用CPU執行的進程,他們放入等待隊列。
殭屍隊列:後續詳解。這裏提及概念。

進程控制

創建進程

不同的操作系統中,進程創建的API不同。
在這裏插入圖片描述
Linux採取fork和exec兩個系統調用來完成新進程創建。
在這裏插入圖片描述
fork複製的兩個進程,他們執行的程序一樣,但是有一個變量不同。就是進程的ID——PID,父進程是原來執行的進程的ID,子進程是新分配的ID。
完成複製之後,接下來由加載exec把新程序加載到內存,重寫當前進程,也就說新創建子進程。此時的PID沒有改變。系統調用返回時,返回兩個進程且第二個進程已經變成一個新的程序在運行。
exec想讓當前的進程執行新的程序,當他執行新的程序後,所有的地址空間的代碼和數據都變成了新的程序裏存的內容了。
在這裏插入圖片描述
在這裏插入圖片描述

加載和執行進程

在這裏插入圖片描述
當執行fork後,返回值-1.代表當前執行的地址空間是在指定的地址空間執行的,接着就是exec執行。printf做一個提醒作用,如果說執行了exec後,其實當前子進程的代碼段已經被覆蓋,也就意味着printf會被新程序覆蓋,也就說這句話不會被執行。如果執行了這句話,那就說明出了問題,比如exec執行失敗了。
如果pid大於0,意味着當前執行空間是在父進程,他會執行else內的代碼。最後還有一個wait(pid)等待子進程結束,因爲pid代表子進程的PID,pid是存在PCB中表示進程ID的變化。假設wait返回意味着子進程結束了。PID的fork返回值可以有三種狀態,大於/等於/小於零。
當pid小於0,則fork是一個失敗的系統調用,這時候就不應該再執行wait。所以應該修改爲

else if(pid>0){
...
}

在這裏插入圖片描述

這個圖可以直觀看出執行fork和exec後的變化。當執行exec時,代碼數據被複制,pid沒變但是代碼變了。
在這裏插入圖片描述
內存中的佈局圖如上,堆棧和代碼段。一旦執行fork和exec後。fork首先創建新進程,新的地址空間,但是fork執行後,子進程的地址空間是完全複製父進程,但是exec執行後,PCB裏的信息已經變化,用戶態的內存空間也有很大的變化,以爲代碼段已經完全被替換。
所以exec的主要作用:
在這裏插入圖片描述
運行一個進程執行不同的控制流。
在這裏插入圖片描述
fork效率高則整個系統效率提升。創建新進程更多的目的是創建新程序。
因爲fork的內存大量拷貝開銷過大,但是exec執行時又會加載新的程序,前面的fork操作就顯得多餘。爲了提高效率,可以通過vfork(虛擬)手段,vfork只複製了一小部分父進程的信息。增加了編程者的負擔。
更好的手段:編程員繼續使用fork,結合內存管理,我們通過虛存管理來有效實現fork機制。Copy on write 寫的時候再進行復制,這樣fork效率就很高。當父進程創建子進程時,如果採用了copy on write 機制,複製時,並沒有真實複製,而只是複製了父進程地址空間的元數據-頁表。父進程或子進程對某數據或者地址單元寫操作時,會觸發一個異常,使得進程完成一個操作——把異常頁複製成兩份,這時候父進程和子進程就分別擁有一個地址空間了。

等待和終止進程

在這裏插入圖片描述

父進程需要等待子進程結束的原因: 當子進程執行完畢如果執行exit退出,OS會根據進程情況回收資源,但是有一個資源很難回收——PCB。執行退出過程時,釋放掉資源,無法回到用戶空間中去執行,但是OS在內核裏在完成回收操作,那麼這個時刻雖然無法在用戶空間執行卻仍在內核空間執行,比如PCB。所以執行完畢後返回,父進程wait操作,根據返回值收到執行完畢的信號,父進程會幫助子進程釋放掉內存中的資源(包括PCB)。
在這裏插入圖片描述
子進程執行完exit且父進程還未執行完wait時,這一段時間內,子進程並沒有釋放且也不處於就緒態、阻塞態等狀態,此時,我們稱子進程處於殭屍(Zombie)狀態。要死還沒死的狀態。殭屍狀態下的進程無法正常工作,只是在等待被父進程回收。
如果父進程先於子進程退出(死亡),也就意味着沒有一個進程wait子進程,殭屍進程會越來越多。root process會定期掃描PCB的鏈表,看是否有進程處於殭屍狀態,則他會代替該進程的父進程完成wait操作,因此使得OS中始終不會有太多的殭屍進程。
在這裏插入圖片描述
5狀態圖到6狀態圖的圖示。
exec和狀態的關係:執行exec時,進程可能處於不同狀態。exec的功能:加載執行程序和運行程序。加載時,會從磁盤把數據讀入內存,這個過程花時間較長,也就意味着可能會出現從執行態到阻塞態來等代碼和數據導入內存。
所以:
Ready <==> Running
Wait ⇒ Ready
Running ⇒ wait
都可以是exec操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章