上一節簡要討論了下Linux操作系統中進程的概念,其實簡單來說,進程無非就是處於運行期的程序及其相關資源的總和。這裡讀者應該注意“相關資源”一詞,Linux 在內核中是如何記錄進程的資源的呢?
上一節簡要討論了下Linux操作系統中進程的概念,其實簡單來說,進程無非就是處於運行期的程序及其相關資源的總和。這裡讀者應該注意“相關資源”一詞,Linux 在內核中是如何記錄進程的資源的呢?
Linux內核如何記錄進程的資源?
首先應該明白,Linux 內核大都是採用C語言編寫的,因此要弄清楚內核如何記錄進程資源,只需要查看相關的C語言代碼就可以了。事實上,Linux 內核是使用 task_struct 結構體描述進程的資源的,它的C語言部分代碼如下,請看:
上一節簡要討論了下Linux操作系統中進程的概念,其實簡單來說,進程無非就是處於運行期的程序及其相關資源的總和。這裡讀者應該注意“相關資源”一詞,Linux 在內核中是如何記錄進程的資源的呢?
Linux內核如何記錄進程的資源?
首先應該明白,Linux 內核大都是採用C語言編寫的,因此要弄清楚內核如何記錄進程資源,只需要查看相關的C語言代碼就可以了。事實上,Linux 內核是使用 task_struct 結構體描述進程的資源的,它的C語言部分代碼如下,請看:
task_struct 結構體很長,在我手中的 Linux 內核C語言源代碼中,它佔用了280行。當然了,這其中包含很多條件編譯部分,在 32 位機器上,task_struct 大約要佔用 1.7 KB 的內存空間,不過考慮到它可以管理完整的進程,1.7kB 其實並不算大了。
鑑於 task_struct 結構體過長,這裡不可能將其成員一一介紹清楚。如果讀者和我一樣好奇,粗略的瀏覽 task_struct 結構體,應該能夠發現一些比較令人熟悉的成員,例如:
上一節簡要討論了下Linux操作系統中進程的概念,其實簡單來說,進程無非就是處於運行期的程序及其相關資源的總和。這裡讀者應該注意“相關資源”一詞,Linux 在內核中是如何記錄進程的資源的呢?
Linux內核如何記錄進程的資源?
首先應該明白,Linux 內核大都是採用C語言編寫的,因此要弄清楚內核如何記錄進程資源,只需要查看相關的C語言代碼就可以了。事實上,Linux 內核是使用 task_struct 結構體描述進程的資源的,它的C語言部分代碼如下,請看:
task_struct 結構體很長,在我手中的 Linux 內核C語言源代碼中,它佔用了280行。當然了,這其中包含很多條件編譯部分,在 32 位機器上,task_struct 大約要佔用 1.7 KB 的內存空間,不過考慮到它可以管理完整的進程,1.7kB 其實並不算大了。
鑑於 task_struct 結構體過長,這裡不可能將其成員一一介紹清楚。如果讀者和我一樣好奇,粗略的瀏覽 task_struct 結構體,應該能夠發現一些比較令人熟悉的成員,例如:
通過C語言註釋以及成員的變量名,能夠看到 task_struct 結構體包含了文件系統,線程結構體,以及進程打開的文件等信息,這就與上一節文章的內容對應上了。其他成員在我之後的文章中會涉及到,這裡暫不贅述。
在創建進程時,Linux 通過 slab 分配器分配 task_struct 結構,這樣可以避免動態分配和釋放帶來的開銷,提高內存的使用效率。
那麼創建 task_struct 結構後,內核如何訪問它呢?
根據我手上的內核C語言源代碼,Linux 中還有一個結構體 thread_info,它的其中一個成員 task 指針正好適合用於索引 task_struct 結構體,在X86_64平臺上,thread_info 的相關C語言代碼如下,請看:
上一節簡要討論了下Linux操作系統中進程的概念,其實簡單來說,進程無非就是處於運行期的程序及其相關資源的總和。這裡讀者應該注意“相關資源”一詞,Linux 在內核中是如何記錄進程的資源的呢?
Linux內核如何記錄進程的資源?
首先應該明白,Linux 內核大都是採用C語言編寫的,因此要弄清楚內核如何記錄進程資源,只需要查看相關的C語言代碼就可以了。事實上,Linux 內核是使用 task_struct 結構體描述進程的資源的,它的C語言部分代碼如下,請看:
task_struct 結構體很長,在我手中的 Linux 內核C語言源代碼中,它佔用了280行。當然了,這其中包含很多條件編譯部分,在 32 位機器上,task_struct 大約要佔用 1.7 KB 的內存空間,不過考慮到它可以管理完整的進程,1.7kB 其實並不算大了。
鑑於 task_struct 結構體過長,這裡不可能將其成員一一介紹清楚。如果讀者和我一樣好奇,粗略的瀏覽 task_struct 結構體,應該能夠發現一些比較令人熟悉的成員,例如:
通過C語言註釋以及成員的變量名,能夠看到 task_struct 結構體包含了文件系統,線程結構體,以及進程打開的文件等信息,這就與上一節文章的內容對應上了。其他成員在我之後的文章中會涉及到,這裡暫不贅述。
在創建進程時,Linux 通過 slab 分配器分配 task_struct 結構,這樣可以避免動態分配和釋放帶來的開銷,提高內存的使用效率。
那麼創建 task_struct 結構後,內核如何訪問它呢?
根據我手上的內核C語言源代碼,Linux 中還有一個結構體 thread_info,它的其中一個成員 task 指針正好適合用於索引 task_struct 結構體,在X86_64平臺上,thread_info 的相關C語言代碼如下,請看:
Linux 通常會在內核棧底或者棧頂保留 thread_info 結構,而內核棧通常大小都是可知的,因此每個進程都能方便的從自己的棧中找到 thread_info 結構,進而找到 task_struct 結構。
查找當前進程的 thread_info 結構,可以調用 current_thread_info() 函數,它的C語言代碼如下,請看:
static inline struct thread_info *current_thread_info(void)
{
register unsigned long sp asm ("sp");
return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
}\t
上一節簡要討論了下Linux操作系統中進程的概念,其實簡單來說,進程無非就是處於運行期的程序及其相關資源的總和。這裡讀者應該注意“相關資源”一詞,Linux 在內核中是如何記錄進程的資源的呢?
Linux內核如何記錄進程的資源?
首先應該明白,Linux 內核大都是採用C語言編寫的,因此要弄清楚內核如何記錄進程資源,只需要查看相關的C語言代碼就可以了。事實上,Linux 內核是使用 task_struct 結構體描述進程的資源的,它的C語言部分代碼如下,請看:
task_struct 結構體很長,在我手中的 Linux 內核C語言源代碼中,它佔用了280行。當然了,這其中包含很多條件編譯部分,在 32 位機器上,task_struct 大約要佔用 1.7 KB 的內存空間,不過考慮到它可以管理完整的進程,1.7kB 其實並不算大了。
鑑於 task_struct 結構體過長,這裡不可能將其成員一一介紹清楚。如果讀者和我一樣好奇,粗略的瀏覽 task_struct 結構體,應該能夠發現一些比較令人熟悉的成員,例如:
通過C語言註釋以及成員的變量名,能夠看到 task_struct 結構體包含了文件系統,線程結構體,以及進程打開的文件等信息,這就與上一節文章的內容對應上了。其他成員在我之後的文章中會涉及到,這裡暫不贅述。
在創建進程時,Linux 通過 slab 分配器分配 task_struct 結構,這樣可以避免動態分配和釋放帶來的開銷,提高內存的使用效率。
那麼創建 task_struct 結構後,內核如何訪問它呢?
根據我手上的內核C語言源代碼,Linux 中還有一個結構體 thread_info,它的其中一個成員 task 指針正好適合用於索引 task_struct 結構體,在X86_64平臺上,thread_info 的相關C語言代碼如下,請看:
Linux 通常會在內核棧底或者棧頂保留 thread_info 結構,而內核棧通常大小都是可知的,因此每個進程都能方便的從自己的棧中找到 thread_info 結構,進而找到 task_struct 結構。
查找當前進程的 thread_info 結構,可以調用 current_thread_info() 函數,它的C語言代碼如下,請看:
static inline struct thread_info *current_thread_info(void)
{
register unsigned long sp asm ("sp");
return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
}\t
可見,current_thread_info() 函數其實就是通過進程棧計算的,因此它的實現與平臺架構有關,上述C語言代碼其實只是 arm 平臺的實現方法,其他平臺的實現方法,讀者可自行查閱。
此時,要獲取當前進程的資源,可以通過 current_thread_info()->task 索引。
進程 PID
Linux 內核為每一個進程分配獨一無二的進程標識(process identification,PID)用於區分不同的進程。PID 是一個整數,在內核的C語言源碼中表示為 pid_t 類型(其實就是 int 類型)。在Linux命令行輸入 ps 命令,即可查看進程的 PID,例如:
上一節簡要討論了下Linux操作系統中進程的概念,其實簡單來說,進程無非就是處於運行期的程序及其相關資源的總和。這裡讀者應該注意“相關資源”一詞,Linux 在內核中是如何記錄進程的資源的呢?
Linux內核如何記錄進程的資源?
首先應該明白,Linux 內核大都是採用C語言編寫的,因此要弄清楚內核如何記錄進程資源,只需要查看相關的C語言代碼就可以了。事實上,Linux 內核是使用 task_struct 結構體描述進程的資源的,它的C語言部分代碼如下,請看:
task_struct 結構體很長,在我手中的 Linux 內核C語言源代碼中,它佔用了280行。當然了,這其中包含很多條件編譯部分,在 32 位機器上,task_struct 大約要佔用 1.7 KB 的內存空間,不過考慮到它可以管理完整的進程,1.7kB 其實並不算大了。
鑑於 task_struct 結構體過長,這裡不可能將其成員一一介紹清楚。如果讀者和我一樣好奇,粗略的瀏覽 task_struct 結構體,應該能夠發現一些比較令人熟悉的成員,例如:
通過C語言註釋以及成員的變量名,能夠看到 task_struct 結構體包含了文件系統,線程結構體,以及進程打開的文件等信息,這就與上一節文章的內容對應上了。其他成員在我之後的文章中會涉及到,這裡暫不贅述。
在創建進程時,Linux 通過 slab 分配器分配 task_struct 結構,這樣可以避免動態分配和釋放帶來的開銷,提高內存的使用效率。
那麼創建 task_struct 結構後,內核如何訪問它呢?
根據我手上的內核C語言源代碼,Linux 中還有一個結構體 thread_info,它的其中一個成員 task 指針正好適合用於索引 task_struct 結構體,在X86_64平臺上,thread_info 的相關C語言代碼如下,請看:
Linux 通常會在內核棧底或者棧頂保留 thread_info 結構,而內核棧通常大小都是可知的,因此每個進程都能方便的從自己的棧中找到 thread_info 結構,進而找到 task_struct 結構。
查找當前進程的 thread_info 結構,可以調用 current_thread_info() 函數,它的C語言代碼如下,請看:
static inline struct thread_info *current_thread_info(void)
{
register unsigned long sp asm ("sp");
return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
}\t
可見,current_thread_info() 函數其實就是通過進程棧計算的,因此它的實現與平臺架構有關,上述C語言代碼其實只是 arm 平臺的實現方法,其他平臺的實現方法,讀者可自行查閱。
此時,要獲取當前進程的資源,可以通過 current_thread_info()->task 索引。
進程 PID
Linux 內核為每一個進程分配獨一無二的進程標識(process identification,PID)用於區分不同的進程。PID 是一個整數,在內核的C語言源碼中表示為 pid_t 類型(其實就是 int 類型)。在Linux命令行輸入 ps 命令,即可查看進程的 PID,例如:
task_struct 結構體使用成員 pid 記錄進程的 PID 值,相關的C語言代碼如下,請看:
上一節簡要討論了下Linux操作系統中進程的概念,其實簡單來說,進程無非就是處於運行期的程序及其相關資源的總和。這裡讀者應該注意“相關資源”一詞,Linux 在內核中是如何記錄進程的資源的呢?
Linux內核如何記錄進程的資源?
首先應該明白,Linux 內核大都是採用C語言編寫的,因此要弄清楚內核如何記錄進程資源,只需要查看相關的C語言代碼就可以了。事實上,Linux 內核是使用 task_struct 結構體描述進程的資源的,它的C語言部分代碼如下,請看:
task_struct 結構體很長,在我手中的 Linux 內核C語言源代碼中,它佔用了280行。當然了,這其中包含很多條件編譯部分,在 32 位機器上,task_struct 大約要佔用 1.7 KB 的內存空間,不過考慮到它可以管理完整的進程,1.7kB 其實並不算大了。
鑑於 task_struct 結構體過長,這裡不可能將其成員一一介紹清楚。如果讀者和我一樣好奇,粗略的瀏覽 task_struct 結構體,應該能夠發現一些比較令人熟悉的成員,例如:
通過C語言註釋以及成員的變量名,能夠看到 task_struct 結構體包含了文件系統,線程結構體,以及進程打開的文件等信息,這就與上一節文章的內容對應上了。其他成員在我之後的文章中會涉及到,這裡暫不贅述。
在創建進程時,Linux 通過 slab 分配器分配 task_struct 結構,這樣可以避免動態分配和釋放帶來的開銷,提高內存的使用效率。
那麼創建 task_struct 結構後,內核如何訪問它呢?
根據我手上的內核C語言源代碼,Linux 中還有一個結構體 thread_info,它的其中一個成員 task 指針正好適合用於索引 task_struct 結構體,在X86_64平臺上,thread_info 的相關C語言代碼如下,請看:
Linux 通常會在內核棧底或者棧頂保留 thread_info 結構,而內核棧通常大小都是可知的,因此每個進程都能方便的從自己的棧中找到 thread_info 結構,進而找到 task_struct 結構。
查找當前進程的 thread_info 結構,可以調用 current_thread_info() 函數,它的C語言代碼如下,請看:
static inline struct thread_info *current_thread_info(void)
{
register unsigned long sp asm ("sp");
return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
}\t
可見,current_thread_info() 函數其實就是通過進程棧計算的,因此它的實現與平臺架構有關,上述C語言代碼其實只是 arm 平臺的實現方法,其他平臺的實現方法,讀者可自行查閱。
此時,要獲取當前進程的資源,可以通過 current_thread_info()->task 索引。
進程 PID
Linux 內核為每一個進程分配獨一無二的進程標識(process identification,PID)用於區分不同的進程。PID 是一個整數,在內核的C語言源碼中表示為 pid_t 類型(其實就是 int 類型)。在Linux命令行輸入 ps 命令,即可查看進程的 PID,例如:
task_struct 結構體使用成員 pid 記錄進程的 PID 值,相關的C語言代碼如下,請看:
在Linux系統中,PID 的最大值是可以調整的,早期為了兼容老版本的 Unix 和 Linux,默認最大值為 32768(short int 類型能夠表示的最大值),這個值可以通過 cat 命令查看:
# cat /proc/sys/kernel/pid_max
32768
PID 的最大值對於Linux系統的運行是有影響的,因為 PID 值是獨一無二的,所以它的最大值實際上就表示系統可以同時運行的最多進程數目。對於普通的個人用戶來說,32768 足夠多,但是對於大型服務器來說,32768 可能就遠遠不夠了,這時可以修改 pid_max 解決這一問題。
進程的狀態
現在知道了Linux內核是如何描述和記錄進程資源,以及如何區分不同進程的了。那麼進程有哪些狀態呢?讀者應該注意到 task_struct 結構體的第一個成員 state 了,它就是用於記錄進程狀態的。進程的狀態在C語言源代碼中是使用幾個宏定義的:
上一節簡要討論了下Linux操作系統中進程的概念,其實簡單來說,進程無非就是處於運行期的程序及其相關資源的總和。這裡讀者應該注意“相關資源”一詞,Linux 在內核中是如何記錄進程的資源的呢?
Linux內核如何記錄進程的資源?
首先應該明白,Linux 內核大都是採用C語言編寫的,因此要弄清楚內核如何記錄進程資源,只需要查看相關的C語言代碼就可以了。事實上,Linux 內核是使用 task_struct 結構體描述進程的資源的,它的C語言部分代碼如下,請看:
task_struct 結構體很長,在我手中的 Linux 內核C語言源代碼中,它佔用了280行。當然了,這其中包含很多條件編譯部分,在 32 位機器上,task_struct 大約要佔用 1.7 KB 的內存空間,不過考慮到它可以管理完整的進程,1.7kB 其實並不算大了。
鑑於 task_struct 結構體過長,這裡不可能將其成員一一介紹清楚。如果讀者和我一樣好奇,粗略的瀏覽 task_struct 結構體,應該能夠發現一些比較令人熟悉的成員,例如:
通過C語言註釋以及成員的變量名,能夠看到 task_struct 結構體包含了文件系統,線程結構體,以及進程打開的文件等信息,這就與上一節文章的內容對應上了。其他成員在我之後的文章中會涉及到,這裡暫不贅述。
在創建進程時,Linux 通過 slab 分配器分配 task_struct 結構,這樣可以避免動態分配和釋放帶來的開銷,提高內存的使用效率。
那麼創建 task_struct 結構後,內核如何訪問它呢?
根據我手上的內核C語言源代碼,Linux 中還有一個結構體 thread_info,它的其中一個成員 task 指針正好適合用於索引 task_struct 結構體,在X86_64平臺上,thread_info 的相關C語言代碼如下,請看:
Linux 通常會在內核棧底或者棧頂保留 thread_info 結構,而內核棧通常大小都是可知的,因此每個進程都能方便的從自己的棧中找到 thread_info 結構,進而找到 task_struct 結構。
查找當前進程的 thread_info 結構,可以調用 current_thread_info() 函數,它的C語言代碼如下,請看:
static inline struct thread_info *current_thread_info(void)
{
register unsigned long sp asm ("sp");
return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
}\t
可見,current_thread_info() 函數其實就是通過進程棧計算的,因此它的實現與平臺架構有關,上述C語言代碼其實只是 arm 平臺的實現方法,其他平臺的實現方法,讀者可自行查閱。
此時,要獲取當前進程的資源,可以通過 current_thread_info()->task 索引。
進程 PID
Linux 內核為每一個進程分配獨一無二的進程標識(process identification,PID)用於區分不同的進程。PID 是一個整數,在內核的C語言源碼中表示為 pid_t 類型(其實就是 int 類型)。在Linux命令行輸入 ps 命令,即可查看進程的 PID,例如:
task_struct 結構體使用成員 pid 記錄進程的 PID 值,相關的C語言代碼如下,請看:
在Linux系統中,PID 的最大值是可以調整的,早期為了兼容老版本的 Unix 和 Linux,默認最大值為 32768(short int 類型能夠表示的最大值),這個值可以通過 cat 命令查看:
# cat /proc/sys/kernel/pid_max
32768
PID 的最大值對於Linux系統的運行是有影響的,因為 PID 值是獨一無二的,所以它的最大值實際上就表示系統可以同時運行的最多進程數目。對於普通的個人用戶來說,32768 足夠多,但是對於大型服務器來說,32768 可能就遠遠不夠了,這時可以修改 pid_max 解決這一問題。
進程的狀態
現在知道了Linux內核是如何描述和記錄進程資源,以及如何區分不同進程的了。那麼進程有哪些狀態呢?讀者應該注意到 task_struct 結構體的第一個成員 state 了,它就是用於記錄進程狀態的。進程的狀態在C語言源代碼中是使用幾個宏定義的:
Linux 系統中的進程必定處於這 5 種狀態之一。從上到下,分別表示進程處於:
- 正在運行或者準備運行
- 正在睡眠,但是可中斷,接收到信號會被提前喚醒
- 正在睡眠,並且不可中斷,也即即使接收到信號也不會被喚醒
- 被其他進程跟蹤中
- 停止運行
現在就明白有時無法通過 kill 命令殺死 D 狀態的進程了,這是因為這些進程處於不響應信號的狀態,kill 命令本質上是發送 SIGKILL 信號,自然無法殺死該進程。
父進程和子進程
進程的父進程和子進程也屬於進程的資源,因此也被記錄在 task_struct 結構體中,請看相關C語言代碼:
上一節簡要討論了下Linux操作系統中進程的概念,其實簡單來說,進程無非就是處於運行期的程序及其相關資源的總和。這裡讀者應該注意“相關資源”一詞,Linux 在內核中是如何記錄進程的資源的呢?
Linux內核如何記錄進程的資源?
首先應該明白,Linux 內核大都是採用C語言編寫的,因此要弄清楚內核如何記錄進程資源,只需要查看相關的C語言代碼就可以了。事實上,Linux 內核是使用 task_struct 結構體描述進程的資源的,它的C語言部分代碼如下,請看:
task_struct 結構體很長,在我手中的 Linux 內核C語言源代碼中,它佔用了280行。當然了,這其中包含很多條件編譯部分,在 32 位機器上,task_struct 大約要佔用 1.7 KB 的內存空間,不過考慮到它可以管理完整的進程,1.7kB 其實並不算大了。
鑑於 task_struct 結構體過長,這裡不可能將其成員一一介紹清楚。如果讀者和我一樣好奇,粗略的瀏覽 task_struct 結構體,應該能夠發現一些比較令人熟悉的成員,例如:
通過C語言註釋以及成員的變量名,能夠看到 task_struct 結構體包含了文件系統,線程結構體,以及進程打開的文件等信息,這就與上一節文章的內容對應上了。其他成員在我之後的文章中會涉及到,這裡暫不贅述。
在創建進程時,Linux 通過 slab 分配器分配 task_struct 結構,這樣可以避免動態分配和釋放帶來的開銷,提高內存的使用效率。
那麼創建 task_struct 結構後,內核如何訪問它呢?
根據我手上的內核C語言源代碼,Linux 中還有一個結構體 thread_info,它的其中一個成員 task 指針正好適合用於索引 task_struct 結構體,在X86_64平臺上,thread_info 的相關C語言代碼如下,請看:
Linux 通常會在內核棧底或者棧頂保留 thread_info 結構,而內核棧通常大小都是可知的,因此每個進程都能方便的從自己的棧中找到 thread_info 結構,進而找到 task_struct 結構。
查找當前進程的 thread_info 結構,可以調用 current_thread_info() 函數,它的C語言代碼如下,請看:
static inline struct thread_info *current_thread_info(void)
{
register unsigned long sp asm ("sp");
return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
}\t
可見,current_thread_info() 函數其實就是通過進程棧計算的,因此它的實現與平臺架構有關,上述C語言代碼其實只是 arm 平臺的實現方法,其他平臺的實現方法,讀者可自行查閱。
此時,要獲取當前進程的資源,可以通過 current_thread_info()->task 索引。
進程 PID
Linux 內核為每一個進程分配獨一無二的進程標識(process identification,PID)用於區分不同的進程。PID 是一個整數,在內核的C語言源碼中表示為 pid_t 類型(其實就是 int 類型)。在Linux命令行輸入 ps 命令,即可查看進程的 PID,例如:
task_struct 結構體使用成員 pid 記錄進程的 PID 值,相關的C語言代碼如下,請看:
在Linux系統中,PID 的最大值是可以調整的,早期為了兼容老版本的 Unix 和 Linux,默認最大值為 32768(short int 類型能夠表示的最大值),這個值可以通過 cat 命令查看:
# cat /proc/sys/kernel/pid_max
32768
PID 的最大值對於Linux系統的運行是有影響的,因為 PID 值是獨一無二的,所以它的最大值實際上就表示系統可以同時運行的最多進程數目。對於普通的個人用戶來說,32768 足夠多,但是對於大型服務器來說,32768 可能就遠遠不夠了,這時可以修改 pid_max 解決這一問題。
進程的狀態
現在知道了Linux內核是如何描述和記錄進程資源,以及如何區分不同進程的了。那麼進程有哪些狀態呢?讀者應該注意到 task_struct 結構體的第一個成員 state 了,它就是用於記錄進程狀態的。進程的狀態在C語言源代碼中是使用幾個宏定義的:
Linux 系統中的進程必定處於這 5 種狀態之一。從上到下,分別表示進程處於:
- 正在運行或者準備運行
- 正在睡眠,但是可中斷,接收到信號會被提前喚醒
- 正在睡眠,並且不可中斷,也即即使接收到信號也不會被喚醒
- 被其他進程跟蹤中
- 停止運行
現在就明白有時無法通過 kill 命令殺死 D 狀態的進程了,這是因為這些進程處於不響應信號的狀態,kill 命令本質上是發送 SIGKILL 信號,自然無法殺死該進程。
父進程和子進程
進程的父進程和子進程也屬於進程的資源,因此也被記錄在 task_struct 結構體中,請看相關C語言代碼:
所以要訪問當前進程的父進程和子進程是方便的,例如:
struct task_struct *p = current->parent;
struct task_stuck *c = current->children;
稍稍思考一下,應該能夠發現進程結構體 task_struct 中的 parent 指針和 children 指針其實構成了一條鏈表,通過這樣的鏈表,我們能夠輕易的訪問進程的父進程,祖父進程…, 以及子進程,孫進程… 等。不過應該明白,對於擁有大量進程的系統來說,重複遍歷所有進程的開銷是很大的。
小結
本節先討論了Linux內核如何記錄和描述進程資源,可以看出,內核管理進程其實就是管理 task_struct 結構體。接著,通過C語言源代碼查看了內核如何訪問 task_struct 結構體,以及如何區分進程,最後我們一起還討論了進程的狀態和家族樹,可見,Linux內核源代碼也並不是神祕到不可理解。
上一節簡要討論了下Linux操作系統中進程的概念,其實簡單來說,進程無非就是處於運行期的程序及其相關資源的總和。這裡讀者應該注意“相關資源”一詞,Linux 在內核中是如何記錄進程的資源的呢?
Linux內核如何記錄進程的資源?
首先應該明白,Linux 內核大都是採用C語言編寫的,因此要弄清楚內核如何記錄進程資源,只需要查看相關的C語言代碼就可以了。事實上,Linux 內核是使用 task_struct 結構體描述進程的資源的,它的C語言部分代碼如下,請看:
task_struct 結構體很長,在我手中的 Linux 內核C語言源代碼中,它佔用了280行。當然了,這其中包含很多條件編譯部分,在 32 位機器上,task_struct 大約要佔用 1.7 KB 的內存空間,不過考慮到它可以管理完整的進程,1.7kB 其實並不算大了。
鑑於 task_struct 結構體過長,這裡不可能將其成員一一介紹清楚。如果讀者和我一樣好奇,粗略的瀏覽 task_struct 結構體,應該能夠發現一些比較令人熟悉的成員,例如:
通過C語言註釋以及成員的變量名,能夠看到 task_struct 結構體包含了文件系統,線程結構體,以及進程打開的文件等信息,這就與上一節文章的內容對應上了。其他成員在我之後的文章中會涉及到,這裡暫不贅述。
在創建進程時,Linux 通過 slab 分配器分配 task_struct 結構,這樣可以避免動態分配和釋放帶來的開銷,提高內存的使用效率。
那麼創建 task_struct 結構後,內核如何訪問它呢?
根據我手上的內核C語言源代碼,Linux 中還有一個結構體 thread_info,它的其中一個成員 task 指針正好適合用於索引 task_struct 結構體,在X86_64平臺上,thread_info 的相關C語言代碼如下,請看:
Linux 通常會在內核棧底或者棧頂保留 thread_info 結構,而內核棧通常大小都是可知的,因此每個進程都能方便的從自己的棧中找到 thread_info 結構,進而找到 task_struct 結構。
查找當前進程的 thread_info 結構,可以調用 current_thread_info() 函數,它的C語言代碼如下,請看:
static inline struct thread_info *current_thread_info(void)
{
register unsigned long sp asm ("sp");
return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
}\t
可見,current_thread_info() 函數其實就是通過進程棧計算的,因此它的實現與平臺架構有關,上述C語言代碼其實只是 arm 平臺的實現方法,其他平臺的實現方法,讀者可自行查閱。
此時,要獲取當前進程的資源,可以通過 current_thread_info()->task 索引。
進程 PID
Linux 內核為每一個進程分配獨一無二的進程標識(process identification,PID)用於區分不同的進程。PID 是一個整數,在內核的C語言源碼中表示為 pid_t 類型(其實就是 int 類型)。在Linux命令行輸入 ps 命令,即可查看進程的 PID,例如:
task_struct 結構體使用成員 pid 記錄進程的 PID 值,相關的C語言代碼如下,請看:
在Linux系統中,PID 的最大值是可以調整的,早期為了兼容老版本的 Unix 和 Linux,默認最大值為 32768(short int 類型能夠表示的最大值),這個值可以通過 cat 命令查看:
# cat /proc/sys/kernel/pid_max
32768
PID 的最大值對於Linux系統的運行是有影響的,因為 PID 值是獨一無二的,所以它的最大值實際上就表示系統可以同時運行的最多進程數目。對於普通的個人用戶來說,32768 足夠多,但是對於大型服務器來說,32768 可能就遠遠不夠了,這時可以修改 pid_max 解決這一問題。
進程的狀態
現在知道了Linux內核是如何描述和記錄進程資源,以及如何區分不同進程的了。那麼進程有哪些狀態呢?讀者應該注意到 task_struct 結構體的第一個成員 state 了,它就是用於記錄進程狀態的。進程的狀態在C語言源代碼中是使用幾個宏定義的:
Linux 系統中的進程必定處於這 5 種狀態之一。從上到下,分別表示進程處於:
- 正在運行或者準備運行
- 正在睡眠,但是可中斷,接收到信號會被提前喚醒
- 正在睡眠,並且不可中斷,也即即使接收到信號也不會被喚醒
- 被其他進程跟蹤中
- 停止運行
現在就明白有時無法通過 kill 命令殺死 D 狀態的進程了,這是因為這些進程處於不響應信號的狀態,kill 命令本質上是發送 SIGKILL 信號,自然無法殺死該進程。
父進程和子進程
進程的父進程和子進程也屬於進程的資源,因此也被記錄在 task_struct 結構體中,請看相關C語言代碼:
所以要訪問當前進程的父進程和子進程是方便的,例如:
struct task_struct *p = current->parent;
struct task_stuck *c = current->children;
稍稍思考一下,應該能夠發現進程結構體 task_struct 中的 parent 指針和 children 指針其實構成了一條鏈表,通過這樣的鏈表,我們能夠輕易的訪問進程的父進程,祖父進程…, 以及子進程,孫進程… 等。不過應該明白,對於擁有大量進程的系統來說,重複遍歷所有進程的開銷是很大的。
小結
本節先討論了Linux內核如何記錄和描述進程資源,可以看出,內核管理進程其實就是管理 task_struct 結構體。接著,通過C語言源代碼查看了內核如何訪問 task_struct 結構體,以及如何區分進程,最後我們一起還討論了進程的狀態和家族樹,可見,Linux內核源代碼也並不是神祕到不可理解。
歡迎在評論區一起討論,質疑。文章都是手打原創,每天最淺顯的介紹C語言、linux等嵌入式開發,喜歡我的文章就關注一波吧,可以看到最新更新和之前的文章哦。