fork()、僵死进程和孤儿进程

  孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。

  僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。

#include <stdio.h>
#include <iostream>
#include "unistd.h"
#include "assert.h"
#include <stdlib.h>

        int glob = 6;
        char buf[] = "a write to stdout\n";

main(int argc, char* argv[]){
        int var;
        pid_t pid;
        var = 88;
        if(write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1)
                assert("write error");
        printf("before fork\n");

        if((pid = fork()) < 0 ){
                assert("fork error");
        }else if(pid == 0){
                glob++;
                var++;
                printf("im child\n");
        }else{
                sleep(10);
                printf("im father\n");
        }
        printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var);
        exit(0);

}

linux上编译上面一段代码后,马上查看进程 ps aux | grep test

会看到进程产生了一个僵死进程

僵死进程产生的原因:

父进程用fork产生了一个子进程,子进程执行完了后,但是其父进程尚未对其进行善后处理(获取终止子进程的有关信息,释放它仍然占用的资源),这样的子进程为僵死进程,进程状态为Z

上述例子解释:

主进程运行然后fork一个子进程,然后子进程正常执行完程序,但是父进程在sleep,未去获取对子进程善后处理,所以此时子进程僵死。

主进程sleep完后,释放资源,同时释放其子进程。

如果父进程异常退出,子进程就会被init进程(pid为1的一个进程)接管,然后init进程会不断去轮询他的子进程是否有僵死进程,有就杀掉。但是如果它的僵死进程越来越多,发现过程就很慢,所以要避免产生僵死进程。

查看子进程状态的函数是:wait   和   waitpid

这两个函数的区别是:

父进程fork一个子进程后,父进程不死,调用wait , 父进程就会阻塞,一直到有一个他的子进程退出(产生僵死进程),然后捕获他子进程的信息,并为子进程善后(完全的kill掉子进程),如果是调用waitpid , 则父进程不会阻塞,就是check一下当前有没得自己的子进程(存在僵死进程不),如果有就善后,没得就继续父进程逻辑。

解决僵死进程的方法:

方法1:采用进程退出的时候会给父进程发送信号。

#include <stdio.h>
#include <iostream>
#include "unistd.h"
#include "assert.h"
#include <stdlib.h>
#include "sys/wait.h"

int glob = 6;
char buf[] = "a write to stdout\n";

void forkTest(){

    int var;
    pid_t pid;
    var = 88;
    if(write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1)
            assert("write error");
    printf("before fork\n");

    if((pid = fork()) < 0 ){
            assert("fork error");
    }else if(pid == 0){
            printf("im child pid=%d , parent id=%d , begin to sleep 5 sec\n", getpid(), getppid());
            sleep(5);
            printf("im child pid=%d , parent id=%d , end sleep\n ", getpid(), getppid());
            exit(0);
    }else{
            //sleep(15);
            printf("im father pid=%d\n", getpid());
    }
    printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var);

}

void func_wait(int sin){
    if(sin == SIGCHLD){
        pid_t pid;
        int stat;
        if((pid = wait(&stat)) > 0){
            printf("child %d killed, state is:%d\n", pid, stat);
        }
    }
    return;
}
void func_waitpid(int sin){
    if(sin == SIGCHLD){
        pid_t pid;
        int stat;
        if((pid = waitpid(-1, &stat, WNOHANG)) > 0){ //第一个参数为-1,表示监听主进程的所有子进程
            printf("child %d killed, state is:%d\n", pid, stat);
        }
    }
    return;
}

int main(int argc, char* argv[]){
    signal(SIGCHLD, &func_waitpid); //在主进程里面注册signal函数 , 子进程退出后会发送信号给父进程,这里捕获并传入信号给注册函数
    printf("im from main before fork\n");
    forkTest();
    for(;;){}
    printf("im from main end fork\n");
}

方法2:(apue上8.6章有介绍)fork两次,用孙子进程来处理事务,子进程创建出孙子进程后就退出,此时孙子进程会成为孤儿进程,父进程为init进程,孙子进程退出后会被init自动善后回收,子进程退出后会被父进程的waitpid回收掉,但有个疑问就是如果父进程先执行waitpid,子进程再创建孙进程并退出成僵死进程,那么父进程就捕获不到了。可以在父进程里面sleep(1)一下,让子进程先执行创建孙进程并退出,父进程再去用waitpid回收(毕竟waitpid不会阻塞)。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

int main()
{
    pid_t  pid;
    //创建第一个子进程
    pid = fork();
    if (pid < 0)
    {
        perror("fork error:");
        exit(1);
    }
    //第一个子进程
    else if (pid == 0)
    {
        //子进程创建孙子进程
        printf("I am the first child process.pid:%d\tppid:%d\n",getpid(),getppid());
        pid = fork();
        if (pid < 0)
        {
            perror("fork error:");
            exit(1);
        }
        //第一个子进程退出,因为下面的else if只会是子进程和孙子进程执行,而子进程此时的pid由于fork返回的是>0的(即孙进程的pid),而孙进程此时的pid是0,所以else if里面的东西只有子进程执行,里面有句exit(0)使子进程为僵死进程,所以从sleep(3)开始只有孙进程会执行
        else if (pid >0)
        {
            printf("first procee is exited.\n");
            exit(0);
        }
        //孙子进程
        //睡眠3s保证第一个子进程退出(如果此时父进程还没waitpid到子进程,那子进程就还是僵死),这样孙进程exit(0)后就会变为孤儿进程。由init进程回收。
        sleep(3);
        printf("I am the second child process.pid: %d\tppid:%d\n",getpid(),getppid());
        exit(0);
    }
    //父进程处理第一个子进程退出,下面的代码只会是父进程执行,并且传入的pid是子进程的pid,
    if (waitpid(pid, NULL, 0) != pid)
    {
        perror("waitepid error:");
        exit(1);
    }
    exit(0);
    return 0;
}

方法3:

如果僵死进程存在,然后父进程死掉,僵死进程就会变成孤儿进程,由init进程回收。

其他:

有一种方法可以让子进程或者父进程先执行。

设计的函数有:

TELL_WAIT();

WAIT_PARENT();

TELL_CHILD(pid);

TELL_PARENT(getppid());

WAIT_CHILD();

实现原理也是通过信号

//这段代码会让父进程先执行,子进程后执行
if((pid = fork()) < 0){
  printf("fork error");
}else if(pid == 0){
  WAIT_PARENT();
  //do sth about child ....
}else{
  //do sth about parent....
  TELL_CHILD(pid);
}
//这段代码会让子进程先执行,父进程后执行
if((pid = fork()) < 0){
  printf("fork error");
}else if(pid == 0){
  //do sth about child ....
  TELL_PARENT(getppid());
}else{
  WAIT_CHILID();
  //do sth about parent....
}

参考僵死进程和孤儿进程的区别:http://www.cnblogs.com/anker/p/3271773.html

时间: 2024-10-26 00:55:00

fork()、僵死进程和孤儿进程的相关文章

(转)僵死进程与孤儿进程

在Unix系统编程中,常常会碰到两个概念:僵死进程和孤儿进程.话说我以前曾经把这两个概念弄混淆过. 什么是僵死进程? 我们知道,在Unix进程模型中,进程是按照父进程产生子进程,子进程产生子子进程这样的方式创建出完成各项相互协作功能的进程的.当一个 进程完成它的工作终止之后,它的父进程需要调用wait()或者waitpid()系统调用取得子进程的终止状态.如果父进程没有这么做的话,会产生什么 后果呢?此时,子进程虽然已经退出了,但是在系统进程表中还为它保留了一些退出状态的信息,如果父进程一直不取

Linux中的僵尸进程和孤儿进程

在UNIX里,除了进程0(即PID=0的交换进程,Swapper Process)以外的所有进程都是由其他进程使用系统调用fork创建的,这里调用fork创建新进程的进程即为父进程,而相对应的为其创建出的进程则为子进程,因而除了进程0以外的进程都只有一个父进程,但一个进程可以有多个子进程.        操作系统内核以进程标识符(Process Identifier,即PID)来识别进程.进程0是系统引导时创建的一个特殊进程,在其调用fork创建出一个子进程(即PID=1的进程1,又称init)

僵尸进程和孤儿进程----概念

这里仅给出僵尸进程和孤儿进程的概念.来源<深入了解计算机系统(原书第3版)>和网上的博客,下面会给出博客来源. 前言:回收子进程 当一个进程由于某种原因终止时,内核并不是立即把它从系统中清除.相反,进程被保持在一种已终止的状态中,直到被它的父进程回收(reaped).当父进程回收已终止的子进程时,内核将子进程的退出状态传递给父进程,然后抛弃已终止的进程.从此时开始,该进程就不存在了. 1.基本概念 僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitp

【转】僵尸进程和孤儿进程

之前写了进程的创建,由于是半年前做的题目了,现在回忆有点生疏,有些概念都忘了,比如僵尸进程.孤儿进程.子进程和父进程的关系...在博客园看到有一篇讲的很好的关于僵尸进程和孤儿进程的文章,没看到有转载的选项,直接复制过来了,方便以后忘了再查看 转自http://www.cnblogs.com/Anker/p/3271773.html 1.前言 之前在看<unix环境高级编程>第八章进程时候,提到孤儿进程和僵尸进程,一直对这两个概念比较模糊.今天被人问到什么是孤儿进程和僵尸进程,会带来什么问题,怎

Linux下的僵尸进程和孤儿进程

说明 在UNIX里,除了进程0(即PID=0的交换进程,Swapper Process)以外的所有进程都是由其他进程使用系统调用fork创建的,这里调用fork创建新进程的进程即为父进程,而相对应的为其创建出的进程则为子进程,因而除了进程0以外的进程都只有一个父进程,但一个进程可以有多个子进程. 操作系统内核以进程标识符(Process Identifier,即PID)来识别进程.进程0是系统引导时创建的一个特殊进程,在其调用fork创建出一个子进程(即PID=1的进程1,又称init)后,进程

Linux-进程描述(3)之进程状态僵尸进程与孤儿进程

进程状态 进程状态反映进程执行过程的变化.这些状态随着进程的执行和外界条件的变化而转换.为了弄明正正在运行的进程是什么意思,我们需要知道进程的不同状态.一个进程可以有多个状态(在Linux内核中,进程有时候也叫做任务).下面的状态在 fs/proc/array.c 文件中定义: /* * The task state array is a strange "bitmap" of * reasons to sleep. Thus "running" is zero,

zombie僵尸进程、孤儿进程和守护进程

以前一直对僵尸进程和孤儿进程都没怎么理解,真是罪过,最近在看liunx编程设计(第四版),看到了他们的概念,所以对它们做个总结!加深印象. 基本概念: 我们知道在unix/linux中,正常情况下,子进程是通过父进程创建的,子进程再创建新的进程.子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束. 当一个 进程完成它的工作终止之后,它的父进程需要调用wait()或者waitpid()系统调用取得子进程的终止状态. 但是如果情况不是这样的会怎么样呢,毕竟世事难料

僵尸进程和孤儿进程-(转自Anker&#39;s Blog)

2.基本概念 我们知道在unix/linux中,正常情况下,子进程是通过父进程创建的,子进程在创建新的进程.子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束. 当一个 进程完成它的工作终止之后,它的父进程需要调用wait()或者waitpid()系统调用取得子进程的终止状态. 孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程.孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作. 僵尸进程

僵尸进程和孤儿进程 转载

孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程.孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作. 僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中.这种进程称之为僵死进程. https://blog.csdn.net/qq_41672715/article/details/79328061 原文地址:http