• 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
    ";
    
    main(int argc, char* argv[]){
            int var;
            pid_t pid;    //其实进程ID就是一个非负的整形,而线程ID是用结构体pthread_t来表示的,所以比较线程ID要用函数int pthread_equal(pthread_t tid1, pthread_t tid2);来比较(头文件 #include <pthread.h>),详见另一篇关于线程的linux文章
            var = 88;
            if(write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1)
                    assert("write error");
            printf("before fork
    ");
    
            if((pid = fork()) < 0 ){
                    assert("fork error");
            }else if(pid == 0){
                    glob++;
                    var++;
                    printf("im child
    ");
            }else{
                    sleep(10);
                    printf("im father
    ");
            }
            printf("pid = %d, glob = %d, var = %d
    ", 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
    ";
    
    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
    ");
    
        if((pid = fork()) < 0 ){
                assert("fork error");
        }else if(pid == 0){
                printf("im child pid=%d , parent id=%d , begin to sleep 5 sec
    ", getpid(), getppid());
                sleep(5);
                printf("im child pid=%d , parent id=%d , end sleep
     ", getpid(), getppid());
                exit(0);
        }else{
                //sleep(15);
                printf("im father pid=%d
    ", getpid());
        }
        printf("pid = %d, glob = %d, var = %d
    ", 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
    ", 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
    ", pid, stat);
            }
        }
        return;
    }
    
    int main(int argc, char* argv[]){
        signal(SIGCHLD, &func_waitpid); //在主进程里面注册signal函数 , 子进程退出后会发送信号给父进程,这里捕获并传入信号给注册函数
        printf("im from main before fork
    ");
        forkTest();
        for(;;){}
        printf("im from main end fork
    ");
    }

    方法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	ppid:%d
    ",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.
    ");
                exit(0);
            }
            //孙子进程
            //睡眠3s保证第一个子进程退出(如果此时父进程还没waitpid到子进程,那子进程就还是僵死),这样孙进程exit(0)后就会变为孤儿进程。由init进程回收。
            sleep(3);
            printf("I am the second child process.pid: %d	ppid:%d
    ",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

  • 相关阅读:
    一个简单的随机数生成算法实现(C++)
    gabor 滤波的c++实现与该类得使用简介
    嵌入式软件的覆盖测试
    scanf()函数用法小结(转载)
    创建动态2维vector (C++)
    HDU 1086 You can Solve a Geometry Problem too
    计算几何多边形的重心
    HDU 1711 Number Sequence
    HDU 2602 Bone Collector
    计算几何基础篇
  • 原文地址:https://www.cnblogs.com/alazalazalaz/p/4375177.html
Copyright © 2020-2023  润新知