所以我知道printf()
的级别高于write()
,最终会使用write()
。缓存Printf()
,write()
进行系统调用。
示例1,如果我在printf()
之前运行write()
的程序,那么它会在值printf()
之前输出write()
的值。
示例2,如果我要运行相同的程序并让它通过输出重定向到文件中,则write()
的值在printf()
之前输出。
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("This is a printf test\n");
write(STDOUT_FILENO, "This is a write test\n", 21);
return 0;
}
我不明白这里发生了什么。在示例1中,程序是否在运行printf()
之前等待write()
输出?在示例2中,程序是否重定向准备好的第一个输出?因为write()
是较低级别,并且不需要像printf()
那样缓冲,那么它会先打印出来吗?
答案 0 :(得分:6)
你回答了自己的问题。
printf
已缓存且write
未缓存。
为了输出到终端,C stdio系统有一个功能,即只要看到换行符'\n'
就会刷新缓冲区。有关stdio缓冲的更多信息,请查看setvbuf
的文档。
但是当输出到文件以提高速度时,stdio系统不会刷新缓冲区。这就是首先出现write
输出的原因。
以下是在我的Linux系统上运行的一些strace
输出:
fstat(1,{st_mode = S_IFCHR | 0620,st_rdev = makedev(136,1),...})= 0
mmap(NULL,4096,PROT_READ | PROT_WRITE,MAP_PRIVATE | MAP_ANONYMOUS,-1,0)= 0x7f7880b41000
写(1,“这是一个printf测试\ n”,22)= 22
write(1,“这是写测试\ n \ 0”,22)= 22
fstat
是stdio系统检测到连接的输出文件描述符1的类型的地方。我相信它会查看st_mode
并看到它是一个角色设备。磁盘文件是块设备。 mmap
是stdio缓冲区的内存分配,即4K。然后写入输出。
答案 1 :(得分:2)
这与C标准库完成的输出缓冲有关。在第一种情况下,由于你在终端上写,libc完成的缓冲是面向行的(每次回车都强制刷新),立即在屏幕上显示文本,特权交互性能(不应该是一个问题,因为预计终端不会成为负载文本的目标。因此,printf
输出会立即写入一些write
调用,该调用将在您明确发出的下一个调用之前发生。
在第二种情况下,libc检测到你正在写一个文件,因此,为了提高性能,它启用了缓冲;因此,通常第一个printf
将不会立即提交,并且您的write
将在libc实际刷新缓冲区之前发生。
同样,这就是通常发生的事情。我不认为这种行为是由任何标准强制实施的( 编辑: 实际上,这是由C99强制执行的,请参阅@ Jonathan的评论)(和在第二个示例中,即使启用了缓冲,库也可能决定执行刷新,例如,如果缓冲区被printf
填充。
答案 2 :(得分:0)
内部printf
在其缓冲区已满时将使用write
。如果它检测到它正在写入交互式输出(例如尚未重定向的stdout),它也可能在缓冲区已满之前执行写入。