c - 管道命令./a.out | cat, printf("line\n") in a.out 未执行且无输出
问题描述
一个很奇怪的事情,a.out 只是printf()
一行然后进入死循环,当 a.out 单独执行时,我可以在 termial 中看到该行,但是如果管道 a.out 与 cat ,那么我们什么都看不到.
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
printf("----------\n");
while (1) {
sleep(1000);
}
return 0;
}
运行结果
$ cc test.c
$ ./a.out
----------
^C
$ ./a.out | cat
^C
如果我 strace a.out | cat, write(1) 系统调用未被调用
$ strace ./a.out | cat
execve("./a.out", ["./a.out"], 0x7ffdaa23b200 /* 65 vars */) = 0
brk(NULL) = 0x5567446dd000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=94391, ...}) = 0
mmap(NULL, 94391, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f977ba22000
close(3) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200l\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2000480, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f977ba20000
mmap(NULL, 2008696, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f977b835000
mmap(0x7f977b85a000, 1519616, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7f977b85a000
mmap(0x7f977b9cd000, 299008, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x198000) = 0x7f977b9cd000
mmap(0x7f977ba16000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e0000) = 0x7f977ba16000
mmap(0x7f977ba1c000, 13944, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f977ba1c000
close(3) = 0
arch_prctl(ARCH_SET_FS, 0x7f977ba21500) = 0
mprotect(0x7f977ba16000, 12288, PROT_READ) = 0
mprotect(0x556743cd8000, 4096, PROT_READ) = 0
mprotect(0x7f977ba64000, 4096, PROT_READ) = 0
munmap(0x7f977ba22000, 94391) = 0
fstat(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
brk(NULL) = 0x5567446dd000
brk(0x5567446fe000) = 0x5567446fe000
nanosleep({tv_sec=1000, tv_nsec=0}, ^C{tv_sec=994, tv_nsec=769383373}) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
strace: Process 5050 detached
如果我 strace a.out 单,那么有 write(1)
$ strace ./a.out
execve("./a.out", ["./a.out"], 0x7ffe09a7c360 /* 65 vars */) = 0
brk(NULL) = 0x564b085a8000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=94391, ...}) = 0
mmap(NULL, 94391, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fac37df5000
close(3) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200l\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2000480, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fac37df3000
mmap(NULL, 2008696, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fac37c08000
mmap(0x7fac37c2d000, 1519616, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7fac37c2d000
mmap(0x7fac37da0000, 299008, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x198000) = 0x7fac37da0000
mmap(0x7fac37de9000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e0000) = 0x7fac37de9000
mmap(0x7fac37def000, 13944, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fac37def000
close(3) = 0
arch_prctl(ARCH_SET_FS, 0x7fac37df4500) = 0
mprotect(0x7fac37de9000, 12288, PROT_READ) = 0
mprotect(0x564b076e3000, 4096, PROT_READ) = 0
mprotect(0x7fac37e37000, 4096, PROT_READ) = 0
munmap(0x7fac37df5000, 94391) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
brk(NULL) = 0x564b085a8000
brk(0x564b085c9000) = 0x564b085c9000
write(1, "----------\n", 11----------
) = 11
nanosleep({tv_sec=100
为什么 ?
解决方案
输出流可以是以下三种不同模式之一:unbuffered、line buffered或full buffered。在无缓冲模式下,立即写入输出。在行缓冲模式下,首先将输出写入内部流缓冲区,直到缓冲区已满或写入换行符,然后将缓冲区刷新到输出。在完全缓冲模式下,首先将输出写入内部流缓冲区,直到缓冲区已满,然后将缓冲区刷新到输出。(某些实现也可能在其他时间刷新输出,例如从交互式设备读取输入时。)
输入流也可以处于相同的三种模式,这决定了读取输入何时可供读取流的函数调用者使用。
该实现在调用函数之前(或至少在对流的任何访问之前)初始化标准输入(stdin
)、标准输出(stdout
)和标准错误输出(stderr
)流的模式。main
在某些情况下,允许实现将标准输入或标准输出初始化为完全缓冲模式。当且仅当实现可以确定它们未链接到交互式设备(例如终端)时,标准输入和输出流才被初始化为完全缓冲模式。(标准错误输出流永远不会初始化为完全缓冲模式。)
通常,POSIX 系统的 C 运行时库将调用isatty
标准输入和输出流的底层文件描述符,如果isatty
返回 0,则将流设置为完全缓冲模式。这发生在main
调用函数之前。
当您运行“./a.out”并将输出发送到终端时,C 运行时库确定输出将发送到交互式设备并且未设置stdout
为完全缓冲模式。它将被设置为其他模式之一,通常是行缓冲模式。但是,当您运行“/a.out”并将输出发送到管道时,C 运行时库会确定输出不会发送到交互式设备并设置为stdout
完全缓冲模式。这就是为什么输出不会立即写入管道的原因。
有两种方法可以解决您的问题。第一个是在第一次调用之前将标准输出流更改为行缓冲模式或非缓冲printf
模式:
setvbuf(stdout, NULL, _IOLBF, 0); // set standard output to line buffered mode
setvbuf(stdout, NULL, _IONBF, 0); // set standard output to unbuffered mode
另一种方法是按需刷新标准输出:
fflush(stdout); // write buffered standard output contents
推荐阅读
- python - 在 discord.py 中获取服务器成员的所有名称
- c - GSL 中是否有内置的广义特征值函数?
- amazon-web-services - 如何安排 AWS lambda cron 在月底前 7 天运行?
- awk - 连接变量和字符串(awk,macos 版本)
- java - 使用 Java 中的 Flickr API 上传图片
- excel - 如何在 VBA 中单击更改形状颜色?
- r - 如何打破ggplot条形图的Y轴
- reactjs - 在 Promise React 中更改接收到的数据
- javascript - 如何更新 mongoose 中 createdAt 字段的 expires 属性以避免文档被删除?
- javascript - 如何从 URL 动态导入 javascript 数组?