stdio库函数与系统调用(open, write, read)的区别

stdio库函数与系统调用在缓冲区上的区别

stdio库函数有自己的数据缓冲区来减少系统调用,这个缓冲区位于用户态内存区,而系统调用对应的是内核态内存区的内核缓冲区高速缓存,具体可见下图:

缓冲小结.PNG
  • 可以用setvbuf函数和_IONBF_IOLBF_IOFBF来控制stdio库函数的缓冲设置,用fflush函数来强制刷新stdio库函数缓冲区中的数据到内核缓冲区。
  • 可以用fsync, fdatasync函数来控制内核缓冲区设置,用sync函数来刷新内核缓冲区数据到磁盘。
  • 可以用O_SYNCO_DSYNCO_SYNC标志在open函数时直接要求刷新文件数据和文件元数据。
  • 刷新同步的两种模式:
    1. synchronized I/O data integrity completion: 更新文件数据和所需的更新的文件元数据
    2. 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.

此时,标准输出发往终端,属于行缓冲,所以不加\nprintf的输出保留在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, 

可以看到不论是否加\nprintf函数输出都在write函数后,这是因为当标准输出发送到磁盘文件时,stdio属于块缓冲,即使检测到\n也不会刷新stdio缓冲区到内核缓冲区,都是到程序结束才刷新,所以write的数据在前printf数据在后。

当然,这2个行缓冲和快缓冲可以用setvbuf函数来改变。

你可能感兴趣的:(stdio库函数与系统调用(open, write, read)的区别)