我正在尝试了解Linux中的重定向,我发现这个代码创建了一个子进程并重定向输入/输出。但在这种情况下,我无法理解dup和dup2正在做什么。我知道dup使用编号最小的未使用描述符作为新描述符。任何人都可以解释这段代码如何帮助多重定向?
int run_child(char *progname, char *argv[], int child_stdin, int child_stdout, int child_stderr)
{
int child;
if ((child = fork()))
{
return child;
}
if (child_stdout == STDIN_FILENO)
{
child_stdout = dup(child_stdout);
RC_CHECK(child_stdout >= 0);
}
while (child_stderr == STDIN_FILENO || child_stderr == STDOUT_FILENO)
{
child_stderr = dup(child_stderr);
RC_CHECK(child_stderr >= 0);
}
child_stdin = dup2(child_stdin, STDIN_FILENO);
RC_CHECK(child_stdin == STDIN_FILENO);
child_stdout = dup2(child_stdout, STDOUT_FILENO);
RC_CHECK(child_stdout == STDOUT_FILENO);
child_stderr = dup2(child_stderr, STDERR_FILENO);
RC_CHECK(child_stderr == STDERR_FILENO);
execvp(progname, argv);
}
答案 0 :(得分:1)
我认为代码正在尝试,但有时会失败,以解决以下情况:
0
,1
和{{ 1}}被关闭了。有几个子场景:
2
已分配到child_stdout
(0
)。可以想象STDIN_FILENO
被分配到child_stderr
(1
)。STDOUT_FILENO
被分配到child_stderr
或0
- 可能意味着1
没有被分配到其中任何一个。child_stdout
和child_stdout
都未分配到child_stderr
,0
或1
。代码叉;父代码立即返回 - 这一切都是干净的。它在失败时返回2
,在成功时返回PID。
对于子方案1,第一个条件运行-1
,它将dup()
的赋值更改为可用的child_stdout
之后的第一个未打开的文件描述符。这可能是0
或1
或更大的数字。
它会丢弃有关2
最初的信息。
然后,循环会尝试确保child_stdout
不是child_stderr
或0
,再次丢弃有关原始内容的信息。
下一个序列三个调用可确保当前1
中的文件描述符复制到child_stdin
,STDIN_FILENO
复制到child_stdout
,STDOUT_FILENO
}被复制到child_stderr
。由于STDERR_FILENO
没有关闭原始文件描述符,如果它与要复制的文件描述符相同(但不是这样),则最终会出现合理的连接。
但是,在正常情况下,输入参数是(例如)dup2()
,3
和5
,代码不能确保这些参数被关闭。
这是一个错误。
然后代码继续使用7
执行命令。它不处理失败的情况;它简单地从函数的末尾掉落,导致未定义的行为,因为该函数应该返回一个值。如果execvp()
函数系列中的任何函数返回,则表示失败。代码应该报告错误消息并退出,可能使用exec*()
或者使用“快速退出”(exit()
,_exit()
或类似的东西)。
如何解决?
我认为代码应该保留子文件描述符的原始值,并准备好在_Exit()
序列后关闭它们,如果它们超出范围dup2()
,{ {1}},0
。除此之外,代码可能处理大多数情况。
请注意,重定向标准错误后报告错误是非常困难的。下面的代码简单地写出了当前标准错误,这是最好的,除非你再次使用1
或类似的系统。
2
这不是简单的代码;你的思绪被各种可能性所摧毁。 (我已经删除了各种特殊情况,在进一步检查时,在编写我的分析时结果并不特别。)我不相信用syslog
修复的预检是值得的; #include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define RC_CHECK(test) assert((test) != 0)
int run_child(char *progname, char *argv[], int child_stdin, int child_stdout, int child_stderr);
int run_child(char *progname, char *argv[], int child_stdin, int child_stdout, int child_stderr)
{
int child;
if ((child = fork()))
{
return child;
}
int fd[3] = { child_stdin, child_stdout, child_stderr };
if (child_stdout == STDIN_FILENO)
{
child_stdout = dup(child_stdout);
RC_CHECK(child_stdout >= 0);
}
while (child_stderr == STDIN_FILENO || child_stderr == STDOUT_FILENO)
{
child_stderr = dup(child_stderr);
RC_CHECK(child_stderr >= 0);
}
child_stdin = dup2(child_stdin, STDIN_FILENO);
RC_CHECK(child_stdin == STDIN_FILENO);
child_stdout = dup2(child_stdout, STDOUT_FILENO);
RC_CHECK(child_stdout == STDOUT_FILENO);
child_stderr = dup2(child_stderr, STDERR_FILENO);
RC_CHECK(child_stderr == STDERR_FILENO);
for (int i = 0; i < 3; i++)
{
if (fd[i] != STDIN_FILENO && fd[i] != STDOUT_FILENO && fd[i] != STDERR_FILENO)
close(fd[i]);
}
execvp(progname, argv);
/* Or: fprintf(stderr, "Failed to execute program %s\n", progname); */
char *msg[] = { "Failed to execute program ", progname, "\n" };
enum { NUM_MSG = sizeof(msg) / sizeof(msg[0]) };
for (int i = 0; i < NUM_MSG; i++)
write(2, msg[i], strlen(msg[i]));
exit(1); /* Or an alternative status such as 126 or 127 based on errno */
}
的作者可以简单地规定所有三个文件描述符dup()
,run_child()
和0
都是打开的,以便所有子文件描述符参数都大于{{1并简单地继续I / O重定向。当然,仍然需要关闭传递给函数的文件描述符。