捋一捋進程、線程、線程池的概念

一次面試中,面試官問:進程、線程和線程池瞭解過麼?

我:進程和線程瞭解過,線程池不太瞭解。

然後就沒有然後了。。。

還是得把基本概念瞭然於胸才行,下次才能給面試官扯清楚了!

一、進程

進程的概念

進程(process),是指計算機中已運行的程序,是操作系統中分配資源的最小單位。但是進程不是基本的運行單位(線程纔是),而是線程的容器

當我們用 C 語言寫一個 hello 程序,編譯後得到可執行文件a.out,在終端輸入./a.out,然後回車,這就等同於我們下達了運行程序的命令,就會產生進程。進程是一個活動的實體,它需要一些資源才能夠完成工作,比如CPU使用時間、存儲器、文件以及I/O設備等。這裏就可以理解前面說的進程就是分配系統資源的最小單位。每個進程都有自己的地址空間,具體看一下進程的存儲空間分佈情況,下圖是32位Inter x86的Linux中典型的存儲空間佈局,主要有四部分組成:

  • 正文段:存儲CPU執行的機器指令
  • 數據段
    • 初始化的數據
    • 未初始化的數據(也被稱爲bss段)
  • 堆棧段
    • 堆:分配的動態內存就存放在堆區
    • 棧:函數調用所需要的信息存放在這一部分

在這裏插入圖片描述

爲什麼要有進程?

最開始,編程是通過在紙帶上打孔,然後將紙帶輸入計算機進行運行,一個紙帶讀完了才能讀下一個紙帶。爲了解決這種低效率的排隊等候問題,有人發明了批處理系統。將程序(也就是任務)成批地提交給計算機,由計算機自動完成後再輸出結果,從而減少作業建立和結束過程中的時間浪費。後來,彙編語言出現,就不再用二進制編碼了。但是也有問題產生,當一個程序運行的時候,會一直佔用CPU的資源,如果這個程序的大部分時間都在執行IO密集型任務(比如網絡IO、磁盤IO等操作,這種任務比較耗時,但CPU消耗不大,因爲操作IO的速度遠低於CPU和內存的速度),該程序霸佔着CPU會造成資源的浪費,此時如果將CPU讓給其他的程序使用,等到當前的程序完成數據的接收,再將CPU的資源交還回來,這樣就達到了資源的最大利用。但是如何才能解決這個問題呢?

核心也就是如何解決管理和控制不同程序間的計算機資源?解決方案:定義最小的資源分配單位,也就是進程,這也是爲什麼說進程是分配資源的最小單位。有了進程概念後,每個要運行的程序都分配指定的資源,這樣將資源細分化,使得資源被充分地利用,多個程序分時共享硬件和軟件資源,因爲速度很快,對於我們用戶,就感覺多個程序在同時執行。

二、線程

線程的概念

線程(thread)是操作系統能夠進行運算調度的最小單位。大部分情況下,它被包含在進程之中,是進程中的實際運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以併發多個線程,每條線程並行執行不同的任務。(摘自wiki

爲什麼要有線程呢?

進程能有效提升CPU的利用率。但進程間依然有資源利用優化空間,以及進程間通信的麻煩問題。多線程則可以共享同一進程地址空間上的資源,能在資源的空閒時刻更好的利用,且不存在進程間通信的麻煩。

線程和進程在Linux內核如何實現的?

Linux系統是如何創建進程的呢?對於操作系統,進程就是一個數據結構,我們直接來看 Linux 的源碼(include/linux/sched.h 632行 ):

struct task_struct {
    // 進程狀態
    volatile long			state;
    // 虛擬內存結構體
    struct mm_struct  *mm;
    // 進程號
    pid_t              pid;
    // 指向父進程的指針
    struct task_struct __rcu  *parent;
    // 子進程列表
    struct list_head        children;
    // 存放文件系統信息的指針
    struct fs_struct        *fs;
    // 一個數組,包含該進程打開的文件指針
    struct files_struct        *files;
};

task_struct就是 Linux 內核對於一個進程的描述,也可以稱爲「進程描述符」。mm指向的是進程的虛擬內存,也就是載入資源和可執行文件的地方;files指針指向一個數組,這個數組裏裝着所有該進程打開的文件的指針。每個進程被創建時,files的前三位被填入默認值,分別指向標準輸入流(files[0])、標準輸出流(files[1])、標準錯誤流(files[2])。

首先要明確的是,多進程和多線程都是併發,可以提高處理器的利用效率。我們知道系統調用fork()可以新建一個進程,函數pthread()可以創建一個線程。但是進程和線程都是用task_struct結構體表示的,他們最大的不同就是共享的內存區域不同。

也就是說創建的子進程,會拷貝一份父進程的內存資源(個人感覺這裏有點深拷貝的意思);然後創建的子線程,共享父進程的那些內存資源(淺拷貝,父進程和子線程都有指針指向同一塊內存區域)。比如mmfiles都是在線程中共享的。畫張圖就明白了。

在這裏插入圖片描述
在這裏插入圖片描述

三、線程池

線程池(thread pool):一種線程使用模式。線程過多會帶來調度開銷,進而影響緩存局部性和整體性能。而線程池維護着多個線程,等待着監督管理者分配可併發執行的任務。這避免了在處理短時間任務時創建與銷燬線程的代價。

任務調度以執行線程的常見方法是使用同步隊列,稱作任務隊列。池中的線程等待隊列中的任務,並把執行完的任務放入完成隊列中。(摘自wiki

爲什麼要有線程池?

因爲有些場景需要頻繁地創建線程和銷燬線程,這樣使得系統的壓力很大。線程池可以在初始化的時候批量創建線程,然後通過隊列等方式提交業務邏輯,線程池中的線程進行邏輯的消費工作,可以在操作的過程中降低線程創建和銷燬的開銷,但是調度的開銷還是存在的。

參考文章

linux進程、線程、線程池和協程的由來

Linux的進程、線程、文件描述符是什麼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章