父進程退出時,子進程會如何?如何確保父進程退出時,子進程也退出? ...
原文地址:https://www.yanbinghu.com/2019/02/14/37859.html
前言
子進程退出的時候,父進程能夠收到子進程退出的信號,便於管理,但是有時候又需要在父進程退出的時候,子進程也退出,該怎麼辦呢?
父進程退出時,子進程會如何?
一般情況下,父進程退出後,是不會通知子進程的,這個時候子進程會成為孤兒進程,最終被init進程收養。我們先來看一下這種情況。
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
int main(void)
{
pid_t pid;
//fork一個進程
pid = fork();
//創建失敗
if (pid < 0)
{
perror("fork error:");
exit(1);
}
//子進程
if (pid == 0)
{
printf("child process.\n");
printf("child pid:%d,parent pid:%d\n",getpid(),getppid());
printf("sleep 10 seconds.\n");
//sleep一段時間,讓父進程先退出,為了便於觀察,sleep 10s
sleep(10);
printf("now child pid: %d parent pid:%d\n",getpid(),getppid());
}
//父進程
else
{
printf("parent process.\n");
sleep(1);
}
return 0;
}
在這個程式中,我們為了讓父進程先退出,子進程sleep了10秒。
運行結果如下:
parent process.
child process.
child pid:17433,parent pid:17432
sleep 10 seconds.
now child pid: 17433 parent pid:1658
從結果中可以看到,一開始子進程17433的父進程id是17432,但是在10秒後,它的父進程變成了1658。1685是什麼進程呢?
$ ls -al /proc/1658/exe
/proc/1658/exe -> /sbin/upstart
由於我使用的環境是帶有圖形界面的ubuntu系統,所以最終並不是被我們所熟知的init進程收養,而是被一個名為/sbin/upstart的進程所收養。另外還可以觀察到,該進程也是其他系統進程的父進程。
如何確保父進程退出的同時,子進程也退出?
既然如此,如何確保父進程退出的同時,子進程也退出呢?或許我們可以在子進程和父進程之間建立通信管道,一旦通信異常,則認為父進程退出,子進程自己也回收資源退出。但是這樣做總覺得不是很正經。有沒有已有的函數幫我們做這件事呢?prctl函數可以幫助我們。第一個參數中,有一個選項,叫做PR_GET_PDEATHSIG:
PR_SET_PDEATHSIG (since Linux 2.1.57)
Set the parent death signal of the calling process to arg2 (either a signal value in the range 1..maxsig, or 0 to clear).
This is the signal that the calling process will get when its parent dies. This value is cleared for the child of a fork(2)
and (since Linux 2.4.36 / 2.6.23) when executing a set-user-ID or set-group-ID binary, or a binary that has associated capa‐
bilities (see capabilities(7)). This value is preserved across execve(2).
內容很多,主要意思為:設置一個信號,當父進程退出的時候,子進程將會收到該信號。
那麼根據這個,我們完全可以在父進程退出時,也給子進程一個退出的信號。程式代碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <signal.h>
int main(void)
{
pid_t pid;
//fork一個進程
pid = fork();
//創建失敗
if (pid < 0)
{
perror("fork error:");
exit(1);
}
//子進程
if (pid == 0)
{
/*父進程退出時,會收到SIGKILL信號*/
prctl(PR_SET_PDEATHSIG,SIGKILL);
printf("child process.\n");
printf("child pid:%d,parent pid:%d\n",getpid(),getppid());
printf("sleep 10 seconds.\n");
//sleep一段時間,讓父進程先退出,為了便於觀察,sleep 10s
sleep(10);
printf("now child pid: %d parent pid:%d\n",getpid(),getppid());
}
//父進程
else
{
printf("parent process.\n");
sleep(1);
}
return 0;
}
運行結果:
parent process.
child process.
child pid:17625,parent pid:17624
sleep 10 seconds.
可以看到,由於加入了
prctl(PR_SET_PDEATHSIG,SIGKILL);
在父進程退出時,子進程將會收到SIGKILL信號,而進程收到該信號的預設動作則是退出。因而最後不會看到它成為孤兒進程,被其他進程所收養。需要註意的是,該函數並非所有系統都支持。
總結
有些情況下,我們常常需要父子進程共存亡,子進程退出時,父進程可以通過wait捕捉子進程的退出狀態,但是父進程退出時,子進程卻難以得知。因此,在最初fork子進程的時候,便表明瞭,當父進程退出的時候,子進程收到SIGKILL信號,最終也退出。以此達到同生共死的目的。當然也可以發送其他信號,由子進程捕獲該信號並做後續處理。
練習
嘗試將上面的代碼在非圖形界面的linux操作系統中運行,看看最終被收養的是否為init進程。
交流
雖然本文方法可行,但並不適用於所有操作系統,你有什麼更好的辦法解決上面的問題?
微信公眾號【編程珠璣】:專註但不限於分享電腦編程基礎,Linux,C語言,C++,演算法,資料庫等編程相關[原創]技術文章,號內包含大量經典電子書和視頻學習資源。歡迎一起交流學習,一起修煉電腦“內功”,知其然,更知其所以然。