上一篇介绍了进程的创建和退出,以及相关的系统函数。
Linux编程入门(15)-进程(三)编程
这篇主要讲讲,子进程退出后,父进程如何获取其退出状态。
有的应用程序,需要父进程知道子进程何时终止或退出,以及其返回给父进程的状态值信息。
那么,父进程在创建完成子进程后,有没有办法获知子进程的退出状态?答案是肯定的。
Linux 提供了系统函数 wait()
,用于检测子进程的终止情况。
系统函数 wait()
主要做两件事:
exit()
的值。wait()
等待调用进程的任意一个子进程终止,其函数原型为:
#include
#include
pid_t wait(int *wstatus);
参数 wstatus 指向的整型变量用于接收子进程的终止状态。
函数执行成功,返回终止的子进程 ID。 失败,则返回 -1。
wait()
详细的执行步骤为:
wai()
会出错,返回 -1。wait()
立即返回。否则,调用进程一直阻塞。wait()
返回值,并返回。注意,如果调用者阻塞并且有多个子进程,那么任意一个子进程终止时,wait()
就立即返回。
因为 wait()
的返回值是终止进程的 PID,所以父进程能够知晓是哪一个进程终止了。若某个子进程用来完成某项任务,当任务处理完毕后退出,父进程可以通过 wait()
了解到该任务完成了,可以继续其他处理了。
一个进程的结束,有三种方式:
exit(0)
或者 return 0
。exit()
函数时传递一个非零值。父进程如何知道子进程是以何种方式退出的呢?可以通过 wait()
返回的退出状态来进行判断。
在调用 wait()
函数时,给其传递一个整型变量的地址。Linux 内核会将子进程的退出状态存储在这个变量中:
exit()
退出,内核会把 exit 的参数值存放到整数变量中。结束状态值的构成
进程退出返回的整型状态值,实际上仅用了最低的 16 位,由 3 部分构成:
终止状态可以用
中的宏来检测。其中,有 4 个宏可用来取得进程终止的原因:
编程示例-1
编程实验,实际验证一下,子进程调用 exit() 是如何触发 wait() 返回的处理流程:
#include
#include
#include
#include
#include
#include
#include
/* 子进程执行函数 */
void child_code(int delay)
{
printf("child %d here. will sleep %d seconds\n", getpid(), delay);
sleep(delay);
printf("child done. exit\n");
exit(17);
}
/* 父进程执行函数 */
void parent_code(int childpid)
{
int wait_ret;
wait_ret = wait(NULL);
printf("done waiting for %d. Wait returned: %d\n", childpid, wait_ret);
}
int main()
{
int newpid;
printf("before: mypid is %d\n", getpid());
/* 创建新进程,并判断返回值 */
if((newpid = fork()) == -1)
{
perror("fork");
}
else if(newpid == 0)
{
/* 子进程执行感函数 */
child_code(2);
}
else
{
/* 父进程执行感函数 */
parent_code(newpid);
}
}
在父进程中,控制流始于程序的开始,在 wait()
的地方阻塞。
在子进程中,控制流始于 main 函数的中部(fork 之后),然后运行 child_code()
函数,最后调用 exit()
结束。
编译,运行结果如下:
$ gcc waitdemo.c -o waitdemo
$ ./waitdemo
before: mypid is 2844
child 2845 here. will sleep 2 seconds
child done. exit
done waiting for 2845. Wait returned: 2845
由程序的执行结果来看,这段程序验证了 wait()
的函数的两个特征:
wait()
函数会阻塞调用它的程序,直到子进程结束。wait()
函数会返回结束进程的 PID。编程示例-2
通过编程来显示子进程的退出状态,代码 waitdemo2.c 如下:
#include
#include
#include
#include
#include
#include
#include
/* 子进程执行函数 */
void child_code(int delay)
{
printf("child %d here. will sleep %d seconds\n", getpid(), delay);
/* 子进程执行函数 */
sleep(delay);
printf("child done. exit\n");
/* 子进程退出 */
exit(17);
}
void parent_code(int childpid)
{
int wait_ret;
int child_status;
int high_8, low_7, bit_7;
wait_ret = wait(&child_status);
printf("done waiting for %d. Wait returned: %d\n", childpid, wait_ret);
/* 解析子进程退出状态信息 */
high_8 = child_status >> 8;
low_7 = child_status & 0x7F;
bit_7 = child_status & 0x80;
printf("status: exit = %d, sig = %d, core %d\n", high_8, low_7, bit_7);
}
int main()
{
int newpid;
printf("before: mypid is %d\n", getpid());
if((newpid = fork()) == -1)
{
perror("fork");
}
else if(newpid == 0)
{
child_code(5);
}
else
{
parent_code(newpid);
}
}
编译、运行。先看看正常运行情况,退出状态从子进程中获取(exit()
的参数):
$ gcc waitdemo2.c -o waitdemo2
$ ./waitdemo2
before: mypid is 5506
child 5507 here. will sleep 5 seconds
child done. exit
done waiting for 5507. Wait returned: 5507
status: exit = 17, sig = 0, core 0
然后再看看异常结束情况。后台运行 waitdemo2,使用 kill 指令给子进程发送 SIGTERM 信号,内核将该信号编号填充到状态字中:
$ ./waitdemo2 & /* 后台运行程序 */
before: mypid is 5592
child 5593 here. will sleep 5 seconds
kill 5593 /* 给子进程发送信号 */
done waiting for 5593. Wait returned: 5593
status: exit = 0, sig = 15, core 0 /* 显示状态信息 */
假如一个进程有多个子进程,只要有一个子进程退出,wait()
就返回。那么要等待一个指定的进程终止(需要直到该进程的 PID),该如何处理?
一种方式是,根据 wait()
返回的进程 ID 与期望的进程 ID 比较。如果终止进程不是所期望的,则再次调用 wait()
函数。
还有一种方式,使用专门的系统函数。Linux 系统提供了一个系统调用,可以用来等待特定的子进程。
该系统函数为 waitpid()
, 其原型为:
#include
#include
pid_t waitpid(pid_t pid, int *wstatus, int options);
参数 pid,表示需要等待的具体子进程,具体含义如下:
可以得知,wait(&status)
等价于 waitpid(-1, &status, 0)
。
参数 options,是一个位掩码,可以包含如下标志位:
本文主要介绍了父进程如何等待子进程退出,以及获取进程退出状态相关的内容。
waitpid()
下次文章,以一个示例来综合运用前面几篇学过的内容,欢迎订阅查看。