僵尸进程与孤儿进程
在unix/linux系统中,大多情况下,子进程是通过父进程fork创建的,注:系统调用fork,是一个比较有意思系统调用,它调用一次,返回两个值,失败返回-1,成功时在子进程返回0,父进程返回所创建子进程的pid。
子进程创建后,子进程的结束和父进程的运行是一个异步过程,也就是说父进程没办法预测子进程什么时候结束。 当一个子进程完成它的工作终止之后,其父进程需要调用wait()或waitpid()去获取子进程的终止状态。
孤儿进程
所谓孤儿进程,顾名思义,和现实生活中的孤儿有点类似,当一个进程的父进程结束时,但是它自己还没有结束,那么这个进程将会成为孤儿进程。最后孤儿进程将会被init进程(进程号为1)的进程收养,当然在子进程结束时也会由init进程完成对它的状态收集工作,因此一般来说,孤儿进程并不会有什么危害。
下面看一个关于孤儿进程的例子:在main函数中,创建子进程,然后让父进程睡眠1s,让子进程先运行打印出其进程id(pid)以及父进程id(ppid);随后子进程睡眠3s(此时会调度到父进程运行直至结束),目的是让父进程先于子进程结束,让子进程有个孤儿的状态;最后子进程再打印出其进程id(pid)以及父进程id(ppid);观察两次打印 其父进程id(ppid)的区别。
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> int main(int argc, char const *argv[]) { pid_t pid; pid = fork(); if (pid < 0){ perror("fork failed"); exit(1); } if (pid == 0){ printf("i am the child process\n"); printf("pid:%d\t ppid:%d\n", getpid(),getppid()); //输出进程ID和父进程ID printf("i will sleep five seconds\n"); // 子进程休眠3秒,保证父进程先退出 sleep(3); printf("now pid:%d\t ppid:%d\n", getpid(),getppid()); printf("child process is exited\n"); }else{//父进程 printf("i am father process\n"); //保证子进程先运行 sleep(1); printf("father process is exited\n"); } return 0; }
输出结果:
/* i am father process father process is exited i am the child process pid:45290 ppid:45289 i will sleep five seconds now pid:45290 ppid:1 child process is exited */
从运行结果来看:当其父进程结束后,子进程成为了孤儿进程,其父进程id(ppid)为1,也就是说,init进程成为该子进程的父进程了。
僵尸进程
僵尸进程是指:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的某些信息如进程描述符仍然保存在系统中。这种进程称之为僵死进程。
下面是1个关于僵尸进程的例子:在main函数中,创建子进程,然后让父进程睡眠10s,让子进程先终止(注意和孤儿进程例子的区别);这里子进程结束后父进程没有调用wait/waitpid函数获取其状态,用ps查看进程状态可以看出子进程为僵尸状态。
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> int main(int argc, char const *argv[]) { pid_t pid; pid = fork(); if (pid < 0){ perror("fork failed"); exit(1); } if (pid == 0){ printf("i am child process,i am exited\n"); exit(0); } printf("i am father process\n"); sleep(10); // 保证子进程结束 printf("father process is exited\n"); return 0; }
此时程序变为了僵尸进程。
注:任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。 如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。
僵尸进程的危害:僵尸进程会在系统中保留其某些信息如进程描述符、进程id等等。以进程id为例,系统中可用的进程id是有限的,如果由于系统中大量的僵尸进程占用进程id,就会导致因为没有可用的进程id系统不能产生新的进程,这种问题可就大了,这就是僵尸进程带来的危害。