【Linux 进程】孤儿进程、僵尸进程和守护进程

1、孤儿进程:

孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上,init进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害

模拟孤儿进程的产生:

#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.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");
}

输出:

before fork pid:27709
parent:pid:27709 
abc:11 
child:27710,parent: 27709
fork after...

然后我们使用命令:ps -ef查看,PPID 为1的这个进程为上述程序产生的孤儿进程。

2、僵尸进程:

僵尸进程是当 子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。如果父进程先退出 ,子进程被init接管,子进程退出后init会回收其占用的相关资源
我们都知道进程的工作原理。我们启动一个程序,开始我们的任务,然后等任务结束了,我们就停止这个进程。 进程停止后, 该进程就会从进程表中移除。 在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他, 那么他将变成一个僵尸进程。 但是 如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程, 因为 每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程, 看有没有哪个进程是刚刚结束的这个进程的子进程,如果是的话,就由Init 来接管他,成为他的父进程……
僵尸进程的危害:
由于子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束. 那么会不会因为父进程太忙来不及wait子进程,或者说不知道子进程什么时候结束,而丢失子进程结束时的状态信息呢? 不会。因 为UNⅨ提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息, 就可以得到。这种机制就是: 在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)。直到父进程通过wait / waitpid来取时才释放. 但这样就导致了问题,如果进程不调用wait / waitpid的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免
模拟僵尸进程的产生:
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.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(20);
        }
        else if(pid == 0){
                abc++;
                printf("child:%d,parent: %d\n",getpid(),getppid());
                printf("abc:%d",abc);
                exit(0);
        }
        printf("fork after...\n");

在父进程休眠的20秒内,产生了僵尸进程,此时可以打开另一个终端,命令ps -ef查看到:

admin 29696 29695 0 20:04 pts/1 00:00:00 [zombie] <defunct>

即我们模拟出来的僵尸进程。

避免僵尸进程的方式:

⒈ 父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起。
⒉ 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为子进程结束后, 父进程会收到该信号,可以在handler中调用wait回收。
⒊ 如果父进程不关心子进程什么时候结束,那么可以用 signal(SIGCHLD,SIG_IGN) 通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收, 并不再给父进程发送信号。
⒋ 还有一些技巧,就是fork两次,父进程fork一个子进程,然后继续工作,子进程fork一 个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收 还要自己做。
 
杀死僵尸进程的方法:
一般僵尸进程很难直接kill掉,不过您可以kill僵尸爸爸。 父进程死后,僵尸进程成为”孤儿进程”, 过继给1号进程init,init始终会负责清理僵尸进程.它产生的所有僵尸进程也跟着消失。
 
3、守护进程:
守护进程:

猜你喜欢

转载自www.cnblogs.com/xuelisheng/p/10073242.html