進(jìn)程描述符
進(jìn)程描述符(process descriptor)包含了一個具體進(jìn)程的所有信息。他的結(jié)構(gòu)體定義在<linux/sched.h>
中,代碼見linux/sched.h转质,類型為task_struct
临庇。進(jìn)城描述符幾乎包含了所有進(jìn)程相關(guān)的上下文,下面簡單列舉了一些:
- state: 描述了進(jìn)程的當(dāng)前狀態(tài)信息(runable=0, unrunable=-1, stopped>0)
- pid: 唯一的進(jìn)程標(biāo)志
- cpu / recent_used_cpu等cpu相關(guān)信息
- mm等內(nèi)存相關(guān)信息
- exitcode / exitstate等進(jìn)程退出相關(guān)信息荣月,通常由
wait4()
接收 - 調(diào)度相關(guān)信息
- parent / sibling: 父進(jìn)程和兄弟進(jìn)程等信息
- fs / files / nsproxy: 文件系統(tǒng)信息,打開的文件描述符以及命名空間等信息
- signal / signalhand: 信號量以及處理函數(shù)等信息
- irq: 軟/硬中斷請求信息
- bio_list / journal_info: 塊設(shè)備信息槐脏,文件系統(tǒng)日志信息等IO相關(guān)的上下文
- cgroups: 控制組相關(guān)信息
- thread: 線程相關(guān)上下文
進(jìn)程描述符的存放
在kernel<2.6中喉童,進(jìn)程的task_struct存放在各自的內(nèi)核棧的尾部,不過在后來的內(nèi)核中,由于使用了slab分配器來動態(tài)生成task_struct堂氯,所以還需要在棧尾部的thread_info中存放task_struct的指針蔑担。
在寄存器富裕的硬件體系架構(gòu),可能會有一個專門的寄存器用來存放當(dāng)前進(jìn)程task_struct的指針咽白,因為在內(nèi)核的進(jìn)程處理相關(guān)邏輯中啤握,這個結(jié)構(gòu)體經(jīng)常被用到。不過在x86這種寄存器不富裕的架構(gòu)晶框,當(dāng)通過current宏返回進(jìn)程描述符時排抬,就需要通過內(nèi)核棧尾部的thread_info尋找task_struct的位置了。
state 進(jìn)程狀態(tài)
當(dāng)內(nèi)核需要調(diào)整某個進(jìn)程的狀態(tài)時授段,可以使用set_task_state(task, state)
蹲蒲,可以調(diào)整到如下五種狀態(tài):
- runable=0
- TASK_RUNNING: 進(jìn)程處于可執(zhí)行狀態(tài),它要么在執(zhí)行隊列中等待被執(zhí)行侵贵,要么正在被執(zhí)行
- unrunable>0
- TASK_INTERRUPTIBLE: 進(jìn)程處于阻塞狀態(tài)届搁,等待某些條件達(dá)成。當(dāng)收到信號的時候窍育,進(jìn)程會被提前喚醒卡睦。
- TASK_UNINTERRUPTIBLE: 進(jìn)程處于阻塞狀態(tài),等待某些條件達(dá)成漱抓。這個狀態(tài)下會忽略信號表锻,不可中斷。
- __TASK_TRACED: 進(jìn)程正在被其他進(jìn)程跟蹤乞娄,通常用于ptrace等調(diào)試工具調(diào)試
- stopped=-1
- __TASK_STOPPED: 進(jìn)程停止運行
進(jìn)程創(chuàng)建
當(dāng)調(diào)用fork
創(chuàng)建子進(jìn)程時瞬逊,Linux會通過拷貝當(dāng)前進(jìn)程來創(chuàng)建子進(jìn)程。由于使用了寫時拷貝頁技術(shù)(CopyOnWrite)仪或,所以進(jìn)程的創(chuàng)建只需要復(fù)制頁表和創(chuàng)建進(jìn)程描述符码耐,速度是比較快的。
進(jìn)程拷貝調(diào)用鏈為fork -> clone -> do_fork(kernel/fork.c) -> copy_process溶其,其中copy_process
- 首先創(chuàng)建一個內(nèi)核棧,并拷貝父進(jìn)程的thread_info以及進(jìn)程描述符到內(nèi)核棧中
- 將進(jìn)程描述符中非繼承的統(tǒng)計量設(shè)初始值敦间,并將狀態(tài)置為TASK_UNINTERRUPTIABLE瓶逃,保證進(jìn)程不會運行
- 分配一個有效的PID
- 拷貝或共享打開的文件句柄,文件系統(tǒng)信息廓块,信號處理函數(shù)厢绝,進(jìn)城地址空間和命名空間等。
- 掃尾工作并返回子進(jìn)程指針
線程僅僅被視為與其他進(jìn)程共享某些資源的進(jìn)程
進(jìn)程終結(jié)
當(dāng)進(jìn)程顯式調(diào)用exit()或者main()函數(shù)返回隱式調(diào)用exit带猴,或者接收到無法處理/忽略的信號量或異常昔汉,就得調(diào)用do_exit來終結(jié)這個進(jìn)程:
- 狀態(tài)轉(zhuǎn)換,將task_struct中的flag設(shè)置為PF_EXITING
- 清理/釋放IPC信號拴清、地址空間靶病、定時器会通、文件引用計數(shù)等系統(tǒng)資源
- 在進(jìn)程描述符中設(shè)置exitcode,并設(shè)置exit_state為EXIT_ZOMBIE
- 最后調(diào)用schedule()切換到執(zhí)行新的進(jìn)程,該進(jìn)程結(jié)束
不過還剩下了task_struct以及內(nèi)核棧中的數(shù)據(jù)沒有被釋放娄周,他們的作用就是通知父進(jìn)程涕侈,子進(jìn)程的結(jié)束狀態(tài)。父進(jìn)程將會收到一個信號量煤辨,此時可以通過wait來獲取exitcode以及刪除子進(jìn)程的參與信息(內(nèi)核棧裳涛,進(jìn)程描述符,線程信息)
孤兒進(jìn)程
當(dāng)一個父進(jìn)程退出時众辨,就需要為他的子進(jìn)程重新設(shè)置父進(jìn)程端三,來接收未來的exitcode。首先會嘗試在當(dāng)前進(jìn)程組中尋找一個進(jìn)程作為父親鹃彻,如果找不到郊闯,就讓init進(jìn)程作為父進(jìn)程。