Linux內核是如何創建線程的,它與windows有哪些不同?

2 個回答
嵌入式时代
2019-08-12

謝邀。

其實Linux創建進程,就是創建進程運行所需的內存空間,填充描述進程的 task_struct 結構體,以及加載進程的程序而已。

Linux 內核並無專門創建線程的機制

我們之前提到,Linux並不特殊對待線程,在Linux看來,線程不過就是一種特殊的進程而已。那麼,Linux是如何創建線程的呢?

線程機制是大多數現代編程語言都會提供的機制,該機制允許在同一進程的共享內存地址空間運行一組“特殊的進程(即線程)”。這些線程不僅共享同一段內存空間,還可以共享已經打開的文件,統計量等其他資源。線程機制支持程序併發運行,在多處理器核心的系統上,該併發機制能夠實現多條線程同時運行。

Linux 管理線程的方式不同於其他一些經典操作系統,Linux 並沒有線程的概念,它把線程當作進程的一個子集來管理。因此,Linux 內核並未為線程提供額外調度算法,也沒有提供額外的數據結構用於描述和存儲線程。

謝邀。

其實Linux創建進程,就是創建進程運行所需的內存空間,填充描述進程的 task_struct 結構體,以及加載進程的程序而已。

Linux 內核並無專門創建線程的機制

我們之前提到,Linux並不特殊對待線程,在Linux看來,線程不過就是一種特殊的進程而已。那麼,Linux是如何創建線程的呢?

線程機制是大多數現代編程語言都會提供的機制,該機制允許在同一進程的共享內存地址空間運行一組“特殊的進程(即線程)”。這些線程不僅共享同一段內存空間,還可以共享已經打開的文件,統計量等其他資源。線程機制支持程序併發運行,在多處理器核心的系統上,該併發機制能夠實現多條線程同時運行。

Linux 管理線程的方式不同於其他一些經典操作系統,Linux 並沒有線程的概念,它把線程當作進程的一個子集來管理。因此,Linux 內核並未為線程提供額外調度算法,也沒有提供額外的數據結構用於描述和存儲線程。

就像進程一樣,Linux 使用 task_struct 結構體描述和記錄線程,每個線程都有唯一屬於自己的 task_struct 結構。從這個角度來看,線程就是一個普通的進程,只不過線程可能和其他進程共享一些資源而已。

以 Windows 為代表的一些操作系統提供了專門用於創建線程的機制,在這些系統中,線程常常被稱作“輕量級進程”,因為相對於進程而言,線程耗費的資源較少,能夠較為迅速的創建和投入運行。

但是對於 Linux 而言,線程不過是進程之間共享資源的一種手段罷了。那麼是不是 Linux 中的線程比 Windows 中的線程更加“重量級”呢?也不是,因為 Linux 中的進程本身就很輕量級,Linux 創建進程所需時間,並不比 Windows 創建線程所需時間多多少。

從C語言代碼層面來看,假設某個進程包含 4 個線程,以 Windows 為代表的一些操作系統一般會有一個包含指向 4 個不同線程的指針的進程描述符,負責描述地址空間、打開的文件等共享資源,而線程本身再去描述自己獨佔的資源。

與之對應的,Linux 的做法就高雅許多,它僅需為這 4 個線程創建 4 個 task_struct 結構體,然後在 task_struct 中指定它們共享的資源就可以了。

創建線程

看了我最近幾篇文章的讀者應該已經明白,Linux 內核中的線程其實就是進程,因此線程的創建與進程的創建過程是類似的,從C語言源代碼層面看,基本上也是通過 fork() 函數和 exec() 函數族實現的。只不過在調用 clone() 函數時需要傳遞一個參數用於描述共享資源,例如:

謝邀。

其實Linux創建進程,就是創建進程運行所需的內存空間,填充描述進程的 task_struct 結構體,以及加載進程的程序而已。

Linux 內核並無專門創建線程的機制

我們之前提到,Linux並不特殊對待線程,在Linux看來,線程不過就是一種特殊的進程而已。那麼,Linux是如何創建線程的呢?

線程機制是大多數現代編程語言都會提供的機制,該機制允許在同一進程的共享內存地址空間運行一組“特殊的進程(即線程)”。這些線程不僅共享同一段內存空間,還可以共享已經打開的文件,統計量等其他資源。線程機制支持程序併發運行,在多處理器核心的系統上,該併發機制能夠實現多條線程同時運行。

Linux 管理線程的方式不同於其他一些經典操作系統,Linux 並沒有線程的概念,它把線程當作進程的一個子集來管理。因此,Linux 內核並未為線程提供額外調度算法,也沒有提供額外的數據結構用於描述和存儲線程。

就像進程一樣,Linux 使用 task_struct 結構體描述和記錄線程,每個線程都有唯一屬於自己的 task_struct 結構。從這個角度來看,線程就是一個普通的進程,只不過線程可能和其他進程共享一些資源而已。

以 Windows 為代表的一些操作系統提供了專門用於創建線程的機制,在這些系統中,線程常常被稱作“輕量級進程”,因為相對於進程而言,線程耗費的資源較少,能夠較為迅速的創建和投入運行。

但是對於 Linux 而言,線程不過是進程之間共享資源的一種手段罷了。那麼是不是 Linux 中的線程比 Windows 中的線程更加“重量級”呢?也不是,因為 Linux 中的進程本身就很輕量級,Linux 創建進程所需時間,並不比 Windows 創建線程所需時間多多少。

從C語言代碼層面來看,假設某個進程包含 4 個線程,以 Windows 為代表的一些操作系統一般會有一個包含指向 4 個不同線程的指針的進程描述符,負責描述地址空間、打開的文件等共享資源,而線程本身再去描述自己獨佔的資源。

與之對應的,Linux 的做法就高雅許多,它僅需為這 4 個線程創建 4 個 task_struct 結構體,然後在 task_struct 中指定它們共享的資源就可以了。

創建線程

看了我最近幾篇文章的讀者應該已經明白,Linux 內核中的線程其實就是進程,因此線程的創建與進程的創建過程是類似的,從C語言源代碼層面看,基本上也是通過 fork() 函數和 exec() 函數族實現的。只不過在調用 clone() 函數時需要傳遞一個參數用於描述共享資源,例如:

上面這行C語言代碼和調用 fork() 函數的結果差不多,只不過輸入的幾個參數標誌位說明了子進程與父進程共享一些資源:地址空間、文件系統、打開的文件、信號處理程序。

對比一下,fork() 基本上就相當於 clone(SIGCHLD, 0),這也是 fork() 函數創建的子進程之後不再與父進程共享資源的原因。

關於 clone() 函數的參數標誌位,可以在Linux中輸入 man 命令查看。

Linux 內核線程

就像用戶空間的C語言程序開發一樣,Linux 內核也經常需要在後臺處理數據,這時就需要藉助內核線程了。Linux 的內核線程一般不會獨立的地址空間,它們只在內核空間運行,不會切換到用戶空間。不過調度是和普通進程一樣的,可以被調度和搶佔。

Linux 創建內核線程由 kthread_create() 函數實現,它的C語言源代碼如下,請看:

謝邀。

其實Linux創建進程,就是創建進程運行所需的內存空間,填充描述進程的 task_struct 結構體,以及加載進程的程序而已。

Linux 內核並無專門創建線程的機制

我們之前提到,Linux並不特殊對待線程,在Linux看來,線程不過就是一種特殊的進程而已。那麼,Linux是如何創建線程的呢?

線程機制是大多數現代編程語言都會提供的機制,該機制允許在同一進程的共享內存地址空間運行一組“特殊的進程(即線程)”。這些線程不僅共享同一段內存空間,還可以共享已經打開的文件,統計量等其他資源。線程機制支持程序併發運行,在多處理器核心的系統上,該併發機制能夠實現多條線程同時運行。

Linux 管理線程的方式不同於其他一些經典操作系統,Linux 並沒有線程的概念,它把線程當作進程的一個子集來管理。因此,Linux 內核並未為線程提供額外調度算法,也沒有提供額外的數據結構用於描述和存儲線程。

就像進程一樣,Linux 使用 task_struct 結構體描述和記錄線程,每個線程都有唯一屬於自己的 task_struct 結構。從這個角度來看,線程就是一個普通的進程,只不過線程可能和其他進程共享一些資源而已。

以 Windows 為代表的一些操作系統提供了專門用於創建線程的機制,在這些系統中,線程常常被稱作“輕量級進程”,因為相對於進程而言,線程耗費的資源較少,能夠較為迅速的創建和投入運行。

但是對於 Linux 而言,線程不過是進程之間共享資源的一種手段罷了。那麼是不是 Linux 中的線程比 Windows 中的線程更加“重量級”呢?也不是,因為 Linux 中的進程本身就很輕量級,Linux 創建進程所需時間,並不比 Windows 創建線程所需時間多多少。

從C語言代碼層面來看,假設某個進程包含 4 個線程,以 Windows 為代表的一些操作系統一般會有一個包含指向 4 個不同線程的指針的進程描述符,負責描述地址空間、打開的文件等共享資源,而線程本身再去描述自己獨佔的資源。

與之對應的,Linux 的做法就高雅許多,它僅需為這 4 個線程創建 4 個 task_struct 結構體,然後在 task_struct 中指定它們共享的資源就可以了。

創建線程

看了我最近幾篇文章的讀者應該已經明白,Linux 內核中的線程其實就是進程,因此線程的創建與進程的創建過程是類似的,從C語言源代碼層面看,基本上也是通過 fork() 函數和 exec() 函數族實現的。只不過在調用 clone() 函數時需要傳遞一個參數用於描述共享資源,例如:

上面這行C語言代碼和調用 fork() 函數的結果差不多,只不過輸入的幾個參數標誌位說明了子進程與父進程共享一些資源:地址空間、文件系統、打開的文件、信號處理程序。

對比一下,fork() 基本上就相當於 clone(SIGCHLD, 0),這也是 fork() 函數創建的子進程之後不再與父進程共享資源的原因。

關於 clone() 函數的參數標誌位,可以在Linux中輸入 man 命令查看。

Linux 內核線程

就像用戶空間的C語言程序開發一樣,Linux 內核也經常需要在後臺處理數據,這時就需要藉助內核線程了。Linux 的內核線程一般不會獨立的地址空間,它們只在內核空間運行,不會切換到用戶空間。不過調度是和普通進程一樣的,可以被調度和搶佔。

Linux 創建內核線程由 kthread_create() 函數實現,它的C語言源代碼如下,請看:

可見,kthread_create() 函數的C語言代碼並不長,而且也可以看出,Linux 內核線程是通過 kthread_create_info 結構體描述的,它的定義C語言代碼如下,可見,內核線程的描述和存儲也是包含 task_struct 結構體的:

謝邀。

其實Linux創建進程,就是創建進程運行所需的內存空間,填充描述進程的 task_struct 結構體,以及加載進程的程序而已。

Linux 內核並無專門創建線程的機制

我們之前提到,Linux並不特殊對待線程,在Linux看來,線程不過就是一種特殊的進程而已。那麼,Linux是如何創建線程的呢?

線程機制是大多數現代編程語言都會提供的機制,該機制允許在同一進程的共享內存地址空間運行一組“特殊的進程(即線程)”。這些線程不僅共享同一段內存空間,還可以共享已經打開的文件,統計量等其他資源。線程機制支持程序併發運行,在多處理器核心的系統上,該併發機制能夠實現多條線程同時運行。

Linux 管理線程的方式不同於其他一些經典操作系統,Linux 並沒有線程的概念,它把線程當作進程的一個子集來管理。因此,Linux 內核並未為線程提供額外調度算法,也沒有提供額外的數據結構用於描述和存儲線程。

就像進程一樣,Linux 使用 task_struct 結構體描述和記錄線程,每個線程都有唯一屬於自己的 task_struct 結構。從這個角度來看,線程就是一個普通的進程,只不過線程可能和其他進程共享一些資源而已。

以 Windows 為代表的一些操作系統提供了專門用於創建線程的機制,在這些系統中,線程常常被稱作“輕量級進程”,因為相對於進程而言,線程耗費的資源較少,能夠較為迅速的創建和投入運行。

但是對於 Linux 而言,線程不過是進程之間共享資源的一種手段罷了。那麼是不是 Linux 中的線程比 Windows 中的線程更加“重量級”呢?也不是,因為 Linux 中的進程本身就很輕量級,Linux 創建進程所需時間,並不比 Windows 創建線程所需時間多多少。

從C語言代碼層面來看,假設某個進程包含 4 個線程,以 Windows 為代表的一些操作系統一般會有一個包含指向 4 個不同線程的指針的進程描述符,負責描述地址空間、打開的文件等共享資源,而線程本身再去描述自己獨佔的資源。

與之對應的,Linux 的做法就高雅許多,它僅需為這 4 個線程創建 4 個 task_struct 結構體,然後在 task_struct 中指定它們共享的資源就可以了。

創建線程

看了我最近幾篇文章的讀者應該已經明白,Linux 內核中的線程其實就是進程,因此線程的創建與進程的創建過程是類似的,從C語言源代碼層面看,基本上也是通過 fork() 函數和 exec() 函數族實現的。只不過在調用 clone() 函數時需要傳遞一個參數用於描述共享資源,例如:

上面這行C語言代碼和調用 fork() 函數的結果差不多,只不過輸入的幾個參數標誌位說明了子進程與父進程共享一些資源:地址空間、文件系統、打開的文件、信號處理程序。

對比一下,fork() 基本上就相當於 clone(SIGCHLD, 0),這也是 fork() 函數創建的子進程之後不再與父進程共享資源的原因。

關於 clone() 函數的參數標誌位,可以在Linux中輸入 man 命令查看。

Linux 內核線程

就像用戶空間的C語言程序開發一樣,Linux 內核也經常需要在後臺處理數據,這時就需要藉助內核線程了。Linux 的內核線程一般不會獨立的地址空間,它們只在內核空間運行,不會切換到用戶空間。不過調度是和普通進程一樣的,可以被調度和搶佔。

Linux 創建內核線程由 kthread_create() 函數實現,它的C語言源代碼如下,請看:

可見,kthread_create() 函數的C語言代碼並不長,而且也可以看出,Linux 內核線程是通過 kthread_create_info 結構體描述的,它的定義C語言代碼如下,可見,內核線程的描述和存儲也是包含 task_struct 結構體的:

kthread_create() 函數創建名為 namefmt 的線程,不過線程被創建後是處於不可運行狀態的,我們可以通過 wake_up_process() 函數喚醒它。當然,也可以通過 kthread_run() 方法實現這一過程,相關的C語言代碼如下,請看:

謝邀。

其實Linux創建進程,就是創建進程運行所需的內存空間,填充描述進程的 task_struct 結構體,以及加載進程的程序而已。

Linux 內核並無專門創建線程的機制

我們之前提到,Linux並不特殊對待線程,在Linux看來,線程不過就是一種特殊的進程而已。那麼,Linux是如何創建線程的呢?

線程機制是大多數現代編程語言都會提供的機制,該機制允許在同一進程的共享內存地址空間運行一組“特殊的進程(即線程)”。這些線程不僅共享同一段內存空間,還可以共享已經打開的文件,統計量等其他資源。線程機制支持程序併發運行,在多處理器核心的系統上,該併發機制能夠實現多條線程同時運行。

Linux 管理線程的方式不同於其他一些經典操作系統,Linux 並沒有線程的概念,它把線程當作進程的一個子集來管理。因此,Linux 內核並未為線程提供額外調度算法,也沒有提供額外的數據結構用於描述和存儲線程。

就像進程一樣,Linux 使用 task_struct 結構體描述和記錄線程,每個線程都有唯一屬於自己的 task_struct 結構。從這個角度來看,線程就是一個普通的進程,只不過線程可能和其他進程共享一些資源而已。

以 Windows 為代表的一些操作系統提供了專門用於創建線程的機制,在這些系統中,線程常常被稱作“輕量級進程”,因為相對於進程而言,線程耗費的資源較少,能夠較為迅速的創建和投入運行。

但是對於 Linux 而言,線程不過是進程之間共享資源的一種手段罷了。那麼是不是 Linux 中的線程比 Windows 中的線程更加“重量級”呢?也不是,因為 Linux 中的進程本身就很輕量級,Linux 創建進程所需時間,並不比 Windows 創建線程所需時間多多少。

從C語言代碼層面來看,假設某個進程包含 4 個線程,以 Windows 為代表的一些操作系統一般會有一個包含指向 4 個不同線程的指針的進程描述符,負責描述地址空間、打開的文件等共享資源,而線程本身再去描述自己獨佔的資源。

與之對應的,Linux 的做法就高雅許多,它僅需為這 4 個線程創建 4 個 task_struct 結構體,然後在 task_struct 中指定它們共享的資源就可以了。

創建線程

看了我最近幾篇文章的讀者應該已經明白,Linux 內核中的線程其實就是進程,因此線程的創建與進程的創建過程是類似的,從C語言源代碼層面看,基本上也是通過 fork() 函數和 exec() 函數族實現的。只不過在調用 clone() 函數時需要傳遞一個參數用於描述共享資源,例如:

上面這行C語言代碼和調用 fork() 函數的結果差不多,只不過輸入的幾個參數標誌位說明了子進程與父進程共享一些資源:地址空間、文件系統、打開的文件、信號處理程序。

對比一下,fork() 基本上就相當於 clone(SIGCHLD, 0),這也是 fork() 函數創建的子進程之後不再與父進程共享資源的原因。

關於 clone() 函數的參數標誌位,可以在Linux中輸入 man 命令查看。

Linux 內核線程

就像用戶空間的C語言程序開發一樣,Linux 內核也經常需要在後臺處理數據,這時就需要藉助內核線程了。Linux 的內核線程一般不會獨立的地址空間,它們只在內核空間運行,不會切換到用戶空間。不過調度是和普通進程一樣的,可以被調度和搶佔。

Linux 創建內核線程由 kthread_create() 函數實現,它的C語言源代碼如下,請看:

可見,kthread_create() 函數的C語言代碼並不長,而且也可以看出,Linux 內核線程是通過 kthread_create_info 結構體描述的,它的定義C語言代碼如下,可見,內核線程的描述和存儲也是包含 task_struct 結構體的:

kthread_create() 函數創建名為 namefmt 的線程,不過線程被創建後是處於不可運行狀態的,我們可以通過 wake_up_process() 函數喚醒它。當然,也可以通過 kthread_run() 方法實現這一過程,相關的C語言代碼如下,請看:

其實就是將 kthread_create() 函數和 wake_up_process() 函數組合到一起而已。Linux 的內核線程被啟動後,會一直運行到調用 do_exit() 退出。我們也可以調用 kthread_stop() 函數提前結束它,相關的C語言代碼如下,請看:

謝邀。

其實Linux創建進程,就是創建進程運行所需的內存空間,填充描述進程的 task_struct 結構體,以及加載進程的程序而已。

Linux 內核並無專門創建線程的機制

我們之前提到,Linux並不特殊對待線程,在Linux看來,線程不過就是一種特殊的進程而已。那麼,Linux是如何創建線程的呢?

線程機制是大多數現代編程語言都會提供的機制,該機制允許在同一進程的共享內存地址空間運行一組“特殊的進程(即線程)”。這些線程不僅共享同一段內存空間,還可以共享已經打開的文件,統計量等其他資源。線程機制支持程序併發運行,在多處理器核心的系統上,該併發機制能夠實現多條線程同時運行。

Linux 管理線程的方式不同於其他一些經典操作系統,Linux 並沒有線程的概念,它把線程當作進程的一個子集來管理。因此,Linux 內核並未為線程提供額外調度算法,也沒有提供額外的數據結構用於描述和存儲線程。

就像進程一樣,Linux 使用 task_struct 結構體描述和記錄線程,每個線程都有唯一屬於自己的 task_struct 結構。從這個角度來看,線程就是一個普通的進程,只不過線程可能和其他進程共享一些資源而已。

以 Windows 為代表的一些操作系統提供了專門用於創建線程的機制,在這些系統中,線程常常被稱作“輕量級進程”,因為相對於進程而言,線程耗費的資源較少,能夠較為迅速的創建和投入運行。

但是對於 Linux 而言,線程不過是進程之間共享資源的一種手段罷了。那麼是不是 Linux 中的線程比 Windows 中的線程更加“重量級”呢?也不是,因為 Linux 中的進程本身就很輕量級,Linux 創建進程所需時間,並不比 Windows 創建線程所需時間多多少。

從C語言代碼層面來看,假設某個進程包含 4 個線程,以 Windows 為代表的一些操作系統一般會有一個包含指向 4 個不同線程的指針的進程描述符,負責描述地址空間、打開的文件等共享資源,而線程本身再去描述自己獨佔的資源。

與之對應的,Linux 的做法就高雅許多,它僅需為這 4 個線程創建 4 個 task_struct 結構體,然後在 task_struct 中指定它們共享的資源就可以了。

創建線程

看了我最近幾篇文章的讀者應該已經明白,Linux 內核中的線程其實就是進程,因此線程的創建與進程的創建過程是類似的,從C語言源代碼層面看,基本上也是通過 fork() 函數和 exec() 函數族實現的。只不過在調用 clone() 函數時需要傳遞一個參數用於描述共享資源,例如:

上面這行C語言代碼和調用 fork() 函數的結果差不多,只不過輸入的幾個參數標誌位說明了子進程與父進程共享一些資源:地址空間、文件系統、打開的文件、信號處理程序。

對比一下,fork() 基本上就相當於 clone(SIGCHLD, 0),這也是 fork() 函數創建的子進程之後不再與父進程共享資源的原因。

關於 clone() 函數的參數標誌位,可以在Linux中輸入 man 命令查看。

Linux 內核線程

就像用戶空間的C語言程序開發一樣,Linux 內核也經常需要在後臺處理數據,這時就需要藉助內核線程了。Linux 的內核線程一般不會獨立的地址空間,它們只在內核空間運行,不會切換到用戶空間。不過調度是和普通進程一樣的,可以被調度和搶佔。

Linux 創建內核線程由 kthread_create() 函數實現,它的C語言源代碼如下,請看:

可見,kthread_create() 函數的C語言代碼並不長,而且也可以看出,Linux 內核線程是通過 kthread_create_info 結構體描述的,它的定義C語言代碼如下,可見,內核線程的描述和存儲也是包含 task_struct 結構體的:

kthread_create() 函數創建名為 namefmt 的線程,不過線程被創建後是處於不可運行狀態的,我們可以通過 wake_up_process() 函數喚醒它。當然,也可以通過 kthread_run() 方法實現這一過程,相關的C語言代碼如下,請看:

其實就是將 kthread_create() 函數和 wake_up_process() 函數組合到一起而已。Linux 的內核線程被啟動後,會一直運行到調用 do_exit() 退出。我們也可以調用 kthread_stop() 函數提前結束它,相關的C語言代碼如下,請看:

kthread_stop() 函數接收的參數為 kthread_create() 函數創建的結構體的 task_struct 成員。從C語言代碼可以看出,kthread_stop() 其實也是會調用 wake_up_process() 函數喚醒線程的,它在喚醒線程後,會等待線程函數退出,並不會調用 threadfn() 函數。

這裡需要注意,如果創建的線程函數 threadfn() 調用了 do_exit() 函數,最好就不要再調用 kthread_stop() 函數了。

kthread_stop() 函數等待線程退出是通過 wait_for_completion() 函數實現的,相關的C語言代碼如下,請看:

謝邀。

其實Linux創建進程,就是創建進程運行所需的內存空間,填充描述進程的 task_struct 結構體,以及加載進程的程序而已。

Linux 內核並無專門創建線程的機制

我們之前提到,Linux並不特殊對待線程,在Linux看來,線程不過就是一種特殊的進程而已。那麼,Linux是如何創建線程的呢?

線程機制是大多數現代編程語言都會提供的機制,該機制允許在同一進程的共享內存地址空間運行一組“特殊的進程(即線程)”。這些線程不僅共享同一段內存空間,還可以共享已經打開的文件,統計量等其他資源。線程機制支持程序併發運行,在多處理器核心的系統上,該併發機制能夠實現多條線程同時運行。

Linux 管理線程的方式不同於其他一些經典操作系統,Linux 並沒有線程的概念,它把線程當作進程的一個子集來管理。因此,Linux 內核並未為線程提供額外調度算法,也沒有提供額外的數據結構用於描述和存儲線程。

就像進程一樣,Linux 使用 task_struct 結構體描述和記錄線程,每個線程都有唯一屬於自己的 task_struct 結構。從這個角度來看,線程就是一個普通的進程,只不過線程可能和其他進程共享一些資源而已。

以 Windows 為代表的一些操作系統提供了專門用於創建線程的機制,在這些系統中,線程常常被稱作“輕量級進程”,因為相對於進程而言,線程耗費的資源較少,能夠較為迅速的創建和投入運行。

但是對於 Linux 而言,線程不過是進程之間共享資源的一種手段罷了。那麼是不是 Linux 中的線程比 Windows 中的線程更加“重量級”呢?也不是,因為 Linux 中的進程本身就很輕量級,Linux 創建進程所需時間,並不比 Windows 創建線程所需時間多多少。

從C語言代碼層面來看,假設某個進程包含 4 個線程,以 Windows 為代表的一些操作系統一般會有一個包含指向 4 個不同線程的指針的進程描述符,負責描述地址空間、打開的文件等共享資源,而線程本身再去描述自己獨佔的資源。

與之對應的,Linux 的做法就高雅許多,它僅需為這 4 個線程創建 4 個 task_struct 結構體,然後在 task_struct 中指定它們共享的資源就可以了。

創建線程

看了我最近幾篇文章的讀者應該已經明白,Linux 內核中的線程其實就是進程,因此線程的創建與進程的創建過程是類似的,從C語言源代碼層面看,基本上也是通過 fork() 函數和 exec() 函數族實現的。只不過在調用 clone() 函數時需要傳遞一個參數用於描述共享資源,例如:

上面這行C語言代碼和調用 fork() 函數的結果差不多,只不過輸入的幾個參數標誌位說明了子進程與父進程共享一些資源:地址空間、文件系統、打開的文件、信號處理程序。

對比一下,fork() 基本上就相當於 clone(SIGCHLD, 0),這也是 fork() 函數創建的子進程之後不再與父進程共享資源的原因。

關於 clone() 函數的參數標誌位,可以在Linux中輸入 man 命令查看。

Linux 內核線程

就像用戶空間的C語言程序開發一樣,Linux 內核也經常需要在後臺處理數據,這時就需要藉助內核線程了。Linux 的內核線程一般不會獨立的地址空間,它們只在內核空間運行,不會切換到用戶空間。不過調度是和普通進程一樣的,可以被調度和搶佔。

Linux 創建內核線程由 kthread_create() 函數實現,它的C語言源代碼如下,請看:

可見,kthread_create() 函數的C語言代碼並不長,而且也可以看出,Linux 內核線程是通過 kthread_create_info 結構體描述的,它的定義C語言代碼如下,可見,內核線程的描述和存儲也是包含 task_struct 結構體的:

kthread_create() 函數創建名為 namefmt 的線程,不過線程被創建後是處於不可運行狀態的,我們可以通過 wake_up_process() 函數喚醒它。當然,也可以通過 kthread_run() 方法實現這一過程,相關的C語言代碼如下,請看:

其實就是將 kthread_create() 函數和 wake_up_process() 函數組合到一起而已。Linux 的內核線程被啟動後,會一直運行到調用 do_exit() 退出。我們也可以調用 kthread_stop() 函數提前結束它,相關的C語言代碼如下,請看:

kthread_stop() 函數接收的參數為 kthread_create() 函數創建的結構體的 task_struct 成員。從C語言代碼可以看出,kthread_stop() 其實也是會調用 wake_up_process() 函數喚醒線程的,它在喚醒線程後,會等待線程函數退出,並不會調用 threadfn() 函數。

這裡需要注意,如果創建的線程函數 threadfn() 調用了 do_exit() 函數,最好就不要再調用 kthread_stop() 函數了。

kthread_stop() 函數等待線程退出是通過 wait_for_completion() 函數實現的,相關的C語言代碼如下,請看:

稍稍跟蹤一下C語言代碼,發現其實這一等待過程是由 do_wait_for_common()函數實現的,它的C語言代碼如下,請看:

謝邀。

其實Linux創建進程,就是創建進程運行所需的內存空間,填充描述進程的 task_struct 結構體,以及加載進程的程序而已。

Linux 內核並無專門創建線程的機制

我們之前提到,Linux並不特殊對待線程,在Linux看來,線程不過就是一種特殊的進程而已。那麼,Linux是如何創建線程的呢?

線程機制是大多數現代編程語言都會提供的機制,該機制允許在同一進程的共享內存地址空間運行一組“特殊的進程(即線程)”。這些線程不僅共享同一段內存空間,還可以共享已經打開的文件,統計量等其他資源。線程機制支持程序併發運行,在多處理器核心的系統上,該併發機制能夠實現多條線程同時運行。

Linux 管理線程的方式不同於其他一些經典操作系統,Linux 並沒有線程的概念,它把線程當作進程的一個子集來管理。因此,Linux 內核並未為線程提供額外調度算法,也沒有提供額外的數據結構用於描述和存儲線程。

就像進程一樣,Linux 使用 task_struct 結構體描述和記錄線程,每個線程都有唯一屬於自己的 task_struct 結構。從這個角度來看,線程就是一個普通的進程,只不過線程可能和其他進程共享一些資源而已。

以 Windows 為代表的一些操作系統提供了專門用於創建線程的機制,在這些系統中,線程常常被稱作“輕量級進程”,因為相對於進程而言,線程耗費的資源較少,能夠較為迅速的創建和投入運行。

但是對於 Linux 而言,線程不過是進程之間共享資源的一種手段罷了。那麼是不是 Linux 中的線程比 Windows 中的線程更加“重量級”呢?也不是,因為 Linux 中的進程本身就很輕量級,Linux 創建進程所需時間,並不比 Windows 創建線程所需時間多多少。

從C語言代碼層面來看,假設某個進程包含 4 個線程,以 Windows 為代表的一些操作系統一般會有一個包含指向 4 個不同線程的指針的進程描述符,負責描述地址空間、打開的文件等共享資源,而線程本身再去描述自己獨佔的資源。

與之對應的,Linux 的做法就高雅許多,它僅需為這 4 個線程創建 4 個 task_struct 結構體,然後在 task_struct 中指定它們共享的資源就可以了。

創建線程

看了我最近幾篇文章的讀者應該已經明白,Linux 內核中的線程其實就是進程,因此線程的創建與進程的創建過程是類似的,從C語言源代碼層面看,基本上也是通過 fork() 函數和 exec() 函數族實現的。只不過在調用 clone() 函數時需要傳遞一個參數用於描述共享資源,例如:

上面這行C語言代碼和調用 fork() 函數的結果差不多,只不過輸入的幾個參數標誌位說明了子進程與父進程共享一些資源:地址空間、文件系統、打開的文件、信號處理程序。

對比一下,fork() 基本上就相當於 clone(SIGCHLD, 0),這也是 fork() 函數創建的子進程之後不再與父進程共享資源的原因。

關於 clone() 函數的參數標誌位,可以在Linux中輸入 man 命令查看。

Linux 內核線程

就像用戶空間的C語言程序開發一樣,Linux 內核也經常需要在後臺處理數據,這時就需要藉助內核線程了。Linux 的內核線程一般不會獨立的地址空間,它們只在內核空間運行,不會切換到用戶空間。不過調度是和普通進程一樣的,可以被調度和搶佔。

Linux 創建內核線程由 kthread_create() 函數實現,它的C語言源代碼如下,請看:

可見,kthread_create() 函數的C語言代碼並不長,而且也可以看出,Linux 內核線程是通過 kthread_create_info 結構體描述的,它的定義C語言代碼如下,可見,內核線程的描述和存儲也是包含 task_struct 結構體的:

kthread_create() 函數創建名為 namefmt 的線程,不過線程被創建後是處於不可運行狀態的,我們可以通過 wake_up_process() 函數喚醒它。當然,也可以通過 kthread_run() 方法實現這一過程,相關的C語言代碼如下,請看:

其實就是將 kthread_create() 函數和 wake_up_process() 函數組合到一起而已。Linux 的內核線程被啟動後,會一直運行到調用 do_exit() 退出。我們也可以調用 kthread_stop() 函數提前結束它,相關的C語言代碼如下,請看:

kthread_stop() 函數接收的參數為 kthread_create() 函數創建的結構體的 task_struct 成員。從C語言代碼可以看出,kthread_stop() 其實也是會調用 wake_up_process() 函數喚醒線程的,它在喚醒線程後,會等待線程函數退出,並不會調用 threadfn() 函數。

這裡需要注意,如果創建的線程函數 threadfn() 調用了 do_exit() 函數,最好就不要再調用 kthread_stop() 函數了。

kthread_stop() 函數等待線程退出是通過 wait_for_completion() 函數實現的,相關的C語言代碼如下,請看:

稍稍跟蹤一下C語言代碼,發現其實這一等待過程是由 do_wait_for_common()函數實現的,它的C語言代碼如下,請看:

還是比較清晰的,這裡就不再贅述了。至此,我們就瞭解了Linux內核是如何創建線程並投入運行,以及如何結束內核線程的了。

小結

本節主要討論了 Linux 內核中的線程的創建,應該能夠看出,其實核心還是圍繞對 task_struct 結構的管理,這與管理進程並無過多區別。因此,說Linux中的線程只是一種特殊的進程,一點也不為過。

陶陶然的心语坊
2019-08-15

首先關於內核心進程創建,涉及到底層的東西,個人認為沒必要太深入,瞭解他是怎麼實現的就可了,如果確實要深入理解,那就得去看源碼了。

1.Linux 進程創建:Linux繼承了UNIX的進程創建方式,用的是fork API函數,什麼是fork呢,就是先clone然後在分支,父子進程各幹各的。

2.Windows

進程創建:Windows沒有fork,但是有CreateProcess這個API函數,用來創建一個新的進程和它的主線程,這個新進程運行指定的可執行文件。

相關推薦

推薦中...