圖文介紹進程和線程的區別

進程和線程的概念

先了解一下操作系統的一些相關概念,大部分操作系統 (如 Windows、Linux) 的任務調度是採用時間片輪轉的搶佔式調度方式,也就是說一個任務執行一小段時間後強制暫停去執行下一個任務,每個任務輪流執行。任務執行的一小段時間叫做時間片,任務正在執行時的狀態叫運行狀態,任務執行一段時間後強制暫停去執行下一個任務,被暫停的任務就處於就緒狀態等待下一個屬於它的時間片的到來。這樣每個任務都能得到執行,由於 CPU 的執行效率非常高,時間片非常短,在各個任務之間快速地切換,給人的感覺就是多個任務在“同時進行”,這也就是我們所說的併發(併發簡單來說多個任務同時執行)。

進程

計算機的核心是 CPU,它承擔了所有的計算任務;而操作系統是計算機的管理者,它負責任務的調度、資源的分配和管理,統領整個計算機硬件;應用程序側是具有某種功能的程序,程序是運行於操作系統之上的。

進程是一個具有一定獨立功能的程序在一個數據集上的一次動態執行的過程,是操作系統進行資源分配和調度的一個獨立單位,是應用程序運行的載體。進程是一種抽象的概念,從來沒有統一的標準定義。進程一般由程序、數據集合和進程控制塊三部分組成。程序用於描述進程要完成的功能,是控制進程執行的指令集;數據集合是程序在執行時所需要的數據和工作區;程序控制塊 (Program Control Block,簡稱 PCB),包含進程的描述信息和控制信息,是進程存在的唯一標誌。

進程具有的特徵:

• 動態性:進程是程序的一次執行過程,是臨時的,有生命期的,是動態產生,動態消亡的;• 併發性:任何進程都可以同其他進程一起併發執行;• 獨立性:進程是系統進行資源分配和調度的一個獨立單位;• 結構性:進程由程序、數據和進程控制塊三部分組成。

進程的生命週期

在早期只有進程的操作系統中,進程有五種狀態,創建、就緒、運行、阻塞 (等待)、退出。

創建:進程正在創建,還不能運行。操作系統在創建進程時要進行的工作包括分配和建立進程控制塊表項、建立資源表格並分配資源、加載程序並建立地址空間;• 就緒:時間片已用完,此線程被強制暫停,等待下一個屬於他的時間片到來;• 運行:此線程正在執行,正在佔用時間片;• 阻塞:也叫等待狀態,等待某一事件 (如 IO 或另一個線程) 執行完;• 退出:進程已結束,所以也稱結束狀態,釋放操作系統分配的資源。

線程

在早期的操作系統中並沒有線程的概念,進程是能擁有資源和獨立運行的最小單位,也是程序執行的最小單位。任務調度採用的是時間片輪轉的搶佔式調度方式,而進程是任務調度的最小單位,每個進程有各自獨立的一塊內存,使得各個進程之間內存地址相互隔離。

後來,隨着計算機的發展,對 CPU 的要求越來越高,進程之間的切換開銷較大,已經無法滿足越來越複雜的程序的要求了。於是就發明了線程,線程是程序執行中一個單一的順序控制流程,是程序執行流的最小單元,是處理器調度和分派的基本單位。一個進程可以有一個或多個線程,各個線程之間共享程序的內存空間。

一個標準的線程由線程 ID、當前指令指針 (PC)、寄存器和堆棧組成。而進程由內存空間(代碼、數據、進程空間、打開的文件) 和一個或多個線程組成。

線程的生命週期

當線程的數量小於處理器的數量時,線程的併發是真正的併發,不同的線程運行在不同的處理器上。但當線程的數量大於處理器的數量時,線程的併發會受到一些阻礙,此時並不是真正的併發,因爲此時至少有一個處理器會運行多個線程。在單個處理器運行多個線程時,併發是一種模擬出來的狀態。操作系統採用時間片輪轉的方式輪流執行每一個線程。現在,幾乎所有的現代操作系統採用的都是時間片輪轉的搶佔式調度方式,如我們熟悉的 Unix、linux、Windows 及 Mac OS X 等流行的操作系統。

• 創建:一個新的線程被創建,等待該線程被調用執行;• 就緒:時間片已用完,此線程被強制暫停,等待下一個屬於他的時間片到來;• 運行:此線程正在執行,正在佔用時間片;• 阻塞:也叫等待狀態,等待某一事件 (如 IO 或另一個線程) 執行完;• 退出:一個線程完成任務或者其他終止條件發生,該線程終止進入退出狀態,退出狀態釋放該線程所分配的資源。

線程優先級

操作系統 (如 Windows、Linux、Mac OS X) 的任務調度除了具有前面提到的時間片輪轉的特點外,還有優先級調度 (Priority Schedule) 的特點。優先級調度決定了線程按照什麼順序輪流執行,在具有優先級調度的系統中,線程擁有各自的線程優先級(Thread Priority)。具有高優先級的線程會更早地執行,而低優先級的線程通常要等沒有更高優先級的可執行線程時纔會被執行。

線程的優先級可以由用戶手動設置,此外系統也會根據不同情形調整優先級。通常情況下,頻繁地進入等待狀態 (進入等待狀態會放棄之前仍可佔用的時間份額) 的線程(如 IO 線程),比頻繁進行大量計算以至於每次都把所有時間片全部用盡的線程更受操作系統的歡迎。因爲頻繁進入等待的線程只會佔用很少的時間,這樣操作系統可以處理更多的任務。我們把頻繁等待的線程稱之爲 IO 密集型線程(IO Bound Thread),而把很少等待的線程稱之爲 CPU 密集型線程(CPU Bound Thread)。IO 密集型線程總是比 CPU 密集型線程更容易得到優先級的提升。

線程餓死 在優先級調度下,容易出現一種線程餓死的現象。一個線程餓死是說它的優先級較低,在它執行之前總是有比它優先級更高的線程等待執行,因此這個低優先級的線程始終得不到執行。當 CPU 密集型的線程優先級較高時,其它低優先級的線程就很可能出現餓死的情況;當 IO 密集型線程優先級較高時,其它線程相對不容易造成餓死的,因爲 IO 線程有大量的等待時間。爲了避免線程餓死,調度系統通常會逐步提升那些等待了很久而得不到執行的線程的優先級。這樣,一個線程只要它等待了足夠長的時間,其優先級總會被提升到可以讓它執行的程度,也就是說這種情況下線程始終會得到執行,只是時間的問題。

在優先級調度環境下,線程優先級的改變有三種方式:

  1. 用戶指定優先級;2. 根據進入等待狀態的頻繁程度提升或降低優先級 (由操作系統完成);3. 長時間得不到執行而被提升優先級。

多線程與多核 上面提到的時間片輪轉的調度方式說一個任務執行一小段時間後強制暫停去執行下一個任務,每個任務輪流執行。很多操作系統的書都說 “同一時間點只有一個任務在執行”。其實“同一時間點只有一個任務在執行” 這句話是不準確的,至少它是不全面的。我們分析一下多核的情況。

這是我的電腦的 CPU 情況圖:

多核 (心) 處理器是指在一個處理器上集成多個運算核心從而提高計算能力,也就是有多個真正並行計算的處理核心,每一個處理核心對應一個內核線程。內核線程(Kernel Thread, KLT)就是直接由操作系統內核支持的線程,這種線程由內核來完成線程切換,內核通過操作調度器對線程進行調度,並負責將線程的任務映射到各個處理器上。一般一個處理核心對應一個內核線程,比如單核處理器對應一個內核線程,雙核處理器對應兩個內核線程,四核處理器對應四個內核線程。

現在的電腦一般是雙核四線程、四核八線程,是採用超線程技術將一個物理處理核心模擬成兩個邏輯處理核心,對應兩個內核線程,所以在操作系統中看到的 CPU 數量是實際物理 CPU 數量的兩倍。但是我的如上圖是四核四線程,似乎沒有用這個超線程技術。

超線程技術就是利用特殊的硬件指令,把一個物理芯片模擬成兩個邏輯處理核心,讓單個處理器都能使用線程級並行計算,進而兼容多線程操作系統和軟件,減少了 CPU 的閒置時間,提高的 CPU 的運行效率。這種超線程技術 (如雙核四線程) 由處理器硬件的決定,同時也需要操作系統的支持才能在計算機中表現出來。

程序一般不會直接去使用內核線程,而是去使用內核線程的一種高級接口——輕量級進程(Light Weight Process,LWP),輕量級進程就是我們通常意義上所講的線程 (我們在這稱它爲用戶線程),由於每個輕量級進程都由一個內核線程支持,因此只有先支持內核線程,纔能有輕量級進程。用戶線程與內核線程的對應關係有三種模型:一對一模型、多對一模型、多對多模型,在這以 4 個內核線程、3 個用戶線程爲例對三種模型進行說明。

一對一模型 對於一對一模型來說,一個用戶線程就唯一地對應一個內核線程 (反過來不一定成立,一個內核線程不一定有對應的用戶線程)。這樣,如果 CPU 沒有采用超線程技術 (如四核四線程的計算機,就如上圖展示的我使用的計算機),一個用戶線程就唯一地映射到一個物理 CPU 的線程,線程之間的併發是真正的併發。一對一模型使用戶線程具有與內核線程一樣的優點,一個線程因某種原因阻塞時其他線程的執行不受影響;此處,一對一模型也可以讓多線程程序在多處理器的系統上有更好的表現。但一對一模型也有兩個缺點:

許多操作系統限制了內核線程的數量,因此一對一模型會使用戶線程的數量受到限制;

許多操作系統內核線程調度時,上下文切換的開銷較大,導致用戶線程的執行效率下降。

多對一模型

多對一模型將多個用戶線程映射到一個內核線程上,線程之間的切換由用戶態的代碼來進行,因此相對一對一模型,多對一模型的線程切換速度要快許多;此外,多對一模型對用戶線程的數量幾乎無限制。但多對一模型也有兩個缺點:

  1. 如果其中一個用戶線程阻塞,那麼其它所有線程都將無法執行,因爲此時內核線程也隨之阻塞了;

  2. 在多處理器系統上,處理器數量的增加對多對一模型的線程性能不會有明顯的增加,因爲所有的用戶線程都映射到一個處理器上了。

多對多模型 多對多模型結合了一對一模型和多對一模型的優點,將多個用戶線程映射到多個內核線程上。多對多模型的優點有:

  1. 一個用戶線程的阻塞不會導致所有線程的阻塞,因爲此時還有別的內核線程被調度來執行;

  2. 多對多模型對用戶線程的數量沒有限制;

  3. 在多處理器的操作系統中,多對多模型的線程也能得到一定的性能提升,但提升的幅度不如一對一模型的高。

進程與線程的區別

線程是程序執行的最小單位,而進程是操作系統分配資源的最小單位;

一個進程由一個或多個線程組成,線程是一個進程中代碼的不同執行路線;

進程之間相互獨立,但同一進程下的各個線程之間共享程序的內存空間 (包括代碼段、數據集、堆等) 及一些進程級的資源(如打開文件和信號),某進程內的線程在其它進程不可見;

調度和切換:線程上下文切換比進程上下文切換要快得多。

總之,線程和進程都是一種抽象的概念,線程是一種比進程更小的抽象,線程和進程都可用於實現併發。

在早期的操作系統中並沒有線程的概念,進程是能擁有資源和獨立運行的最小單位,也是程序執行的最小單位。它相當於一個進程裏只有一個線程,進程本身就是線程。所以線程有時被稱爲輕量級進程 (Lightweight Process,LWP)。

後來,隨着計算機的發展,對多個任務之間上下文切換的效率要求越來越高,就抽象出一個更小的概念——線程,一般一個進程會有多個 (也可是一個) 線程。

漫話進程和線程

  1. 計算機的核心是 CPU,它承擔了所有的計算任務。它就像一座工廠,時刻在運行。

  1. 假定工廠的電力有限,一次只能供給一個車間使用。也就是說,一個車間開工的時候,其他車間都必須停工。背後的含義就是,單個 CPU 一次只能運行一個任務。

  1. 進程就好比工廠的車間,它代表 CPU 所能處理的單個任務。任一時刻,CPU 總是運行一個進程,其他進程處於非運行狀態。

  1. 一個車間裏,可以有很多工人。他們協同完成一個任務。

  1. 線程就好比車間裏的工人。一個進程可以包括多個線程。

  1. 車間的空間是工人們共享的,比如許多房間是每個工人都可以進出的。這象徵一個進程的內存空間是共享的,每個線程都可以使用這些共享內存。

  1. 可是,每間房間的大小不同,有些房間最多隻能容納一個人,比如廁所。裏面有人的時候,其他人就不能進去了。這代表一個線程使用某些共享內存時,其他線程必須等它結束,才能使用這一塊內存。

  1. 一個防止他人進入的簡單方法,就是門口加一把鎖。先到的人鎖上門,後到的人看到上鎖,就在門口排隊,等鎖打開再進去。這就叫” 互斥鎖”(Mutual exclusion,縮寫 Mutex),防止多個線程同時讀寫某一塊內存區域。

  1. 還有些房間,可以同時容納 n 個人,比如廚房。也就是說,如果人數大於 n,多出來的人只能在外面等着。這好比某些內存區域,只能供給固定數目的線程使用。

  1. 這時的解決方法,就是在門口掛 n 把鑰匙。進去的人就取一把鑰匙,出來時再把鑰匙掛回原處。後到的人發現鑰匙架空了,就知道必須在門口排隊等着了。這種做法叫做” 信號量”(Semaphore),用來保證多個線程不會互相沖突。

不難看出,mutex 是 semaphore 的一種特殊情況(n=1 時)。也就是說,完全可以用後者替代前者。但是,因爲 mutex 較爲簡單,且效率高,所以在必須保證資源獨佔的情況下,還是採用這種設計。

操作系統的設計,因此可以歸結爲三點:

(1)以多進程形式,允許多個任務同時運行;

(2)以多線程形式,允許單個任務分成不同的部分運行;

(3)提供協調機制,一方面防止進程之間和線程之間產生衝突,另一方面允許進程之間和線程之間共享資源。

原文作者:Luckylau

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/eu-2xRS2YN61zRvwApXIag