【发布时间】:2015-10-24 00:31:11
【问题描述】:
我打电话给execvp(),在fork()'ed 的孩子中故意犯了错误的论点。 errno 数字在子进程中正确设置为 ENOENT。然后我用_exit(errno); 终止子进程。
我的主进程调用wait()。当我使用WIFEXITED 和WEXITSTATUS 检查返回的状态时,我总是第一次调用时得到EINVAL。所有其他调用都返回正确的ENOENT 代码。
我无法解释这种行为。下面是完整的函数,它完成了上面所有的事情,但是有点复杂。
QVariantMap
System::exec(const QString & prog, const QStringList & args)
{
pid_t pid = fork();
if (pid == 0) {
int cargs_len = args.length() + 2;
char * cargs[cargs_len];
cargs[cargs_len - 1] = NULL;
QByteArrayList as;
as.push_back(prog.toLocal8Bit());
std::transform(args.begin(), args.end(), std::back_inserter(as),
[](const QString & s) { return s.toLocal8Bit(); });
for (int i = 0; i < as.length(); ++i) {
cargs[i] = as[i].data();
}
execvp(cargs[0], cargs);
// in case execvp fails, terminate the child process immediately
qDebug() << "(" << errno << ") " << strerror(errno); // <----------
_exit(errno);
} else if (pid < 0) {
goto fail;
} else {
sigset_t mask;
sigset_t orig_mask;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
if (sigprocmask(SIG_BLOCK, &mask, &orig_mask) < 0) {
goto fail;
}
struct timespec timeout;
timeout.tv_sec = 0;
timeout.tv_nsec = 10 * 1000 * 1000;
while (true) {
int ret = sigtimedwait(&mask, NULL, &timeout);
if (ret < 0) {
if (errno == EAGAIN) {
// timeout
goto win;
} else {
// error
goto fail;
}
} else {
if (errno == EINTR) {
// not SIGCHLD
continue;
} else {
int status = 0;
if (wait(&status) == pid) {
if (WIFEXITED(status)) {
return { { "error", strerror(WEXITSTATUS(status)) } };
} else {
goto fail;
}
} else {
goto fail;
}
}
}
}
}
win:
return {};
fail:
return { { "error", strerror(errno) } };
}
事实证明,使用qDebug() 调用删除该行会使问题消失。为什么添加调试调用会改变程序的行为?
【问题讨论】:
-
虽然
exit(和_exit)接受int,但它真的不能处理除了有符号字节之外的任何东西,这意味着该值应该在0到255(含)之间。如果退出代码超出该范围,我认为行为未定义。 -
为什么这个标签是
c?在我看来,它更像c++。 -
@JoachimPileborg 当然你是对的,但是
ENOENT == 2,所以至少它应该是正确的。 -
奇怪的是你屏蔽了
SIGCHLD,却从未解除屏蔽。您将累积待处理的SIGCHLD信号队列。如果您不想处理它们,那么您不需要阻止它们——默认情况下它们将被忽略。但是请注意,这与您将信号处置显式设置为SIG_IGN时不同——在后一种情况下,退出状态被丢弃,您不能在子节点上使用wait(),但在前者中则不然。 -
@JohnBollinger 信号仅在使用实时信号接口时才排队,此处并非如此。标准信号不排队;如果在同一个信号的另一个实例已经挂起时引发了一个信号,那么当(并且如果)您取消阻止它时,只会传递一个信号。所以这里没有排队信号的积累。
标签: c++ wait exit errno qdebug