先备知识:fork系统调用
对wait()的调用会阻塞调用进程,直到它的一个子进程退出或接收到信号。子进程终止后,父进程继续执行wait()系统调用后面的指令。
子进程可能会由于以下任何原因而终止:
- 它调用exit();
- 它从main返回(一个int)。
- 它接收一个信号(来自OS或另一个进程),该信号的默认操作是终止信号。
在C语言中的用法
#include
#include
// 接受一个参数status并返回终止的子进程的ID
pid_t wait(int *stat_loc);
如果进程有多个子进程,则在调用wait()后,如果没有子进程终止,则父进程必须处于等待状态。
如果只有一个子进程终止,则返回wait()返回终止的子进程的进程ID。
如果终止了多个子进程,则wait()将获取任意的子进程并返回该子进程的进程ID。
当wait()返回时,可以通过传入的int *status
指针我们可以知道进程为什么终止,如果传入NULL,则代表我们不想获取该信息。
如果任何进程没有子进程,则wait()立即返回“-1”。
例子1
注:“由于环境问题,此代码不能在简单的IDE中运行,需要使用终端来运行代码”
// 演示wait()工作的C程序
#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<unistd.h>
int main()
{
pid_t cpid;
if (fork()== 0)
exit(0); /* 子进程终止 */
else
cpid = wait(NULL); /* 父进程等待子进程终止 */
printf("Parent pid = %d\n", getpid());
printf("Child pid = %d\n", cpid);
return 0;
}
输出
Parent pid = 12345678
Child pid = 89546848
// 演示wait()工作的C程序
#include<stdio.h>
#include<sys/wait.h>
#include<unistd.h>
int main()
{
if (fork()== 0)
printf("HC: hello from child\n");
else {
printf("HP: hello from parent\n");
wait(NULL);
printf("CT: child has terminated\n");
}
printf("Bye\n");
return 0;
}
输出
HC: hello from child
Bye
HP: hello from parent
CT: child has terminated
(或)
HP: hello from parent
HC: hello from child
CT: child has terminated // 这段没有在 HC 之前打印,这是wait函数的原因
Bye
子进程的状态信息(status):
wait()报告的关于孩子的状态信息不仅仅是孩子的退出状态,它还包括:
- 正常/异常终止
- 终止原因
- 退出状态
要查找有关状态的信息,我们可以使用WIF…
相关的宏
编号 | 调用 | 说明 |
---|---|---|
1 | WIFEXITED(status) | 子进程正常退出 |
. | WEXITSTATUS(status) | 当子进程退出时返回代码 |
2 | WIFSIGNALED(status) | 由于未捕获信号,子进程退出 |
. | WTERMSIG(status) | 给出终止信号的编号 |
3 | WIFSTOPPED(status) | 子进程项停止 |
. | WSTOPSIG(status) | 给出停止信号的编号 |
/* 如果要打印有关信号的信息 */
void psignal(unsigned sig,const char*s)
例子2
检查以下程序的输出
// C程序演示如何使用wait()返回的status状态信息
#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<unistd.h>
void waitexample()
{
int stat;
if (fork() == 0)
// 子进程自己退出,退出返回值为1
// 此状态信息 1 由WEXITSTATUS报告
exit(1);
else
wait(&stat);
// 如果是自愿退出
if (WIFEXITED(stat))
printf("Exit status: %d\n", WEXITSTATUS(stat));
// 如果是收到终止信号退出的
else if (WIFSIGNALED(stat))
psignal(WTERMSIG(stat), "Exit signal");
}
// 主程序
int main()
{
waitexample();
return 0;
}
输出
Exit status: 1
例子3
我们知道如果多个子进程被终止,则wait()将获取任意的子进程,但如果我们想等待特定子进程终止,则使用waitpid()函数。
// waitpid在C语言中的用法
pid_t waitpid (pit_t child_pid, int *status, int options);
options
如果是0表示没有选项,则父节点必须等待终止子节点。
options
如果是WNOHANG表示父进程不等待,如果子进程没有终止,则也往下执行。
如果child_pid
为-1,则表示任意子进程,此处waitpid()的工作与wait()的工作相同。
waitpid()的返回值
- 如果options为0且子进程退出,则返回子进程的PID
- 如果options为
WNOHANG
选项且子进程未退出,也返回子进程的PID
// C程序演示如何使用waitpid()函数
#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<unistd.h>
void waitexample()
{
int i, stat;
pid_t pid[5];
// 由主进程创建了5个子进程
for (i=0; i<5; i++) {
if ((pid[i] = fork()) == 0) {
sleep(1);
exit(100 + i);
}
}
// 使用waitpid()并打印子进程的退出状态
for (i=0; i<5; i++) {
// options为0,阻塞等待
pid_t cpid = waitpid(pid[i], &stat, 0);
// 如果是子进程是调用exit()退出的
if (WIFEXITED(stat))
printf("Child %d terminated with status: %d\n",
cpid, WEXITSTATUS(stat));
}
}
int main()
{
waitexample();
return 0;
}
输出
Child 50 terminated with status: 100
Child 51 terminated with status: 101
Child 52 terminated with status: 102
Child 53 terminated with status: 103
Child 54 terminated with status: 104
这个程序依次创建了5个子进程,5个进程都是由主进程创建,因为
if ((pid[i] = fork()) == 0)
使得子进程刚出生就接着sleep()+exit(),子进程没有创建自己的子进程的机会。最后主进程根据pid数组,获取每个子进程返回的状态。