行程
關于行程和執行緒方面的知識相信大家都很熟悉,因為這是作業系統里面最基礎也是最重要的內容,而且在面試的時候,面試官很喜歡問這方面的問題,所以有必要深刻的理解一下這部分的內容,
首先,什么行程?
行程的概念
簡言之,一個行程就是一個正在執行程式的實體,包括程式計數器、暫存器和變數的當前值,從概念上說,每個行程擁有它自己的虛擬CPU,當然,實際上真正的CPU在各行程之間來回切換,
這里要把行程和程式區分開,程式是行程的一部分,行程主要包括以下內容:
- 文本段(text section)(或代碼段(code section))
- 程式計數器(programcounter)的值
- 處理器暫存器的內容
- 行程堆疊(stack)(包括臨時資料,如函式引數、回傳地址和區域變數)
- 資料段(data section)(包括全域變數)
- 堆(heap)(行程運行時動態分配的記憶體)
可以看到,行程與程式的區別在于:
-
行程是動態的,程式是靜態的
- 程式是有序代碼的集合
- 行程是程式的執行,行程有核心態/用戶態
-
行程是暫時的,程式的永久的
- 行程是一個狀態變化的程序
- 程式可長久保存
-
行程與程式的組成不同
行程的組成包括程式、資料和行程控制塊
一個行程是某種型別的一個活動,它有程式、輸入、輸出以及狀態,單個處理器可以被若干行程共享,它使用某種調度演算法決定何時停止一個行程的作業,并轉而為另一個行程提供服務,
程式本身不是行程,程式只是被動(passive) 物體,如存盤在磁盤上包含一系列指令的檔案(經常稱為可執行檔案(executable file) ),相反,行程是活動(active) 物體,具有一個程式計數器用于表示下個執行命令和一組相關資源,當一個可執行檔案被加載到記憶體時,這個程式就成為行程,
簡言之,程式只是行程的一部分,總結一下行程的特征:
- 動態性:行程是程式的一次執行程序,是臨時的,有生命期的,是動態產生,動態消亡的;
- 并發性:任何行程都可以同其他進行一起并發執行;
- 獨立性:行程是系統進行資源分配和調度的一個獨立單位;
- 結構性:行程由程式,資料和行程控制塊三部分組成,
下面來看行程是如何實作的,
行程實作
為了實作行程模型,作業系統維護著一張表格(一個結構陣列),即行程表(process table),每個行程占用一個行程表項,稱為行程控制塊(Process Control Block, PCB),表項包含了行程狀態的重要資訊,包括程式計數器、堆疊指標、記憶體分配狀況、所打開檔案的狀態、賬號和調度資訊,以及其他在行程由運行態轉換到就緒態或阻塞態時必須保存的資訊,從而保證該行程隨后能再次啟動,就像從未被中斷過一樣,
行程控制塊的關鍵欄位:

第一列中的欄位與行程管理有關,其他兩列分別與存盤管理和檔案管理有關,
接下來看一下行程是如何創建的,
行程創建
關于行程創建的時機,主要有4種主要事件導致行程的創建:
1、系統初始化
啟動作業系統時,系統會創建若干行程,有和用戶互動的前臺行程,同時也有很多后臺行程,停留在后臺處理諸如電子郵件、Web頁面、新聞、列印之類活動的行程稱為守護行程(daemon),
2、執行了正在運行的行程所呼叫的行程創建系統呼叫,
一個正在運行的行程經常發出系統呼叫,以便創建一個或多個新行程協助其作業,
3、用戶請求創建一個新行程,
在互動式系統中,鍵入一個命令或者點(雙)擊一個圖示就可以啟動一個程式,
4、一個批處理作業的初始化,
用戶在批處理系統中提交批處理作業,在作業系統認為有資源可運行另一個作業時,它創建一個新的行程,并運行其輸入佇列中的下一個作業,
可以看到,在所有這些情形中,新行程都是由于一個已存在的行程執行了一個用于創建行程的系統呼叫而創建的,創建行程稱為父行程,而新的行程稱為子行程,每個新行程可以再創建其他行程,從而形成行程樹( process tree),
當一個行程創建子行程時,該子行程會需要一定的資源(CPU時間、記憶體、檔案、I/O設備等)來完成任務,子行程可以從作業系統那里直接獲得資源,也可以只從父行程那里獲得資源子集,父行程可能要在子行程之間分配資源或共享資源(如記憶體或檔案),
限制子行程只能使用父行程的資源,可以防止創建過多行程,導致系統超載,
當行程創建新行程時,可有兩種執行可能:
- 父行程與子行程并發執行,
- 父行程等待,直到某個或全部子行程執行完,
新行程的地址空間也有兩種可能:
- 子行程是父行程的復制品(它具有與父行程同樣的程式和資料),
- 子行程加載另一個新程式,
我們具體來看一下Unix和Windows系統下行程的創建,
Unix系統的行程創建
在UNIX系統中,只有一個系統呼叫可以用來創建新行程:fork,這個系統呼叫會創建一個與呼叫行程相同的副本,在呼叫了fork后,這兩個行程(父行程和子行程)擁有相同的存盤映像、同樣的環境字串和同樣的打開檔案,這就是全部情形,通常,子行程接著執行exec()或一個類似的系統呼叫,以修改其存盤映像并運行一個新的程式,
新行程的地址空間復制了原來行程的地址空間,這種機制允許父行程與子行程輕松通信,這兩個行程(父和子)都繼續執行處于系統呼叫fork()之后的指令,但有一點不同:對于新(子)行程,系統呼叫fork()的回傳值為0 ;而對于父行程,回傳值為子行程的行程識別符號(非零),
通常,在系統呼叫fork()之后,有個行程使用系統呼叫exec(),以用新程式來取代行程的記憶體空間,系統呼叫exec()加載二進制檔案到記憶體中(破壞了包含系統呼叫exec()的原來程式的記憶體內容),并開始執行,
父行程能夠創建更多子行程,或者如果在子行程運行時沒有什么可做.那么它采用系統呼叫wait()把自己移出就緒佇列,直到子行程終止,因為呼叫exec()用新程式覆寫了行程的地址空間,所以呼叫exec()除非出現錯誤,不會回傳控制,
大多數的作業系統(包括UNIX、Linux和Windows) 對行程的識別采用的是唯一的行程識別符號(process identifier, pid),這通常是一個整數值,系統內的每個行程都有一個唯一pid,它可以用作索引,以便訪問內核中的行程的各種屬性,

Windows的行程創建
行程創建采用Windows API函式CreateProcess() ,它類似于fork() (這是父行程用于創建子行程的),不過,fork()讓子行程繼承了父行程的地址空間,而CreateProcess()在行程創建時要求將一個特定程式加載到子行程的地址空間,
再者,fork()不需要傳遞任何引數,而CreateProcess()需要傳遞至少10個引數,其中包括要執行的程式、輸入給該程式的命令列引數、各種安全屬性、有關打開的檔案是否繼承的控制位、優先級資訊、為該行程(若有的話)所需要創建的視窗規格以及指向一個結構的指標,在該結構中新創建行程的資訊被回傳給呼叫者,
總結一下:
在UNIX和Windows中,行程創建之后,父行程和子行程有各自不同的地址空間,
- 在UNIX中,子行程的初始地址空間是父行程的一個副本,但是這里涉及兩個不同的地址空間,不可寫的記憶體區是共享的(某些UNIX的實作使程式正文在兩者間共享,因為它不能被修改),但是,對于一個新創建的行程而言,確實有可能共享其創建者的其他資源,諸如打開的檔案等,
- 在Windows中,從一開始父行程的地址空間和子行程的地址空間就是不同的,
行程不可能一直存在,有行程的創建就必然有行程的終止,
行程終止
引起行程終止的條件也主要有四種:
- 正常退出(自愿的)
- 出錯退出(自愿的)
- 嚴重錯誤(非自愿)
- 被其他行程殺死(非自愿)
當行程完成執行最后陳述句并且通過系統呼叫exit()請求作業系統洗掉自身時,行程終止,這時,行程可以回傳狀態值(通常為整數)到父行程(通過系統呼叫wait()),所有行程資源,如物理和虛擬記憶體、打開檔案和I/O緩沖區等,會由作業系統釋放,
通常,只有終止行程的父行程才能執行這一系統呼叫,否則,用戶可以任意終止彼此的作業,記住,如果終止子行程,則父行程需要知道這些子行程的識別符號,因此,當一個行程創建新行程時,新創建行程的識別符號要傳遞到父行程,
父行程終止子行程的原因有很多,如:
- 子行程使用了超過它所分配的資源,(為判定是否發生這種情況,父行程應有一個機制,以檢查子行程的狀態),
- 分配給子行程的任務,不再需要,
- 父行程正在退出,而且作業系統不允許無父行程的子行程繼續執行,
父行程可以通過系統呼叫wait(),等待子行程的終止,系統呼叫wait()可以通過引數,讓父行程獲得子行程的退出狀態;這個系統呼叫也回傳終止子行程的識別符號,這樣父行程能夠知道哪個子行程已經終止了 ,
當一個行程終止時,作業系統會釋放其資源,不過,它位于行程表中的條目還是在的,直到它的父行程呼叫wait() ;這是因為行程表包含了行程的退出狀態,
當行程已經終止,但是其父行程尚未呼叫wait(),這樣的行程稱為僵尸行程(zombie process),
所有行程終止時都會過渡到這種狀態,但是一般而言僵尸行程只是短暫存在,一旦父行程呼叫了 wait(),僵尸行程的行程識別符號和它在行程表中的條目就會釋放,
如果父行程沒有呼叫wait()就終止,以致于子行程成為孤兒行程(orphan process),
Linux和UNIX對這種情況的處理是:將init行程作為孤兒行程的父行程,行程init定期呼叫wait(),以便收集任何孤兒行程的退出狀態,并釋放孤兒行程識別符號和行程表目,
行程從創建到終止的程序中,其狀態可能會不斷的改變,在各種狀態之間相互切換,
行程狀態
行程主要的5種狀態:
-
運行狀態(Running)
行程正在處理機上運行,
-
就緒狀態(Ready)
行程獲得了除了處理機之外的所有所需的資源,得到處理機即可運行,
-
阻塞狀態(Block)
行程正在等待某一事件的出現而暫停運行,
-
創建狀態(New)
一個行程正在被創建,還沒被轉到就緒狀態之前的狀態,是一個過渡狀態,也就是在分配資源和相應的資料結構,
-
退出狀態(Exit)
一個行程反正在從系統中消失時的狀態,這是因為行程結束或者由于其他原因所致(也就是系統正在回收資源),

轉換1:運行態到阻塞態
運行的行程需要等待I/O或者其他行程的結果作為輸入,此時行程自動阻塞,
轉換2:運行態到就緒態
一個行程運行的時間片用完或被高優先級的行程搶占,
轉換3:就緒態到運行態
輪到就緒行程運行時,
轉換4:阻塞態到就緒態
當行程等待的一個外部事件發生時(如一些輸入到達),
當行程從運行態轉換為就緒態時,這時候其他的行程在得到了處理機,這個程序發生了行程切換,
行程切換
切換CPU到另一個行程需要保存當前行程狀態和恢復另一個行程的狀態, 這個任務稱為背景關系切換(context switch),當進行背景關系切換時, 內核會將舊行程狀態保存在其PCB中, 然后加載經調度而要執行的新行程的背景關系,
背景關系切換的時間是純粹的開銷, 因為在切換時系統并沒有做任何有用作業, 背景關系切換的速度因機器不同而有所不同, 它依賴于記憶體速度、 必須復制的暫存器數量、 是否有特殊指令(如加載或存盤所有暫存器的單個指令),典型速度為幾毫秒,
所以行程的切換是非常耗時的,浪費了cpu的資源,所以引入了執行緒的概念,
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/345469.html
標籤:其他
上一篇:面試基礎篇|作業系統|行程
