一、宏介绍
以下三组宏在fork调用过程中一般成对使用:
WIFEXITED + WEXITSTATUS
WIFSIGNALED + WTERMSIG
WIFSTOPPED + WSTOPSIG
第一组:WIFEXITED + WEXITSTATUS
1、WIFEXITED是一个宏,用来检查子进程是否正常退出。
函数原型:
#include <sys/wait.h>
Int WIFEXITED(int status)
参数:status:子进程退出的状态码,一般由wait()/waitpid()函数返回。
返回值:子进程正常退出(执行exit()退出或者在main函数中返回为正常退出)返回值为非0(一般为1);子进程因为接收到信号而异常中止,则返回0。
2、WEXITSTATUS 是一个宏,用于从子进程的退出状态中提取退出码(exit code)。
函数原型:
#include <sys/wait.h>
Int WEXITSTATUS (int status)
参数:status:子进程退出的状态码,一般由wait()/waitpid()函数返回。
返回值:返回子进程的退出状态码,介于0~255之间的整数值,表示子进程退出时传递给exit()函数的参数值。子进程正常退出,且WEXITSTATUS (status)返回0,代表子进程成功执行,子进程正常退出,WEXITSTATUS(status) 返回非 0,则表示子进程退出时出现了错误。
使用场景:
调用fork()新建子进程后,调用wait()/waitpid()父进程等待子进程结束,wait()/waitpid()会返回子进程的ID和状态码(status)。使用WIFEXITED检查状态码,确定子进程是否正常退出。如果WIFEXITED(status)为真(即正常退出),则可以使用WEXITSTATUS (status)获取子进程的退出码。
C示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
pid_t pid = fork();
if (pid < 0) {
perror("fork failed");
exit(EXIT_FAILURE);
}
if (pid == 0) {
// 子进程
printf("这是子进程\n");
exit(0); // 正常退出,状态码为0
} else {
// 父进程
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status)) {
int exit_status = WEXITSTATUS(status);
printf("子进程正常退出,退出状态码: %d\n", exit_status);
} else if (WIFSIGNALED(status)) {
int signal_number = WTERMSIG(status);
printf("子进程因信号终止,信号编号: %d\n", signal_number);
} else if (WIFSTOPPED(status)) {
int stop_signal = WSTOPSIG(status);
printf("子进程因信号停止,信号编号: %d\n", stop_signal);
} else if (WIFCONTINUED(status)) {
printf("子进程已继续运行\n");
}
}
return 0;
}
第二组:WIFSIGNALED + WTERMSIG
WIFSIGNALED 是一个宏,判断子进程退出是否因为信号而中止,因信号而中止,会返回非0值。WTERMSIG(status)可以获取导致子进程退出的信号的编号。示例见第一组示例。
第三组:WIFSTOPPED + WSTOPSIG
WIFSTOPPED是一个宏,用于判断子进程是否因信号而被停止,如果子进程因为信号被暂停,则返回非0值,否则返回0;
WSTOPSIG宏,获取导致子进程停止的信号的编号。
示例见第一组示例。
二、linux中LTP测试用例使用WEXITSTATUS分析
int main(void)
{
char mqname[NAMESIZE], msgrv[BUFFER];
const char *msgptr = "test message ";
mqd_t mqdes;
int prio = 1;
int pid;
struct timespec ts;
struct mq_attr attr;
int unresolved = 0, failure = 0;
sprintf(mqname, "/" FUNCTION "_" TEST "_%d", getpid());
attr.mq_msgsize = BUFFER;
attr.mq_maxmsg = BUFFER;
mqdes = mq_open(mqname, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, &attr);
if (mqdes == (mqd_t) - 1) {
perror(ERROR_PREFIX "mq_open");
unresolved = 1;
}
pid = fork();
if (pid != 0) {
/* Parent process */
int status;
ts.tv_sec = TIME_T_MAX;
ts.tv_nsec = 0;
if (mq_timedreceive(mqdes, msgrv, BUFFER, NULL, &ts) > 0) {
wait(&status);
if (WEXITSTATUS(status)) {
printf("mq_send error\n");
unresolved = 1;
}
} else {
printf("mq_timedreceive didn't block on waiting\n");
wait(NULL); /* wait for child to exit */
perror(ERROR_PREFIX "mq_timedreceive");
failure = 1;
}
if (mq_close(mqdes) != 0) {
perror(ERROR_PREFIX "mq_close");
unresolved = 1;
}
if (mq_unlink(mqname) != 0) {
perror(ERROR_PREFIX "mq_unlink");
unresolved = 1;
}
if (failure == 1) {
printf("Test FAILED\n");
return PTS_FAIL;
}
if (unresolved == 1) {
printf("Test UNRESOLVED\n");
return PTS_UNRESOLVED;
}
printf("Test PASSED\n");
return PTS_PASS;
} else {
/* Child Process */
sleep(2); /* sleep 2 seconds,
assume that parent will block on waiting then */
if (mq_send(mqdes, msgptr, strlen(msgptr), prio) == -1) {
perror(ERROR_PREFIX "mq_send");
return PTS_UNRESOLVED;
}
return 0;
}
return PTS_UNRESOLVED;
}
WEXITSTATUS(status)返回值为0则执行成功,返回其1~255,则代表子进程退出时的错误码(只有在正常退出时解析这个错误码才有意义)
怎么没有先使用WIFEXITED 判断是否正常退出呢?
在使用 waitpid() 函数等待子进程时,首先使用 WIFEXITED(status) 判断子进程是否正常退出是必要的,因为 WIFEXITED() 用于检查子进程是否通过正常的退出状态(即调用 exit() 或返回)。如果 WIFEXITED(status) 返回真,则可以使用 WEXITSTATUS(status) 宏来获取子进程的退出状态码。
如果不先检查 WIFEXITED(status),直接使用 WEXITSTATUS(status) 可能会导致未定义行为,因为 WEXITSTATUS(status) 仅在子进程正常退出的情况下有效。如果子进程是由于信号终止的(例如,调用了 abort() 或收到了未处理的信号),则 WIFEXITED(status) 会返回假,这时使用 WEXITSTATUS(status) 是不可靠的。