Linux, waitpid, WNOHANG and zombies

Linux, waitpid, WNOHANG and zombies

本文关键字:zombies and waitpid Linux WNOHANG      更新时间:2023-10-16

我需要能够:

  1. 派生一个进程并使其成为execvp(我就是这么做的)
  2. 检查子进程execvp是否成功(不知道如何)
  3. 检查子进程是否已完成(存在问题)

我正在分叉一个进程,我没有任何方法来检查子进程的execvp是否工作或失败。如果它失败了,我需要知道它失败了。目前我正在使用

-1 != waitpid( pid, &status, WNOHANG )

但是,如果pid进程的execv失败,waitpid似乎不会返回-1。

我该怎么查?我读了等待的人页,但我不清楚;也许我的英语不够好。

编辑:为了解释更多:
我正在为家庭作业建造自己的终端。我需要获得一个命令字符串作为输入,比如说"ls",然后我必须执行该命令
在子分叉之后,子调用execvp以执行命令(在我解析字符串之后),父需要检查是否存在'&'是否在命令结束时
如果符号"&"不存在,则父级需要等待子级执行。

所以我需要知道execvp是否失败了。如果没有失败,则父级使用waitpid等待子级完成执行。如果失败,则父级将不会等待子级。

#2的一个常见解决方案是在fork()之前打开一个管道,然后在exec之后的子级中写入。在父级中,成功读取表示exec失败;读取失败意味着exec成功,而写入从未发生。

// ignoring all errors except from execvp...
int execpipe[2];
pipe(execpipe);
fcntl(execpipe[1], F_SETFD, fcntl(execpipe[1], F_GETFD) | FD_CLOEXEC);
if(fork() == 0)
{
close(execpipe[0]);
execvp(...); // on success, never returns
write(execpipe[1], &errno, sizeof(errno));
// doesn't matter what you exit with
_exit(0);
}
else
{
close(execpipe[1]);
int childErrno;
if(read(execpipe[0], &childErrno, sizeof(childErrno)) == sizeof(childErrno))
{
// exec failed, now we have the child's errno value
// e.g. ENOENT
}
}

这让父级明确地知道exec是否成功,以及作为副产品,如果不成功,errno值是多少。

如果exec成功,则子进程可能仍会失败,并返回退出代码,使用WEXITSTATUS宏检查状态也会给出该条件。

注意:使用WNOHANG标志调用waitpid是非阻塞的,您可能需要轮询进程,直到返回有效的pid。

如果exec调用成功,则根本不应返回,因为它将用另一个进程映像替换当前进程映像,因此如果返回,则意味着发生了错误:

execvp(...);
/* exec failed and you should exit the
child process here with an error */
exit(errno);

要让父进程知道exec是否失败,您应该读取子进程的状态:

waitpid(pid, &status, WNOHANG);

然后使用WEXITSTATUS(status)宏,从手册页:

WEXITSTATUS(status)返回子级的退出状态这个由状态自变量的最低有效8位组成在对exit(3)或_exit(2)的调用中指定的子级,或在main()中指定为return语句的参数

请注意,最后一条语句意味着,如果exec成功并运行该命令,您将获得该命令的main()函数的退出状态,换句话说,您无法通过这种方式可靠地区分失败的exec和失败的命令,所以这取决于您是否对此感兴趣。

另一个问题:

如果符号'&'不存在于命令末尾,则父级需要等待子级执行。

您需要在程序中的某个时刻调用子进程上的wait(),而不考虑&,以避免使子进程处于僵尸状态,

注意:当你使用WNOHANG时,这意味着如果没有进程改变其状态,waitpid()将立即返回,即它不会阻塞,我想你知道,否则使用wait()或调用waitpid()作为主循环的一部分。