Linux進(jìn)程管理知識整理
1、進(jìn)程有哪些狀態(tài)?什么是進(jìn)程的可中斷等待狀態(tài)?進(jìn)程退出后為什么要等待調(diào)度器刪除其task_struct結(jié)構(gòu)?進(jìn)程的退出狀態(tài)有哪些?
TASK_RUNNING(可運(yùn)行狀態(tài))
TASK_INTERRUPTIBLE(可中斷等待狀態(tài))
TASK_UNINTERRUPTIBLE(不可中斷等待狀態(tài))
TASK_STOPPED(進(jìn)程被其它進(jìn)程設(shè)置為暫停狀態(tài))
TASK_TRACED(進(jìn)程被調(diào)試器設(shè)置為暫停狀態(tài))
TASK_DEAD(退出狀態(tài))
進(jìn)程由于所需資源得不到滿足,從而進(jìn)入等待隊(duì)列,但是該狀態(tài)能夠被信號中斷。比如當(dāng)一個正在運(yùn)行的進(jìn)程因進(jìn)行磁盤I/O操作而進(jìn)入可中斷等待狀態(tài)時(shí),在I/O操作完成之前,用戶可以向該進(jìn)程發(fā)送SIGKILL,從而使該進(jìn)程提前結(jié)束等待狀態(tài),進(jìn)入可運(yùn)行態(tài),以便響應(yīng)SIGKILL,執(zhí)行進(jìn)程退出代碼,從而結(jié)束該進(jìn)程。
當(dāng)進(jìn)程退出時(shí)(例如調(diào)用exit或者從main函數(shù)返回),需要向父進(jìn)程發(fā)送信號,父進(jìn)程進(jìn)行信號處理時(shí),需要獲取子進(jìn)程的信息,因此這時(shí)不能刪除子進(jìn)程的task_struct。另外每個進(jìn)程都有一個內(nèi)核態(tài)的堆棧,當(dāng)進(jìn)程調(diào)用exit()時(shí),在切換到另外一個進(jìn)程前,總是要使用內(nèi)核態(tài)堆棧,因此當(dāng)進(jìn)程調(diào)用exit()時(shí),完成必要的處理后,就把state設(shè)置為TASK_DEAD,并切換到其他進(jìn)程。當(dāng)順利地切換到其他進(jìn)程后,由于該進(jìn)程的狀態(tài)設(shè)置為TASK_DEAD,因此這個進(jìn)程不會被調(diào)度,之后當(dāng)調(diào)度器檢查到狀態(tài)為TASK_DEAD的進(jìn)程時(shí),就會刪除這個進(jìn)程的task_struct結(jié)構(gòu),這樣這個進(jìn)程就徹底的消失了。
EXIT_ZOMBIE(僵死進(jìn)程):父進(jìn)程等待子進(jìn)程結(jié)束時(shí)發(fā)送的SIGCHLD信號(默認(rèn)情況下,創(chuàng)建進(jìn)程都會設(shè)置在進(jìn)程退出的時(shí)候向父進(jìn)程發(fā)送信號的標(biāo)志,除非創(chuàng)建的是輕權(quán)進(jìn)程),此時(shí)子進(jìn)程已退出,并且SIGCHLD信號已經(jīng)發(fā)送,但是父進(jìn)程還沒有被調(diào)度運(yùn)行;EXIT_DEAD(僵死撤銷狀態(tài)):父進(jìn)程對子進(jìn)程的退出信號“沒興趣”,或者在子進(jìn)程退出時(shí),父進(jìn)程通過waitpid()調(diào)用等待子進(jìn)程的SIGCHLD信號。
2、僵尸進(jìn)程
1) 怎么產(chǎn)生僵尸進(jìn)程
一個進(jìn)程在調(diào)用exit命令結(jié)束自己的時(shí)候,其實(shí)它并沒有真正的被銷毀,只是進(jìn)程不能被調(diào)度并處于EXIT_ZOMBIE狀態(tài),它占用的所有內(nèi)存就是內(nèi)核棧、thread_info結(jié)構(gòu)和task_struct結(jié)構(gòu)。此時(shí)進(jìn)程存在的唯一目的就是向它的父進(jìn)程提供信息,如果它的父進(jìn)程沒有調(diào)用wait或waitpid等待子進(jìn)程結(jié)束,又沒有顯示地忽略該信號,那么它就一直保持EXIT_ZOMBIE狀態(tài)。
2) 怎么查看僵尸進(jìn)程
利用命令ps,看到有標(biāo)記為Z的進(jìn)程就是僵尸進(jìn)程。
3) 怎么清理僵尸進(jìn)程
- 父進(jìn)程可以調(diào)用waitpid、wait函數(shù)來等待子進(jìn)程結(jié)束
- 把父進(jìn)程殺掉,父進(jìn)程死后,僵尸進(jìn)程成為“孤兒進(jìn)程”,過繼給init進(jìn)程,init進(jìn)程始終負(fù)責(zé)清理僵尸進(jìn)程,它產(chǎn)生的所有僵尸進(jìn)程也跟著消失。
3、PID管理
在Linux系統(tǒng)中用pid結(jié)構(gòu)體來標(biāo)識一個進(jìn)程,通過pidmap位圖來管理所有的進(jìn)程號(即pid:與前面的pid結(jié)構(gòu)體不是同一個意思),目的就是要更快的找到目標(biāo)進(jìn)程。用pid結(jié)構(gòu)體來表示進(jìn)程的優(yōu)點(diǎn):比直接用數(shù)字pid_t更容易管理(進(jìn)程退出時(shí)pid回收再分配效率高),比直接用task_struct標(biāo)識進(jìn)程占用空間小。
pid結(jié)構(gòu)體如下所示:
- struct pid
- {
- atomic_t count;
- int nr; /*存放pid數(shù)值*/
- struct hlist_node pid_chain; /*把該pid鏈到哈希表中*/
- struct hlist_head tasks[PIDTYPE_MAX];
- struct rcu_head rcu;
- };
因?yàn)閷τ?2位系統(tǒng)來說,默認(rèn)最大的pid為32768,由于pidmap位圖中每一位表示這個pid是否可用,共需要32768位,正好一個物理頁的大小(4*1024*8)。
pidmap結(jié)構(gòu)體如下所示:
- struct pidmap {
- /*
- *這個變量用來統(tǒng)計(jì)這個結(jié)構(gòu)體對應(yīng)的一頁物理內(nèi)存中有多少個位
- *是0的,即空閑pid的數(shù)量
- */
- atomic_t nr_free;
- void *page; /*這個就是指向存放這個位圖的內(nèi)存頁的指針*/
- };
下面首先來看Linux內(nèi)核啟動之初在start_kernel函數(shù)中對pidmap位圖的初始化函數(shù)pidmap_init如下所示:
- void __init pidmap_init(void)
- {
- /*申請一頁物理內(nèi)存,并初始化為0*/
- init_pid_ns.pidmap[0].page = kzalloc(PAGE_SIZE, GFP_KERNEL);
- /*將第0位設(shè)置為1,表示當(dāng)前進(jìn)程使用pid為0,即現(xiàn)在就是0號進(jìn)程*/
- set_bit(0, init_pid_ns.pidmap[0].page);
- /*同時(shí)更新nr_free統(tǒng)計(jì)空閑pid的值*/
- atomic_dec(&init_pid_ns.pidmap[0].nr_free);
- pid_cachep = KMEM_CACHE(pid, SLAB_PANIC);
- }
再來看Linux內(nèi)核啟動之初在start_kernel函數(shù)中對pid hash表的初始化函數(shù)pidhash_init如下所示:
- void __init pidhash_init(void)
- {
- int i, pidhash_size;
- /*
- *nr_kernel_pages表示內(nèi)核內(nèi)存總頁數(shù),就是系統(tǒng)DMA和NORMAL內(nèi)
- *存頁區(qū)的實(shí)際物理內(nèi)存總頁數(shù)
- *megabytes:統(tǒng)計(jì)的是內(nèi)核內(nèi)存有多少M(fèi)B
- */
- unsigned long megabytes = nr_kernel_pages >> (20 - PAGE_SHIFT);
- /*從下面兩行代碼可以看出pidhash_shift是在4~12之間的*/
- pidhash_shift = max(4, fls(megabytes * 4));
- pidhash_shift = min(12, pidhash_shift);
- pidhash_size = 1 << pidhash_shift;
- printk("PID hash table entries: %d (order: %d, %Zd bytes)\n",
- pidhash_size, pidhash_shift,
- pidhash_size * sizeof(struct hlist_head));
- /*
- *由alloc_bootmem可知pid_hash是在低端物理內(nèi)存申請的,由于
- *pidhash_init函數(shù)是在mem_init函數(shù)執(zhí)行之前被調(diào)用的,所以這里申請
- *的內(nèi)存是不會被回收的
- */
- pid_hash = alloc_bootmem(pidhash_size * sizeof(*(pid_hash)));
- if (!pid_hash)
- panic("Could not alloc pidhash!\n");
- for (i = 0; i < pidhash_size; i++)
- /*初始化每個表的每個表項(xiàng)的鏈表*/
- INIT_HLIST_HEAD(&pid_hash[i]);
- }
總結(jié):內(nèi)核維護(hù)兩個數(shù)據(jù)結(jié)構(gòu)來維護(hù)進(jìn)程號pid,一個是哈希表pid_hash,還有一個位圖pidmap。在do_fork()中每調(diào)用一次alloc_pid(),首先會通過調(diào)用alloc_pidmap()修改相應(yīng)的位圖,該函數(shù)的主要思想是:last記錄上次分配的pid,此次分配的pid為last+1,如果pid超出最大值,那么就循環(huán)回到最初值(RESERVED_PIDS),然后測試pidmap上該pid所對應(yīng)的bit是否為0,直到找到為止。其次通過hlist_add_head_rcu函數(shù)在pid_hash表中增加一項(xiàng)。
#p#
4、進(jìn)程的堆棧
一個進(jìn)程有兩個堆棧:用戶態(tài)堆棧和內(nèi)核態(tài)堆棧。用戶態(tài)堆棧的空間指向用戶地址空間,內(nèi)核態(tài)堆棧的空間指向內(nèi)核地址空間。
當(dāng)進(jìn)程由于中斷或系統(tǒng)調(diào)用從用戶態(tài)(進(jìn)程在執(zhí)行用戶自己的代碼)轉(zhuǎn)換到內(nèi)核態(tài)(進(jìn)程在執(zhí)行內(nèi)核代碼)時(shí),進(jìn)程所使用的棧也要從用戶棧切換到內(nèi)核棧。
用戶棧向內(nèi)核棧的切換:進(jìn)入內(nèi)核態(tài)后,首先把用戶態(tài)的堆棧地址保存在內(nèi)核堆棧中,然后設(shè)置堆棧指針寄存器的地址為內(nèi)核棧地址。
內(nèi)核棧向用戶棧的切換:把保存在內(nèi)核棧中的用戶棧地址恢復(fù)到堆棧指針寄存器即可。
5、Linux下進(jìn)程與線程的區(qū)別
1)進(jìn)程是資源分配的基本單位,線程是CPU調(diào)度的基本單位
2)進(jìn)程有獨(dú)立的地址空間,線程有自己的堆棧和局部變量,但是沒有獨(dú)立的地址空間(同一個進(jìn)程內(nèi)的線程共享進(jìn)程的地址空間)
6、寫時(shí)拷貝機(jī)制(copy on write)
為了節(jié)約物理內(nèi)存,在調(diào)用fork()生成新進(jìn)程時(shí),新進(jìn)程與原進(jìn)程會共享同一物理內(nèi)存區(qū)(調(diào)用clone()建立線程,還會共享虛擬地址空間),只有當(dāng)其中一進(jìn)程進(jìn)行寫操作時(shí),系統(tǒng)才會為其另外分配物理內(nèi)存頁面,這就是寫時(shí)拷貝機(jī)制。
詳細(xì)解釋如下:當(dāng)進(jìn)程A使用系統(tǒng)調(diào)用fork()創(chuàng)建一個子進(jìn)程B時(shí),由于子進(jìn)程B實(shí)際上是父進(jìn)程A的一個拷貝,因此會擁有與父進(jìn)程相同的物理頁面。為了節(jié)約內(nèi)存和加快創(chuàng)建速度的目標(biāo),fork()函數(shù)會讓子進(jìn)程B以只讀方式共享父進(jìn)程A的物理頁面,同時(shí)將父進(jìn)程A對這些物理頁面的訪問權(quán)限也設(shè)為只讀,這樣,當(dāng)父進(jìn)程A或子進(jìn)程B任何一方對這些已共享的物理頁面執(zhí)行寫操作時(shí),都會產(chǎn)生頁面出錯異常中斷,此時(shí)CPU會執(zhí)行系統(tǒng)提供的異常處理函數(shù)do_wp_page()來解決這個異常,do_wp_page()會對這塊導(dǎo)致寫入異常中斷的物理頁面取消共享操作,為寫進(jìn)程復(fù)制一份新的物理頁面。最后,從異常處理函數(shù)返回時(shí),CPU就會重新執(zhí)行剛才導(dǎo)致異常的寫入操作指令,使進(jìn)程繼續(xù)執(zhí)行下去。
7、0號進(jìn)程的建立
內(nèi)核啟動時(shí)“手工”建立了0號進(jìn)程,即swapper進(jìn)程,這是一個內(nèi)核態(tài)進(jìn)程,它的頁表swapper_pg_dir和內(nèi)核態(tài)堆棧是在內(nèi)核啟動建立的,這個進(jìn)程定義如下:
- struct task_struct init_task = INIT_TASK(init_task);
init_task的各種進(jìn)程資源對象都是通過INIT_xxx進(jìn)程初始化的,在start_kernel()的最后由rest_init()函數(shù)調(diào)用kernel_thread()函數(shù),以swapper進(jìn)程為“模板”建立了kernel_init內(nèi)核進(jìn)程,之后這個進(jìn)程會建立init進(jìn)程,執(zhí)行/sbin//-init文件,從而把啟動過程傳遞到用戶態(tài)。而swapper進(jìn)程則執(zhí)行cpu_idle()函數(shù)讓出CPU,以后如果沒有任何就緒的進(jìn)程可調(diào)度執(zhí)行,就會調(diào)度swapper進(jìn)程,執(zhí)行cpu_idle()函數(shù),這個函數(shù)將調(diào)用tick_nohz_stop_sched_tick()進(jìn)入tickless狀態(tài)。
8、進(jìn)程的切換
1) 主動切換
- 當(dāng)前進(jìn)程主動進(jìn)行可能引起阻塞的I/O操作,此時(shí)當(dāng)前進(jìn)程被設(shè)置為等待狀態(tài),加入到相關(guān)資源的等待隊(duì)列,并調(diào)用schedule()函數(shù)讓出CPU。
- 進(jìn)程主動通過exit系統(tǒng)調(diào)用退出。
2) 被動切換
- 時(shí)間片到期
- I/O中斷喚醒了某個I/O等待隊(duì)列中的更高優(yōu)先級的進(jìn)程
由于這兩種情況通常發(fā)生在時(shí)鐘中斷或者其他I/O中斷處理函數(shù)中,而中斷上下文環(huán)境下不能阻塞進(jìn)程,所以通常中斷處理程序中通過設(shè)置need_resched標(biāo)志請求調(diào)度,這個調(diào)度被延遲到中斷返回處理。
9、Linux系統(tǒng)的進(jìn)程間通信的方式
管道(pipe):管道是一種半雙工的通信方式,數(shù)據(jù)只能單向流動,而且只能在具有親緣關(guān)系的進(jìn)程間使用(進(jìn)程的親緣關(guān)系通常是指父子進(jìn)程關(guān)系)。
命名管道(named pipe):命名管道也是半雙工的通信方式,但是它允許無親緣關(guān)系進(jìn)程間的通信。
信號量(semophore):信號量是一個計(jì)數(shù)器,可以用來控制多個進(jìn)程對共享資源的訪問。它常作為一種鎖機(jī)制,防止某進(jìn)程正在訪問共享資源時(shí),其他進(jìn)程也訪問該資源。因此,主要作為進(jìn)程間以及同一進(jìn)程內(nèi)不同線程之間的同步手段。
消息隊(duì)列(message queue):消息隊(duì)列就是一個消息的鏈表,存放在內(nèi)核中并由消息隊(duì)列標(biāo)識符標(biāo)識。消息隊(duì)列克服了信號傳遞信息少、管道只能承載無格式字節(jié)流以及緩沖區(qū)大小受限等缺點(diǎn)。
信號(sinal):信號是一種比較復(fù)雜的通信方式,用于通知接收進(jìn)程某個事件已經(jīng)發(fā)生。
共享內(nèi)存(shared memory):共享內(nèi)存就是映射一段能被其他進(jìn)程所訪問的內(nèi)存,這段共享內(nèi)存由一個進(jìn)程創(chuàng)建,但多個進(jìn)程都可以訪問。共享內(nèi)存是最快的IPC方式,它是針對其他進(jìn)程間通信方式運(yùn)行效率低而專門設(shè)計(jì)的。它往往與其他通信機(jī)制,如信號配合使用,來實(shí)現(xiàn)進(jìn)程間的同步和通信。
套接字(socket):套接字也是一種進(jìn)程間通信機(jī)制,與其他通信機(jī)制不同的是,它可用于不同主機(jī)間的進(jìn)程通信。
10、Linux進(jìn)程調(diào)度機(jī)制
1) 什么是調(diào)度
從就緒的進(jìn)程中選出最適合的一個來執(zhí)行
2) 學(xué)習(xí)調(diào)度需要掌握哪些知識點(diǎn)
- 調(diào)度策略
- 調(diào)度時(shí)機(jī)
- 調(diào)度步驟
3) 調(diào)度策略
SCHED_NORMAL:普通的進(jìn)程
SCHED_FIFO:先入先出的實(shí)時(shí)進(jìn)程
SCHED_RR:時(shí)間片輪轉(zhuǎn)的實(shí)時(shí)進(jìn)程
4) 調(diào)度器類
分為CFS調(diào)度類和實(shí)時(shí)調(diào)度類。
- CFS調(diào)度類是針對普通進(jìn)程的,采用的方法是完全摒棄時(shí)間片而是分配給進(jìn)程一個處理器使用比重。
- 實(shí)時(shí)調(diào)度類分為SCHED_FIFO和SCHED_RR。
SCHED_FIFO實(shí)現(xiàn)了一種簡單的、先入先出的調(diào)度算法:它不使用時(shí)間片,可以一直執(zhí)行下去,只有更高優(yōu)先級的SCHED_FIFO或者SCHED_RR任務(wù)才能搶占SCHED_FIFO任務(wù)。如果有兩個或者更多的同優(yōu)先級的SCHED_FIFO進(jìn)程,它們會輪流執(zhí)行,但是依然只有在它們愿意讓出處理器時(shí)才會退出。
SCHED_RR與SCHED_FIFO大體相同,只是SCHED_RR級的進(jìn)程在耗盡事先分配給它的時(shí)間后就不能再繼續(xù)執(zhí)行了。
5) 調(diào)度時(shí)機(jī)
- 主動式
在內(nèi)核中直接調(diào)用schedule():當(dāng)進(jìn)程需要等待資源而暫時(shí)停止運(yùn)行時(shí),會把進(jìn)程的狀態(tài)
設(shè)置為等待狀態(tài),并主動請求調(diào)度,讓出CPU。
例:current->state=TASK_INTERRUPTIBLE;
schedule();
- 被動式
用戶搶占:內(nèi)核即將返回用戶空間的時(shí)候,如果need_resched標(biāo)志被設(shè)置,會導(dǎo)致schedule()被調(diào)用,此時(shí)就會發(fā)生用戶搶占。
內(nèi)核搶占:只要重新調(diào)度是安全的,那么內(nèi)核就可以在任何時(shí)間搶占正在執(zhí)行的任務(wù)。
6) 調(diào)度步驟
- 清理當(dāng)前運(yùn)行中的進(jìn)程
- 選擇下一個要運(yùn)行的進(jìn)程
- 設(shè)置新進(jìn)程的運(yùn)行環(huán)境
- 進(jìn)程上下文切換
#p#
Linux進(jìn)程管理之問題
1、為什么調(diào)用fork()函數(shù)將返回兩次?
這是因?yàn)樵赿o_fork->copy_process->copy_thread函數(shù)中,將子進(jìn)程的用戶態(tài)堆棧的開始地址設(shè)置為父進(jìn)程的用戶態(tài)堆棧的開始地址,這樣當(dāng)父子進(jìn)程從內(nèi)核態(tài)返回到用戶態(tài)的時(shí)候,返回的地址相同,這就解釋了為什么fork一次卻返回兩次的原因。
2、為什么要在task_struct中設(shè)置mm和active_mm兩個mm_struct成員呢?
這是由于內(nèi)核線程沒有用戶態(tài)地址空間,所以它的mm設(shè)置為NULL,但是由于頁目錄的地址是保存在mm結(jié)構(gòu)中的,從其他進(jìn)程切換到這個內(nèi)核態(tài)線程時(shí),調(diào)度器可能需要切換頁表,為此增加了一個active_mm,對于mm為NULL的內(nèi)核態(tài)線程,就借用其他進(jìn)程的mm_struct,也就是說把它的active_mm指向其他進(jìn)程的mm結(jié)構(gòu),當(dāng)進(jìn)行進(jìn)程切換時(shí),統(tǒng)一使用active_mm就可以了。但是其他進(jìn)程不是有自己獨(dú)立的頁表嗎?由于內(nèi)核態(tài)線程只使用內(nèi)核地址空間,因此這不會有問題。
3、有如下說法:1.task_struct的mm成員用來描述3GB用戶態(tài)虛擬地址空間;2.內(nèi)核線程可以借用上一個調(diào)用的用戶進(jìn)程的mm中的頁表來訪問內(nèi)核地址空間。如果是這樣的話,那么task_struct的mm成員能不能描述1GB的內(nèi)核地址空間?如果不能的話,為什么會有2這種說法?
task_struct的mm成員不能描述1GB的內(nèi)核地址空間,只是因?yàn)閙m成員中保存了頁目錄的信息pgd_t,而且所有進(jìn)程共享1G的內(nèi)核態(tài)地址空間,所以可以使用上一個用戶進(jìn)程的mm中的頁表訪問內(nèi)核地址空間。
4、為什么所有進(jìn)程共享1G的內(nèi)核態(tài)地址空間?
因?yàn)閒ork()會復(fù)制當(dāng)前進(jìn)程的task_struct結(jié)構(gòu),同時(shí)會為新進(jìn)程復(fù)制mm結(jié)構(gòu)。此時(shí)當(dāng)前進(jìn)程的3GB~4GB的內(nèi)核態(tài)虛擬地址對應(yīng)的頁表項(xiàng)(頁目錄項(xiàng))被復(fù)制到進(jìn)程的頁表項(xiàng)(頁目錄項(xiàng))中,所以說所有進(jìn)程共享1G內(nèi)核態(tài)地址空間。但是對于用戶態(tài)虛擬地址區(qū)域,則把它的進(jìn)程頁表項(xiàng)(頁目錄項(xiàng))設(shè)置為只讀,這樣當(dāng)其中一個進(jìn)程對其進(jìn)行寫入操作時(shí),do_page_fault()會分配新的物理頁面,并建立映射,從而實(shí)現(xiàn)COW機(jī)制。
5、父進(jìn)程要求子進(jìn)程退出時(shí)發(fā)送信號,那么父進(jìn)程要求子線程退出時(shí)發(fā)送信號嗎?為什么?
父進(jìn)程不要求子線程退出時(shí)發(fā)送信號,這是因?yàn)樽泳€程共享父進(jìn)程的一些資源,所以不需要父進(jìn)程來獲取這些信息,也就不需要向父進(jìn)程發(fā)送信號。這一點(diǎn)可以在do_fork->copy_process
p->exit_signal=(clone_flags & CLONE_THREAD) ? -1 :(clone_flags &CSIGNAL);
以及do_exit->exit_notify
if (tsk->exit_signal != -1 && thread_group_empty(tsk)) {
int signal = tsk->parent == tsk->real_parent ? tsk->exit_signal : SIGCHLD;
do_notify_parent(tsk, signal);
} else if (tsk->ptrace) {
do_notify_parent(tsk, SIGCHLD);
}
中看出來。
6、 為什么子進(jìn)程退出時(shí),如果父進(jìn)程沒有調(diào)用wait等待子進(jìn)程結(jié)束,則子進(jìn)程會變成僵尸進(jìn)程?
分析如下:在內(nèi)核源碼中有如下的代碼:
do_exit->exit_notify->
state = EXIT_ZOMBIE
if (tsk->exit_signal == -1 &&
(likely(tsk->ptrace == 0) ||
unlikely(tsk->parent->signal->flags & SIGNAL_GROUP_EXIT)))
state = EXIT_DEAD;
tsk->exit_state = state;
說明如果定義了子進(jìn)程退出時(shí)向父進(jìn)程發(fā)送信號,則設(shè)置進(jìn)程狀態(tài)為EXIT_ZOMBIE,否則為EXIT_DEAD。而子進(jìn)程退出時(shí)一定會向父進(jìn)程發(fā)送
信號,所以進(jìn)程的狀態(tài)為EXIT_ZOMBIE,如果此時(shí)父進(jìn)程調(diào)用wait等待子進(jìn)程結(jié)束的話,由do_wait->wait_task_zombie函數(shù)可以將進(jìn)程的狀態(tài)設(shè)置為EXIT_DEAD,并且釋放進(jìn)程的內(nèi)核堆棧資源,最后由put_task_struct將其task_struct結(jié)構(gòu)體釋放掉。否則子進(jìn)程會變成僵尸進(jìn)程。