一次面試中,面試官問:進程、線程和線程池瞭解過麼?
我:進程和線程瞭解過,線程池不太瞭解。
然後就沒有然後了。。。
還是得把基本概念瞭然於胸才行,下次才能給面試官扯清楚了!
一、進程
進程的概念
進程(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
結構體表示的,他們最大的不同就是共享的內存區域不同。
也就是說創建的子進程,會拷貝一份父進程的內存資源(個人感覺這裏有點深拷貝的意思);然後創建的子線程,共享父進程的那些內存資源(淺拷貝,父進程和子線程都有指針指向同一塊內存區域)。比如mm
和files
都是在線程中共享的。畫張圖就明白了。
三、線程池
線程池(thread pool):一種線程使用模式。線程過多會帶來調度開銷,進而影響緩存局部性和整體性能。而線程池維護着多個線程,等待着監督管理者分配可併發執行的任務。這避免了在處理短時間任務時創建與銷燬線程的代價。
任務調度以執行線程的常見方法是使用同步隊列,稱作任務隊列。池中的線程等待隊列中的任務,並把執行完的任務放入完成隊列中。(摘自wiki)
爲什麼要有線程池?
因爲有些場景需要頻繁地創建線程和銷燬線程,這樣使得系統的壓力很大。線程池可以在初始化的時候批量創建線程,然後通過隊列等方式提交業務邏輯,線程池中的線程進行邏輯的消費工作,可以在操作的過程中降低線程創建和銷燬的開銷,但是調度的開銷還是存在的。