架構學習-多工

ZhanLi發表於2024-04-13
  • 架構學習-多工:程序,執行緒,協程
    • 多工
    • 參考

架構學習-多工:程序,執行緒,協程

多工

多工處理:是指計算機同時執行多個程式的能力。比如說,我們在使用電腦的時候,可以邊聽音樂,邊寫文件。

從物理層面上看,最早的 CPU 都是單核的,也就是同一時間只能執行一條指令。

單核 CPU 是如何支援多工處理的呢?方法是把 CPU 切成一段段的時間片,每個時間片只執行某一個軟體。多工的一般方法執行第一個程式的一段程式碼,儲存工作環境;在執行第二個程式的一段程式碼,儲存環境;恢復第一個程式的工作環境,執行第一個程式的下的一段程式碼...現代的多工,每個程式的時間分配相對平均。

每個時間片,只執行某一個軟體,因為時間片很小,我們會感覺這些軟體在同時執行。這種多時間分片實現的多工系統,我們稱之為分時系統。

那麼這個任務指的是什麼呢?從今天的現實看,任務的抽象並不是唯⼀的。⼤部分作業系統提供了兩套:程序和執行緒。有的 作業系統還會提供第三套叫協程。

來看下這幾個的區別

| 執行體 | 地址空間 | 排程方 | 時間片排程 | 主動排程 |
| 程序 |不同的執行體有不同的地址空間| 作業系統核心 | 基於時鐘中斷 | 系統呼叫syscall |
| 執行緒 |不同的執行體共享地址空間 | 作業系統核心 | 基於時鐘中斷 | 系統呼叫syscall |
| 協程 |不同的執行體共享地址空間 | 使用者態 | 一般不支援 | 包裝系統呼叫 |

這裡著重來了解下協程

協程是一種比執行緒更加輕量的執行體,協程完全有程式控制(在使用者態執行),能夠帶來效能的大幅度提升。

一個作業系統中可以有多個程序;一個程序可以有多個執行緒;同理,一個執行緒可以有多個協程。

協程的存在能夠高效的利用執行緒,減少執行緒的建立。為什麼需要減少執行緒的數量呢,因為執行緒建立的成本比較高。

執行緒的時間成本:

1、執行體切換本身的開銷,主要是暫存器儲存和回覆的成本;

2、執行體的排程開銷,主要是如何在大量已準備好的執行體中選出誰獲得執行權;

3、執行體之間的同步互斥成本。

執行緒的空間成本:

1、執行體的執行狀態;

2、TLS(執行緒區域性儲存);

3、執行體的堆疊。

這裡首先來看下,什麼是暫存器,暫存器是中央處理器用來佔存記憶體指令,資料和地址的電腦儲存器。暫存器的儲存容量有限,讀寫速度非常快。在計算機體系結構裡,暫存器儲存在已知的時間點作為計算的中間結果,透過快速的訪問資料來加速計算器程式的訪問。

預設情況下,Linux 中執行緒數量在數 MB 左右,最大的成本是堆疊(雖然堆疊的大小可以設定的,但是處於執行緒執行的安全考慮,執行緒的堆疊不能太小),假定一個執行緒 1MB,那麼 1000 個執行緒的大小就到了 GB 級別了,消耗還是很大的。

協程的存在能夠降低執行體的空間和時間成本。

一個完備的協程庫可以理解成使用者態的操作執行緒,而協程就是使用者態中的執行緒。

有兩個語言實現了完整的協程庫,GO 語言和 Erlang 語言。Go 語言裡面的使用者態"程序"叫 goroutine。有下面的一些設計:

1、堆疊開始很小(只有4k),可以按需增長;

2、幹掉了"執行緒區域性儲存(TLS)"特性的支援,執行體更加精簡;

3、提供了同步,互斥和其他常規執行體的通訊手段,比如 channel;

4、提供了幾乎所有重要的系統呼叫(尤其是IO請求)的包裝。

作業系統所有涉及系統呼叫的方法都在核心空間,包括磁碟讀寫,記憶體分配回收,網路介面讀寫,都是 web 應用巨頻繁使用的。

如果是多執行緒的,執行緒在進行 I/O 操作時需要從使用者態切換到核心態,等待 I/O 的過程,需要進行核心態執行緒的切換,然後再從核心態切換到使用者態,時間和空間的開銷成本都很大。

這裡提到了核心態和使用者態,這裡來了解下,為什麼會有核心態和使用者態,以及為什麼會有核心態和使用者態的切換過程。

CPU將指令分為特權和非特權兩類,以防止危險指令如清記憶體或設定時鐘被濫用導致系統崩潰。只有作業系統級別的程式才能執行特權指令,而普通應用程式只能執行安全的非特權指令。Intel CPU 設有四個特權級別:RING0 到 RING3,其中 RING0 許可權最高。

Linux核心為每個使用者程序提供了一種機制,使得它們在執行系統呼叫時能夠安全地從使用者模式切換到核心模式,並在核心地址空間中執行,彷彿每個程序都有自己的核心副本。

當一個任務(程序)執行系呼叫而陷入到核心程式碼中執行時,我們就稱程式碼處於核心執行態(簡稱為核心態)。此時,程式處於特權級最高的(0級)核心程式碼中執行。當程序處於核心態的時候,執行的核心程式碼會使用當前程序的核心棧,每個程序也都有自己的核心棧。

當程序在執行自己的程式碼的時,則稱為使用者使用者執行態(使用者態),即此時處理器在特群最低的(3級)使用者程式碼中執行。當正在執行使用者程式突然被中斷程式中斷時,此時使用者程式也會象徵性的處於程序的核心態,因為中斷程序將使用當前程序的核心棧,這與處於核心態的程序有點類似。

使用者態切換到核心態的三種方式

1、系統呼叫

這是使用者態主動切換到核心態的一種方式,使用者態透過系統呼叫申請操作系提供的服務程式完成工作,比如 fork() 實際上是執行了一個建立新程序的系統呼叫。而系統呼叫機制的核心還是使用了作業系統給使用者特別開放的一箇中斷實現的,例如Linux的int 80h中斷。

2、中斷

當外圍裝置完成使用者請求的操作後,會向 CPU 發出相應的中斷訊號,這時 cpu 會暫停執行下一條即將執行的指令轉而去執行與中斷訊號對應的處理程式,如果先前執行的指令是使用者態下面的程式,那麼這個過程就發生了從使用者態核心態的切換。比如硬碟的讀寫操作完成,系統會切換到硬碟讀寫的中斷處理程式執行的後續操作。

3、異常

當 cpu 在執行執行在使用者態下的程式時,發生了事先不可知的異常,這時會觸發由當前執行程序切換到處理此異常的核心相關程式中,也就轉換到了核心態,比如缺頁中斷。

使用者態和核心態主要限制不同程式之間的訪問能力,防止他們獲取別的程式的記憶體資料,或者外圍裝置的資料,併傳送到網路,CPU 劃分出兩個許可權等級-使用者態和核心態。

參考

【多工處理】https://zh.wikipedia.org/wiki/多工處理
【物件導向程式設計】https://www.liaoxuefeng.com/wiki/1016959663602400/1017495723838528
【許式偉的架構課】https://time.geekbang.org/column/article/89668
【作業系統為什麼要分使用者態和核心態】https://blog.csdn.net/chen134225/article/details/81783980

相關文章