C 在fork()调用';拉波本
在我的系统上执行此操作时:C 在fork()调用';拉波本,c,linux,C,Linux,在我的系统上执行此操作时: FILE* pipe = popen("something_that_doesnt_exist", "r"); printf("pipe: %p\n", pipe); 我得到了管道的有效指针,尽管该程序不存在。我想编写一个popen,它可以检测到这一点,并返回一个指示启动失败的空指针。但是,我不知道如何在保持/bin/sh调用进行解释的同时实现这一点。是否有人知道我如何检查呼叫的返回状态,如: execl("/bin/sh", "sh", "-c", "someth
FILE* pipe = popen("something_that_doesnt_exist", "r");
printf("pipe: %p\n", pipe);
我得到了管道的有效指针,尽管该程序不存在。我想编写一个popen,它可以检测到这一点,并返回一个指示启动失败的空指针。但是,我不知道如何在保持/bin/sh调用进行解释的同时实现这一点。是否有人知道我如何检查呼叫的返回状态,如:
execl("/bin/sh", "sh", "-c", "something_that_doesnt_exist");
#包括
#包括
#包括
int main()
{
int fd=open(“/dev/null”,仅限O_wr);
/*备份*/
int f1=dup(1);
int f2=dup(2);
dup2(fd,1);
dup2(fd,2);
int res=系统(“不存在的东西”);
/*恢复*/
dup2(f1,1);
dup2(f2,2);
FILE*pipe=popen(“不存在的东西”,“r”);
如果(res!=0)
{
管道=空;
}
}
重定向stdout和stderr以避免未接受的输出。如果popen()将成功执行命令,则无法判断。这是因为进程首先调用fork(),因此将始终创建管道和子进程。但是,如果fork()之后的execv()调用失败,那么子进程将死亡,父进程将无法判断这是由execv()失败引起的,还是您希望在没有任何输出的情况下刚刚完成的命令引起的。如果您的进程没有其他子进程,那么您可以使用waitpid
int stat;
File* fp = popen("something_that_doesnt_exist", "r");
waitpid(-1, &stat, 0);
然后您可以确定stat的值,如果popen成功,stat=0。对这种方式不是很确定,需要有人确认才能这样做,你需要使用低级设施。您需要创建一个额外的管道,即close on exec,当exec失败时,子级将使用该管道编写错误代码 由于exec上的管道是关闭的,因此内核将在新二进制文件开始执行时关闭管道。(我们实际上不知道该命令是否正在运行;我们只知道exec没有失败。因此,不要假设关闭的管道意味着该命令已经在运行。它只意味着它还没有失败。) 父进程关闭不必要的管道端点,并从控制管道读取数据。如果读取成功,则子进程无法执行命令,并且读取的数据描述了错误。如果管道关闭(读取返回0),命令执行将开始(禁止执行时没有错误) 之后,我们可以像往常一样继续从管道中读取数据。当子进程关闭管道时,我们应该使用 考虑下面的示例程序。它执行在命令行上指定的命令——如果您想要与
system()
和popen()相同的行为,请使用sh-c'command'
。(即,pathname==“sh”
和argv=={“sh”、“-c”、“command”、NULL}
)
它逐个字符读取命令的输出,并对它们进行计数,直到子命令结束(通过关闭管道)。之后,我们获取子进程,并报告状态。
如果命令无法执行,也会报告原因。(由于非可执行文件报告为enoint
(“没有这样的文件或目录”),因此exec\u suboc()
将该情况修改为eaprocess
(“权限被拒绝”)
并运行例如
./example date
./example ./example date -u
./example /bin/sh -c 'date | tr A-Za-z a-zA-Z'
pclose()
为您提供所需的退出状态。这还不够好吗?那不行,因为我想在决定是否关闭管道之前需要状态。首先,如果要调用命令,请不要使用sh-c
。只有在使用POSIX外壳特性(如替换或作业控制)时才需要它。其次,为了从子进程检索exec*()
状态,可以使用额外的关闭exec管道,该管道仅在exec*()
失败时使用。我下面的回答详细说明了这种方法。如果你需要sh
shell行为,那么你可以将你的不存在的东西
包装在错误报告shell脚本片段中,而你不需要我的答案,只需要POSIX shell脚本。不幸的是,在这种情况下,我会有很多孩子,我不能阻止和等待。我喜欢它,但不幸的是,我的命令要么是长时间运行的,要么不是幂等的,所以我不能真正运行它们两次……不幸的是,我认为我需要posix shell语义来完成我的工作。我正在编写一个库,它提供了超级popen功能,包括可以有多个读卡器的命名管道。在linux中似乎真的没有什么好方法可以做到这一点,这既令人惊讶又令人失望。@gct:别傻了:)问题是“真的没有好方法可以做到这一点”,因为您将自己限制在语义上,不允许为基础问题提供健壮、有用的解决方案。例如,考虑Python模块,它旨在解决潜在问题的子集。如果您想要一个具有多个管道(甚至套接字)的类似popen的设施来互连进程(包括父进程),请使用结构来描述每个进程,而不是字符串!然后您也可以轻松地获取所有错误消息。
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
int reap_subproc(pid_t pid)
{
int status;
pid_t p;
if (pid < 1) {
errno = EINVAL;
return -1;
}
do {
status = 0;
p = waitpid(pid, &status, 0);
} while (p == -1 && errno == EINTR);
if (p == -1)
return -1;
errno = 0;
return status;
}
FILE *exec_subproc(pid_t *pidptr, const char *pathname, char **argv)
{
char buffer[1];
int datafd[2], controlfd[2], result;
FILE *out;
ssize_t n;
pid_t pid, p;
if (pidptr)
*pidptr = (pid_t)0;
if (!pidptr || !pathname || !*pathname || !argv || !argv[0]) {
errno = EINVAL;
return NULL;
}
if (pipe(datafd) == -1)
return NULL;
if (pipe(controlfd) == -1) {
const int saved_errno = errno;
close(datafd[0]);
close(datafd[1]);
errno = saved_errno;
return NULL;
}
if (fcntl(datafd[0], F_SETFD, FD_CLOEXEC) == -1 ||
fcntl(controlfd[1], F_SETFD, FD_CLOEXEC) == -1) {
const int saved_errno = errno;
close(datafd[0]);
close(datafd[1]);
close(controlfd[0]);
close(controlfd[1]);
errno = saved_errno;
return NULL;
}
pid = fork();
if (pid == (pid_t)-1) {
const int saved_errno = errno;
close(datafd[0]);
close(datafd[1]);
close(controlfd[0]);
close(controlfd[1]);
errno = saved_errno;
return NULL;
}
if (!pid) {
/* Child process. */
close(datafd[0]);
close(controlfd[0]);
if (datafd[1] != STDOUT_FILENO) {
do {
result = dup2(datafd[1], STDOUT_FILENO);
} while (result == -1 && errno == EINTR);
if (result == -1) {
buffer[0] = errno;
close(datafd[1]);
do {
n = write(controlfd[1], buffer, 1);
} while (n == -1 && errno == EINTR);
exit(127);
}
close(datafd[1]);
}
if (pathname[0] == '/')
execv(pathname, argv);
else
execvp(pathname, argv);
buffer[0] = errno;
close(datafd[1]);
/* In case it exists, we return EACCES instead of ENOENT. */
if (buffer[0] == ENOENT)
if (access(pathname, R_OK) == 0)
buffer[0] = EACCES;
do {
n = write(controlfd[1], buffer, 1);
} while (n == -1 && errno == EINTR);
exit(127);
}
*pidptr = pid;
close(datafd[1]);
close(controlfd[1]);
do {
n = read(controlfd[0], buffer, 1);
} while (n == -1 && errno == EINTR);
if (n == -1) {
close(datafd[0]);
close(controlfd[0]);
kill(pid, SIGKILL);
do {
p = waitpid(pid, NULL, 0);
} while (p == (pid_t)-1 && errno == EINTR);
errno = EIO;
return NULL;
} else
if (n == 1) {
close(datafd[0]);
close(controlfd[0]);
do {
p = waitpid(pid, NULL, 0);
} while (p == (pid_t)-1 && errno == EINTR);
errno = (int)buffer[0];
return NULL;
}
close(controlfd[0]);
out = fdopen(datafd[0], "r");
if (!out) {
close(datafd[0]);
kill(pid, SIGKILL);
do {
p = waitpid(pid, NULL, 0);
} while (p == (pid_t)-1 && errno == EINTR);
errno = EIO;
return NULL;
}
errno = 0;
return out;
}
int main(int argc, char *argv[])
{
FILE *cmd;
pid_t pid;
int c;
unsigned long bytes = 0UL;
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s command [ arguments ... ]\n", argv[0]);
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
cmd = exec_subproc(&pid, argv[1], argv + 1);
if (!cmd) {
fprintf(stderr, "%s: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
while ((c = getc(cmd)) != EOF) {
bytes++;
putchar(c);
}
fflush(stdout);
fclose(cmd);
c = reap_subproc(pid);
if (errno) {
fprintf(stderr, "%s: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
if (WIFEXITED(c) && !WEXITSTATUS(c)) {
fprintf(stderr, "%s: %lu bytes; success.\n", argv[1], bytes);
return 0;
}
if (WIFEXITED(c)) {
fprintf(stderr, "%s: %lu bytes; failed (exit status %d)\n", argv[1], bytes, WEXITSTATUS(c));
return WEXITSTATUS(c);
}
if (WIFSIGNALED(c)) {
fprintf(stderr, "%s: %lu bytes; killed by signal %d (%s)\n", argv[1], bytes, WTERMSIG(c), strsignal(WTERMSIG(c)));
return 128 + WTERMSIG(c);
}
fprintf(stderr, "%s: %lu bytes; child lost.\n", argv[1], bytes);
return EXIT_FAILURE;
}
gcc -Wall -Wextra -O2 example.c -o example
./example date
./example ./example date -u
./example /bin/sh -c 'date | tr A-Za-z a-zA-Z'