? ? ? ? 進程是UNIX操作系統(tǒng)抽象概念中最基本的一種,其中涉及進程的定義以及相關的概念,比如線程;它們在內(nèi)核中如何被列舉?如何創(chuàng)建?最終又如何消亡?讓我們通過下面的分析,一步步解開內(nèi)核進程的神秘面紗。
1. 進程和線程
進程和線程是程序運行時狀態(tài),是動態(tài)變化的,進程和線程的管理操作(比如,創(chuàng)建,銷毀等)都是有內(nèi)核來實現(xiàn)的。
Linux中的進程于Windows相比是很輕量級的,而且不嚴格區(qū)分進程和線程,線程不過是一種特殊的進程。
所以下面只討論進程,只有當線程與進程存在不一樣的地方時才提一下線程。
進程提供2種虛擬機制:虛擬處理器和虛擬內(nèi)存
每個進程有獨立的虛擬處理器和虛擬內(nèi)存,
每個線程有獨立的虛擬處理器,同一個進程內(nèi)的線程有可能會共享虛擬內(nèi)存。
內(nèi)核中進程的信息主要保存在task_struct中(include/linux/sched.h)
進程標識PID和線程標識TID對于同一個進程或線程來說都是相等的。
Linux中可以用ps命令查看所有進程的信息:
ps -eo pid,tid,ppid,comm
2. 進程的生命周期
進程的各個狀態(tài)之間的轉(zhuǎn)化構成了進程的整個生命周期。
3. 進程的創(chuàng)建
Linux中創(chuàng)建進程與其他系統(tǒng)有個主要區(qū)別,Linux中創(chuàng)建進程分2步:fork()和exec()。
fork: 通過拷貝當前進程創(chuàng)建一個子進程
exec: 讀取可執(zhí)行文件,將其載入到內(nèi)存中運行
創(chuàng)建的流程:
調(diào)用dup_task_struct()為新進程分配內(nèi)核棧,task_struct等,其中的內(nèi)容與父進程相同。
check新進程(進程數(shù)目是否超出上限等)
清理新進程的信息(比如PID置0等),使之與父進程區(qū)別開。
新進程狀態(tài)置為 TASK_UNINTERRUPTIBLE
更新task_struct的flags成員。
調(diào)用alloc_pid()為新進程分配一個有效的PID
根據(jù)clone()的參數(shù)標志,拷貝或共享相應的信息
做一些掃尾工作并返回新進程指針
創(chuàng)建進程的fork()函數(shù)實際上最終是調(diào)用clone()函數(shù)。
創(chuàng)建線程和進程的步驟一樣,只是最終傳給clone()函數(shù)的參數(shù)不同。
比如,通過一個普通的fork來創(chuàng)建進程,相當于:clone(SIGCHLD, 0)
創(chuàng)建一個和父進程共享地址空間,文件系統(tǒng)資源,文件描述符和信號處理程序的進程,即一個線程:clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0)
在內(nèi)核中創(chuàng)建的內(nèi)核線程與普通的進程之間還有個主要區(qū)別在于:內(nèi)核線程沒有獨立的地址空間,它們只能在內(nèi)核空間運行。
這與之前提到的Linux內(nèi)核是個單內(nèi)核有關。
4. 進程的終止
和創(chuàng)建進程一樣,終結一個進程同樣有很多步驟:
子進程上的操作(do_exit)
設置task_struct中的標識成員設置為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設置為進程的返回值
調(diào)用exit_notify()向父進程發(fā)送信號,并把自己的狀態(tài)設為EXIT_ZOMBIE
切換到新進程繼續(xù)執(zhí)行
子進程進入EXIT_ZOMBIE之后,雖然永遠不會被調(diào)度,關聯(lián)的資源也釋放掉了,但是它本身占用的內(nèi)存還沒有釋放,
比如創(chuàng)建時分配的內(nèi)核棧,task_struct結構等。這些由父進程來釋放。
父進程上的操作(release_task)
父進程受到子進程發(fā)送的exit_notify()信號后,將該子進程的進程描述符和所有進程獨享的資源全部刪除。
從上面的步驟可以看出,必須要確保每個子進程都有父進程,如果父進程在子進程結束之前就已經(jīng)結束了會怎么樣呢?
子進程在調(diào)用exit_notify()時已經(jīng)考慮到了這點。如果子進程的父進程已經(jīng)退出了,那么子進程在退出時,exit_notify()函數(shù)會先調(diào)用forget_original_parent(),然后再調(diào)用find_new_reaper()來尋找新的父進程。
find_new_reaper()函數(shù)先在當前線程組中找一個線程作為父親,如果找不到,就讓init做父進程。(init進程是在linux啟動時就一直存在的)
?
評論