本章介紹linux系統進程概念以及線程,討論在linux內核中是如何對進程進行管理的以及進程結束流程。
關於進程的概念在操作系統概念--操作系統中的進程、線程中已經描述過,這裏不在贅述,在應用層一般理解是一個應用實例是一個進程,一個進程又可以包含多個線程,但是在傳統的unix系統中每個進程都是由一個線程組成的,而Linux系統內核中也有單獨的對線程的實現機制:即不區分線程和進程。
進程創建銷燬概述
Linux中一個進程生命週期的開始要從fork()這個大名鼎鼎的系統調用開始,fork()調用返回兩次:一次是父進程pid,然後是子進程pid,fork完成後子進程和父進程還是一樣的,直到調用exec()家族函數爲子進程創建自己的地址空間以及加載相關代碼到內存,fork函數實際上是調用的clone系統調用來實現進程拷貝的,
進程的結束是通過調用exit系統調用來實現的,此函數會結束當前進程並且釋放當前進程所佔用的相關資源,父進程可以通過wait4()系統調用來收集子進程的退出信息,當進程結束的時候會被置爲zombie狀態,如果父進程調用wait或者waitpid,標記爲zombie狀態的進程就會結束。
進程描述符
內核會把進程存儲在一個叫做task list的循環雙鏈表結構中,雙鏈表中的每個元素都使用task_struct結構體描述該結構體被定義在頭文件<linux/sched.h>中<include/linux/sched.h>:
struct task_struct {
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
struct thread_info *thread_info;
atomic_t usage;
unsigned long flags; /* per process flags, defined below */
unsigned long ptrace;
int lock_depth; /* Lock depth */
int prio, static_prio;
struct list_head run_list;
prio_array_t *array;
...
...
#endif
#ifdef CONFIG_NUMA
struct mempolicy *mempolicy;
short il_next;
#endif
};
該結構體內容較多,請自行下載內核源代碼進行預覽,此結構體在32爲機器上大概會佔用1.7kb,但是考慮到此結構體所攜帶的所有內核所需要的關於進程的信息而言,這點空間佔用也不算大,它包含了描述進程的數據:打開的文件、進程空間地址、信號量、進程狀態等等。
分配進程描述符
task_struct結構體通過slab內存分配器來分配空間,linux kernel 2.6系列中task_struct被存儲在每個進程的內核棧的尾部,這樣可以避免使用額外的寄存器去存儲結構體的位置,這就爲寄存器緊張的架構如x86帶來了好處,通過計算棧指針就可以找到task_struct的存儲位置了,在linux系統中,雖然task_struct是用來描述一個進程,但是task_struct描述的是進程的通用部分,不同CPU架構之間的差異化描述則由thread_info結構體來描述,該結構體存儲在內核棧的尾部,它包含一個task元素指向和他對應的一個task_struct,所以實際上一個進程是由thread_info和task_struct兩個結構體共同來描述的,定義在thread_info.h頭文件中
<include/asm-xxx/thread_info.h>:
struct thread_info {
struct task_struct *task; /* main task structure */
struct exec_domain *exec_domain; /* execution domain */
__u32 flags; /* low level flags */
__u32 status; /* thread synchronous flags */
__u32 cpu; /* current CPU */
int preempt_count;
mm_segment_t addr_limit;
struct restart_block restart_block;
};
進程狀態