回收子进程

1.孤儿进程
父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程
2.僵尸进程
子进程终止,父进程尚未回收,子进程残留资源(PCB)存放在内核中,变成僵尸(zombie)进程
特别注意:僵尸进程是不能使用kill命令清除掉的。以为kill命令只能终止活着的进程,而僵尸进程已经终止。
3.回收僵尸进程
模拟一个僵尸进程:zombie.c

   #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/wait.h>
    
    int main(void)
    {
            pid_t pid;
            pid = fork();
    
            if(pid == 0) {
                    printf("----child, going to sleep,my parent=%d
", getppid());
                    sleep(10);
                    printf("----child die----
");
            } else if(pid > 0) {
                    while(1){
                            printf("I am parent, pid=%d, myson=%d
", getpid(), pid);
                            sleep(1);
                    }
            } else {
                    perror("fork error");
                    exit(1);
            }
    
            return 0;
    }

ps aux 查询结果如下:
root 3230 0.0 0.0 4216 352 pts/0 S+ 17:36 0:00 ./zombie.out
root 3231 0.0 0.0 0 0 pts/0 Z+ 17:36 0:00 [zombie.out] <defunct>--僵尸进程

wait函数
一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:
如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用
wait或者waitpid获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态我们可以在shell中用特殊变量$?
查看,因为shell是它的父进程,当它终止时shell调用wait或waitpid得到它的退出状态同时彻底清除这个进程。

父进程调用wait函数可以回收子进程终止信息,该函数有三个功能:
1).阻塞等待子进程退出
2).回收子进程残留资源
3).获取子进程结束状态(退出原因)
pid_t wait(int *status);
成功:清理掉子进程ID;
失败:-1(没有子进程)

当进程终止时,操作系统的隐式回收机制会做如下操作:
1).关闭所有的文件描述符
2).释放用户空间分配的内存
内核的PCB仍存在,其中保存该进程的退出状态。(正常终止-->退出值,异常终止-->终止信号)

可使用wait函数传出参数status来保存进程的退出状态。借助宏函数来进一步判断进程终止的具体原因。
宏函数可分为三组:
1).WIFEXITED(status) 为非0 -->进程正常结束
WEXITSTATUS(status) 如上宏为真,使用此宏-->获取进程退出状态(exit的参数)

2).WIFSIGNALED(status) 为非0 -->进程异常终止
WTERMSIG(status) 如上宏为真,使用此宏-->取得进程终止的那个信号的编号(kill的参数)

wait第一版:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/wait.h>
    
    int main(void)
    {
        pid_t pid, wpid;
        pid = fork();
    
        if(pid == 0) {
            printf("----child, going to sleep,my parent=%d
", getppid());
            sleep(10);
            printf("----child die----
");
        } else if(pid > 0) {
            wpid = wait(NULL);
            if(wpid == -1){
                perror("wait error");
                exit(1);
            }
            
            while(1){
                printf("I am parent, pid=%d, myson=%d
", getpid(), pid);
                sleep(1);
            }
        } else {
            perror("fork error");
            exit(1);
        }
    
        return 0;
    }

wait使用宏版:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/wait.h>
    
    int main(void)
    {
        pid_t pid, wpid;
        int status;
        pid = fork();
    
        if(pid == 0) {            
            printf("----child, going to sleep,my parent=%d
", getppid());
            sleep(20);
            printf("----child die----
");
            return 100;
        } else if(pid > 0) {
            wpid = wait(&status);
            if(wpid == -1){
                perror("wait error");
                exit(1);
            }
            if(WIFEXITED(status)) {
                printf("child exit with %d
",WEXITSTATUS(status));
            }
            if(WIFSIGNALED(status)) {
                printf("child killed by %d
",WTERMSIG(status));
            }
            
            while(1){
                printf("I am parent, pid=%d, myson=%d
", getpid(), pid);
                sleep(1);
            }
        } else {
            perror("fork error");
            exit(1);
        }
    
        return 0;
    }

gcc abnor.c -o abnor.out 测试段错误和浮点错误

    int main(void){
    char *p = "test of wait abnormally
";
    //p[0] = 'h';//测试段错误
    int a = 5/0;//测试浮点错误
    return 65;
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/wait.h>
    
    int main(void)
    {
        pid_t pid, wpid;
        int status;
        pid = fork();
    
        if(pid == 0) {        
            execl("abnor.out", "abnor.out", NULL);
            printf("----child, going to sleep,my parent=%d
", getppid());
            sleep(20);
            printf("----child die----
");
            return 100;
        } else if(pid > 0) {
            wpid = wait(&status);
            if(wpid == -1){
                perror("wait error");
                exit(1);
            }
            if(WIFEXITED(status)) {
                printf("child exit with %d
",WEXITSTATUS(status));
            }
            if(WIFSIGNALED(status)) {
                printf("child killed by %d
",WTERMSIG(status));
            }
            
            while(1){
                printf("I am parent, pid=%d, myson=%d
", getpid(), pid);
                sleep(1);
            }
        } else {
            perror("fork error");
            exit(1);
        }
    
        return 0;
    }

根据kill -l找到对应的信号

waitpid函数
可以指定pid进程清理,可以不阻塞。
pid_t waitpid(pid_t pid, int *status, int options);
成功:返回清理掉的子进程ID; 失败:-1(无子进程结束)
特殊参数和返回值情况:
参数1 pid:
>0 回收指定ID的子进程
-1 回收任意子进程(相当于wait)
0 回收和当前调用waitpid一个组的所有子进程
<-1回收指定进程组内的任意子进程
参数2 status

参数3 设置为WNOHANG,非阻塞回收(轮询)
设置为0,父进程阻塞等待子进程执行完,阻塞回收
返回值: 成功:pid
失败:-1
返回0:参数3设置为WNOHANG,且子进程正在运行

注意:一次wait或waitpid调用只能清理一个子进程,清理多个子进程应使用循环。

原文地址:https://www.cnblogs.com/zheaven/p/14262367.html