为什么将输出写入正在缓冲的管道
Why is output being written to a pipe being buffered
我正在编写一个程序,其中父进程需要能够与另一个子进程进行通信,因此我编写了一个将子进程的标准输出和输入重定向到管道的函数。
int create_child_piped_io(int* pipe_in, int* pipe_out, const char* program, char* argv[], char* envp[])
{
int stdin_pipe[2];
int stdout_pipe[2];
int child;
if(pipe(stdin_pipe) < 0)
{
return -1;
}
if(pipe(stdout_pipe) < 0)
{
close(stdin_pipe[0]);
close(stdin_pipe[1]);
}
child = fork();
if(child == 0)
{
close(stdin_pipe[1]);
close(stdout_pipe[0]);
if(dup2(stdin_pipe[0], STDIN_FILENO) < 0)
{
close(stdin_pipe[0]);
close(stdout_pipe[1]);
exit(errno);
}
if(dup2(stdout_pipe[1], STDOUT_FILENO) < 0)
{
close(stdin_pipe[0]);
close(stdout_pipe[1]);
exit(errno);
}
close(stdin_pipe[0]);
close(stdout_pipe[1]);
execve(program, argv, envp);
exit(1);
}
else if(child > 0)
{
close(stdin_pipe[0]);
close(stdout_pipe[1]);
*pipe_in = stdin_pipe[1];
*pipe_out = stdout_pipe[0];
return child;
}
else
{
close(stdin_pipe[0]);
close(stdin_pipe[1]);
close(stdout_pipe[0]);
close(stdout_pipe[1]);
exit(errno);
}
return child;
}
int main()
{
int input, output;
char* argv[] = {"program"};
char* envp[] = {NULL};
int cpid = create_child_piped_io(&input, &output, "/home/jaden/dev/c++/pipes/program", argv, envp);
char c;
while(read(output, &c, 1) == 1)
{
printf("%c", c);
}
printf("done\n");
int status;
waitpid(cpid, &status, 0);
close(input);
close(output);
}
这工作正常,但我注意到当从子进程写入标准输出时,它不会立即发送到管道。这是子进程将 运行.
的程序
int main()
{
// i < 1025 gets sent to pipe after sleep(10)
// i > 1025 send first 1024 bytes to pipe immediately
for(int i = 0; i < 1024; i++)
{
write(1, "a", 1);
}
sleep(10);
return 0;
}
输出只会在子进程结束后发送到管道。我尝试发送不同数量的数据。结果每次我向 stdout 写入超过 1024 时,它都会被发送到管道。这让我相信它正在被缓冲。我不明白为什么会这样,因为管道也是内存中的缓冲区,所以它只是缓冲缓冲区。如果这是标准行为,有什么办法可以将其关闭,因为我想在写入数据后立即获取数据。
我正在使用 linux mint 20.3。
你的问题在这里:
while(read(output, &c, 1) == 1)
{
printf("%c", c);
}
当您使用系统调用读取时,您正在使用连接到终端的 printf()
通过 glibc stdio 进行写入。输出为 Line-Buffered。如果您将代码更改为也使用系统调用编写,例如
while(read(output, &c, 1) == 1)
{
write (1, &c, 1);
}
神秘的缓冲消失了。
作为@ShadowRanger points out, if you need the formatting provided by the stdio.h
functions, then you can use setvbuf()
to change the normal line-buffering of stdout
when connected to a terminal to no buffering with. e.g. setvbuf(stdout, NULL, _IONBF, 0);
This will enable output through printf()
, etc.. to be used in an unbuffered manner. See man 3 setbuf
我正在编写一个程序,其中父进程需要能够与另一个子进程进行通信,因此我编写了一个将子进程的标准输出和输入重定向到管道的函数。
int create_child_piped_io(int* pipe_in, int* pipe_out, const char* program, char* argv[], char* envp[])
{
int stdin_pipe[2];
int stdout_pipe[2];
int child;
if(pipe(stdin_pipe) < 0)
{
return -1;
}
if(pipe(stdout_pipe) < 0)
{
close(stdin_pipe[0]);
close(stdin_pipe[1]);
}
child = fork();
if(child == 0)
{
close(stdin_pipe[1]);
close(stdout_pipe[0]);
if(dup2(stdin_pipe[0], STDIN_FILENO) < 0)
{
close(stdin_pipe[0]);
close(stdout_pipe[1]);
exit(errno);
}
if(dup2(stdout_pipe[1], STDOUT_FILENO) < 0)
{
close(stdin_pipe[0]);
close(stdout_pipe[1]);
exit(errno);
}
close(stdin_pipe[0]);
close(stdout_pipe[1]);
execve(program, argv, envp);
exit(1);
}
else if(child > 0)
{
close(stdin_pipe[0]);
close(stdout_pipe[1]);
*pipe_in = stdin_pipe[1];
*pipe_out = stdout_pipe[0];
return child;
}
else
{
close(stdin_pipe[0]);
close(stdin_pipe[1]);
close(stdout_pipe[0]);
close(stdout_pipe[1]);
exit(errno);
}
return child;
}
int main()
{
int input, output;
char* argv[] = {"program"};
char* envp[] = {NULL};
int cpid = create_child_piped_io(&input, &output, "/home/jaden/dev/c++/pipes/program", argv, envp);
char c;
while(read(output, &c, 1) == 1)
{
printf("%c", c);
}
printf("done\n");
int status;
waitpid(cpid, &status, 0);
close(input);
close(output);
}
这工作正常,但我注意到当从子进程写入标准输出时,它不会立即发送到管道。这是子进程将 运行.
的程序int main()
{
// i < 1025 gets sent to pipe after sleep(10)
// i > 1025 send first 1024 bytes to pipe immediately
for(int i = 0; i < 1024; i++)
{
write(1, "a", 1);
}
sleep(10);
return 0;
}
输出只会在子进程结束后发送到管道。我尝试发送不同数量的数据。结果每次我向 stdout 写入超过 1024 时,它都会被发送到管道。这让我相信它正在被缓冲。我不明白为什么会这样,因为管道也是内存中的缓冲区,所以它只是缓冲缓冲区。如果这是标准行为,有什么办法可以将其关闭,因为我想在写入数据后立即获取数据。
我正在使用 linux mint 20.3。
你的问题在这里:
while(read(output, &c, 1) == 1)
{
printf("%c", c);
}
当您使用系统调用读取时,您正在使用连接到终端的 printf()
通过 glibc stdio 进行写入。输出为 Line-Buffered。如果您将代码更改为也使用系统调用编写,例如
while(read(output, &c, 1) == 1)
{
write (1, &c, 1);
}
神秘的缓冲消失了。
作为@ShadowRanger points out, if you need the formatting provided by the stdio.h
functions, then you can use setvbuf()
to change the normal line-buffering of stdout
when connected to a terminal to no buffering with. e.g. setvbuf(stdout, NULL, _IONBF, 0);
This will enable output through printf()
, etc.. to be used in an unbuffered manner. See man 3 setbuf