我们知道在unix/linux中,正常情况下,子进程是通过父进程创建的,子进程在创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束。当一个进程完成它的工作终止之后,它的父进程需要调用wait()或者waitpid()系统调用取得子进程的终止状态。如果一个子进程结束了,但是他的父进程没有等待他, 那么这个子进程将变成一个僵尸进程。 但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程,,因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程,看有没有哪个进程是刚刚结束的这个进程的子进程,如果是的话,就由Init(进程号为1)来接管他,成为他的父进程,此时称这个进程为孤儿进程,其状态收集工作由init进程负责。
OK,首先说简单的结论:没有父进程的进程就是孤儿进程,孤儿进程会被init领养,成为一个准守护进程。如果进程他爹活着,但是不给子进程收尸(wait、waitpid),子进程就会变成僵尸(zombie)。守护进程(Daemon)是在一类脱离终端在后台执行的程序, 通常以 d 结尾, 随系统启动, 其父进程 (ppid) 通常是 init 进程。
下面详细介绍一下各个进程。
一、孤儿进程
一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程所收养,并由init进程对它们完成状态收集工作。
实例:以下是一个孤儿进程的示例程序,在此程序中,让父进程先退出,然后子进程再次打印自己的父进程号:
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<unistd.h>
int main()
{
pid_t pid;
//创建一个进程
pid = fork();
//创建失败
if (pid < 0)
{
perror("fork error:");
exit(1);
}
//子进程
if (pid == 0)
{
printf("I am the childprocess.\n");
//输出进程ID和父进程ID
printf("pid:%d\tppid:%d\n",getpid(),getppid());
printf("I will sleep fiveseconds.\n");
//睡眠5s,保证父进程先退出
sleep(5);
printf("pid:%d\tppid:%d\n",getpid(),getppid());
printf("child process isexited.\n");
}
//父进程
else
{
printf("I am fatherprocess.\n");
//父进程睡眠1s,保证子进程输出进程id
sleep(1);
printf("father process is exited.\n");
}
return 0;
}
注:getpid函数可以获得当前进程的pid,getppid函数可以获得当前进程的父进程号。
以上程序编译运行后的输出为:
可以看出子进程1091后来被init进程接管(pid=1)。
二、僵尸进程
一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
如果我们了解过linux进程状态及转换关系,我们应该知道进程这么多状态中有一种状态是僵死状态,就是进程终止后进入僵死状态(zombie),等待告知父进程自己终止后才能完全消失。但是如果一个进程已经终止了,但是其父进程还没有获取其状态,那么这个进程就称之为僵尸进程。僵尸进程还会消耗一定的系统资源,并且还保留一些概要信息供父进程查询子进程的状态可以提供父进程想要的信息。一旦父进程得到想要的信息,僵尸进程就会结束。
1. 僵尸进程怎样产生的:
一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被销毁,而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用 exit,它的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)。