stdio的缓冲区

来源:互联网 发布:数控仿真模拟软件 编辑:程序博客网 时间:2024/06/06 19:11

为什么要有缓冲区(Buffer)

原因为多种,有两个重点:

1.  从内存中读取数据比从文件中读取数据要快得多(这只是一种浅显地说法,但确实能说明问题)。

2.  对文件的读写需要用到open、read、write等系统底层函数,而用户进程每调用一次系统函数都要从用户态切换到系统态,等执行完毕后再返回用户态,这种切换要花费一定时间成本(对于高并发程序而言,这种状态的切换会影响到程序性能)。

举个例子,如果程序需要处理10K个整数(或者10K个字符串等等),而这些整数事先存在某个文件中,如果程序每处理一个整数就要从文件中读一个整数(read系统调用),那么每次都要进行硬件I/O、进程状态切换等操作,这样效率是非常低下的。如果每次从文件中读出1K个整数到内存,程序从内存中读取数据并处理,那么程序的性能会明显提高,存储这1K个整数的内存区域就是一个缓冲区。

由于目前计算机体系结构的限制,有时必须采用某些低效的手段。利用低效的手段一次性读出适量的数据到内存,以后的各种操作就只针对内存中的数据展开,从而实现对低效手段的缓冲,提高效率,这种机制称为缓冲机制,而内存中存储数据的区域就称为缓冲区。

stdio库(libc库的一部分)中的函数已经对文件实现了缓冲机制,并高度优化,可以直接应用到很多项目中。





Stdio的缓冲机制

fread函数

fread函数的缓冲机制中有如下几个重要的变量:

1.   pBase:指向缓冲区的指针

2.   nBufSize:缓冲区长度

3.   pToRead:指向缓冲区中未读取数据的指针

4.   nByteLeft:缓冲区中未读取数据字节数

如图所示,红色区域表示已经读入数据的缓冲区,灰色表示空闲的缓冲区。默认缓冲区一般为4096字节(缓冲区可以由用户指定,详见后文),当然,这个长度可以根据文件长度灵活地变化。


即使用户读取了一个字节的数据,fread也会事先从文件中读取缓冲区大小的数据来填充缓冲区,当然,如果文件长度小于缓冲区大小,那么文件的全部内容都会被读入缓冲区。

当缓冲区有了数据,fread就可以从缓冲区中读取数据了。设nCount是要读入的数据长度,nFileSize是文件的长度,在初始状态下(即在第一次执行fread函数时),pToRead= pBase,nByteLeft = FileSize > nBufSize?nFileSize:nBufSize。

如果nCount小于nByteLeft,那么每次从缓冲区的pToRead位置拷贝nCount个字节到用户定义的空间中去(即fread的第一个参数指向的空间),拷贝之后将pToRead向后移动nCount个字节,并将nByteLeft减少nCount,当nByteLeft为零时,fread将重新调用系统函数读取文件来刷新缓冲区(各参数又回到了一开始的状态)。

如果nCount大于nByteLeft,需要分多次才能将数据拷贝到用户定义的空间中,先从缓冲区中读取nByteLeft个字节,并刷新缓冲区,再读取剩下的字节,显然这样的开销更大。这里需要注意的是,当一次读入的数据长度超过缓冲区大小时(如大于4K),fread函数将不再采用缓冲机制。所以用fread读数据时,如何确定数据的长度应该三思而行。

上面只是fread函数缓冲机制的一个笼统的介绍,而具体的机制是怎么样的呢?聪明的读者可以自己试着实现一下。


fwrite函数

与缓冲相关的库函数

Stdio的安全问题

并发读写文件引起的数据正确性问题。


0 0