如何在fork()之后处理execvp(...)错误?

Łuk*_*Lew 11 c++ linux posix fork exec

我做常规的事情:

  • 叉子()
  • 小孩的execvp(cmd,)

如果execvp因为没有找到cmd而失败,我怎么能在父进程中注意到这个错误?

eph*_*ent 19

著名的自管技巧可以适用于这一目的.

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <sysexits.h>
#include <unistd.h>

int main(int argc, char **argv) {
    int pipefds[2];
    int count, err;
    pid_t child;

    if (pipe(pipefds)) {
        perror("pipe");
        return EX_OSERR;
    }
    if (fcntl(pipefds[1], F_SETFD, fcntl(pipefds[1], F_GETFD) | FD_CLOEXEC)) {
        perror("fcntl");
        return EX_OSERR;
    }

    switch (child = fork()) {
    case -1:
        perror("fork");
        return EX_OSERR;
    case 0:
        close(pipefds[0]);
        execvp(argv[1], argv + 1);
        write(pipefds[1], &errno, sizeof(int));
        _exit(0);
    default:
        close(pipefds[1]);
        while ((count = read(pipefds[0], &err, sizeof(errno))) == -1)
            if (errno != EAGAIN && errno != EINTR) break;
        if (count) {
            fprintf(stderr, "child's execvp: %s\n", strerror(err));
            return EX_UNAVAILABLE;
        }
        close(pipefds[0]);
        puts("waiting for child...");
        while (waitpid(child, &err, 0) == -1)
            if (errno != EINTR) {
                perror("waitpid");
                return EX_SOFTWARE;
            }
        if (WIFEXITED(err))
            printf("child exited with %d\n", WEXITSTATUS(err));
        else if (WIFSIGNALED(err))
            printf("child killed by %d\n", WTERMSIG(err));
    }
    return err;
}
Run Code Online (Sandbox Code Playgroud)

这是一个完整的计划.

$ ./a.out foo
child's execvp: No such file or directory
$ (sleep 1 && killall -QUIT sleep &); ./a.out sleep 60
waiting for child...
child killed by 3
$ ./a.out true
waiting for child...
child exited with 0

这是如何工作的:

创建管道,并创建写入端点CLOEXEC:exec成功执行时自动关闭.

在孩子,尝试exec.如果成功,我们将无法控制,但管道已关闭.如果失败,请将失败代码写入管道并退出.

在父级中,尝试从其他管道端点读取.如果read返回零,则管道已关闭且子项必须已exec成功.如果read返回数据,那就是我们孩子写的失败代码.

  • 这个解决方案非常简单,摇滚. (3认同)

Isa*_*avo 6

你终止了孩子(通过调用_exit())然后父母可以注意到这一点(通过例如waitpid()).例如,您的孩子可以退出,退出状态为-1,表示执行失败.有一点需要注意的是,无法告诉你的父母处于原始状态(即exec之前)的孩子是否返回-1或者是否是新执行的过程.

正如下面的评论中所建议的,使用"不寻常"的返回代码是合适的,以便更容易区分您的特定错误和exec()'ed程序中的错误.常见的是1,2,3等,而更高的数字99,100等更不寻常.您应该将数字保持在255(无符号)或127(有符号)以下,以提高可移植性.

由于waitpid会阻止你的应用程序(或者更确切地说是调用它的线程),你需要将它放在后台线程上,或者使用POSIX中的信令机制来获取有关子进程终止的信息.请参阅SIGCHLD信号和sigaction函数以挂接侦听器.

您还可以在分叉之前执行一些错误检查,例如确保可执行文件存在.

如果您使用像Glib这样的东西,那么有实用程序功能可以执行此操作,并且它们具有非常好的错误报告功能.看一下本手册的" 产卵过程 "部分.