Linux文件系统预读(二)

本文详细探讨了单进程文件顺序读取时操作系统预读机制的工作原理,特别关注于读取大小变化的情况。通过具体示例代码,展示了不同读取大小下预读窗口的初始化与更新过程。

        前一篇文章仔细描述了最简单的一种预读情况:单进程文件顺序读,且读大小不超过32页面,这里我们来看另外一种情境:单进程文件顺序读,读大小为256KB,看看预读逻辑如何处理这种情况,照例首先给出事例代码:

  1. {  
  2.         ...  
  3.         f   = open("file", ....);  
  4.         ret = read(f, buf, 40 * 4096);  
  5.         ret = read(f, buf, 16 * 4096);  
  6.         ret = read(f, buf, 32 * 4096);  
  7.         ...  
  8. }  
{
        ...
        f   = open("file", ....);
        ret = read(f, buf, 40 * 4096);
        ret = read(f, buf, 16 * 4096);
        ret = read(f, buf, 32 * 4096);
        ...
}
        事例代码中我们一共进行了三次读,顺序读,且读的大小不定,有超过最大预读量的,也有低于最大预读量的。

Read 1

        毫无疑问,由于第一次读肯定未在缓存命中,前一篇博客告诉我们需要进行一次同步预读,需要初始化预读窗口

  1. initial_readahead:  
  2.     ra->start = offset;  
  3.     ra->size = get_init_ra_size(req_size, max);  
  4.     // ra->size 一定是>= req_size的,这个由get_init_ra_size保证  
  5.     // 如果req_size >= max,那么ra->async_size = ra_size  
  6.     ra->async_size = ra->size > req_size ? ra->size - req_size : ra->size;  
  7.   
  8. readit:  
  9.     /* 
  10.      * Will this read hit the readahead marker made by itself? 
  11.      * If so, trigger the readahead marker hit now, and merge 
  12.      * the resulted next readahead window into the current one. 
  13.      */  
  14.     if (offset == ra->start && ra->size == ra->async_size) {  
  15.         ra->async_size = get_next_ra_size(ra, max);  
  16.         ra->size += ra->async_size;  
  17.     }  
  18.   
  19.     return ra_submit(ra, mapping, filp);  
  20. }  
initial_readahead:
	ra->start = offset;
	ra->size = get_init_ra_size(req_size, max);
	// ra->size 一定是>= req_size的,这个由get_init_ra_size保证
	// 如果req_size >= max,那么ra->async_size = ra_size
	ra->async_size = ra->size > req_size ? ra->size - req_size : ra->size;

readit:
	/*
	 * Will this read hit the readahead marker made by itself?
	 * If so, trigger the readahead marker hit now, and merge
	 * the resulted next readahead window into the current one.
	 */
	if (offset == ra->start && ra->size == ra->async_size) {
		ra->async_size = get_next_ra_size(ra, max);
		ra->size += ra->async_size;
	}

	return ra_submit(ra, mapping, filp);
}
        在初始化预读窗口中判断得出:ra->size=32 pages,即使应用程序要读的数量是40 pages,这样ra->async_size = ra->size=32 pages,在readit逻辑判断成立,因此会重设ra->async_size的值,根据计算应该是32 pages,而总的ra->size=初始值+ra->async_size=64 pages。形成的预读窗口为(0, 64, 32),如下图:

     
        由于应用程序本次访问的实际页面是PAGE0 ~PAGE40(由于同步预读会全部在缓存命中),因此在访问过程中会碰到page32,此时触发一次异步预读,并向前推进预读窗口:

  1. /* 如果: 
  2.       ** 1. 顺序读(本次读偏移为上次读偏移 (ra->start) + 读大小(ra->size,包含预读量) -  
  3.       **    上次预读大小(ra->async_size)) 
  4.       ** 2. offset == (ra->start + ra->size)??? 
  5.      */  
  6.     if ((offset == (ra->start + ra->size - ra->async_size) ||  
  7.          offset == (ra->start + ra->size))) {  
  8.           // 设置本次读的  
  9.         ra->start += ra->size;   
  10.         ra->size = get_next_ra_size(ra, max);  
  11.         ra->async_size = ra->size;  
  12.         goto readit;  
  13.     }  
/* 如果:
      ** 1. 顺序读(本次读偏移为上次读偏移 (ra->start) + 读大小(ra->size,包含预读量) - 
      **    上次预读大小(ra->async_size))
      ** 2. offset == (ra->start + ra->size)???
	 */
	if ((offset == (ra->start + ra->size - ra->async_size) ||
	     offset == (ra->start + ra->size))) {
	      // 设置本次读的
		ra->start += ra->size; 
		ra->size = get_next_ra_size(ra, max);
		ra->async_size = ra->size;
		goto readit;
	}
        更新后的当前预读窗口为(64, 32, 32),如下:

        因此,经过第一次读以后,该文件在内存中page cache状态如下图所示:

Read 2

        由于第二次读只需读出page40 ~ page55,直接在page cache中命中,也不会触发一次异步预读,预读窗口也不会更新,因此,该过程非常简单。本次读完以后,文件在内存page cache的状态如下:

Read3

        应用程序第三次读的范围为page56 ~ page87,由上图可知,这些均可以在page cache中命中,但是由于访问了PAGE64,因此会触发一次异步预读,且当前的预读窗口为(64, 32, 32),根据上面的算法更新预读窗口为(96, 32, 32),因此,本次预读完成以后,文件在page cache中的缓存状态如下:

总结

        我们可以将本情境与上篇博客中描述的情境对比,其实可以发现两者思想完全一致,只是由于应用程序读的粒度不同导致了预读的粒度更大,仅此而已。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值