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返回数据,那就是我们孩子写的失败代码.
你终止了孩子(通过调用_exit())然后父母可以注意到这一点(通过例如waitpid()).例如,您的孩子可以退出,退出状态为-1,表示执行失败.有一点需要注意的是,无法告诉你的父母处于原始状态(即exec之前)的孩子是否返回-1或者是否是新执行的过程.
正如下面的评论中所建议的,使用"不寻常"的返回代码是合适的,以便更容易区分您的特定错误和exec()'ed程序中的错误.常见的是1,2,3等,而更高的数字99,100等更不寻常.您应该将数字保持在255(无符号)或127(有符号)以下,以提高可移植性.
由于waitpid会阻止你的应用程序(或者更确切地说是调用它的线程),你需要将它放在后台线程上,或者使用POSIX中的信令机制来获取有关子进程终止的信息.请参阅SIGCHLD信号和sigaction函数以挂接侦听器.
您还可以在分叉之前执行一些错误检查,例如确保可执行文件存在.
如果您使用像Glib这样的东西,那么有实用程序功能可以执行此操作,并且它们具有非常好的错误报告功能.看一下本手册的" 产卵过程 "部分.
| 归档时间: |
|
| 查看次数: |
13155 次 |
| 最近记录: |