僵尸进程,孤儿进程以及守护进程

孤儿进程

如果父进程先退出,子进程还没退出那么子进程将被托孤给 init 进程,这是子进程的父进程就是 init 进程( 1 号进程).用到的 Linux 函数有:

  • fork 创建一个新进程
  • vfork 创建一个子进程,以供执行新程序,常与execve等同时使用
  • execve 运行可执行文件
  • getpid 获取进程标识号
  • getppid 获取父进程标识号
  • wait 等待子进程终止
  • waitpid 等待指定子进程终止
  • exit 中止进程
  • pause 挂起进程,等待信号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <signal.h>

int main(void)
{
pid_t pid ;
signal(SIGCHLD,SIG_IGN);
printf("before fork pid:%d\n",getpid());
int abc = 10;
pid = fork();
if(pid == -1)
{
perror("tile");
return -1;
}
if(pid > 0) //父进程先退出
{
abc++;
printf("parent:pid:%d \n",getpid());
printf("abc:%d \n",abc);
sleep(5);
}
else if(pid == 0){ //值进程后退出,被托付给init进程
abc++;
printf("child:%d,parent: %d\n",getpid(),getppid());
printf("abc:%d",abc);
sleep(100);
}
printf("fork after...\n");
}

测试结果:我们执行程序后由于子进程进入 sleep(100) ,而父进程先退出.通过 ps -ef 命令我们可以知道,此时 27710 号进程的父进程编程了 1 号进程.也就是我们所说的 init 进程.

1
2
3
4
5
6
7
8
before fork pid:27709
parent:pid:27709
abc:11
child:27710,parent: 27709
fork after...

disda 27710 1 0 10:47 pts/1 00:00:00 ./review
disda 27713 25948 47 10:47 pts/3 00:00:00 ps -ef

僵尸进程

一个进程使用 fork 创建子进程,如果子进程退出,而父进程并没有调用 wait 或 waitpid 获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程。僵尸进程还会消耗一定的系统资源,并且还保留一些概要信息供父进程查询子进程的状态可以提供父进程想要的信息。一旦父进程得到想要的信息,僵尸进程就会结束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int main(void)
{
pid_t pid ;
//signal(SIGCHLD,SIG_IGN);
printf("before fork pid:%d\n",getpid());
int abc = 10;
pid = fork();
if(pid == -1)
{
perror("tile");
return -1;
}
if(pid > 0)
{
abc++;
printf("parent:pid:%d \n",getpid());
printf("abc:%d \n",abc);
sleep(20);
}
else if(pid == 0){
abc++;
printf("child:%d,parent: %d\n",getpid(),getppid());
printf("abc:%d",abc);
exit(0);
}
printf("fork after...\n");

同样通过 ps -ef 我们可以得知进程信息和进程 pid,可以看到子进程就是处于 defunct 状态。

1
2
disda 27881 23047 0 11:12 pts/1 00:00:00 ./fork01
disda 27882 27881 0 11:12 pts/1 00:00:00 [fork01] <defunct>

僵尸进程危害

如果进程不调用 wait / waitpid 的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免   

避免僵尸进程的方式

  • 通过信号机制,子进程退出时向父进程发送 SIGCHILD 信号,父进程处理 SIGCHILD 信号。在信号处理函数中调用 wait 进行处理僵尸进程。
  • fork 两次,原理是将子进程成为孤儿进程,从而其的父进程变为 init 进程,通过 init 进程可以处理僵尸进程。

守护进程