linux18:进程等待

进程等待的必要性

1:子进程创建的目的是要完成父进程指派的某个任务,当子进程运行完毕退出时,父进程需要通过进程等待的方式,回收子进程资源,获取子进程退出信息(子进程有无异常?没有异常结果是否正确?检查退出码查看错误原因)
2:父进程如果一直不回收,就可能造成子进程'僵尸进程'的问题,子进程一直得不到父进程的回收,进而造成内存泄漏。
3:子进程一旦变成僵尸状态,那就刀枪不入,"杀人不眨眼"的kill -9 也无能为力杀死一个已经死去的进程。只能通过进程等待将他进行回收

子进程等待的两个方法 wait 与 waitpid

1:wait方法:

wait函数用于等待一个已启动的子进程结束。这个函数通常与 fork 函数一起使用,fork 用于创建子进程。wait 函数会**挂起(父进程阻塞等待)**调用它的父进程,直到有一个子进程结束或到达指定的时间限制。

头文件

cpp 复制代码
#include <sys/types.h>
#include <sys/wait.h>

函数原型:

cpp 复制代码
pid_t wait(int *status);

status:输出型参数,一个指向整数的指针,如果提供一个变量,子进程的结束状态将被存储在所指向的整数变量中

子进程退出结束状态的三种情况:1:代码运行完毕,结果正确

2:代码运行完毕,结果不正确

3:异常终止

退出码 (status>>8)&0xFF 终止信息 status&0x7F

返回值:

  • 成功时,wait 返回结束的子进程的进程ID。
  • 如果有错误发生,返回 -1,并设置 **errno**以指示错误类型。

例子:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
    pid_t pid;
    int status;

    pid = fork(); // 创建子进程

    if (pid == -1) // 返回值是1时,说明fork() 调用失败
    {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    else if (pid == 0) // 子进程
    {
        printf("Child process, PID: %d, PPID: %d\n", getpid(), getppid());
        exit(EXIT_SUCCESS);
    }
    else  // 父进程 
    {
        printf("Parent process, PID: %d, PPID: %d\n", getpid(), getppid());
        pid = wait(&status);    // 挂起直到子进程结束
        if (pid == -1)     // 返回值是1时,说明wait() 调用失败
        {
            perror("wait");
            exit(EXIT_FAILURE);
        }
        if (WIFEXITED(status))  // 最后查看检查子进程的结束状态
        {
            printf("Child process exited with status %d\n", WEXITSTATUS(status));
        }
    }

    return 0;
}

这个例子中,父进程使用**fork** 创建了一个子进程。

子进程打印出它的进程ID和父进程ID,然后退出。

父进程则调用**wait** 函数并传入一个指向整数的指针,这个整数将存储子进程的结束状态。

因为有wait方法存在,一旦子进程结束,父进程将继续执行,并检查子进程的结束状态。

2:waitpid方法:

wait 函数不同,**waitpid**允许父进程指定要等待的子进程的进程标识符(PID),这提供了更细粒度的控制。

**waitpid**对于处理多个子进程的情况特别有用,因为它允许父进程等待特定的子进程或一组子进程。

头文件与wait文件相同

cpp 复制代码
#include <sys/types.h>
#include <sys/wait.h>

函数原型:

cpp 复制代码
pid_t waitpid(pid_t pid, int *status, int options);
  • pid_t pid:指定要等待的子进程的 PID。

  • 如果**pid > 0,waitpid** 等待特定的pid子进程。

  • 如果**pid == -1** ,waitpid 等待任何子进程,类似于**wait** 函数。

  • 如果**pid < -1,waitpid** 等待进程组 ID(PGID)等于**pid** 绝对值的任何子进程。


  • int *status:一个指向整数的指针,如果提供一个变量,结束状态将被存储在所指向的整数变量中

  • WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)

  • WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)


  • int options :指定 waitpid 调用的行为的选项,

  • WNOHANG (非阻塞轮询,使等待非阻塞)
    若 pid 指定的子进程没有结束,则 waitpid() 函数返回 0 ,不予以等待。若正常结束,则返回该子进程的ID 。

  • WUNTRACED(除了已结束的子进程,还报告已停止的子进程)。

  • 0表示默认选项


返回值与wait一样:

  • 成功时,wait 返回结束的子进程的进程ID。
  • 如果有错误发生,返回 -1,并设置 **errno**以指示错误类型。

例子:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main()
{
    pid_t pid, wpid;
    int status;

    // 创建子进程
    pid = fork();
    if (pid == -1) 
    {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (pid == 0)     // 子进程
    {
        printf("Child process, PID: %d, PPID: %d\n", getpid(), getppid());
        sleep(5);
        printf("Child process terminating\n");
        exit(EXIT_SUCCESS);
    }

    //  waitpid方法,父进程等待特定的子进程结束
    wpid = waitpid(pid, &status, 0); 
    if (wpid == -1)  
    {
        perror("waitpid");
        exit(EXIT_FAILURE);
    }

    printf("Child process exited with status %d\n", WEXITSTATUS(status));

    return 0;
}

在这个示例中,父进程创建了一个子进程,

一旦sleep(5);5s过后,子进程结束,**waitpid**返回子进程的 PID,父进程可以检查子进程的退出状态。

阻塞轮询例子

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>

int main()
{
    pid_t pid;
    int status;

    pid = fork(); // 创建子进程
    if (pid < 0)  // fork 出错 
    {
        perror("fork failed");
        exit(EXIT_FAILURE);
    }
    else if (pid == 0)  // 子进程
    {
        printf("Child process, PID: %d\n", getpid());
        sleep(5); 
        exit(0); 
    }
    else   // 父进程
    {
        while (1) 
        {
            pid_t ret = waitpid(pid, &status, WNOHANG); // 非阻塞等待
            if (ret == 0) // 子进程未结束,继续轮询
            {
                printf("Child process is still running.\n");
                sleep(1); // 等待1秒后再次检查
            }
            else if (ret == pid)  // 子进程已结束
            {
                if (WIFEXITED(status)) 
                {
                    printf("Child process exited with status %d\n", WEXITSTATUS(status)); //提取子进程退出码
                }
                else if (WIFSIGNALED(status)) 
                {
                    printf("Child process terminated by signal %d\n", WTERMSIG(status));  //终止子进程信号
                }
                break; 
            }
            else  // waitpid 出错
            {
                perror("waitpid failed");
                exit(EXIT_FAILURE);
            }
        }
    }

    return 0;
}

多个进程等待

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>


//0--5s   子进程运行,父进程运行
//5--15s  子进程回收,父进程运行

int main()
{
    pid_t child_pid;
    int status;
    int i;

    for (i = 0; i < 5; ++i)  // 创建5个子进程
    {
        child_pid = fork();
        if (child_pid == -1) // fork() 调用失败
        {
            perror("fork");
            exit(EXIT_FAILURE);
        }

        if (child_pid == 0)  // 这是子进程,每个子进程运行5s
        {
            int n = 5;
            while (n--)
            {
                printf("Child process, PID: %d, PPID: %d\n", getpid(), getppid());
                sleep(1);
            }
            exit(EXIT_SUCCESS);
        }
    }

    for (i = 0; i < 5; ++i) 
    {
        if (wait(&status) == -1) //循环5次wait来等待创建的5个子进程
        {
            perror("wait");
            exit(EXIT_FAILURE);
        }
        // 检查子进程的结束状态
        if (WIFEXITED(status)) 
        {
            printf("Child process exited with status %d\n", WEXITSTATUS(status));
        }
    }
    sleep(10); //父进程等待10s
    return 0;
}

我们首先通过一个**for** 循环调用 fork() 五次,创建五个子进程。

每个子进程都会打印出自己的 PID 和 PPID,然后调用**sleep(1)** 以模拟执行任务,接着通过**exit()** 函数正常退出。

父进程在创建了所有子进程之后,会进入另一个**for** 循环,使用 **wait()**函数等待每个子进程结束。

wait() 会挂起父进程,直到有一个子进程结束。一旦有子进程结束,**wait()**返回,父进程会检查子进程的结束状态,如果子进程是正常结束的,会打印出子进程的退出状态码。

相关推荐
什么鬼昵称6 分钟前
Pikachu- Over Permission-垂直越权
运维·服务器
码农小白18 分钟前
linux驱动:(22)中断节点和中断函数
linux·运维·服务器
4647的码农历程19 分钟前
Linux网络编程 -- 网络基础
linux·运维·网络
醉颜凉44 分钟前
银河麒麟桌面操作系统V10 SP1:取消安装应用的安全授权认证
运维·安全·操作系统·国产化·麒麟·kylin os·安全授权认证
2401_857610031 小时前
SpringBoot实现:校园资料分享平台开发指南
服务器·spring boot·php
C++忠实粉丝2 小时前
Linux环境基础开发工具使用(2)
linux·运维·服务器
康熙38bdc2 小时前
Linux 环境变量
linux·运维·服务器
存储服务专家StorageExpert2 小时前
DELL SC compellent存储的四种访问方式
运维·服务器·存储维护·emc存储
hakesashou3 小时前
python如何比较字符串
linux·开发语言·python
Ljubim.te3 小时前
Linux基于CentOS学习【进程状态】【进程优先级】【调度与切换】【进程挂起】【进程饥饿】
linux·学习·centos