stdio
库函数与系统调用在缓冲区上的区别
stdio
库函数有自己的数据缓冲区来减少系统调用,这个缓冲区位于用户态内存区,而系统调用对应的是内核态内存区的内核缓冲区高速缓存,具体可见下图:
- 可以用
setvbuf
函数和_IONBF
,_IOLBF
,_IOFBF
来控制stdio
库函数的缓冲设置,用fflush
函数来强制刷新stdio
库函数缓冲区中的数据到内核缓冲区。 - 可以用
fsync
,fdatasync
函数来控制内核缓冲区设置,用sync
函数来刷新内核缓冲区数据到磁盘。 - 可以用
O_SYNC
,O_DSYNC
,O_SYNC
标志在open
函数时直接要求刷新文件数据和文件元数据。 - 刷新同步的两种模式:
- synchronized I/O data integrity completion: 更新文件数据和所需的更新的文件元数据
- synchronized I/O file integrity completion: 更新文件数据和所有发生更新的文件元数据
- 可以用
O_DIRECT
标志绕过缓冲区从用户空间直接将数据传输到磁盘,但是对内存对齐有要求
混用stdio
库函数和系统调用
因为stdio
库函数在系统调用的基础上多了一个自己的缓冲区,stdio
库函数需要等到用户内存区的流缓冲区满的时候才会同步到内核缓冲区,所以在两者混用的时候有可能发生数据位置不一致的问题。
示例:
printf("To man the world is twofold, ");
if (argc > 1)
printf("\n");
write(STDOUT_FILENO, "in accordance with his twofold attitude.\n", 41);
输出:
./mix23io.out
in accordance with his twofold attitude.
To man the world is twofold, root
./mix23io.out 2
To man the world is twofold,
in accordance with his twofold attitude.
此时,标准输出发往终端,属于行缓冲,所以不加\n
时printf
的输出保留在stdio
缓冲区中,而write
的数据直接写入内核缓冲区中,当程序退出时,将stdio
缓冲区中的数据更新到内核缓冲区,所以printf
输出在write
数据后。而加了\n
后,printf
输出一行结束,将stdio
缓冲区更新到内核缓冲区,比write
先放入内核缓冲区,所以显示也在write
数据前。
而如果将标准输出发送到磁盘文件的时候,又会有所不同:
./mix23io.out >> test2.txt
./mix23io.out 2 >> test2.txt
test2.txt:
in accordance with his twofold attitude.
To man the world is twofold, in accordance with his twofold attitude.
To man the world is twofold,
可以看到不论是否加\n
,printf
函数输出都在write
函数后,这是因为当标准输出发送到磁盘文件时,stdio
属于块缓冲,即使检测到\n
也不会刷新stdio
缓冲区到内核缓冲区,都是到程序结束才刷新,所以write
的数据在前printf
数据在后。
当然,这2个行缓冲和快缓冲可以用setvbuf
函数来改变。
网友评论