使用在这里找到的建议:Restoring stdout after using dup我试图恢复标准输入和标准输出。但是,当使用printf检查stdout是否已恢复时,我无法读取输出。代码如下。将stdout恢复为1后,我尝试打印“完成”。
#include <stdio.h>
#include <unistd.h>
void main(int argv, char *argc) {
int stdin_copy = dup(STDIN_FILENO);
int stdout_copy = dup(STDOUT_FILENO);
int testpipe[2];
pipe(testpipe);
int PID = fork();
if (PID == 0) {
dup2(testpipe[0], 0);
close(testpipe[1]);
execl("./multby", "multby", "3", NULL);// Just multiplies argument 3 with the number in testpipe.
} else {
dup2(testpipe[1], 1);
close(testpipe[0]);
printf("5");
fclose(stdout);
close(testpipe[1]);
char initialval[100];
read(testpipe[0], initialval, 100);
fprintf(stderr, "initial value: %s\n", initialval);
wait(NULL);
dup2(stdin_copy, 0);
dup2(stdout_copy, 1);
printf("done");//was not displayed when I run code.
}
}
但是,我在运行代码时没有看到“完成”。 (应该在15点之后完成)。这是我的输出:初始值:执行成功a:3b:5多人成功15
还原标准输出时我做错了什么?
您正在调用fclose(stdout);
,它将关闭stdout FILE
对象以及STDOUT_FILELO
– stdout文件描述符(它们是两个不同的东西,链接在一起)。因此,当您稍后调用dup2(stdout_copy, 1);
时,将还原文件描述符,但是FILE
对象保持关闭状态,因此您无法使用它来输出任何内容。无法重新打开FILE
对象以引用文件描述符1,因此最好的选择是删除fclose
行。 dup2
将关闭您要替换的文件描述符(因此您确实不需要单独关闭),并且应该能够看到输出
1 在某些系统上,可以将freopen
与/ dev / fd一起使用,但是/ dev / fd是不可移植的
重申我之前在其他答案中所说的。
您在子进程中没有关闭足够的文件描述符。
拇指法则:如果您dup2()
管道的一端到标准输入或标准输出,同时关闭两个由返回的原始文件描述符dup2()
尽快地。特别是,在使用任何pipe()
功能系列。
如果您将描述符与pipe()
要么exec*()
用exec*()
或dup()
。
如果父进程不会通过以下方式与其任何子进程通信,管道,必须确保尽早关闭管道的两端足够(例如在等待之前),以便其子代可以接收EOF指示在读取(或获取SIGPIPE信号或在写),而不是无限期地阻止。即使父级在不使用dup()
的情况下使用管道,也应通常至少会关闭管道的一端,这种情况极少发生用于在单个管道的两端进行读写的程序。
请注意,fcntl()
选项fcntl()
,并且F_DUPFD
的F_DUPFD_CLOEXEC
和dup2()
选项也可以进入讨论。
如果您使用O_CLOEXEC
及其广泛的支持功能系列(总共21个功能),您将需要查看如何在生成的过程中关闭文件描述符(open()
,等)。
请注意,使用open()
比使用FD_CLOEXEC
更安全由于多种原因。一种是,如果您要强制文件描述符大于通常,F_DUPFD_CLOEXEC
是唯一可行的方法。另一个是如果fcntl()
与posix_spawn()
相同(例如,两个posix_spawn()
),则posix_spawn_file_actions_addclose()
正确处理它(在复制posix_spawn_file_actions_addclose()
之前不会关闭dup2(a, b)
)而单独的close(b); dup(a);
和dup2()
严重失败。这是不太可能的,但不是不可能的情况。
问题包含代码:
a
由于没有使用命令行参数,因此b
行应为0
。您还具有与常规约定相反的名称dup2()
和b
-第一个参数通常称为a
(参数计数),第二个参数通常称为close()
(参数向量)。此外,第二个参数的类型应为dup()
(如果您想使所有普通读者感到困惑,则应为#include <stdio.h>
#include <unistd.h>
void main(int argv, char *argc) {
int stdin_copy = dup(STDIN_FILENO);
int stdout_copy = dup(STDOUT_FILENO);
int testpipe[2];
pipe(testpipe);
int PID = fork();
if (PID == 0) {
dup2(testpipe[0], 0);
close(testpipe[1]);
execl("./multby", "multby", "3", NULL);// Just multiplies argument 3 with the number in testpipe.
} else {
dup2(testpipe[1], 1);
close(testpipe[0]);
printf("5");
fclose(stdout);
close(testpipe[1]);
char initialval[100];
read(testpipe[0], initialval, 100);
fprintf(stderr, "initial value: %s\n", initialval);
wait(NULL);
dup2(stdin_copy, 0);
dup2(stdout_copy, 1);
printf("done");//was not displayed when I run code.
}
}
)。另请参见void main(int argv, char *argc) {
下一个值得讨论的代码块是:
int main(void)
这违反了经验法则。您还应该在argv
之后放置错误处理代码。
argc
下一个要分析的代码块是:
argc
[理论上,您应该使用argv
而不是char **argv
,但是我对char **argc
的使用持相当同情的态度(尤其是因为当我第一次学习C时,没有这样的符号常量)。您实际上确实关闭了管道的两端,但是我更希望看到在What should main()
return in C and C++?调用之后两个管道都立即关闭,这符合经验法则。不带换行符的main()
确实可以通过管道发送任何内容。它将 if (PID == 0) {
dup2(testpipe[0], 0);
close(testpipe[1]);
execl("./multby", "multby", "3", NULL);// Just multiplies argument 3 with the number in testpipe.
}
隐藏在I / O缓冲区中。
正如在他们的execl()
中标识的if (PID == 0)
{
dup2(testpipe[0], STDIN_FILENO);
close(testpipe[0]);
close(testpipe[1]);
execl("./multby", "multby", "3", (char *)NULL);
fprintf(stderr, "failed to execute ./multby\n");
exit(EXIT_FAILURE);
}
一样,dup2(testpipe[1], 1);
close(testpipe[0]);
printf("5");
fclose(stdout);
close(testpipe[1]);
调用带来了很多麻烦。您可能应该简单地将其替换为STDOUT_FILENO
。
继续:
1
您没有检查1
是否有效。没有它因EBADF而失败,因为在此之上您使用了dup2()
。在printf()
上打印的内容有一个未初始化的字符串-不好。实际上,如果要可靠地从孩子那里读取信息,则需要两个管道,一个用于父子之间的通信,另一个用于子对父母的通信。否则,不能保证父母不会读它写的东西。如果您等孩子死后才读书,那么孩子工作的机会就很大,但您不能总是依靠这样做。
两个5
调用中的第一个是没有意义的;您没有更改标准输入的重定向(因此实际上Chris Dodd是不必要的)。第二个更改标准输出文件描述符的分配,但是您已经关闭了answer,因此没有简单的方法可以重新打开它以使fclose(stdout)
正常工作。该消息应以换行符结尾-大多数fflush(stdout)
格式的字符串应以换行符结尾,除非有意将其用于构建输出的零碎单行。但是,如果您注意返回值,您会发现它失败(char initialval[100];
read(testpipe[0], initialval, 100);
fprintf(stderr, "initial value: %s\n", initialval);
wait(NULL);
dup2(stdin_copy, 0);
dup2(stdout_copy, 1);
printf("done");
),并且很有可能再次找到read()
。
为close(testpipe[0]);
提供此代码:
stderr
和此代码(作为dup2()
,编译为stdin_copy
):
stdout
这种组合几乎行不通-这不是一个弹性的解决方案。例如,我在printf()
之后添加了换行符。孩子在printf()
之后等待另一个字符来确定它已经完成了对数字的读取。它没有获得EOF,因为它已打开管道的写端以将响应发送给父级,即使它已挂起从管道读取,因此也永远不会对其进行写操作。但是因为它只尝试读取一个数字,所以可以。
输出为:
-1
如果要处理任意数量的数字,则需要使用两个管道-这是完成任务的唯一可靠方法。当然,这也适用于传递给孩子的单个数字。
这里是修改后的errno == EBADF
,会在阅读时循环播放:
multby.c
这是经过修改的#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
if (argc != 2)
{
fprintf(stderr, "Usage; %s number", argv[0]);
return 1;
}
int multiplier = atoi(argv[1]);
int number;
if (scanf("%d", &number) == 1)
printf("%d\n", number * multiplier);
else
fprintf(stderr, "%s: failed to read a number from standard input\n", argv[0]);
return 0;
}
,它使用两个管道并将3个数字写入子级,并返回三个结果。请注意,该组织无需在第三个数字之后插入换行符(尽管如果包含换行符也不会造成任何危害)。另外,如果您不满意,则数字列表中的第二个空格也是不必要的; pipe23.c
不是第二个数字的一部分,因此扫描在pipe23
之后停止。
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
int stdout_copy = dup(STDOUT_FILENO);
int testpipe[2];
pipe(testpipe);
int PID = fork();
if (PID == 0)
{
dup2(testpipe[0], 0);
dup2(testpipe[1], 1);
close(testpipe[1]);
close(testpipe[0]);
execl("./multby", "multby", "3", NULL);// Just multiplies argument 3 with the number in testpipe.
fprintf(stderr, "failed to execute ./multby (%d: %s)\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
else
{
dup2(testpipe[1], 1);
close(testpipe[1]);
printf("5\n");
fflush(stdout);
close(1);
wait(NULL);
char initialval[100];
read(testpipe[0], initialval, 100);
close(testpipe[0]);
fprintf(stderr, "initial value: [%s]\n", initialval);
dup2(stdout_copy, 1);
printf("done\n");
}
}
[请注意,那里有很多对5
的调用-处理两个管道所涉及的4个描述符中的每个都有两个。这是正常的。不小心关闭文件描述符很容易导致系统挂起。
运行此5
的输出是这个,这就是我想要的:
initial value: [15
]
done