dm_cache中缓存查询与替换策略分析
2014-03-14 00:02
543 查看
注:dm-cache为兼容linux内核2.6.29的稳定版
cache_lookup函数涉及dm_cache的缓存块映射方式、查找算法及缓存替换策略,详细分析该函数有窥一斑而知全豹的效果。
注:dm-cache缓存块的几种状态
有效块(valid):与原磁盘数据块一致;
保留块(reserved):该缓存块已分配,但尚未写入数据;
脏块(dirty):脏数据块是相对于原数据块而言的,是指被修改过的,与原数据不一致的数据块;
写回块(writeback):该缓存块上的数据正被写回原磁盘;
无效块(invalid):该缓存块上数据已失效;
cache_lookup函数包含3个参数,其中dmc为指向表示dm_cache结构的指针,block为请求在原磁盘上的起始扇区。函数返回1表示命中缓存,cache_block指向请求扇区映射后所对应的缓存块。函数返回0表示不命中但是仍然分配了缓存块,并且当存在空的缓存块时,cache_block指向遇到的第一个空的缓存块。否则,如果存在干净的缓存块,则cache_block指向根据LRU替换策略进行替换后的缓存块。函数返回2表示没有命中缓存并且没有分配缓存块,此时的cache_block指向经过LRU替换策略淘汰的脏缓存块,在这种情况下,函数返回后首先应该将脏数据写回磁盘。函数返回-1表示没有命中缓存并且没有缓存块可供分配,它发生在当请求映射到的集合内所有的块的状态都为RESERVED或WRITEBACK时。
dm_cache对原始的LRU算法进行了改进,和一条队列的LRU算法不同,你可以认为改进后的LRU算法实际上使用了两条队列,分别管理最近干净块和脏块,这两条队列都是按照LRU替换算法进行管理。优先从干净队列中选择缓存块,如果没有干净块则从脏块中选择,将访问次数(cache.count)最少的缓存块淘汰掉。
在cache_lookup函数中,通过hash_block函数计算请求扇区映射到缓存上所对应的组的编号。
cache_lookup函数涉及dm_cache的缓存块映射方式、查找算法及缓存替换策略,详细分析该函数有窥一斑而知全豹的效果。
注:dm-cache缓存块的几种状态
有效块(valid):与原磁盘数据块一致;
保留块(reserved):该缓存块已分配,但尚未写入数据;
脏块(dirty):脏数据块是相对于原数据块而言的,是指被修改过的,与原数据不一致的数据块;
写回块(writeback):该缓存块上的数据正被写回原磁盘;
无效块(invalid):该缓存块上数据已失效;
cache_lookup函数包含3个参数,其中dmc为指向表示dm_cache结构的指针,block为请求在原磁盘上的起始扇区。函数返回1表示命中缓存,cache_block指向请求扇区映射后所对应的缓存块。函数返回0表示不命中但是仍然分配了缓存块,并且当存在空的缓存块时,cache_block指向遇到的第一个空的缓存块。否则,如果存在干净的缓存块,则cache_block指向根据LRU替换策略进行替换后的缓存块。函数返回2表示没有命中缓存并且没有分配缓存块,此时的cache_block指向经过LRU替换策略淘汰的脏缓存块,在这种情况下,函数返回后首先应该将脏数据写回磁盘。函数返回-1表示没有命中缓存并且没有缓存块可供分配,它发生在当请求映射到的集合内所有的块的状态都为RESERVED或WRITEBACK时。
static int cache_lookup(struct cache_c *dmc, sector_t block, sector_t *cache_block) { unsigned long set_number = hash_block(dmc, block); /* 参数block表示请求的起始扇区编号,利用hash_block函数得到请求起始扇区所属的组(set_number)*/ sector_t index; int i, res; unsigned int cache_assoc = dmc->assoc; /*磁盘块映射方式采用组相连,dmc->assoc表示组相连的路数,也就是说每组所包含的缓存块数*/ struct cacheblock *cache = dmc->cache; int invalid = -1, oldest = -1, oldest_clean = -1; unsigned long counter = ULONG_MAX, clean_counter = ULONG_MAX; index=set_number * cache_assoc; /*组号乘以路数,求得请求起始扇区所属的缓存块号*/ for (i=0; i<cache_assoc; i++, index++) { /*首先从状态为VALID和RESERVED的缓存块中进行查找*/ if (is_state(cache[index].state, VALID) || is_state(cache[index].state, RESERVED)) { if (cache[index].block == block) /*判断是否命中?*/ { *cache_block = index; /* Reset all counters if the largest one is going to overflow */ if (dmc->counter == ULONG_MAX) cache_reset_counter(dmc); cache[index].counter = ++dmc->counter; break; } else { /* Don't consider blocks that are in the middle of copying */ if (!is_state(cache[index].state, RESERVED) && !is_state(cache[index].state, WRITEBACK)) { if (!is_state(cache[index].state, DIRTY) && cache[index].counter < clean_counter) /* Alex:For clean blocks */ { clean_counter = cache[index].counter; /* Alex:A smart vist time record method */ oldest_clean = i; /* Alex:Record the block index */ } if (cache[index].counter < counter) { counter = cache[index].counter; oldest = i; } } } } else { if (-1 == invalid) invalid = i; } } res = i < cache_assoc ? 1 : 0; if (!res) { /* Cache miss */ if (invalid != -1) /* 选择遇到的第一个空的缓存块 */ *cache_block = set_number * cache_assoc + invalid; else if (oldest_clean != -1) /* 使用LRU算法优先选择干净的缓存块 */ *cache_block = set_number * cache_assoc + oldest_clean; else if (oldest != -1) { /* 使用LRU算法选择脏缓存块 */ res = 2; *cache_block = set_number * cache_assoc + oldest; } else { res = -1; } } /* Alex:What is about the condition that res requal to -1? */ if (-1 == res) DPRINTK("Cache lookup: Block %llu(%lu):%s", block, set_number, "NO ROOM"); else DPRINTK("Cache lookup: Block %llu(%lu):%llu(%s)", block, set_number, *cache_block, 1 == res ? "HIT" : (0 == res ? "MISS" : "WB NEEDED")); return res; }
dm_cache对原始的LRU算法进行了改进,和一条队列的LRU算法不同,你可以认为改进后的LRU算法实际上使用了两条队列,分别管理最近干净块和脏块,这两条队列都是按照LRU替换算法进行管理。优先从干净队列中选择缓存块,如果没有干净块则从脏块中选择,将访问次数(cache.count)最少的缓存块淘汰掉。
在cache_lookup函数中,通过hash_block函数计算请求扇区映射到缓存上所对应的组的编号。
static unsigned long hash_block(struct cache_c *dmc, sector_t block) { unsigned long set_number, value; value = (unsigned long)(block >> (dmc->block_shift + dmc->consecutive_shift)); set_number = hash_long(value, dmc->bits) / dmc->assoc; return set_number; }
相关文章推荐
- 让EFCore更疯狂些的扩展类库(二):查询缓存、分部sql、表名替换、遍历的策略配置
- 让EFCore更疯狂些的扩展类库(二):查询缓存、分部sql、表名替换、遍历的策略配置
- 让EFCore更疯狂些的扩展类库(二):查询缓存、分部sql、表名替换、遍历的策略配置
- Hibernate查询缓存全面分析
- ViewPage的缓存策略persistentDrawingCache
- Hibernate查询缓存全面分析
- ABP源码分析十三:缓存Cache实现
- Hibernate查询缓存全面分析
- Hibernate查询缓存全面分析
- Cache的替换策略
- android轻量级开源缓存框架——ASimpleCache(ACache)源码分析
- Hibernate第十二篇【二级缓存介绍、缓存策略、查询缓存、集合缓存】
- asp.net中Session缓存与Cache缓存的区别分析
- 浅谈公司核心业务数据表的重构——结合Memcache分析缓存策略与系统数据交互
- 淘淘商城系列——查询商品详情添加缓存分析
- NSURLRequestCachePolicy 缓存策略
- CI源码分析(四)—DB查询缓存
- OkHttp3源码分析[缓存策略]
- hibernate查询缓存详细分析