Linux-内核缓存区和write行为
《Unix环境高级编程》
应用缓冲技术能很明显的提高系统效率。内核与外围设备的数据交换,内核与用户空间的数据交换都是比较费时的,使用缓冲区就是为了优化这些费时的操作。其实核心到用户空间的操作本身是不buffer的,是由I/O库用buffer来优化了这个操作。比如read本来从内核读取数据时是比较费时的,所以一次取出一块,以避免多次陷入内核。
内核缓冲区的 主要思想就是一次读入大量的数据放在缓冲区,需要的时候从缓冲区取得数据。
管理员模式和用户模式之间的切换需要消耗时间,但相比之下,磁盘的I/O操作消耗的时间更多,为了提高效率,内核也使用缓冲区技术来提高对磁盘的访问速度。磁
盘是数据块的集合,内核会对磁盘上的数据块做缓冲。内核将磁盘上的数据块复制到内核缓冲区中,当一个用户空间中的进程要从磁盘上读数据时,内核一般不直接
读磁盘,而是将内核缓冲区中的数据复制到进程的缓冲区中。当进程所要求的数据块不在内核缓冲区时,内核会把相应的数据块加入到请求队列,然后把该进程挂
起,接着为其他进程服务。一段时间之后(其实很短的时间),内核把相应的数据块从磁盘读到内核缓冲区,然后再把数据复制到进程的缓冲区中,最后唤醒被挂起的进程。
注:理解内核缓冲区技术的原理有助于更好的掌握系统调用read&write,read把数据从内核缓冲区复制到进程缓冲区,write把数据从进程缓冲区复制到内核缓冲区,它们不等价于数据在内核缓冲区和磁盘之间的交换。
从理论上讲,内核可以在任何时候写磁盘,但并不是所有的write操作都会导致内核的写动作。内核会把要写的数据暂时存在缓冲区中,积累到一定数量后再一 次写入。有时会导致意外情况,比如断电,内核还来不及把内核缓冲区中的数据写道磁盘上,这些更新的数据就会丢失。
应用内核缓冲技术导致的结果是:提高了磁盘的I/O效率;优化了磁盘的写操作;需要及时的将缓冲数据写到磁盘
Write行为
从write()调用返回时,内核已经将缓冲区所提供的数据到内核的缓冲区,但是无法保证数据已经写出到其预定的目的地。的确,写入调用返回的速度实在太快了,可能没有时间完成该项目的工作。处理器和硬盘之间的性能差异使得此类令人头痛的行为显而易见。
事实上,如果用户空间应用程序发出write()系统调用,Linux内核会先进行若干检查,接着将数据复制进缓冲区。稍后,内核会在后台收集所有 “脏”(有数据写入)缓冲区(内容跟相应磁盘块不同的所有缓冲区),将它们安排成最佳顺序,接着写进磁盘。这让写入调用的执行快如闪电,几乎立即返回,这 也让内核可以将写入操作延后到较空闲的时段再进行,并且是多笔写入操作会整批一起进行。
延后进行的写入操作并不会改变POSIX的语义。举例来说,数据刚写入缓冲区而尚未写回磁盘,此时如果发出读取请求,此请求可从缓冲区得到满足,而 且不会因此而读取到地盘上的旧数据。此行为会实际提高性能,因为读取请求可从内存中的缓冲区得到满足,而不必从磁盘。当读取和写入请求如预期般交替出现 时,结果也和预期一样,也就是说,数据被写回磁盘之前系统不会崩溃!即使应用程序相信写入请求已经成功完成了,但事实上数据尚未写回磁盘。
延后写入的另一个问题是无法安排写入顺序,尽管应用程序可能会安排写入请求的顺序,好让他们能够按照特定的顺序写回磁盘,内核会以它认为合适的方式 重新安排写入请求的顺序,主要是基于新能的考虑。除非系统崩溃,否则这通常不是一个问题,因为所有缓冲区最后都会写回磁盘,所以一切都很好。即使如此,绝 大多数的应用程序实际上并不关心写入请求的顺序。
延迟写入必须探讨的最后一个问题是汇报I/O错误。写回磁盘期间可能会发生任何无法向发出写入请求的进程汇报的I/O错误,例如磁盘驱动器故障。的 确,缓冲区与这些进程毫无关系。假如有多个进程“弄脏”(将数据写入)单一缓冲区,而这些进程可能在数据写入缓冲区之后并且在数据写回磁盘之前先借宿了。
内核会试图尽量降低延后写入的风险,为了确保数据可以被及时写出,内核未缓冲区设立了一个时间上限,而且会在时间超过上限之前写出所有“脏”缓冲 区。用户可通过/proc/sys/vm/dirty_expire_centiseconds来设定此值,此值以厘秒(百分之一秒)为单位。