【Linux系统编程】23.孤儿进程、僵尸进程、wait、waitpid

目录

孤儿进程

测试代码1

测试结果

僵尸进程

测试代码2

测试结果

wait

参数*wstatus

返回值

测试代码3

测试结果

测试代码4

测试结果

测试代码5

测试结果

waitpid

参数pid

参数*wstatus

参数options

返回值

测试代码6

测试结果

测试代码7

测试结果

测试代码8

测试结果

测试代码9

测试结果

测试代码10

测试结果

孤儿进程

        父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程。

测试代码1

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

int main(int argc, char *argv[])
{
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    pid_t jin_cheng = fork();
    if (jin_cheng < 0)
    {
        perror("创建进程错误!");
        exit(1);
    }
    else if (jin_cheng == 0)
    {
        while(1){
            printf("这是子进程,当前进程的ID是%d,父进程ID是%d。\n", getpid(),getppid());
            sleep(1);
        }
    }
    else if (jin_cheng > 0)
    {
        sleep(3);
        printf("这是父进程,当前进程的ID是%d,子进程ID是%d。\n", getpid(),jin_cheng);
        printf("父程序结束!\n");
        printf("孤儿进程产生!\n");
    }
    return 0;
}

测试结果

查看进程的对应父进程。

ps ajx

 

 父进程死后,由1319进程进行收养。

 

僵尸进程

        进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸进程。 僵尸进程不能使用kill命令清除掉的。因为kill命令只是用来终止进程的, 而僵尸进程已经终止。

测试代码2

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

int main(int argc, char *argv[])
{
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    pid_t jin_cheng = fork();
    if (jin_cheng < 0)
    {
        perror("创建进程错误!");
        exit(1);
    }
    else if (jin_cheng == 0)
    {
        sleep(3);
        printf("这是子进程,当前进程的ID是%d,父进程ID是%d。\n", getpid(), getppid());
        printf("子进程结束!\n");
        printf("僵尸进程产生!\n");
    }
    else if (jin_cheng > 0)
    {
        while (1)
        {
            printf("这是父进程,当前进程的ID是%d,子进程ID是%d。\n", getpid(), jin_cheng);
            sleep(1);
        }
    }
    return 0;
}

测试结果

 6138进程已死亡,成为僵尸进程,等待父进程进行回收。

 想要除掉僵尸进程,可以杀掉父进程。

kill -9 6137

 

wait

        一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。

        父进程调用wait函数可以回收子进程终止信息。该函数有三个功能:

  1. 阻塞等待子进程退出,就相当于等待子进程死亡。

  2. 回收子进程残留资源。

  3. 获取子进程结束状态(退出原因)。

        当进程终止时,操作系统的隐式回收机制会:

  1. 关闭所有文件描述符。

  2. 释放用户空间分配的内存。

        内核的PCB仍存在,其中保存该进程的退出状态。可使用wait函数传出参数status来保存进程的退出状态。借助宏函数来进一步判断进程终止的具体原因。

 进程正常结束

WIFEXITED(status)!=0

 获取进程退出状态 (exit的参数),可以在进程正常结束的情况下使用。

WEXITSTATUS(status)

 进程异常终止

WIFSIGNALED(status)!=0

 获取使进程终止信号的编号,可以在进程异常终止的情况下使用。

WTERMSIG(status)

 进程处于暂停状态

WIFSTOPPED(status)!=0

 获取使进程暂停信号的编号,可以在进程暂停的情况下使用。

WSTOPSIG(status)

 进程暂停后已经继续运行

WIFCONTINUED(status)!=0

 父进程牛逼,不关心子进程死亡原因,参数传NULL。

ZiJinCheng_ID = wait(NULL);
man 2 wait

参数*wstatus

传出参数,回收进程的状态。

返回值

成功:清理掉的子进程ID。

失败:-1,没有子进程。

测试代码3

使用wait回收进程。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    int i, wstatus;
    pid_t ZiJinCheng_ID; //子进程ID
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    pid_t jin_cheng = fork();
    if (jin_cheng < 0)
    {
        perror("创建进程错误!");
        exit(1);
    }
    else if (jin_cheng == 0)
    {
        for (i = 0; i < 3; i++)
        {
            printf("这是子进程,当前进程的ID是%d,父进程ID是%d,延时%d秒。\n", getpid(), getppid(), i + 1);
            sleep(1);
        }
        printf("子进程结束!\n");
        printf("僵尸进程产生!\n");
    }
    else if (jin_cheng > 0)
    {
        printf("这是父进程,当前进程的ID是%d,子进程ID是%d,父进程开始等待收尸!\n", getpid(), jin_cheng);
        ZiJinCheng_ID = wait(&wstatus);
        printf("这是父进程,当前进程的ID是%d,子进程ID是%d,父进程收尸完成,僵尸进程ID是%d。\n", getpid(), jin_cheng, ZiJinCheng_ID);
        printf("父程序结束!\n");
    }
    return 0;
}

测试结果

测试代码4

查看子进程正常终止情况。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    int i, wstatus;
    pid_t ZiJinCheng_ID; //子进程ID
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    pid_t jin_cheng = fork();
    if (jin_cheng < 0)
    {
        perror("创建进程错误!");
        exit(1);
    }
    else if (jin_cheng == 0)
    {
        for (i = 0; i < 3; i++)
        {
            printf("这是子进程,当前进程的ID是%d,父进程ID是%d,延时%d秒。\n", getpid(), getppid(), i + 1);
            sleep(1);
        }
        printf("子进程结束!\n");
        printf("僵尸进程产生!\n");
        return 100;
    }
    else if (jin_cheng > 0)
    {
        printf("这是父进程,当前进程的ID是%d,子进程ID是%d,父进程开始等待收尸!\n", getpid(), jin_cheng);
        ZiJinCheng_ID = wait(&wstatus); //子进程未结束,父进程阻塞在这个函数上
        printf("这是父进程,当前进程的ID是%d,子进程ID是%d,父进程收尸完成,僵尸进程ID是%d。\n", getpid(), jin_cheng, ZiJinCheng_ID);
        if (WIFEXITED(wstatus)) //判断子进程是否正常结束
        {
            printf("子进程正常死亡,死亡原因:%d。\n", WEXITSTATUS(wstatus));
        }
        if (WIFSIGNALED(wstatus)) //判断子进程是否异常结束
        {
            printf("子进程被杀,终止信号是%d。\n", WTERMSIG(wstatus));
        }
        printf("父程序结束!\n");
    }
    return 0;
}

测试结果

测试代码5

查看子进程异常终止情况。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    int i, wstatus;
    pid_t ZiJinCheng_ID; //子进程ID
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    pid_t jin_cheng = fork();
    if (jin_cheng < 0)
    {
        perror("创建进程错误!");
        exit(1);
    }
    else if (jin_cheng == 0)
    {
        while(1)
        {
            printf("这是子进程,当前进程的ID是%d,父进程ID是%d,我是长生不老的。\n", getpid(), getppid());
            sleep(1);
        }
    }
    else if (jin_cheng > 0)
    {
        printf("这是父进程,当前进程的ID是%d,子进程ID是%d,父进程开始等待收尸!\n", getpid(), jin_cheng);
        ZiJinCheng_ID = wait(&wstatus); //子进程未结束,父进程阻塞在这个函数上
        printf("这是父进程,当前进程的ID是%d,子进程ID是%d,父进程收尸完成,僵尸进程ID是%d。\n", getpid(), jin_cheng, ZiJinCheng_ID);
        if (WIFEXITED(wstatus)) //判断子进程是否正常结束
        {
            printf("子进程正常死亡,死亡原因:%d。\n", WEXITSTATUS(wstatus));
        }
        if (WIFSIGNALED(wstatus)) //判断子进程是否异常结束
        {
            printf("子进程被杀,终止信号是%d,还说是长生不老,直接把你给杀了。\n", WTERMSIG(wstatus));
        }
        printf("父程序结束!\n");
    }
    return 0;
}

测试结果

waitpid

指定某一个进程进行回收。

man 2 waitpid

 

参数pid

大于0:指定回收子进程的ID。

0:回收和当前调用waitpid一个组的所有子进程。

-1:回收任意一个子进程。

小于-1:回收指定进程组内的任意子进程。

参数*wstatus

传出参数,回收进程的状态。

参数options

WNOHANG:指定回收方式为非阻塞。

0:阻塞回收,功能等于wait函数。

返回值

大于0:成功回收子进程的PID。

0:函数调用时,参数options指定了WNOHANG,并且子进程没有结束。

-1:失败。

测试代码6

以不阻塞的方式回收指定的一个进程。父进程不延时,子进程没有结束,返回0。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    int i;
    pid_t temp_ZiJinCheng_ID;  //临时子进程ID
    pid_t JinCheng_ID;         //子进程ID
    pid_t HuiShou_JinCheng_ID; //回收进程ID
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    printf("开始创建进程!\n");
    for (i = 0; i < 5; i++)
    {
        temp_ZiJinCheng_ID = fork();
        if (temp_ZiJinCheng_ID == 0) //子进程,不参与创建进程
        {
            break;
        }
        if (i == 2)
        {
            JinCheng_ID = temp_ZiJinCheng_ID;
        }
    }
    if (i == 5)
    {
        HuiShou_JinCheng_ID=waitpid(JinCheng_ID,NULL,WNOHANG);//以非阻塞的方式回收一个进程ID,JinChen_ID
        if(HuiShou_JinCheng_ID<0){
            perror("回收进程错误");
            exit(1);
        }
        printf("这是父进程,当前进程的ID是%d,回收的子进程是%d。\n", getpid(),HuiShou_JinCheng_ID);
    }
    else
    {
        sleep(i); //延时i秒,按顺序输出
        printf("这是第%d个子进程,当前进程的ID是%d。\n", i + 1, getpid());
    }
    return 0;
}

测试结果

测试代码7

以不阻塞的方式回收指定的一个进程。父进程延时,子进程结束,返回回收的进程ID。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    int i;
    pid_t temp_ZiJinCheng_ID;  //临时子进程ID
    pid_t JinCheng_ID;         //子进程ID
    pid_t HuiShou_JinCheng_ID; //回收进程ID
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    printf("开始创建进程!\n");
    for (i = 0; i < 5; i++)
    {
        temp_ZiJinCheng_ID = fork();
        if (temp_ZiJinCheng_ID == 0) //子进程,不参与创建进程
        {
            break;
        }
        if (i == 2)
        {
            JinCheng_ID = temp_ZiJinCheng_ID;
        }
    }
    if (i == 5)
    {
        sleep(5); //延时,最后输出父进程
        HuiShou_JinCheng_ID = waitpid(JinCheng_ID, NULL, WNOHANG); //以非阻塞的方式回收一个进程ID,JinChen_ID
        if (HuiShou_JinCheng_ID < 0)
        {
            perror("回收进程错误");
            exit(1);
        }
        printf("这是父进程,当前进程的ID是%d,回收的子进程是%d。\n", getpid(), HuiShou_JinCheng_ID);
    }
    else
    {
        sleep(i); //延时i秒,按顺序输出
        printf("这是第%d个子进程,当前进程的ID是%d。\n", i + 1, getpid());
    }
    return 0;
}

测试结果

测试代码8

回收一个任意子进程。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    int i;
    pid_t temp_ZiJinCheng_ID;  //临时子进程ID
    pid_t HuiShou_JinCheng_ID; //回收进程ID
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    printf("开始创建进程!\n");
    for (i = 0; i < 5; i++)
    {
        temp_ZiJinCheng_ID = fork();
        if (temp_ZiJinCheng_ID == 0) //子进程,不参与创建进程
        {
            break;
        }
    }
    if (i == 5)
    {
        sleep(6);	//延时,确保所有子进程都已经结束,最后输出父进程
        HuiShou_JinCheng_ID = waitpid(-1, NULL, WNOHANG); //以非阻塞的方式回收任意一个子进程ID
        if (HuiShou_JinCheng_ID < 0)
        {
            perror("回收进程错误");
            exit(1);
        }
        printf("这是父进程,当前进程的ID是%d,回收的子进程是%d。\n", getpid(), HuiShou_JinCheng_ID);
    }
    else
    {
        sleep(i); //延时i秒,按顺序输出
        printf("这是第%d个子进程,当前进程的ID是%d。\n", i + 1, getpid());
    }
    return 0;
}

测试结果

        网上教程说是回收任意一个子进程,但是从结果来看,每次都是回收第一个结束的子进程,不排除较新版本的Ubuntu系统优化了回收进程的算法,实现先结束先回收的情况。

 

测试代码9

以阻塞的方式回收全部死亡的子进程。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    int i;
    pid_t temp_ZiJinCheng_ID;  //临时子进程ID
    pid_t HuiShou_JinCheng_ID; //回收进程ID
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    printf("开始创建进程!\n");
    for (i = 0; i < 5; i++)
    {
        temp_ZiJinCheng_ID = fork();
        if (temp_ZiJinCheng_ID == 0) //子进程,不参与创建进程
        {
            break;
        }
    }
    if (i == 5)
    {
        while ((HuiShou_JinCheng_ID = waitpid(-1, NULL, 0)) != -1) //以阻塞的方式回收任意一个子进程ID
        {
            printf("这是父进程,当前进程的ID是%d,回收的子进程是%d。\n", getpid(), HuiShou_JinCheng_ID);
        }
        if (HuiShou_JinCheng_ID < 0)
        {
            perror("这是父进程,回收子进程完成,父进程结束!\n");
        }
    }
    else
    {
        sleep(i); //延时i秒,按顺序输出
        printf("这是第%d个子进程,当前进程的ID是%d。\n", i + 1, getpid());
    }
    return 0;
}

测试结果

测试代码10

以非阻塞的方式回收全部死亡的子进程。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    int i;
    pid_t temp_ZiJinCheng_ID;  //临时子进程ID
    pid_t HuiShou_JinCheng_ID; //回收进程ID
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    printf("开始创建进程!\n");
    for (i = 0; i < 5; i++)
    {
        temp_ZiJinCheng_ID = fork();
        if (temp_ZiJinCheng_ID == 0) //子进程,不参与创建进程
        {
            break;
        }
    }
    if (i == 5)
    {
        sleep(6);                                                  //延时,确保所有的子进程都死亡
        while ((HuiShou_JinCheng_ID = waitpid(-1, NULL, WNOHANG)) != -1) //以非阻塞的方式回收任意一个子进程ID
        {
            printf("这是父进程,当前进程的ID是%d,回收的子进程是%d。\n", getpid(), HuiShou_JinCheng_ID);
        }
        if (HuiShou_JinCheng_ID < 0)
        {
            perror("这是父进程,回收子进程完成,父进程结束!\n");
        }
    }
    else
    {
        sleep(i); //延时i秒,按顺序输出
        printf("这是第%d个子进程,当前进程的ID是%d,该进程结束。\n", i + 1, getpid());
    }
    return 0;
}

测试结果

 

猜你喜欢

转载自blog.csdn.net/CETET/article/details/132267131
今日推荐