有兩個(gè)這樣的進(jìn)程:僵尸進(jìn)程&孤兒進(jìn)程,藍(lán)瘦香菇
點(diǎn)擊上方“全棧IT技術(shù)前線”,選擇“星標(biāo)”
每天實(shí)用技術(shù)分享?在看|留言|真愛
先來說下什么是進(jìn)程:
來看下百度是怎么說的:

光看說的不夠形象,在windows系統(tǒng)中,它長這樣:

在Mac系統(tǒng)中,它長這樣:

Linux中是這樣的:(有點(diǎn)長截圖一部分好了)
[root@iz2ze76ybn73dvwmdij06zz?~]#?ps?-ef
UID????????PID??PPID??C?STIME?TTY??????????TIME?CMD
root?????????1?????0??0?5月20?????????00:00:33?/usr/lib/systemd/systemd?--system?--deserialize?21
root?????????2?????0??0?5月20?????????00:00:00?[kthreadd]
root?????????3?????2??0?5月20?????????00:00:06?[ksoftirqd/0]
root?????????5?????2??0?5月20?????????00:00:00?[kworker/0:0H]
root?????????7?????2??0?5月20?????????00:00:02?[migration/0]
root?????????8?????2??0?5月20?????????00:00:00?[rcu_bh]
root?????????9?????2??0?5月20?????????00:30:40?[rcu_sched]
root????????10?????2??0?5月20?????????00:00:17?[watchdog/0]
root????????11?????2??0?5月20?????????00:00:16?[watchdog/1]
root????????12?????2??0?5月20?????????00:00:02?[migration/1]
root????????13?????2??0?5月20?????????00:00:03?[ksoftirqd/1]
root????????15?????2??0?5月20?????????00:00:00?[kworker/1:0H]
root????????17?????2??0?5月20?????????00:00:00?[kdevtmpfs]
root????????18?????2??0?5月20?????????00:00:00?[netns]
root????????19?????2??0?5月20?????????00:00:01?[khungtaskd]
root????????20?????2??0?5月20?????????00:00:00?[writeback]
root????????21?????2??0?5月20?????????00:00:00?[kintegrityd]
root????????22?????2??0?5月20?????????00:00:00?[bioset]
root????????23?????2??0?5月20?????????00:00:00?[kblockd]
OK,以上每一行都是對一個(gè)進(jìn)程的描述,來具體看一下每個(gè)參數(shù)的含義:
| 標(biāo)示 | 描述 |
|---|---|
| UID | 用戶ID |
| PID | 進(jìn)程ID |
| PPID | 父進(jìn)程ID |
| C | 進(jìn)程占cpu百分比 |
| STIME | 進(jìn)程啟動(dòng)的時(shí)間 |
| TTY | 終端機(jī)位置 |
| TIME | 實(shí)際使用cpu的時(shí)間 |
| CMD | 命令以及參數(shù) |
我們現(xiàn)在知道了每個(gè)參數(shù)的含義,既然講到進(jìn)程嘛,首先,進(jìn)程ID是唯一的并且是非負(fù)數(shù)的,但是進(jìn)程ID是可以復(fù)用的,畢竟進(jìn)程也會(huì)終止。
可以看到?jīng)]有進(jìn)程PID是0的,這是為什么呢?黑人問號臉?
0一般來說是系統(tǒng)進(jìn)程,屬于內(nèi)核的一部分,不執(zhí)行任何磁盤上的程序。
fork
一個(gè)進(jìn)程可以通過調(diào)用fork函數(shù)創(chuàng)建新的進(jìn)程,被創(chuàng)建出來的這個(gè)進(jìn)程就叫子進(jìn)程。
這里需要注意一下,fork函數(shù)的返回值父子進(jìn)程區(qū)別。
子進(jìn)程 :返回值是0,返回0的理由是子進(jìn)程的父進(jìn)程是可以唯一確定的,通過getppid方法可以獲取到父進(jìn)程id。 父進(jìn)程 : 返回的是新創(chuàng)建的子進(jìn)程的id,因?yàn)楦高M(jìn)程可以有多個(gè)子進(jìn)程,也沒有這樣的函數(shù)可以獲取該線程的子線程的所有id。
下邊的話我們來驗(yàn)證一下上說的這一段話。準(zhǔn)備好腳本。
#include?
#include?
#include?
int?main(int?argc,?char?const?*argv[])
{
????pid_t?p1?=?fork();
????printf("%d\n",p1);
????if(p1?>?0)
????{
????????printf("父進(jìn)程?pid?=?%d,?p1?=?%d\n",?getpid(),?p1);
????}
????else
????{
????????printf("子進(jìn)程?pid?=?%d?,?ppid?=?%d,?p1?=?%d\n",?getpid(),?getppid(),?p1);
????}
????return?0;
}
運(yùn)行看結(jié)果:
[root@iz2ze76ybn73dvwmdij06zz?~]#?./fork2
10213
父進(jìn)程?pid?=?10212,?p1?=?10213
0
子進(jìn)程?pid?=?10213?,?ppid?=?10212,?p1?=?0
通過上面的小例子我們可以看到父進(jìn)程的返回值是子進(jìn)程的ID,子進(jìn)程的返回是0。
孤兒進(jìn)程
孤兒我們都懂就是。。。

是的,沒錯(cuò)孤兒進(jìn)程也是這樣的,就是沒有父進(jìn)程的進(jìn)程。當(dāng)然創(chuàng)建的時(shí)候肯定是要先創(chuàng)建父進(jìn)程了,當(dāng)父進(jìn)程退出時(shí),它的子進(jìn)程們(一個(gè)或者多個(gè))就成了孤兒進(jìn)程了。
接下來在繼續(xù)做一個(gè)小測試,讓父進(jìn)程現(xiàn)退出。準(zhǔn)備好腳本。
[root@iz2ze76ybn73dvwmdij06zz?~]#?cat?guer.c
#include?
#include?
#include?
#include?
int?main()
{
????pid_t?pid?=?fork();
????if?(pid?0)?{
?perror("fork?error;");
????????exit(1);
????}?else?if?(pid?==?0)?{
?sleep(5);
????????printf?("子進(jìn)程?:?[?pid]?=?%d?,?父進(jìn)程?[ppid]?=?%d\n",getpid(),getppid());
????????exit(0);
????}?else?if?(pid?>?0)?{
?printf("我是父線程,我先退出一步~\n");
?exit(0);
????}
??return?0;
}
執(zhí)行并看結(jié)果:

到這里估計(jì)很多童鞋估計(jì)已經(jīng)看懂了,父進(jìn)程退出后,子進(jìn)程被一個(gè)進(jìn)程ID為1的進(jìn)程領(lǐng)養(yǎng)的。還挺好這個(gè)結(jié)果,至少還是有人管的,被暖到了~ 進(jìn)程id為1的進(jìn)程是init進(jìn)程,每當(dāng)有孤兒進(jìn)程出現(xiàn)時(shí),init進(jìn)程就會(huì)收養(yǎng)它并成為它的父進(jìn)程 ,來照顧它以孤兒進(jìn)程以后的生活。
危害
因?yàn)楣聝哼M(jìn)程會(huì)被init進(jìn)程接管,所以孤兒進(jìn)程是沒有危害的。
僵尸進(jìn)程
和孤兒進(jìn)程相反的是,這次是子進(jìn)程先退出,而父進(jìn)程又沒有去處理回收釋放子進(jìn)程的資源,這個(gè)時(shí)候子進(jìn)程就成了僵尸進(jìn)程。
先準(zhǔn)備好代碼:
[root@iz2ze76ybn73dvwmdij06zz?~]#?cat?zombie.c
??#include?
??#include?
??#include?
??#include?
??int?main()
??{
??????pid_t?pid;
??????pid?=?fork();
?????if?(pid?0)
?????{
?????????perror("fork?error:");
?????????exit(1);
?????}
?????else?if?(pid?==?0)
?????{
?????????printf("我是子進(jìn)程,我要先退出一步了.\n");
??printf("子進(jìn)程?id?:?%d\n"?,getpid());
?????????exit(0);
?????}?else?{
?????????printf("我是父進(jìn)程,我先睡2秒\n");
?????????printf("父進(jìn)程?id?:?%d\n"?,getpid());
?????????sleep(2);
?????????while(2);?//來個(gè)死循環(huán),不退出的那種
????}
???return?0;
?}
運(yùn)行看下結(jié)果:

再來開一個(gè)終端看下進(jìn)程結(jié)果:

大家可以看到,子進(jìn)程并沒有完全退出,釋放資源,而是變成了僵尸進(jìn)程。
危害
資源上是占用不了什么資源。但是通常系統(tǒng)的進(jìn)程數(shù)量都是有限制的,如果有大量的僵尸進(jìn)程占用進(jìn)程號,導(dǎo)致新的進(jìn)程無法創(chuàng)建,這個(gè)危害類似于占個(gè)坑,不辦事。
處理
1.干掉父進(jìn)程
干掉父進(jìn)程后,讓剩下的子進(jìn)程成為孤兒進(jìn)程,成為孤兒進(jìn)程后就和我們上面說的一樣了,由init進(jìn)程來領(lǐng)養(yǎng)這些進(jìn)程,并且來處理這些進(jìn)程的資源釋放等工作。
2.父進(jìn)程調(diào)用wait或waitpid
等函數(shù)等待子進(jìn)程結(jié)束,這會(huì)導(dǎo)致父進(jìn)程掛起。執(zhí)行wait()或 waitpid()系統(tǒng)調(diào)用,則子進(jìn)程在終止后會(huì)立即把它在進(jìn)程表中的數(shù)據(jù)返回給父進(jìn)程,此時(shí)系統(tǒng)會(huì)立即刪除該進(jìn)入點(diǎn)。在這種情形下就不會(huì)產(chǎn)生defunct進(jìn)程。
3.fork兩次
第一次 fork : 父進(jìn)程fork一個(gè)子進(jìn)程
第二次 fork : 子進(jìn)程fork一個(gè)孫進(jìn)程后退出
那么孫進(jìn)程被init接管,當(dāng)孫進(jìn)程結(jié)束后,init會(huì)回收。
但子進(jìn)程的回收還要自己做。
4.signal函數(shù)
父進(jìn)程來處理:用signal函數(shù)為SIGCHLD安裝handler,在子進(jìn)程結(jié)束后,父進(jìn)程會(huì)收到該信號,可以在handler中調(diào)用wait回收。
內(nèi)核來處理: 如果父進(jìn)程不關(guān)心子進(jìn)程什么時(shí)候結(jié)束,可以通過以下兩個(gè)函數(shù)通知內(nèi)核自己不感興趣子進(jìn)程的結(jié)束,此時(shí),子進(jìn)程結(jié)束后,內(nèi)核會(huì)回收并不再給你父進(jìn)程發(fā)信號。
signal(SIGCLD, SIG_IGN) signal(SIGCHLD, SIG_IGN)
總結(jié)
本來以為簡單的一個(gè)問題,沒想到這個(gè)篇幅其實(shí)也不算短,所以感覺程序員真的不能說一個(gè)什么知識點(diǎn)就簡單,很容易理解啊,一旦你想要深入也是需要一定的時(shí)間花費(fèi)和精力的~
參考:
《UNIX環(huán)境高級編程(中文第三版)》
http://suo.im/6tOqJz
http://suo.im/67gdou
---END---
好用的IT工具、精彩的IT培訓(xùn)視頻、前言的技術(shù)分享,定期技術(shù)資源大放送!涵蓋云計(jì)算、大數(shù)據(jù)、人工智能、虛擬化、存儲、服務(wù)器、Linux、Docker&Kubernetes等。在公眾號內(nèi)回復(fù)「1」,即可免費(fèi)獲取??!



