前言:
当一个进程发起某种操作(如I/O请求、信号、锁的获取等),但该操作需要的资源暂时不可用时,进程会被操作系统挂起,进入“等待队列”或“阻塞状态”。在此期间,进程不占用CPU,但仍保留其内存、文件描述符等资源
进程等待的必要性
僵尸进程的存在
僵尸进程的成因
- 当子进程终止后,它的退出状态需要由父进程通过调用
wait()
或waitpid()
系统调用回收。 - 如果父进程未回收子进程的退出状态,子进程会以“僵尸进程”的形式保留在进程表中。
特征:
- 在 Linux 系统中,可以用
ps
命令查看,僵尸进程的状态为Z
(Zombie)。 - 僵尸进程是操作系统保留的一个条目,主要用于父进程检查子进程的退出状态。
如下:
从图片中可以看到一个典型的 僵尸进程 的现象:
- 进程
27864
被强制终止(kill -9 27864
),但它的父进程(27863
)没有调用wait()
或waitpid()
来回收其子进程的退出状态。 - 因此,
27864
被标记为<defunct>
状态,即僵尸进程。 ps
输出的STAT
列中显示Z+
,这是僵尸进程的状态标识。
进程等待
进程等待是操作系统中一种重要的状态,指的是某个进程由于资源不足或条件未满足,暂时无法继续执行而被挂起的现象。
- 使用
wait()
或waitpid()
回收子进程
wait ( )
参数:
-
int *status:
- 用于保存子进程的状态信息(如退出码或终止信号)。
- 如果不需要获取子进程状态,可以将其传入
NULL
。
返回值:
- 成功:
- 返回已终止的子进程的 PID。
- 失败:
- 返回
-1
,并设置errno
。 - 常见错误包括:
ECHILD
:当前进程没有子进程。EINTR
:调用被信号中断。
- 返回
wait()
的作用
- 阻塞父进程:
wait()
会阻塞父进程,直到任意一个子进程状态发生变化(通常是终止)。
- 回收子进程资源:
- 子进程终止后,其资源仍然保留在系统中,直到父进程调用
wait()
或waitpid()
回收它。 - 如果父进程不调用
wait()
或waitpid()
,子进程会变成 僵尸进程。
- 子进程终止后,其资源仍然保留在系统中,直到父进程调用
示例:
#include<iostream>
#include<unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
void childtast()
{
for(int i = 0; i < 10; i++) // 循环打印从 0 到 9 的数字
{
cout << i << endl; // 输出当前的循环变量 i
}
sleep(3); // 睡眠 3 秒,模拟子进程的运行延迟
}
int main()
{
pid_t id = fork(); // 创建子进程
cout << "id" << ":" << id << endl;
if(id == 0) // 判断是否是子进程
{
sleep(3); // 子进程先睡眠 3 秒
childtast(); // 子进程调用 childtast(),打印数字并睡眠
}
// 父进程等待任意一个子进程终止
pid_t ret = wait(NULL); // 父进程调用 wait(),阻塞等待子进程终止
if(ret == id) // 判断 wait() 返回的进程 ID 是否是创建的子进程 ID
{
cout << "ret" << ":" << ret << endl; // 输出子进程的 ID
cout << "wait success" << endl; // 输出等待成功的消息
}
sleep(3); // 父进程再睡眠 3 秒,模拟延迟
return 0;
}
fork()
创建子进程:
- 父进程和子进程同时运行。
- 父进程的
id
是子进程的 PID,子进程的id
是 0。
子进程的任务:
- 子进程先睡眠 3 秒,然后执行
childtast()
,打印0
到9
。
父进程的等待:
- 父进程调用
wait(NULL)
,阻塞自身,直到子进程终止。 - 当子进程完成任务并退出后,
wait()
返回子进程的 PID。
父进程的后续操作:
- 父进程输出子进程的
PID
和等待成功的消息。 - 父进程再睡眠 3 秒后退出。
waitpid ( )
waitpid()
是 wait()
的增强版本,提供了更灵活的功能,允许父进程:
- 等待特定的子进程。
- 非阻塞等待子进程。
- 获取子进程的状态(如退出状态或被信号终止)。
pid_t waitpid(