作業系統:三個簡單的部分。 第 2 部分:抽象:過程(翻譯)

操作系統簡介

嘿哈布爾! 我想提請您注意一系列文章——我認為是一本有趣的文學作品的翻譯——OSTEP。 本材料非常深入地討論了類 unix 操作系統的工作,即與進程、各種調度程序、內存和構成現代操作系統的其他類似組件一起工作。 你可以在這裡看到所有材料的原件 這裡. 請注意,翻譯是非專業的(相當隨意),但我希望我保留了一般意思。

關於這個主題的實驗室工作可以在這裡找到:

其他部分:

您也可以查看我的頻道 電報 =)

讓我們看看作業系統為使用者提供的最基本的抽象:進程。 過程的定義非常簡單——它是 運行程式。 程式本身是位於磁碟上的無生命的東西——它是一組指令,可能還有一些等待啟動的靜態資料。 作業系統取得這些位元組並運行它們,將程式轉換成有用的東西。
大多數情況下,使用者希望同時執行多個程序,例如,您可以在筆記型電腦上執行瀏覽器、遊戲、媒體播放器、文字編輯器等。 事實上,典型的系統可以同時運行數十或數百個進程。 這一事實使得系統更易於使用,您永遠不必擔心CPU是否空閒,您只需運行程式即可。

這就提出了一個問題:如何提供許多CPU的假象? 即使只有一個實體 CPU,作業系統如何創造出幾乎無限數量的 CPU 的假象?

作業系統透過 CPU 虛擬化創造了這種錯覺。 透過啟動一個進程,然後停止它,啟動另一個進程,依此類推,作業系統可以維持有許多虛擬 CPU 的假象,而實際上會有一個或多個實體處理器。 這種技術稱為 CPU資源依時間劃分。 該技術允許用戶根據需要運行任意數量的並發進程。 此解決方案的成本是效能 - 因為如果 CPU 由多個進程共享,則每個進程的處理速度都會更慢。
為了實現CPU虛擬化,尤其是做好它,作業系統需要底層和高層的支援。 低水準支持稱為 機制 是實現功能所需部分的低階方法或協定。 此類功能的一個範例是上下文切換,它使作業系統能夠停止一個程式並在處理器上執行另一個程式。 所有現代作業系統都實現了這種時間劃分。
這些機制之上是以「策略」的形式內建在作業系統中的一些邏輯。 政策 是作業系統一定的決策演算法。 例如,此類策略決定應先啟動哪個程式(從命令清單中)。 例如,這個問題將透過一項名為 調度器(調度策略) 在選擇解決方案時,會以以下數據為指導:啟動歷史記錄(最後幾分鐘啟動時間最長的程序)、該進程承載的負載(啟動了哪些類型的程序)、性能指標(系統是否啟動)針對互動式互動或吞吐量進行了優化)等等。

抽象:過程

作業系統執行的運行程序的抽象就是我們所說的 過程。 如前所述,進程只是在任何瞬時時間段內執行的程式。 一個程序,透過它我們可以從該程序在執行過程中存取或影響的各種系統資源中獲取摘要資訊。
要了解進程的組成部分,您需要了解系統的狀態:程式在運行期間可以讀取或更改什麼。 在任何給定時間,您都需要了解系統的哪些元素對於程式的執行很重要。
該過程包含的系統狀態的明顯元素之一是 記憶。 指令位於記憶體中。 程式讀取或寫入的資料也位於記憶體中。 因此,進程可以尋址的記憶體(稱為位址空間)是進程的一部分。
暫存器也是系統狀態的一部分。 許多指令的目的是改變暫存器的值或讀取其值,因此暫存器也成為進程操作的重要組成部分。
需要注意的是,機器狀態也是由一些特殊暫存器組成的。 例如, IP——指令指針 — 指向程式目前正在執行的指令的指標。 還有 堆棧指針 以及與之相關的 幀指針,用於管理:函數參數、局部變數和返回地址。
最後,程式經常存取 ROM(唯讀記憶體)。 此“I/O”(輸入/輸出)資訊應包括進程目前開啟的檔案清單。

流程API

為了加深我們對該過程如何運作的理解,讓我們研究應該包含在任何作業系統介面中的系統呼叫的範例。 這些 API 可以在任何作業系統上以一種或另一種形式使用。

創建 (創建):作業系統必須包含某種允許您建立新進程的方法。 當您在終端機中輸入命令或透過雙擊圖示啟動應用程式時,系統會向作業系統發送呼叫以建立新進程,然後啟動指定的程式。
切除:既然有創建進程的接口,那麼作業系統也應該提供強制刪除進程的能力。 大多數程式在運行時會自然地自行啟動和終止。 否則,用戶希望能夠殺死它們,因此停止該進程的介面將很有用。
等待 (等待):有時等待進程完成很有用,因此提供了一些介面來提供等待的能力。
雜項控制 (各種控制):除了殺死和等待進程外,還有其他各種控制方式。 例如,大多數作業系統都提供凍結進程(停止其執行一段時間)然後恢復進程(繼續執行)的功能
Status (state):有各種介面用於獲取有關進程狀態的一些信息,例如它已經運行了多長時間或當前處於什麼狀態。

作業系統:三個簡單的部分。 第 2 部分:抽象:過程(翻譯)

流程創建:詳細信息

有趣的事情之一是程式到底是如何轉化為進程的。 特別是作業系統如何選擇並運行程式。 流程到底是如何創造的。
首先,作業系統必須將程式碼和靜態資料載入到記憶體(進程位址空間)中。 程式通常以某種可執行格式位於磁碟或固態磁碟機上。 因此,將程式和靜態資料載入記憶體的過程要求作業系統能夠從磁碟讀取這些位元組並將它們放置在記憶體中的某個位置。

在早期的作業系統中,載入過程是急切完成的,這意味著整個程式碼在程式啟動之前就已載入到記憶體中。 現代作業系統會惰性地執行此操作,即僅在程式執行期間​​需要時才載入程式碼或資料片段。

一旦程式碼和靜態資料載入到作業系統記憶體中,在進程運行之前還需要完成一些事情。 必須為堆疊分配一定量的記憶體。 程式使用堆疊來儲存局部變數、函數參數和返回地址。 作業系統分配該記憶體並將其提供給進程。 堆疊也可以指派一些參數,特別是它填入 main() 函數的參數,例如使用 argc 和 argv 陣列。

作業系統也可能會分配一些記憶體給程式堆。 程式使用堆來明確請求動態分配的數據。 程式透過呼叫函數來請求這個空間 malloc() 並透過呼叫函數明確清除它 自由()。 堆是連結表、哈希表、樹等資料結構所必需的。 起初,少量記憶體被分配給堆,但隨著時間的推移,隨著程式的運行,堆可以透過函式庫 API 呼叫 malloc() 請求更多記憶體。 作業系統參與分配更多記憶體以幫助滿足這些呼叫的過程。

作業系統也會執行初始化任務,特別是與 I/O 相關的初始化任務。 例如,在 UNIX 系統上,每個程序預設有 3 個開啟的檔案描述符,用於標準輸入、輸出和錯誤。 這些句柄允許程式讀取來自終端的輸入以及在螢幕上顯示資訊。

因此,透過將程式碼和靜態資料載入到記憶體中、建立和初始化堆疊以及執行與執行 I/O 任務相關的其他工作,作業系統為進程的執行做好準備。 最後,剩下最後一項任務:透過其入口點(稱為 main() 函數)運行程式。 透過執行main()函數,作業系統將CPU控制權轉移給新建立的進程,以便程式開始執行。

行程狀態

現在我們對進程是什麼以及它是如何創建有了一些了解,讓我們列出它可以處於的進程狀態。 在最簡單的形式中,進程可以處於以下狀態之一:
運行。 運行時,進程在處理器上運行。 這意味著指令正在執行。
各就各位。 在就緒狀態下,進程已準備好運行,但由於某種原因作業系統沒有在指定時間執行它。
封鎖。 在阻塞狀態下,進程執行一些操作,阻止其準備好執行,直到發生某些事件。 一個常見的例子是,當一個行程啟動 IO 操作時,它會被阻塞,以便其他行程可以使用處理器。

作業系統:三個簡單的部分。 第 2 部分:抽象:過程(翻譯)

您可以以圖表的形式想像這些狀態。 正如我們在圖中看到的,進程狀態可以根據作業系統的判斷在 RUNNING 和 READY 之間變化。 當一個行程的狀態從READY變成RUNNING時,就表示該行程已經被調度了。 在相反的方向 - 從佈局中刪除。 當一個行程變成BLOCKED的那一刻,像是我發起一個IO操作,作業系統會保持這個狀態,直到某個事件發生,像是IO完成。 此時轉換到 READY 狀態,如果作業系統決定的話,可能會立即轉換到 RUNNING 狀態。
讓我們來看一個範例,了解兩個進程如何經歷這些狀態。 首先,我們假設兩個進程都在運行,並且每個進程僅使用 CPU。 在這種情況下,他們的狀態將如下所示。

作業系統:三個簡單的部分。 第 2 部分:抽象:過程(翻譯)

在下面的範例中,第一個進程在運行一段時間後請求 IO 並進入 BLOCKED 狀態,從而允許另一個進程運行(圖 1.4)。 作業系統發現進程0沒有使用CPU並啟動進程1。當進程1運行時,IO完成,進程0的狀態變成READY。 最後,進程 1 完成,完成後,進程 0 啟動、執行並完成其工作。

作業系統:三個簡單的部分。 第 2 部分:抽象:過程(翻譯)

資料結構

作業系統本身是一個程序,就像任何其他程序一樣,它有一些關鍵的資料結構來追蹤各種相關的資訊。 為了追蹤每個進程的狀態,作業系統將支援一些 行程清單 處於 READY 狀態的所有進程以及一些用於追蹤當前正在運行的進程的附加資訊。 此外,作業系統應該監視阻塞的進程。 IO完成後,作業系統必須喚醒所需的進程並將其置於準備運行的狀態。

例如,作業系統必須保留處理器暫存器的狀態。 當進程停止時,暫存器的狀態儲存在進程的位址空間中,當其操作繼續時,暫存器的值會恢復,從而繼續執行該進程。

除了就緒、阻塞、運作狀態之外,還有一些其他狀態。 有時,在建立時,進程可能處於 INIT 狀態。 最後,當進程已經完成但其資訊尚未清除時,可以將其置於 FINAL 狀態。 在 UNIX 系統上,此狀態稱為 殭屍行程。 此狀態對於父進程想要知道子進程的回傳程式碼的情況很有用,例如,通常 0 表示成功,1 表示錯誤,但程式設計師可以發出額外的輸出程式碼來表示不同的問題。 當父進程終止時,它會進行最終的系統調用,例如 wait(),以等待子進程終止並向作業系統發出信號,表明它可以清除與已終止進程相關的任何資料。

作業系統:三個簡單的部分。 第 2 部分:抽象:過程(翻譯)

講座要點:

過程 ——作業系統中正在執行的程式的主要抽象。 在任何給定時間,進程都可以透過其狀態來描述:其位址空間中的記憶體內容、處理器暫存器的內容(包括指令指標和堆疊指標)以及IO 資訊(例如正在讀取或寫入的開啟文件)。
流程API 由程式可以對進程進行的呼叫組成。 通常這些是創建、刪除或其他呼叫。
● 進程處於多種狀態之一,包括運作、就緒、阻塞。 各種事件(例如調度、調度異常或等待)都可以將進程的狀態從一種狀態變更為另一種狀態。
進程列表 包含系統中所有進程的資訊。 其中的每個條目稱為進程控制塊,它實際上是一個包含有關特定進程的所有必要資訊的結構。 

來源: www.habr.com

添加評論