任何不同位置的数据IO传输,一定是有缓冲区的,然后缓冲区再通过他自身特定的刷新策略,将数据刷新到外设中,这样合理的安排相比不断的循环检测,有利于节省CPU的资源.一般发出数据就是将数据写入到特有的缓冲区中,例如对于同样大的100Mb数据,如果没有缓冲区策略,那么他这100M数据可能会被分好多次写入到外设中。要知道计算机访问外设需要很长时间的,所以如果将这100M数据存到缓冲区中访问,一次外设全部写入,那么将会大大提高计算机的IO效率,而刷新缓冲区则有多种策略,第一种策略便是立即刷新,就是一旦有写入便就刷新到外设中。第二种是行刷新:就是写入时检测,如果有换行符便开始刷新,第三种就是全缓冲刷新:也就是当缓冲区被写满以后再进行刷新,这三种刷新方式并没有优劣之分,需要具体考虑应用场景来选择合适的缓冲区刷新策略.
对于一种场景,我们使用一个C语言程序,其中写入pintf打印一串字符串,然后再用write系统调用,同样打印一份不一样的字符串到屏幕上,在使用一次fork创建一次子进程,然后我们编译他使用命令行执行这个程序,我们可以看到显示器中有两行字符串数据,但是如果我们重定向输出数据到一个文件中,我们会发现使用C语言打印的数据在文件中打印两份,这就是因为缓冲区刷新策略的不同,以及缓冲区的位置不同的原因.
如图可以看同一可执行程序在打印到文件中和显示器上的结果不同.
其中的原理是因为C语言中的文件结构体中也有一个单独的缓冲区,而这个缓冲区是位于进程空地址空间中的,当使用wite时,因为它是系统调用的写入函数,因此不会写入C语言维护的缓冲区中,而printf将数据临时写入c语言的缓冲区中,当向显示器中打印时,采用行缓冲的策略,因为行缓冲的策略符合肉眼的观看习惯,而文件重定向则是使用全缓冲的缓冲区刷新策略。当我们调用fork函数以后,父子进程同时拷贝了一份进程地址空间,当子进程向文件中刷新缓冲区时,会发生写实拷贝父进程的缓冲区不会改变,因此会有printf的内容打印两遍.
而像显示器中打印的时候,因为是行缓冲刷新的策略,当遇到换行符时,会直接将缓冲区中的内容打印到显示器上,因此进程地址空间不会有东西,所以就算调用了创建子进程也不会发生写时拷贝将内容写两遍.解决上述将可执行程序的输出重定向文件中打印多了的情况的方法是使用fflush强制刷新缓冲区使子进程的地址空间缓冲区中不要有数据.
如上图当加入了fflush后不会出现重定向后多打印的问题.
需要注意的是,就算调用了系统调用的write也不会立即将数据写入到文件中,因为操作系统中自身也有一个文件操作的缓冲区,这个缓冲区对C语言而来说是透明的,所以一个printf需要透过好几个缓冲区才能被正确打印到显示器上,系统调用也有一个叫sfync的接口,强制将操作系统的缓冲区数据打印出来.
标签:操作系统,打印,写入,C语言,刷新,缓冲区,数据 From: https://www.cnblogs.com/qjwxlj/p/18227396