1. 進程的概念
進程(處於執行期的程序),一段可執行的程序代碼+相關的的資源
程序:完全有可能存在兩個進程共同執行同一個程序
資源包括:打開的文件,掛起的信號,內核的內部數據,處理器的狀態,內存地址空間,一個或多個執行線程
線程(執行線程),都擁有一個獨立的程序計數器,進程棧和一組進程寄存器。
內核的調度對象是線程而不是進程。
在線程之間,可以共享虛擬內存,但是每個都擁有各自的虛擬處理器。
對於Linux而言,線程只不過是一種特殊的進程(共享某些資源)。
2. 進程描述符及任務結構
進程描述符: 結構體task_struct,包含一個具體進程的所有信息:打開的文件,進程的地址空間,掛起的信號,進程的狀態等。<linux/sched.h>
任務隊列:保存進程描述符的雙向循環鏈表。
(1)進程的描述表分配和存放
Linux通過slab分配器分配task_strack結構,這樣可以達到對象的複用(避免動態分配和釋放所帶來的資源消耗)和緩存着色。
task_strack結構的存放位置:
tread_info(<asm/thread_info.h>),在分配task_strack結構的時候,在內核棧底創建一個tread_info結構。其中內部成員task指向當前進程的task_struct。
//通過hread_info->task 獲取當前進程 current
static inline struct task_struct *get_current(void)
return current_thread_info()->task;
//通過sp寄存器找到當前進程的 thread_info
static inline struct thread_info *current_thread_info(void)
register unsigned long sp asm ("sp");
return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
(2)進程的狀態
TASK_RUNNING :進程可執行,正在執行或者在運行隊列中等待執行。
進程在用戶空間中執行的唯一可能的狀態
TASK_INTERRUPTIBLE:進程正在睡眠(被阻塞),等待某些條件完成。
進程可以被信號喚醒。
TASK_UNINTERRUPTIBLE:除了不會被信號喚醒之外,與TASK_INTERRUPTIBLE相同
TASK_STOPPED進程停止執行。
(3)設置當前進程的狀態
set_task_state(task,state) ;
set_current_state(state);
(4)進程的家族樹
所有進程都是PID爲1的init進程的後代,每一個進程必須有一個父進程。
每一個task_struct都包含一個指向其父進程task_struct的指針parent。
還包含一個稱爲children的子進程鏈表。
3.進程的創建
fork通過拷貝當前進程,創建一個新進程,
exec負責讀取可只執行文件,並將其載入到地址空間開始執行
(1)寫時拷貝(copy-on-write)
fork函數使用寫時拷貝頁實現,在創建新進程的時候,不復制整個進程地址空間,而是讓父進程和子進程共享同一個拷貝。只有在需要寫入的時候,數據纔會被複制。
地址空間上的頁的拷貝被推遲到實際發生寫入的時候才進行。
fork的開銷就是複製父進程的頁表以及給子進程創建唯一的進程描述符。
(2)fork
Linux通過clone系統調用實現fork,通過一系列的參數標誌來指明父子進程需要共享的資源。fork,vfork,__clone庫函數都根據各自的需要的參數標誌調用clone。
clone去調用do_fork。
do_fork調用copy_process函數,然後讓進程開始運行。
copy_process函數:(創建唯一的進程描述符)
1)調用dup_task_struct爲進程創建一個內核棧、thread_info結構和task_struct。
此時,子進程和父進程的描述符完全相同
2)檢查當前用戶所擁有的進程數目是否超出限制
3)子進程部分描述符成員清0或設置爲初始值,主要是統計信息。
4)子進程的狀態設置爲TASK_UNINTERRUTIBLE,不會投入運行。
5)調用copy_flags以更新task_struct的flag成員。
6)調用alloc_pid爲新進程分配一個有效的PID
7)根據傳遞給clone的參數標誌,copy_process拷貝或共享打開的文件、文件系統信息、信號處理函數、進程地址空間和命名空間等。
8)做掃尾工作,並且返回一個指向子進程的指針
do_fork函數:在copy_process成功返回後,新創建的進程被喚醒並投入運行,內核有意選擇子進程首先執行。
(3)vfork
不拷貝父進程的頁表項。
父進程被阻塞,直到子進程退出或者執行exec()。
4.線程在Linux中的實現
線程機制,在同一程序內共享內存地址空間,共享打開的文件及其他資源。
Linux,線程僅僅被視爲一個與其他進程共享某些資源的進程。
(1)創建線程
在調用clone的時候需要傳遞一些參數標誌來指明需要共享的資源
clone(CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND,0)
與父進程共享地址空間,文件系統,打開的文件,信號處理函數及被阻斷的信號
(2)內核線程
完成內核在後臺執行的一些操作,沒有獨立的地址空間,只在內核空間運行,從不切換到用戶空間,可以被調度,可以被搶佔。
內核線程的創建,只能由其他內核線程創建:
structtask_struct *kthread_create (int (*threadfn)(void *data), void *data,
constchar namefmt[], ….....)
新創建的進程處於不可運行的狀態,需要調用wake_up_process明確地喚醒。
5.進程的終結
進程的析構是由自身引發的,發生在進程調用exit系統調用時。(C語言編譯器會在main函數的返回點後面放置調用exit的代碼)
大部分的工作都依靠do_exit來完成。進程所有的資源都被釋放掉,進程不可運行並處於EXIT_ZOMBIE退出狀態。
它現在佔用的所有內存就是內核棧,thread_info結構和task_struct結構。用來向父進程提供信息。
(1)刪除進程描述符
在父進程獲得已終結的子進程的信息後或者通知內核它並不關注那些信息後,子進程的task_struct結構才被釋放。
(2)孤兒進程
如果父進程在子進程之前退出,給子進程在當前線程組內找一個線程作爲父親,如果不行,就讓init做它們的父進程。