FILE * "/dev/stdout" 和标准输出之间的区别
Difference between FILE * "/dev/stdout" and stdout
我们来看看这个Hello World程序
#include <stdio.h>
int main(int argc, char ** argv) {
printf("Hello, World!");
const char* sFile = "/dev/stdout"; // or /proc/self/fd/0
const char* sMode = "w";
FILE * output = fopen(sFile, sMode);
//fflush(stdout) /* forces `correct` order */
putc('!', output); // Use output or stdout from stdio.h
return 0;
}
当使用 output
文件描述符编译时,输出为:
!Hello, World!
当使用 stdio.h
提供的 stdout
文件描述符编译时,输出符合预期:
Hello, World!!
我想当用后者调用 putc
时,它将 直接打印 到 stdout
并且在 [=22= 上使用文件描述符时] 它将打开一个 pipe 并打印到其中。不过我不确定。
这个行为更有趣,因为它不会覆盖 'Hello' 的第一个字符,而是将自己推入已推入字符串前面的行缓冲区的第一个位置。
从逻辑的角度来看,这是出乎意料的。
谁能解释一下这里到底发生了什么?
我正在使用
cc (Ubuntu 4.8.2-19ubuntu1) 4.8.2
和一个 3.13.0-52 linux 内核编译 w/ gcc 4.8.2
编辑:我已经完成了两个程序的 strace
,这里是重要的部分:
output
(fopen("/dev/stdout", "w")) 没有 fflush(stdout)
场景产生:
...
open("/dev/stdout", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fstat(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f62f21e9000
write(3, "!", 1!) = 1
write(1, "Hello, World!", 13Hello, World!) = 13
exit_group(0) = ?
使用 fflush(stdout)
生成并执行正确的顺序:
...
open("/dev/stdout", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
write(1, "Hello, World!", 13Hello, World!) = 13
fstat(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5ad4557000
write(3, "!", 1!) = 1
exit_group(0) = ?
stdout
(来自stdlib.h)场景产生:
...
write(1, "Hello, World!!", 14Hello, World!!) = 14
exit_group(0) = ?
看来 FILE * output = fopen("/dev/stdout")
流使用了与 stdout
不同的文件描述符
而且看起来 printf
使用 stdout
因此在第三种情况下,字符串在被推送到流之前被组装。
两个流(stdout
和 output
)都被缓冲。在它们被刷新之前,实际上没有任何内容被写入。由于您没有明确地刷新它们,也没有安排它们自动刷新,因此它们只会在关闭时自动刷新。
您也没有明确关闭它们,因此它们被标准库的 on_exit
挂钩关闭(并刷新)。正如 William Pursell 正确指出的那样,未指定缓冲 I/O 流关闭的顺序。
查看 fflush(3)
、fclose(3)
和 setbuf(3)
手册页,了解有关控制刷新输出的时间和方式的更多信息。
/dev/stdout
与 /proc/self/fd/0
不同。事实上,如果您没有足够的权限,您将无法写入 /dev/stdout
,因为 /dev/stdout
不是 Linux 中的任何标准字符设备,并且任何尝试使用"w"
选项将尝试在该目录上创建一个实际的常规文件。您要找的字符设备是/dev/tty
在C语言中,stdout
是一个初始化的FILE *
类型的全局变量,指向标准输出文件,即描述符为1的文件。仅stdout
存在于 C 命名空间中,与任何名为 "stdout"
的实际文件无关
我们来看看这个Hello World程序
#include <stdio.h>
int main(int argc, char ** argv) {
printf("Hello, World!");
const char* sFile = "/dev/stdout"; // or /proc/self/fd/0
const char* sMode = "w";
FILE * output = fopen(sFile, sMode);
//fflush(stdout) /* forces `correct` order */
putc('!', output); // Use output or stdout from stdio.h
return 0;
}
当使用 output
文件描述符编译时,输出为:
!Hello, World!
当使用 stdio.h
提供的 stdout
文件描述符编译时,输出符合预期:
Hello, World!!
我想当用后者调用 putc
时,它将 直接打印 到 stdout
并且在 [=22= 上使用文件描述符时] 它将打开一个 pipe 并打印到其中。不过我不确定。
这个行为更有趣,因为它不会覆盖 'Hello' 的第一个字符,而是将自己推入已推入字符串前面的行缓冲区的第一个位置。
从逻辑的角度来看,这是出乎意料的。
谁能解释一下这里到底发生了什么?
我正在使用
cc (Ubuntu 4.8.2-19ubuntu1) 4.8.2
和一个 3.13.0-52 linux 内核编译 w/ gcc 4.8.2
编辑:我已经完成了两个程序的 strace
,这里是重要的部分:
output
(fopen("/dev/stdout", "w")) 没有 fflush(stdout)
场景产生:
...
open("/dev/stdout", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fstat(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f62f21e9000
write(3, "!", 1!) = 1
write(1, "Hello, World!", 13Hello, World!) = 13
exit_group(0) = ?
使用 fflush(stdout)
生成并执行正确的顺序:
...
open("/dev/stdout", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
write(1, "Hello, World!", 13Hello, World!) = 13
fstat(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5ad4557000
write(3, "!", 1!) = 1
exit_group(0) = ?
stdout
(来自stdlib.h)场景产生:
...
write(1, "Hello, World!!", 14Hello, World!!) = 14
exit_group(0) = ?
看来 FILE * output = fopen("/dev/stdout")
流使用了与 stdout
不同的文件描述符
而且看起来 printf
使用 stdout
因此在第三种情况下,字符串在被推送到流之前被组装。
两个流(stdout
和 output
)都被缓冲。在它们被刷新之前,实际上没有任何内容被写入。由于您没有明确地刷新它们,也没有安排它们自动刷新,因此它们只会在关闭时自动刷新。
您也没有明确关闭它们,因此它们被标准库的 on_exit
挂钩关闭(并刷新)。正如 William Pursell 正确指出的那样,未指定缓冲 I/O 流关闭的顺序。
查看 fflush(3)
、fclose(3)
和 setbuf(3)
手册页,了解有关控制刷新输出的时间和方式的更多信息。
/dev/stdout
与 /proc/self/fd/0
不同。事实上,如果您没有足够的权限,您将无法写入 /dev/stdout
,因为 /dev/stdout
不是 Linux 中的任何标准字符设备,并且任何尝试使用"w"
选项将尝试在该目录上创建一个实际的常规文件。您要找的字符设备是/dev/tty
在C语言中,stdout
是一个初始化的FILE *
类型的全局变量,指向标准输出文件,即描述符为1的文件。仅stdout
存在于 C 命名空间中,与任何名为 "stdout"