【Linux】缓冲区

一、前言

先来看如下代码:

  1 #include <stdio.h>  
  2 #include <string.h>  
  3 #include <unistd.h>
  4 
  5 int main()
  6 {
  7   fprintf(stdout, "hello fprintf\n");
  8 
  9   const char* msg = "hello write\n";
 10   write(1, msg, strlen(msg));
 11 
 12   fork();                                                                                                                               
 13                                                                                                                                      
 14   return 0;                                                                                                                          
 15 }   

运行查看结果:

 直接运行结果符合我们的预期。但是经过重定向之后,却打印了两遍 "hello fprintf" 。这时为什么呢?其实这与 fork();有关。

二、缓冲区

 函数 fprintf 的返回值类型为 FILE* ,在调用 fprintf 函数时,会在内存中 malloc 出一个结构体 FILE 并返回其地址。结构体 FILE 中包含文件描述符与缓冲区。当用户使用 fprintf stdout 打印字符串时,其实只是把字符串放进了C库的缓冲区里,暂时还没有把用户层缓冲区里的数据拷贝到操作系统内核的缓冲区里。C库会结合一定的刷新策略,调用 write 函数将用户层缓冲区中的数据写入给OS。

C库的刷新策略有如下三种:

  1. 无缓冲:数据直接刷新到OS内核
  2. 行缓冲:数据先放到用户层缓冲区中,如果碰到了 '\n' ,就把 '\n' 之前的所有数据刷新到OS 内核。
  3. 全缓冲:数据先放到用户层缓冲区中,如果用户层缓冲区被写满了,就把所有数据刷新到OS 内核。

 一般而言,显示器采用的刷新策略是行缓冲普通文件采用的刷新策略是全缓冲。缓冲区存在的意义是节省调用者的时间。

有了以上知识,就可以很好的理解上面代码为什么重定向后,会出现 "hello fprintf" 打印了两遍的问题。

 当直接运行程序时,默认是向显示器刷新,采取的是行缓冲。遇到 '\n' ,直接把用户缓冲区里 '/n' 之前的内容刷新到了OS中,这就导致在程序最后执行 fork();时,用户缓冲区里已经没有数据了,我们观测到的结果就是正常显示。

 当运行程序并重定向到普通文件时,是向普通文件刷新,采取的是全缓冲。而我们打印的内容还不足以将缓冲区填满,所以到程序最后执行 fork();时,用户缓冲区里的数据仍然没有被刷新,父子进程各自持有一份数据,程序结束时一起刷新到OS中,就打印了两次。

 因为打印 "hello write" 使用的是系统调用 write ,是直接向OS写入数据的,无需用户层刷新,所以不管有没有重定向,都只打印一次。

猜你喜欢

转载自blog.csdn.net/weixin_74078718/article/details/130242522
今日推荐