LRU算法

来源:互联网 发布:java 实现websocket 编辑:程序博客网 时间:2024/05/22 13:39

一,实现LRU的主要数据结构

  内核使用LRU算法对物理页面进行回收,即内核总是试图将最久没有被使用的老页面回收到伙伴系统。X86上Linux对LRU算法的实现貌似比较粗糙,页面的“新旧”程度仅被分为四个等级,内核使用两条链来组织相关物理页面:

      active_list代表活跃页面链表,inactive_list代表非活跃页面链表。页面通过struct page结构中的lru成员链入这两条LRU链,当页面被链入时,置PG_lru标志;更进一步,当页面是被链入active_list的时候要置PG_active标志;此外还有一个PG_referenced标志,用于当页面被访问时设置。

  当页面最近被使用时会被移到active_list中,如果页面久未使用则会被移到inactive_list中去。回收时只会回收inactive_list中的页面。

二,页面在LRU链表中的移动

  首先明确哪些页面会在LRU链中出现。内核并不是把所有使用的物理页面都链入LRU中,而是只有某些使用量大易于回收的页面,总的来说有两大类页面会被链入LRU链中:

  1,用于进程地址空间映射的匿名页

  2,page cache页(包含swap cache和普通的page cache)

细分来讲,这两大类可分为很多不同的情况,一是进程映射的页且未加入swap cache;二是进程映射的页且加入了swap cache;三是进程映射的页且属于普通的page cache,即mmap的页面;四是仅仅为page cache,即进程未映射但页面属于普通的page cache或是swap cache。不管属于哪种情况,他们的共性是要么可以与磁盘I/O将页面洗干净,要么就是可以直接丢弃以达到回收的目的。

  在X86上,LRU中的页面由旧到新分为四个等级:

  1,属于inactive_list,没有设置PG_referenced。    [PG_lru]

      2,属于inactive_list,设置了PG_referenced。       [PG_lru|PG_referenced]

      3,属于active_list,没有设置PG_referenced。  [PG_lru|PG_active]

      4,属于active_list,设置了PG_referenced。   [PG_lru|PG_active|PG_referenced]

当页面被访问时调用mark_page_accessed(),执行的就是1->2->3->4的状态转换。内核在很多地方需要使用mark_page_accessed()来标志页面最近已被访问,将页面从“老”到“新”提升等级。如果说内核各执行路径纷纷将各自用过的页面往“新”的状态刷,那么显然就需要一个反对者将它们又改回“老”状态,不然页面个个都是新页面,页页都不能回收了。这个反对者就是内存的回收者,回收者的工作就是在LRU链中找出最老的页面进行回收,它只关注inactive_list,因为显然inactive_list中的比较老,然而在扫描inactive_list并进行回收之前,它要走一遍active_list,把active_list中相对较老的降级到inactive_list中,即完成状态4->3->2的降级,这是通过函数refill_inactive()实现,它有唯一调用点在shrink_caches()中shrink_cache()之前。

  最后要强调一点,了解LRU中链了哪些类型的页面是理解内存回收关键函数shrink_cache()的关键。

 

Linux kernel version: 2.4.22 for x86