進程可以說是操作系統最為核心的一個抽象,而線程可以認為是一種輕量級的進程,或者說一個進程內的多個迷你進程。
一、進程的模型
進程(process):進程是一個正在運行的程序的實例。對於一個單核的處理器,每個時刻只能運行一個程序,但在每一個時間段,它可能運行多個進程,這樣就產生了並行的錯覺。
從概念上說,每個進程擁有自己的虛擬CPU,好像它獨占了CPU的使用權一樣,雖然實際的CPU是不斷切換的。進程的另外一大特性,是獨立的虛擬地址空間。
進程的創建
有四種事件導致進程的創建:
(1)系統初始化。系統初始化會創建許多進程,如windows剛開機的時候。
(2)執行了正在運行的進程所調用的系統調用。如程序運行了一個fork()調用。
(3)用戶請求創建一個進程。如命令行中輸入./a.out。
(4)一個批處理作業的初始化。
進程的終止
進程是正在運行的程序的實例,一個程序是會運行完的,所以進程也有終止的時候:
(1)正常退出(自願)。如程序正常結束。
(2)出錯退出(自願)。如編寫一個程序,當錯誤時調用exit(num)。
(3)嚴重錯誤(非自願)。
(4)被其他進程殺死(非自願)。如另外一個進程調用了kill(pid)。
進程的層次結構
不同操作系統有不同的概念。Linux系統中區分父進程與子進程,windows系統則不區分。
進程的狀態
進程有三種主要狀態:
(1)運行態:正在CPU上運行的進程。
(2)就緒態:已經就緒但是還沒有被調度程序選中。
(3)阻塞態:因為某種原因(如等待I/O完成)暫時無法執行,需要等待外部事件。
下面有一幅圖畫出了可能的轉換關系。需要注意的是,阻塞態的進程必須先進入就緒態,等待處理器的調度。

進程的實現
進程的實現,相對於線程來說是比較確定的。操作系統內核維護一個進程表,也稱為進程控制塊(PCB)。進程表項為一個進程啟動的必要信息,包括進程管理(寄存器,PC,PSW,調度信息,打開文件的狀態等)、存儲管理(代碼段、數據段、堆棧段指針等)、文件管理(目錄、PID等)信息。
當進程從運行態進入其他狀態時,PCB保存着啟動它的所有信息,當該進程再次被調度程序選中時,就要恢復這些信息。保存的步驟,包括:硬件壓入PC等,把中斷向量裝入新的PC,然后通過匯編語言保存寄存器並設置新的堆棧,運行中斷服務程序(通常為C),再通過調度程序選中一個進程,把它的運行信息載入寄存器以及PC等。
二、線程的模型
線程的使用,是因為進程創建、撤銷、切換的代價很大,並且需要共享內存空間和數據,以及提高運行速度。一個例子是Web服務器,它如果采用進程的方式,那么一個頁面請求被響應時,進程就進入了阻塞態,從而無法提供其他服務。而采用線程,可以使用一個線程接受請求,然后把請求分派給工作線程,實現同時響應,並且共享內存。
線程模型基於兩個概念:資源分組處理與執行。線程的目的,是共享資源,並共同完成一個任務。
線程與進程非常類似,不過比進程輕量,原因在於:進程擁有獨立的虛擬地址空間,而同一個進程中的線程,共享內存空間與資源。因此,線程只需要保存PC、寄存器、堆棧等,無需保存大量的文件、進程管理信息。線程之間也是沒有保護並且平等的,因為它們共享了同樣的資源,包括打開的文件、子進程等。
POSIX(portable operating system interface of UNIX)規定了UNIX系統的通用線程包pthread。
線程的實現
線程的實現,總的來說有兩種:在用戶空間中實現線程,以及在內核中實現線程。
用戶空間中實現線程
這種情況下,內核是不知道線程存在的,調度單位是進程。因此,為了體現多線程的作用,就必須考慮阻塞的問題。
因為用戶管理線程,所以每個進程都要有線程表,與進程表類似,不過記錄的內容比較少。
用戶級線程的優點有很多:
(1)可以在不支持線程的系統上運行。
(2)速度非常快,因為它不需要陷入內核,不需要上下文切換,也不需要cache刷新。
(3)可以允許每個進程定制自己的線程調度算法。
當然,因為內核不知道線程的存在,需要解決阻塞的問題,可以通過非阻塞系統調用,或者檢查調用是否會阻塞(如select,包裝器)。
另外,因為內核不知道線程的存在,進程內部沒有時鍾中斷,所以線程必須主動讓出CPU,不然其他線程無法搶占。
內核中實現線程
內核中實現線程,即在內核中維護一個線程表,而進程不維護。與用戶空間中實現相比,優缺點基本是相反的。
內核中的實現,自然不需要非阻塞I/O,但是代價就是,線程的操作花銷很大。
