前言
Windows的內部實現也近似於“一切皆文件”的思想,當然,這一切都只在內核里才有,下載一個WinObj這軟件就可以看到,Windows上各種設備、分區、虛擬對象都是掛載到根“\”下的,通過這個樹可以訪問各種設備、驅動、文件系統等等。
Windows與Linux不同的就是把這些對象又重新封裝了一層WindowsAPI,對外以設備、盤符、文件等等表現出來,重新封裝WindowsAPI的目的是為了兼容性,而設備、盤符、文件這些是為了讓普通用戶更好理解。
所以進程在內核中以數據的形式保存在內存中,也可以看成一個文件。
進程概述
系統分配資源的最小單位!
就是擔當分配系統資源(CPU時間、內存等)的基本單位。
cpu分配:一個cpu一個時間片只能執行一個進程,不同cpu內核並行運行這個進程中不同的線程。
虛擬內存分配:一個進程默認可以分配到和物理內存一樣大小的虛擬內存。
進程的運行不僅僅需要CPU,還需要很多其他資源,如內存啊,顯卡啊,GPS啊,磁盤啊等等,統稱為程序的執行環境,也就是程序上下文。
單CPU進行進程調度的時候,需要讀取上下文+執行程序+保存上下文,即進程切換。所以進程的創建和銷毀都是相對於系統資源,所以是一種比較昂貴的操作。
進程將內存的地址空間划分為:
- 代碼段(文本段 code segment/text segment):保存應用的執行代碼。通常是指用來存放程序執行代碼的一塊內存區域
- 數據段(data segment):全局變量,常量,靜態變量。例如C#。 通常是指用來存放程序中已初始化的全局變量的一塊內存區域。數據段屬於靜態內存分配。
- 堆棧段:
- 堆:存放各種變量數據(new的對象),大小動態調整。
- 棧:子任務(線程、協程)獨立存放數據的地方(函數調用、參數、局部變量等)
進程的虛擬地址空間雖然很大,但是它被划分成了很多分區
詳細查看:https://docs.microsoft.com/zh-cn/windows-hardware/drivers/gettingstarted/virtual-address-spaces
PCB
進程控制塊(Processing Control Block),是操作系統核心中一種數據結構,主要表示進程狀態,其作用是使一個程序成為一個能夠獨立運行的基本單位,並且可以並發執行的進程。
進程是運行中的程序實例,是操作系統程序動態執行的基本單元。為了滿足操作系統對進程的控制,例如調度,中斷,執行等操作,操作系統將每個進程描述為一個叫做進程控制塊(PCB) 的數據結構,在PCB中存儲着操作系統對控制一個進程所需要的全部信息,可以根據PCB找到程序代碼,找到程序的數據,程序獲得的資源等等。所以一個進程對於操作系統來說就是一個PCB。
在windows中執進程控制塊是由 EPROCESS 塊來表示的。 EPROCESS 塊位於內核層之上,它側重於提供各種管理策略,同時為上層應用程序提供基本的功能接口。所以,在執行體層的進程和線程數據結構中,有些成員直接對應於上層應用程序中所看到的功能實體。
EPROCESS
結構屬於內核的執行體層,包含了進程的資源相關信息諸如句柄表、虛擬內存、安全、調試、異常、創建信息、I/O轉移統計以及進程計時等。
進程生命周期
三狀態模型:
-
阻塞態:等待某個事件的完成(比如等待IO數據)。
-
就緒態:等待系統分配處理器運行(隨時可以運行)。
-
運行態:占有處理器運行。
-
一般不太討論創建態和結束態。5狀態和7狀態模型很少用到
五狀態模型
如果所有進程都做好了准備,操作系統會從未運行隊列中以輪轉的方式調度每個進程。但是這里有個問題,如果並非所有進程都做好了准備呢?也許未運行的進程中有些進程正在等待某一事件的發生,也就是處於阻塞,因此單純的對所有未運行的進程進行輪轉是不科學的,應該對所有已經就緒的進程進行調度。解決這種問題的最好方法就是將未執行進程隊列拆分為兩個隊列分別是就緒隊列和阻塞隊列,由此進程的狀態由2狀態變為了3狀態,此外還要增加新建和退出態,這十分有必要。改進后的狀態模型如下圖所示。
運行態:進程正在執行。
就緒態:進程做好了准備,隨時接收調度。
阻塞態:進程在等待某些事件的發生,在事件發生前不能執行,如I/O操作。
新建態:剛剛新建的進程,操作系統還未將其加載至內存,通常是PCB已經創建但是還並未加載到內存中的新程序。
退出態:操作系統從可執行進程組中釋放的進程。
新建態與推出態十分有必要。在一個進程被新建時它並非絕對會被調入內存,通常是分兩步,首先創建該進程的PCB,並與之關聯,但是此時可能面臨內存不足或者操作系統限制了最大進程數導致這個進程還無法被調入進程,因此該進程被暫時留在新建態,在這個狀態的進程PCB已經創建並且加載進內存,但是進程的代碼和數據往往還留在外村中等待加載。
退出態也和新建態同理。當進程因為某些人原因要被終止時,此時並不直接將其調出內存,首先操作系統會停止執行該進程的代碼,但是暫時讓其留在內存中,因為某些輔助程序或是支持程序會來記錄該進程相關數據和信息,此時進程停留在退出態。等相關程序收集完所需信息后,再將其所有數據從內存中移除。
關於阻塞,就緒和運行三種狀態的轉換更為普遍和便於理解。操作系統從就緒隊列中調度某個進程進入運行態運行,當時間片結束后操作系統將其放回就緒態執行其他進程,如果在執行期間進程必須等待某些事件,便將其放入阻塞態,然后調度其他進程執行。當該進程等待的事件完成后操作系統則將其放回就緒態等待調度。
但是此時又有一個問題,如果所有阻塞進程放在同一個阻塞隊列中,當一個事件完成后操作系統不得不掃描整個隊列找到那些等待該事件的進程然后將其放進就緒隊列中,這樣的效率十分低下,因此通常是為每一個事件創建一個阻塞隊列。同理當按照優先級進行調度時,也會將優先級相同的進程放進一個就緒隊列,避免掃描等低效的做法,這是典型的用空間換時間的做法。
七狀態模型
在介紹七狀態模型前,我們思考一個問題,三個基本狀態(就緒,運行和阻塞)的所有進程都必須存儲在內存中,此時就可能出現一種情況,即所有進程都處於阻塞態,沒有就緒狀態的進程,此時又開始了處理器的空轉,處理器沒辦法執行進程只能開始等待進程從阻塞態恢復就緒態,並且加入此時又有新的進程處於新建態,由於內存不足,處於新建態的進程也沒辦法進入內存無法執行,這是一個十分致命的處理器空轉問題,解決這個問題有兩個方法:擴大內存,很顯然成本太高了;將阻塞態的進程暫時調出內存放回磁盤,來讓新建態的進程有足夠內存進入就緒態開始處理器的調度和運行。
但是在將一個阻塞態進程掛起后,操作系統可以選擇接納一個新建態進程進入就緒隊列,也可以選擇將一個之前掛起的進程恢復就緒態,並且為了減少操作系統的負載操作系統更傾向於后者。但是處於掛起的進程也可能還並未接觸阻塞,將一個阻塞進程放回內存沒有任何意義,於是更好的方法是將掛起區分為兩個狀態即就緒/掛起態和阻塞/掛起態,這樣每次操作系統就只需要考慮是否應該把進程從就緒/掛起態換回就緒態即可。完整的七狀態模型如下:
阻塞/掛起態:進程在外存中並等待一個事件。
就緒/掛起態:進程在外存中,但只要載入內存即可開始運行。
並且操作系統允許進程從就緒變為就緒/掛起態,或從阻塞/掛起態變更為阻塞態,只是這樣做的意義不大,因此並不會這樣做。
導致進程被掛起的事件有以下幾種:
1、交換。為了釋放內存空間。
2、其他OS原因。操作系統可能會掛起后台進程或者工具進程,或掛起可能會導致問題的進程。
3、交互式用戶請求。用戶希望掛起一個進程來進行調試。
4、定時。進程可被周期性的執行,並在等待下一個時間間隔時掛起。
5、父進程請求。父進程可能希望掛起后代進程的執行,以檢查或修改掛起的進程。
進程調度算法
搶占式調度
在“搶先式調度”中,大多數任務都分配有其優先級。有時,即使較低優先級的任務仍在運行,在另一個較低優先級的任務之前運行具有較高優先級的任務也很重要。較低優先級的任務會保留一段時間,並在較高優先級的任務完成執行時恢復。
非搶占式調度
在這種調度方法中,已將CPU分配給特定的進程。使CPU保持繁忙的進程將通過切換上下文或終止來釋放CPU。這是可用於各種硬件平台的唯一方法。那是因為它不需要搶先式調度之類的特殊硬件(例如計時器)。
詳細請看:https://zhuanlan.zhihu.com/p/356089708
- 先來先服務調度算法
- 最短作業優先調度算法
- 高響應比優先調度算法
- 時間片輪轉調度算法
- 最高優先級調度算法
- 多級反饋隊列調度算法
進程間通信的方式
-
管道(pipe)及有名管道(named pipe):管道可用於具有親緣關系的父子進程間的通信,有名管道除了具有管道所具有的功能外,它還允許無親緣關系進程間的通信。
-
信號(signal):信號是在軟件層次上對中斷機制的一種模擬,它是比較復雜的通信方式,用於通知進程有某事件發生,一個進程收到一個信號與處理器收到一個中斷請求效果上可以說是一致的。
-
消息隊列(message queue):消息隊列是消息的鏈接表,它克服了上兩種通信方式中信號量有限的缺點,具有寫權限的進程可以按照一定的規則向消息隊列中添加新信息;對消息隊列有讀權限的進程則可以從消息隊列中讀取信息。
-
共享內存(shared memory):可以說這是最有用的進程間通信方式。它使得多個進程可以訪問同一塊內存空間,不同進程可以及時看到對方進程中對共享內存中數據的更新。這種方式需要依靠某種同步操作,如互斥鎖和信號量等。
-
信號量(semaphore):主要作為進程之間及同一種進程的不同線程之間的同步和互斥手段。
-
套接字(socket):這是一種更為一般的進程間通信機制,它可用於網絡中不同機器之間的進程間通信,應用非常廣泛。
進程特征
- 動態性 進程既然是進程實體的執行過程,因此進程是有一定的生命期。而程序只是一組有序指令的集合,並放在某種介質上,本身無運行的含義,因此程序是個靜態的實體。
- 並發性(concurrence)
並行性是指兩個或者多個事件在同一時刻發生,這是一個具有微觀意義的概念,即在物理上這些事件是同時發生的;而並發性是指兩個或者多個事件在同一時間的間隔內發生,它是一個較為宏觀的概念。
在多道程序環境下,並發性是指在一段時間內有多道程序在同時運行,但在單處理機的系統中,每一時刻僅能執行一道程序,故微觀上這些程序是在交替執行的。應當指出,通常的程序是靜態實體,它們是不能並發執行的。為了使程序能並發執行,系統必須分別為每個程序建立進程。進程,又稱任務,簡單來說,是指在系統中能獨立運行並作為資源分配的基本單位,它是一個活動的實體。多個進程之間可以並發執行和交換信息。一個進程在運行時需要運行時需要一定的資源,如 cpu,存儲空間,及i/o設備等。在操作系統中引入進程的目的是使程序能並發執行。
- 獨立性 這是指進程實體是一個能獨立運行的基本單位,同時也是系統種獨立獲得資源和調度的基本單位。
- 異步性(asynchronism) 在多道程序設計環境下,允許多個進程並發執行,由於資源等因素的限制,通常,進程的執行並非“一氣呵成”,而是以“走走停停”的方式運行。內存中每個進程在何時執行,何時暫停,以怎樣的方式向前推進,每道程序總共需要多少時間才能完成,都是不可預知的。或者說,進程是以異步的方式運行的。盡管如此,但只要運行環境相同,作業經過多次運行,都會獲得完全相同的結果,因此,異步運行方式是允許的。
- 結構特征 從結構上看,進程實體是由程序段、數據段及進程控制塊三部分組成。
(進程控制塊(PCB):進程控制塊是進程實體的一部分,它記錄了操作系統所需要的、用於描述進程情況及控制進程運行所需的全部信息。os 是根據PCB來對並發執行的進程進行控制和管理的)
多進程
在同一個時間里,同一個計算機系統中如果允許兩個或兩個以上的進程處於運行狀態,這便是多任務(多進程)。現代的操作系統幾乎都是多進程操作系統,能夠同時管理多個進程的運行。 多進程帶來的好處是明顯的。但是多進程對於系統的資源要求甚高,資源浪費也比較嚴重。應用多進程場景最多的是windows系統,例如同時打開運行軟件,每個軟件打開相當於運行一個進程。
進程調度
由於CPU同時刻只能執行一個進程,如果我們不加以控制的話,一個進程可能使用CPU直到運行結束,於是出現了操作系統調度器,而進程也成為了調度單位。
進程上下文
進程的運行不僅僅需要CPU,還需要很多其他資源,如內存啊,顯卡啊,GPS啊,磁盤啊等等,統稱為程序的執行環境,也就是程序上下文。
進程並發
在這里就出現了並發的概念,調度器切換CPU給不同進程使用的速度非常快,於是在使用者看來程序是在同時運行,這就是並發,而實際上CPU在同一時刻只在運行一個進程。
單個CPU只有並發,沒法並行。
進程同步
1、進程同步的基本概念
多道程序環境下,進程是並發執行的,不同進程間存在着不同的相互制約關系。為了協調進程之間的相互制約關系,達到資源共享和進程協作,避免進程之間的沖突,引入了進程同步的概念。
(1) 臨界資源
多個進程可以共享系統中的各種資源,但其中許多資源一次只能為一個進程所使用,我們把一次只允許一個進程使用的資源成為臨界資源。
對臨界資源的訪問,必須互斥的進行。每個進程中,訪問臨界資源的那段代碼成為臨界區。
為了保證臨界資源的正確使用,可以把臨界資源的訪問過程分為四個部分。
1) 進入區。為了進入臨界區使用臨界資源,在進入去要檢查可否進入臨界區。
2) 臨界區。進程中訪問臨界資源的那段代碼。
3) 退出區。將正在訪問臨界區的標志清除。
4) 剩余區。代碼中的其余部分。
do {
entry section;
critical section;
exit section;
remainder section;
}while (true)
(2) 同步
同步已成為直接制約關系,它是為完成某種任務而建立的兩個或多個進程。這些進程因為需要在某些位置上協調他們的工作次序而等待、傳遞信息所產生的制約關系。進程間的直接制約關系就是它們之間的相互合作。
(3) 互斥
互斥亦稱間接制約關系。當一個進程進入臨界區使用臨界資源時,另一個進程必須等待,當占用臨界資源的進程退出臨界區后,另一個進程才允許去訪問此臨界資源。
實現臨界區互斥的基本方法
在進入區設置和檢查一些標志來表名是否有進程在臨界區中,如果已有進程在臨界區,則在進入區通過循環檢查進行等待,進程離開臨界區后則在退出區修改標志。
進程和線程之間的區別
這是十分常見的問題,在此做同一歸納和梳理:
1、進程是資源分配的基本單位,線程是處理器調度的基本單位。
2、同一進程內線程共享進程狀態和資源,例如數據段,代碼段,I/O信息等。但是每個線程內也有獨立的數據,每個線程都擁有屬於自己的棧,線程屬性信息存在線程控制塊中,例如上下文數據,線程狀態,調度信息等。
3、線程是輕量級進程,因此創建和銷毀所消耗的系統資源更少,更快。
4、同一進程內線程切換所消耗的資源相比進程切換更少且更快。
5、同一進程內線程共享大部分數據因此通信起來更加方便,無需借助內核。