【Linux】再谈文件描述符之缓冲区

因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访问的。所以C库当中的FILE结构体内部,必定封装了fd。

往标准输出写入有三种方法:

  • printf(str1);//C库函数
  • fwrite(str2,1,strlen(str2),stdout);//C库函数
  • write(1,str3,strlen(str3));//系统调用

来段代码研究下:

运行出结果:

但是如果对进程实现输出重定向呢?结果此时变成了:

我们发现 printf 和 fwrite (库函数)都输出了2次,而 write 只输出了一次(系统调用)。为什么呢?肯定和fork有关!先来了解下缓冲区。

 

缓冲方式有三种

  • 无缓冲  (write)
  • 行缓冲(写入显示器):遇到 \n 或者缓冲区写满就刷新,执行真正的IO操作(printf)
  • 全缓冲(写入硬盘):碰到1或者缓冲区写满才刷新(fwrite)
  • 一般C库函数写入文件时是全缓冲的,而写入显示器是行缓冲。
  • printf、fwrite 库函数会自带缓冲区(进度条例子就可以说明),当发生重定向到普通文件时,数据的缓冲方式由行缓冲变成了全缓冲。
  • 而我们放在缓冲区中的数据,就不会被立即刷新,甚至fork之后
  • 进程退出之后,会统一刷新,写入文件当中。
  • 但是fork的时候,父子数据会发生写时拷贝,所以当你父进程准备刷新的时候,子进程也就有了同样的一份数据,随即产生两份数据。
  • write 没有变化,说明没有所谓的缓冲。

 此时再来说说为什么 printf 和 fwrite (库函数)都输出了2次,而 write 只输出了一次(系统调用)?

解析:因为在fork之前调用write,它的数据只写到标准输出一次,而它没有缓冲区的概念,所以即使文件重定向到文件数据也是直接被输出。fork出的子进程并没有得到父进程的代码数据。而printf和fwrite都是库函数,他们自带缓冲区,并且,重定向后行缓冲变为全缓冲,即使遇到换行符也不输出,直到缓冲区写满,此时fork便有了两份数据。 

 

综上: printf fwrite 库函数会自带缓冲区,而 write 系统调用没有带缓冲区。另外,我们这里所说的缓冲区,
都是用户级缓冲区。其实为了提升整机性能,OS也会提供相关内核级缓冲区,不过不再我们讨论范围之内。
那这个缓冲区谁提供呢? printf fwrite 是库函数, write 是系统调用,库函数在系统调用的“上层”, 是对系统
调用的“封装”,但是 write 没有缓冲区,而 printf fwrite 有,足以说明,该缓冲区是二次加上的,又因为是
C,所以由C标准库提供。

 

猜你喜欢

转载自blog.csdn.net/Miss_Monster/article/details/86479145