前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >wait和waitpid

wait和waitpid

作者头像
ljw695
发布2024-10-18 08:21:56
发布2024-10-18 08:21:56
10700
代码可运行
举报
文章被收录于专栏:ljwljw
运行总次数:0
代码可运行

在Linux中,waitwaitpid 是用于进程控制的系统调用,它们主要用来让父进程等待子进程的终止,并获取子进程的退出状态。下面详细介绍它们的用法和区别。

1. wait() 函数

wait() 会阻塞调用进程,直到一个子进程终止。它的典型用法如下:

函数原型
代码语言:javascript
代码运行次数:0
运行
复制
#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *status);
  • 参数
    • status: 这是一个指向整数的指针,用来存储子进程的退出状态。如果不关心退出状态,可以传递 NULL
  • 返回值
    • 成功时,返回终止的子进程的 PID。
    • 失败时,返回 -1 并设置 errno
用法
代码语言:javascript
代码运行次数:0
运行
复制
int status;
pid_t pid = wait(&status);

if (pid == -1) {
    perror("wait");
} else {
    if (WIFEXITED(status)) {
        printf("Child exited with status %d\n", WEXITSTATUS(status));
    }
}
关键宏函数

status 是由子进程退出状态生成的,可以通过以下宏来检查子进程的退出原因:

  • WIFEXITED(status): 子进程是否正常退出。
  • WEXITSTATUS(status): 获取子进程的退出状态(当 WIFEXITED(status) 为真时使用)。
  • WIFSIGNALED(status): 子进程是否因为信号终止。
  • WTERMSIG(status): 获取导致子进程终止的信号。
  • WIFSTOPPED(status): 子进程是否处于暂停状态。
  • WSTOPSIG(status): 获取导致子进程暂停的信号。
特点
  • wait() 只会等待任意一个终止的子进程,并返回其 PID。
  • 如果有多个子进程,wait() 可能随机返回任意一个已终止的子进程,而不确定是哪一个。
  • wait() 在没有子进程时返回 -1 并设置 errnoECHILD

2. waitpid() 函数

waitpid() 提供了更强的灵活性,可以指定等待特定的子进程,也可以选择非阻塞模式。

函数原型
代码语言:javascript
代码运行次数:0
运行
复制
#include <sys/types.h>
#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *status, int options);
  • 参数
    • pid: 指定要等待的子进程的 PID,具体值可以为以下几种:
      • -1: 等待任意一个子进程(等价于 wait() 的行为)。
      • > 0: 等待特定的子进程,PID 为 pid 的子进程。
      • 0: 等待与调用进程在同一个进程组中的任意子进程。
      • < -1: 等待进程组 ID 为 |pid| 的任意子进程。
    • status: 和 wait() 类似,用来存储子进程的状态信息。
    • options: 控制行为的选项,常见的取值有:
      • 0: 阻塞等待,直到子进程终止。
      • WNOHANG: 非阻塞模式,如果没有子进程终止,waitpid() 返回 0
      • WUNTRACED: 还可以返回停止的子进程(收到 SIGSTOPSIGTSTP 等信号)。
  • 返回值
    • 成功时,返回子进程的 PID。
    • 如果使用 WNOHANG 且没有终止的子进程,返回 0
    • 失败时,返回 -1 并设置 errno
用法
代码语言:javascript
代码运行次数:0
运行
复制
int status;
pid_t pid = waitpid(-1, &status, 0); // 等待任意子进程

if (pid == -1) {
    perror("waitpid");
} else if (pid == 0) {
    // 当使用 WNOHANG 时,可以处理没有子进程退出的情况
    printf("No child has exited yet.\n");
} else {
    if (WIFEXITED(status)) {
        printf("Child exited with status %d\n", WEXITSTATUS(status));
    }
}
特点
  • waitpid()wait() 更灵活,可以指定等待特定的子进程。
  • 可以使用 WNOHANG 选项来避免阻塞。
  • waitpid() 支持等待子进程暂停的状态。

3. wait()waitpid() 的区别

功能

wait()

waitpid()

等待特定子进程

不可以

可以(通过 pid 参数)

阻塞/非阻塞模式

只能阻塞

支持非阻塞(通过 WNOHANG 选项)

等待任意子进程

可以(默认行为)

可以(通过 pid = -1)

支持停止的子进程

不支持

支持(通过 WUNTRACED)

等待同一进程组中的进程

不支持

可以(通过 pid = 0 或 pid = -<pgid>)


4. 总结

  • wait() 用于简单的父子进程同步,父进程等待任意一个子进程的退出。
  • waitpid() 提供更多控制,可以指定等待特定的子进程,并支持非阻塞模式和等待停止的子进程。
  • 它们主要用于进程管理和控制,是系统编程中非常重要的工具

 1:是什么

2:为什么也就是必要性

之前说过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。

另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法 杀死一个已经死去的进程。

最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对, 或者是否正常退出。

父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

wait回收僵尸进程(父进程等待是必须的)(wait成功返回的是回收子进程的PI,失败返回-1)

运行后子进程的状态是S+,然后5秒后就会变成Z+(僵尸状态),然后再过5秒,父进程就会用wait给回收掉(wait是只能等待任意一个子进程)

变成Z

回收了

如果有多个子进程呢,wait等待哪一个呢

首先创建多个子进程,此代码的意思是,父进程进入for循环后,经过fork创建了一个子进程进入了RunChild,然后执行完RunChild后,exit(0),就退出子进程了,然后父进程再次进入循环,再次创建一个子进。。。。。。以此类推

一个wait等待任意一个,10个子进程要用循环了,wait回收僵尸进程(父进程等待是必须的,意思是需要等子进程结束后父进程再进行回收)(wait返回的是回收子进程的PID)

如果子进程一直不死,父进程就会一直等待也不返回,只要你退出了我才返回,这叫做阻塞等待

waitpid(可以保证最后结束的一定是父进程)

waitpid的单个进程总代码

这个waitpid(代码例子是单个子进程的)与wait(单个的进程用法一样)

status的使用,waitpid的第一个参数是自己的子进程,不能等待别人的子进程

子进程,一共有几种退出的场景

而不用全局变量,先在子进程改完,再从父进程中拿到,这是不可以的,因为进程具有独立性,所以要用wait等系统调用

查看子进程错误信息码,和exit返回的值        退出信号(signal)

另一种写法(这里的进程出异常是子进程出异常了,wait failed是父进程调取出异常了)

代码例子是多个子进程的waitpid用法,从父进程中获取exit(i)中多个i的值

 -1的情况总代码(没写)

非阻塞轮询(总代码没写)

这个第三个参数为0时就是默认是阻塞方式

举个例子:小张是操作系统,我是用户,我约着小张去学习,小张让我等他,我打电话催他就是系统调用的过程,打电话的本质就是问小张好了没,本质就是检查状态,小张说他好了或者没好,或者说还得等一会,这叫检查不成功,然后我把电话挂掉,这叫做系统调用立马返回,在这个过程中,我在不断不断的打电话询问,然后直接返回结果,立马挂断,叫做非阻塞,基于非阻塞然后一直给小张打电话(打电话不会被卡住),问他好了没有,这个过程就叫做轮询,所以非阻塞+循环就叫做非阻塞轮询。

然后第二天又约小张,因为有上次的教训,我就告诉小张,你不要挂电话,等你好的时候再说你好了,再挂电话,然后我一直抱着电话听他状态,这种就叫做纯阻塞式调用。(最常用够简单,父进程什么都做不了只能等待)

接着第三天, 又约他,变聪明了,一边打电话催,催完了,再自己玩自己的,在等他的过程中,也在做自己的事情,这就是非阻塞轮询+做自己的事情(这样就可以让父进程即在等待,又可以做自己的事情)

三种返回值结果:

这个第三个参数是非阻塞等待,是0就是阻塞等待

返回值有三种

记得加循环 

如果把第三个参数改成0就没有这种情况了,父进程就不可以做自己的事情了

用到了函数指针

ret==0的情况,父进程做自己的事代码(单进程版本,多进程的waitpid的第一个参数 改成-1就好):makefile中加上-std=c99,j就可以用int i=0;在for循环中了

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-10-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. wait() 函数
    • 函数原型
    • 用法
    • 关键宏函数
    • 特点
  • 2. waitpid() 函数
    • 函数原型
    • 用法
    • 特点
  • 3. wait() 和 waitpid() 的区别
  • 4. 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档