linux内核的进程管理,Linux内核设计与实现——进程管理
主要內(nèi)容
進程
進程描述符及任務(wù)結(jié)構(gòu)
進程創(chuàng)建
線程在linux中的實現(xiàn)
進程終結(jié)
1. 進程
進程不僅僅是一段可執(zhí)行程序代碼,還包含其他資源,如打開的文件,掛起的信號,內(nèi)核內(nèi)部數(shù)據(jù),處理器狀態(tài),一個或多個具有內(nèi)存映射的內(nèi)存地址空間及一個或多個執(zhí)行線程,存放全局變量的數(shù)據(jù)段等等。具體可見進程的地址空間。
線程是進程中活動的對象,擁有獨立的PC程序計數(shù)器,進程棧和一組進程寄存器,是內(nèi)核調(diào)度的基本對象。
進程的兩種虛擬機制:虛擬處理器和虛擬內(nèi)存
相關(guān)函數(shù):
fork(),系統(tǒng)調(diào)用從內(nèi)核返回兩次,一次回到父進程,一次回到新產(chǎn)生的子進程
exec(),創(chuàng)建新的地址空間并載入程序
exit() 退出執(zhí)行
wait() , waitpid(),父進程等待子進程終結(jié)
2. 進程描述符及任務(wù)結(jié)構(gòu)
雙向鏈表的任務(wù)隊列
分配進程描述符
linux通過slab分配器分配task_struct結(jié)構(gòu),通過預(yù)先分配和重復(fù)使用,可以避免動態(tài)分配和釋放帶來的資源消耗。
在進程的內(nèi)核棧中,每個任務(wù)的thread_info結(jié)構(gòu)在它內(nèi)核棧的尾端分配,結(jié)構(gòu)中task域存放的是指向該任務(wù)實際task_struct的指針。
進程描述符的存放
pid,最大值默認為short int的最大值32768,可以修改pid_max文件來提高上限
進程狀態(tài)
進程狀態(tài)轉(zhuǎn)化.png
TASK_RUNNING,運行——進程或者正在執(zhí)行,或者在運行隊列中等待執(zhí)行
TASK_INTERRUPTIBLE,可中斷——進程正在睡眠(被阻塞),等待某些條件的達成
TASK_UNINTERRUPTIBLE,不可中斷
_TASK_TRACED,被其它進程跟蹤的進程
_TASK_STOPPED,進程停止執(zhí)行,如接收到SIGSTOP,SIGTINT等信號
設(shè)置當前進程狀態(tài)
set_task_state(task, state)
進程上下文
當一個程序執(zhí)行了系統(tǒng)調(diào)用或者觸發(fā)了某個異常,它就陷入了內(nèi)核空間,此時,我們呈內(nèi)核“代表進程執(zhí)行”并處于進程上下文中
進程家族樹
Unix系統(tǒng)的進程之間存在一個明顯的集成關(guān)系,所有進程都是PID為1的init進程的后代
進程關(guān)系:父子,兄弟,在進程描述符中存放,每個task_struct都有指向父進程、子進程的指針
3. 進程創(chuàng)建
linux中創(chuàng)建進程分兩步,fork和exec
linux的fork()使用寫時拷貝(copy-on-write)實現(xiàn),因為有可能fork后是執(zhí)行一個新的映像,父進程和子進程共享同一份拷貝,只有在需要寫入的時候,才會被復(fù)制。因此,fork()的實際開銷就是復(fù)制父進程的頁表以及給子進程創(chuàng)建唯一的進程描述符
linux通過clone()系統(tǒng)調(diào)用do_fork(),do_fork()調(diào)用copy_process()函數(shù),然后讓進程開始運行,copy_process()完成的工作如下:
調(diào)用dup_task_struct()為新進程分配內(nèi)核棧,task_struct等,其中的值與當前進程相同,此時,子進程與父進程的描述符也相同
檢查創(chuàng)建子進程后,當前用戶所擁有的進程數(shù)目沒有超出給它分配的資源限制
子進程使自己與父進程區(qū)別開來。進程描述符內(nèi)許多統(tǒng)計信息成員都要被清0或設(shè)為初始值
子進程狀態(tài)設(shè)置為TASK_UNINTERRUPTIBLE,以保證不會投入運行
更新task_struct的flags成員,表明進程權(quán)限等
調(diào)用alloc_pid()為新進程分配一個有效的PID
根據(jù)傳遞給clone()的參數(shù),copy_process()拷貝或共享打開的文件,文件系統(tǒng)信息,信號處理函數(shù),進程地址空間和命名空間等
掃尾工作,返回指向子進程的指針
回到do_fork()函數(shù)后,內(nèi)核會優(yōu)先選擇子進程首先執(zhí)行,因為子進程通常會馬上調(diào)用exec()函數(shù),可以避免寫時拷貝的額外開銷。
創(chuàng)建進程的fork()函數(shù)實際上最終是調(diào)用clone()函數(shù)。
vfork()調(diào)用,不拷貝父進程的頁表項,子進程作為父進程的一個單獨的線程在它的地址空間里運行,父進程被阻塞直到子進程退出或執(zhí)行exec(),子進程不能向地址空間寫入。
現(xiàn)在for()引入了寫時拷貝并且明確了子進程先執(zhí)行,vfork()的好處就僅限于不執(zhí)行父進程的頁表項了。
而且如果exec()調(diào)用失敗會怎樣?
4. 線程在Linux中的實現(xiàn)
從內(nèi)核的角度來說,并沒有線程這個概念,Linux把所有的線程都當做進程來實現(xiàn),線程僅僅被視為一個與其他進程共享某些資源的進程。
Windows在內(nèi)核中提供了專門支持線程的機制。而對Linux來說,只是一種進程間共享資源的手段。
線程的創(chuàng)建和普通進程的創(chuàng)建類似,只不過調(diào)用clone()的時候需要傳遞額外參數(shù)標識
一個普通的fork():clone(SIGCHLD, 0)
創(chuàng)建線程:clone(CLONE_VFORK | CLONE_VM | SIGCHLD, 0) 使子進程和父進程共享地址空間,文件資源,文件描述符,信號處理程序等。
內(nèi)核線程:
內(nèi)核線程沒有獨立的地址空間,只在內(nèi)核空間運行,可以被調(diào)度或搶占
5. 進程終結(jié)
當一個進程終結(jié)時,內(nèi)核必須釋放它所占有的資源并告知父進程
終結(jié)任務(wù)依靠do_exit()來完成:
設(shè)置task_struct中的標識成員設(shè)置為PF_EXITING
調(diào)用del_timer_sync()刪除內(nèi)核定時器, 確保沒有定時器在排隊和運行
調(diào)用exit_mm()釋放進程占用的mm_struct
調(diào)用sem__exit(),使進程離開等待IPC信號的隊列
調(diào)用exit_files()和exit_fs(),釋放進程占用的文件描述符和文件系統(tǒng)資源
把存放在task_struct的exit_code成員中的任務(wù)退出代碼置為exit()提供的退出代碼,或者去完成任何其他由內(nèi)核機制規(guī)定的退出動作,退出代碼存放在此供父進程隨時檢索
調(diào)用exit_notify()向父進程發(fā)送信號,給子進程重新找養(yǎng)父(init進程),并把進程狀態(tài)設(shè)為EXIT_ZOMBIE
do_exit()調(diào)用schedule()切換到新的進程,不會再被調(diào)度,do_exit()永不返回
至此,與進程相關(guān)聯(lián)的所有資源都被釋放掉了,進程處于(EXIT_ZONBIE退出狀態(tài))且不可運行,占用的內(nèi)存僅剩內(nèi)核棧、thread_info結(jié)構(gòu)和tast_struct結(jié)構(gòu),此時進程存在的唯一目的就是向它的父進程提供信息。父進程檢索到信息后,由進程所持有的剩余內(nèi)存被釋放,歸還給系統(tǒng)。
wait()通過系統(tǒng)調(diào)用wait4()來實現(xiàn),返回子進程的PID,調(diào)用該函數(shù)時提供的指針會包含子函數(shù)退出時的退出代碼
刪除進程描述符,父進程調(diào)用release_task()
如果沒有父進程,子進程exit_notify()的時候,調(diào)用forget_original_parent() -> find_new_reaper()來執(zhí)行尋父過程
總結(jié)
以上是生活随笔為你收集整理的linux内核的进程管理,Linux内核设计与实现——进程管理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux dma拷贝数据到用户态,图解
- 下一篇: 镁光ssd管理工具 linux,在 SS