我有一个简单的 C 文件,它使用
printf
和 write(1,..)
函数写入标准输出。
文件:main.c
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("Hello\n");
printf("Mars\n");
fsync(1);
write(1,"Ola\n",4);
write(1,"Earth\n",6);
}
> gcc main.c -o test
当我将输出重定向到文件时,我看到的顺序与终端中的输出不同。看起来,当我使用重定向时
write(..)
比 printf(..)
更早写入,甚至很难,printf()
在代码中更早,并且中间有一个 fsync(..)
。
是否有一些重定向运算符的变体可以确保顺序,或者我做错了什么。
终端输出:
> ./test
Hello
Mars
Ola
Earth
输出到
out
文件:
> ./test >out
# or ./test|tee out
> cat out
Ola
Earth
Hello
Mars
printf
写入C流stdout
,由C标准库例程缓冲并最终写入Unix文件描述符1。
fsync
和 write
对 Unix 文件描述符 1 进行操作。 fsync
刷新文件描述符 1 中的数据,但对 C 标准库流的缓冲区没有影响。
C 标准库 I/O 例程的操作方式有所不同,具体取决于标准输出是发送到终端还是发送到文件。这是在程序启动期间检测到的。当输出发送到终端时,将其发送给用户被认为很重要,并且当将
\n
写入流时,缓冲区将被刷新。这称为 line buffered,默认情况下,到终端的输出设置为 line buffered。
当输出进入文件时,及时刷新它并不重要,因为用户不会立即看到它,并且如果输出完全缓冲,性能会更好。因此,仅当缓冲区已满、流关闭或发出显式刷新请求时才会刷新缓冲区。这称为完全缓冲,默认情况下将输出设置为完全缓冲。
这在 C 2018 7.21.3 中得到了普遍解决,其中第 7 段说:
…当且仅当可以确定该流不引用交互设备时,标准输入和标准输出流才被完全缓冲。
(当输出是终端时,C 实现也可以使用 unbuffered 模式而不是 line buffered,在这种情况下,所有字符在写入后都会立即发送到终端。这是不常见的。)
您可以使用
stdout
明确请求刷新 fflush(stdout)
。
在
stdout
打开之后但在对其执行任何其他操作之前,您可以使用 setvbuf(stdout, NULL, mode, 0)
更改其缓冲方法,其中 mode
是 _IOFBF
、_IOLBF
或 _IONBF
(表示全、行、或分别没有缓冲。 (您还可以传递 char
数组及其大小来代替 NULL
和 0
,库将使用该数组作为缓冲区,之后您不应将该数组用于任何其他目的。如果您传递 NULL
和大小,库可以使用该大小来执行自己的缓冲区分配,但这并不能保证。) setvbuf
返回一个 int
,如果它无法满足请求,则该值可能为非零。
看看 dup2 系统调用:https://www.geeksforgeeks.org/dup-dup2-linux-system-call/
您可以将标准输出/输入重定向到文件