原文地址:http://blog.sina.com.cn/s/blog_93b45b0f01014qyb.html

 

前多时间梳理了一些Linux上的编程,其实就是认识的大量的系统调用(POSIX)。这里有一个我们经常提出的问题就是fread,read的区别。(当然这两个分别代表了操作文件系统的两套不同的函数,包括open,read, write, seek 等)。我们都知道,他们的区别就是一个(read)是UNIX 中的系统调用,是类UNIX系统,提供给程序员操作文件的接口(要不然你如何操作文件?);而另外一个则是C语言提供的读取文件的函数库,自然这个函数库(ANSI)的实现是以对应的系统调用为基础的。

那么为什么C语言的函数库需要向我们提供这样的包装,而其他的系统调用(像进程、线程管理与通信等等)没有这样的包装呢?

这当然是有原因的,也就是我们常说的有缓冲读写和无缓冲读写。

 

但是,我们进一步学习了UNIX系统中,直接I/O的概念之后,对上面的了解需要更深一步了。

 

为什么要缓冲?

 

我们先不管用户态、核心态这些事情,我们先思考,读取外设上的文件为什么需要缓冲。

(我们知道,一般意义上流的概念已经被使用到了文件读写中,这个概念与缓冲的概念没有关系。)

我们使用函数向某个文件从当前流标签所在位置,读取n个字符。我们也会使用函数想文件的某个流位置写如n字符。但是,当这种写的动作小、而频繁。每次写又必须反映到硬盘上,也就是说需要频繁的操作硬盘,写一些小的更改,这是非常耗损效率。所以自然我们想到了,我们设立一个缓冲区,将那些要写如的数据先写到缓冲区中,当缓冲区满,或者其他情况发生的是否,我们在一起将他们写入到硬盘上。这样可以大大提高应用程序读写文件的速度。

 

这就是为什么需要缓冲,根本原因我认为是磁盘等外设的数据还和内存的速度相差甚远,所以我们不希望由于操作外设的原因让本来很快的内存和CPU跟着一起慢,我们想的办法:一是异步写(但这有时候不能符合应用要求),二就是缓冲读写。

 

而我们通常所理解的就是,read体系,就是那种无缓冲读写,不管是读还是写,调用这个体系的函数,会马上启动一次外设操作,读取数据或者写入数据。而fread体系则,使用了缓冲读写的方式,一定时间才调用read体系的函数。

 

上面的认识,事实上可以一定程度上说明问题的。但是,我们也太低估UNIX系统的智商了,要知道,不只是应用程序需要与外设进行交互,操作系统更是要与外设频繁的交互。(事实上应用程序是通过操作系统与外设交互的,所以就有了用户态和核心态概念)。

 

我们知道,应用程序要读写文件,一般是通过操作系统的(这样才发挥了操作系统管理系统的各项硬软件的功能)。读写的过程并不是直接将外设中的数据,直接往用户应用程序的空间传送(虽然理论上好像可以,内核还不能往用户空间写数据?但是这不符合这种隔离的思想),而是首先由内核发出外设操作,将数据传送到内核空间中(DMA技术),然后从内核传送的用户空间中。这样的好处就是能够隔离开应用程序与硬件设备的直接交流(那会导致应用软件设计的复杂都增长)。但是这也导致了我们通常说的“多拷贝”问题。

 

在操作系统内核的管理下,数据想要从一个文件到另外一个文件,需要经过:

文件1(外设)——>内核空间——>用户空间——>内核空间——文件2(外设)。

这里还是假设用户空间里面该数据没有拷贝动作,我们可以看到,中间的拷贝过程全部发生在内存中,只是分在内核与用户的区别。

 

这种样的过程其实是伤害效率的,但是如果不是对读写效率的要求过于苛刻,仍然使用这种方法。

 

事实上,read体系也有自己的缓冲机制,但是我们可以通过设置(open函数中DIRECT标志),放弃这种方式。

我们可以说,fread是在用户应用程序空间中,自己建立了一个缓冲机制,这种缓冲机制和内核read的缓冲机制并不冲突。

事实上,当我们使用read体系的是否,我们放弃了应用程序空间中的缓冲机制,但是如果不设置DIRECT标志,内核中的缓冲机制依然存在。

 

我们还有一种特殊的读写应用程序,叫着自缓冲读写,其实fread就是这样一种方式。只是它没有放弃内核的缓冲机制。但是对于复杂应用程序,例如数据库,这种对读写效率要求高的地方,他们使用了自己定义缓冲机制并且毅然放弃了内核的缓冲方式,为什么呢?因为内核缓冲方式,对缓冲数据何时应该存入到硬盘上,那些数据应该留下来更久没有应用背景相关的信息,所以它所出来的决策,通常是盲目的。但是,如果应用程序自己设置这种缓冲,可以很大程度上弥补这一问题。

 

 

缓冲I/O绝对有它的好处,这种机制的使用不仅在用户空间中,也在内核实现时会使用。当然有是否我们为了最求某些特别的需求,会使用直接I/O方式。当然事实上,这种I/O方式,与缓冲与否不是对立的。但是往往这样做了之后,缓冲就不够。我们要使用直接I/O要慎重考虑,你应用程序的需求,例如,你需要存取大块数据。这个是否“多拷贝”无疑是一个问题。 其中取舍需要仔细考虑、权衡。

posted on 2013-03-12 13:31  NeilHappy  阅读(4988)  评论(0编辑  收藏  举报