本博客原创文章属本人lurker0ster所有,欢迎转载。
转载时需同意以下条件:
1. 必须保持版权信息,以及文章出处http://blog.csdn.net/lurker0ster/
2. 不准演绎,修改,必须完整转载全部内容。
===================分割线====================
前面的blog提到利用sreadahead程序可以加快启动,那么究竟是它是利用什么原理做到的呢?
其实,sreadahead程序的原理是在启动时预先读取启动中需要的文件加载到内存中,从而减少文件等待时间。这个程序是Arjan van de Ven <arjan@linux.intel.com>写的。
sreadahead的核心就是linux的readahead API。要达到目的需要做两件事:记录启动过程中加载过的文件信息;在内核启动启动结束之后早启动之前预读取这些文件。对于前者,Arjan最早是通过hack ext3文件系统的Inode来实现的,并且新增加了EXT3_IOC_INODE_JIFFIES的ioctl调用。网上能查到的资料都是说这样实现,实际上是错误的。这样的做法,只能对ext3文件系统起作用,为了达到通用和优化效率的目的,最新版本的patch改为对sys_open进行hook【1】。
通过研读源代码可以发现,sreadahead的追踪功能是通过对debugfs的tracing/current_tracer和tracing/tracing_enabled进行设置打开的。代码中对应的函数是trace_start。patch过的内核会启动sys_open的Hook,在内存中记录下启动过程中读取过的文件,以及其偏移地址和内容大小。
当程序调用trace_stop时,它会打开debugfs中的tracing/trace,从而得到所有的文件列表,然后记录在ra数组中。由于debugfs存在于内存中,程序最后会把数据保存在PACK文件中(/var/lib/sreadahead/pack)。
下次启动过程中sreadahead启动后会先检查PACK文件是否存在。如果存在,则从里面读取文件信息,并通过readahead系统调用把文件读入cache中。之后,lilinux如何需要读取对应的文件,就直接在内存中读取。这里的做法,实际上就是把零散的IO读操作集中在一起(起了四个线程,依次读取文件列表中的文件),只是减少了等待时间,文件读取时间没有节省。
一个需要提到的内容是文件系统的readahead参数。我们知道linux文件系统优化措施之一就是readahead(预读取),在正常的文件读取操作中,linux会预读取请求内容后面的128KB内容存在内存中,如何应用程序继续读取,则直接从内存中读取。在sreadahead程序中由于通常都是小文件,128KB的预读取对性能影响比较大,所有在执行读取操作时程序会设置改参数为16KB(通过函数readahead_set_len实现)。
另外Ubuntu的列表上有人提到trace_stop函数会对文件列表进行冒泡排序和剔除重复记录(
sort_ra_by_name();
remove_dupes();
get_ra_blocks();
)使得程序很占用CPU。其实冒泡排序没有什么必要,就算一定要排序也可以延后到启动完成之后再排序。
在实践中,必须实际测试sreadahead的性能,因为小内存的系统已经报告使用了该程序反而使得启动时间变长。
ubuntu维护了自己的实现(ureadahead)【2】,由Scott James Remnant 维护,原理是一样的。
参考文献: