一文深入理解Linux進(jìn)程!
進(jìn)程
程序是指儲存在外部存儲(如硬盤)的一個可執(zhí)行文件, 而進(jìn)程是指處于執(zhí)行期間的程序, 進(jìn)程包括 ?和 , 除了代碼段和數(shù)據(jù)段外, 進(jìn)程一般還包含打開的文件, 要處理的信號和CPU上下文等等.代碼段(text section)數(shù)據(jù)段(data section)
進(jìn)程描述符
Linux進(jìn)程使用 來描述(include/linux/sched.h), 如下:struct task_struct
Linux把所有的進(jìn)程使用雙向鏈表連接起來, 如下圖(來源<Linux設(shè)計與實(shí)現(xiàn)>):

Linux內(nèi)核為了加快獲取當(dāng)前進(jìn)程的的task_struct結(jié)構(gòu), 使用了一個技巧, 就是把task_struct放置在內(nèi)核棧的棧底, 這樣就可以通過 快速獲取到當(dāng)前運(yùn)行進(jìn)程的task_struct結(jié)構(gòu).如下圖:esp寄存器

【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實(shí)戰(zhàn)項目及代碼)? ? ?


??
取當(dāng)前運(yùn)行進(jìn)程的task_struct代碼如下:
進(jìn)程狀態(tài)
進(jìn)程描述符的state字段用于保存進(jìn)程的當(dāng)前狀態(tài), 進(jìn)程的狀態(tài)有以下幾種:
TASK_RUNNING (運(yùn)行)– 進(jìn)程處于可執(zhí)行狀態(tài), 在這個狀態(tài)下的進(jìn)程要么正在被CPU執(zhí)行, 要么在等待執(zhí)行(CPU被其他進(jìn)程占用的情況下).
TASK_INTERRUPTIBLE (可中斷等待)– 進(jìn)程處于等待狀態(tài), 其在等待某些條件成立或者接收到某些信號, 進(jìn)程會被喚醒變?yōu)檫\(yùn)行狀態(tài).
TASK_UNINTERRUPTIBLE (不可中斷等待)– 進(jìn)程處于等待狀態(tài), 其在等待某些條件成立, 進(jìn)程會被喚醒變?yōu)檫\(yùn)行狀態(tài), 但不能被信號喚醒.
TASK_TRACED (被追蹤)– 進(jìn)程處于被追蹤狀態(tài), 例如通過ptrace命令對進(jìn)程進(jìn)行調(diào)試.
TASK_STOPPED (停止)– 進(jìn)程處于停止?fàn)顟B(tài), 進(jìn)程不能被執(zhí)行.一般接收到SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU信號進(jìn)程會變成TASK_STOPPED狀態(tài).
個鐘狀態(tài)間的轉(zhuǎn)換如下圖:

進(jìn)程的創(chuàng)建
在Linux系統(tǒng)中,進(jìn)程的創(chuàng)建使用fork()系統(tǒng)調(diào)用,fork()調(diào)用會創(chuàng)建一個與父進(jìn)程一樣的子進(jìn)程,唯一不同就是fork()的返回值,父進(jìn)程返回的是子進(jìn)程的進(jìn)程ID,而子進(jìn)程返回的是0。
Linux創(chuàng)建子進(jìn)程時使用了,也就是創(chuàng)建子進(jìn)程時使用的是父進(jìn)程的內(nèi)存空間,當(dāng)子進(jìn)程或者父進(jìn)程修改數(shù)據(jù)時才會復(fù)制相應(yīng)的內(nèi)存頁。寫時復(fù)制(Copy On Write)
當(dāng)調(diào)用fork()系統(tǒng)調(diào)用時會陷入內(nèi)核空間并且調(diào)用sys_fork()函數(shù),sys_fork()函數(shù)會調(diào)用do_fork()函數(shù),代碼如下(arch/i386/kernel/process.c):
do_fork()主要的工作是申請一個進(jìn)程描述符, 然后初始化進(jìn)程描述符的各個字段, 包括調(diào)用 copy_files() 函數(shù)復(fù)制打開的文件, 調(diào)用 copy_sighand() 函數(shù)復(fù)制信號處理函數(shù), 調(diào)用 copy_mm() 函數(shù)復(fù)制進(jìn)程虛擬內(nèi)存空間, 調(diào)用 copy_namespace() 函數(shù)復(fù)制命名空間.代碼如下:
值得注意的是do_fork() 還調(diào)用了 copy_thread() 這個函數(shù), copy_thread()這個函數(shù)主要用于設(shè)置進(jìn)程的CPU執(zhí)行上下文 結(jié)構(gòu).代碼如下:struct thread_struct
do_fork() 函數(shù)最后調(diào)用 wake_up_process() 函數(shù)喚醒子進(jìn)程, 讓子進(jìn)程進(jìn)入運(yùn)行狀態(tài).
內(nèi)核線程
Linux內(nèi)核有很多任務(wù)需要去做, 例如定時把緩沖中的數(shù)據(jù)刷到硬盤, 當(dāng)內(nèi)存不足的時候進(jìn)行內(nèi)存的回收等, 這些工作都需要通過內(nèi)核線程來完成.內(nèi)核線程與普通進(jìn)程的主要區(qū)別就是: 內(nèi)核線程沒有自己的 , 每次內(nèi)核線程執(zhí)行的時候都是借助當(dāng)前運(yùn)行進(jìn)程的虛擬內(nèi)存空間結(jié)構(gòu)來運(yùn)行, 因?yàn)閮?nèi)核線程只會運(yùn)行在內(nèi)核態(tài), 而每個進(jìn)程的內(nèi)核態(tài)空間都是一樣的, 所以借助其他進(jìn)程的虛擬內(nèi)存空間結(jié)構(gòu)來運(yùn)行是完成可行的.虛擬空間結(jié)構(gòu)(struct mm)
內(nèi)核線程使用 kernel_thread() 函數(shù)來創(chuàng)建, 代碼如下:
因?yàn)檫@個函數(shù)式使用嵌入?yún)R編來實(shí)現(xiàn)的, 所以有點(diǎn)難懂, 不過主要過程就是通過調(diào)用 ?來創(chuàng)建一個新的進(jìn)程, 而創(chuàng)建進(jìn)程是通過傳入 ?標(biāo)志來指定進(jìn)程借用其他進(jìn)程的虛擬內(nèi)存空間結(jié)構(gòu)_clone()函數(shù)CLONE_VM
