内存_页框回收

  • Post author:
  • Post category:其他


页框回收算法(page frame reclaiming algorithm,PFRA)


页框回收算法,是回收一些物理内存页。 在两种情况下会触发这个算法,


1, 当进行alloc_page时等申请内存时,但内存中空闲的page已经不够了。就会触发PFRA去回收一些页。得到更多的free的page。


2, 如果仅仅在第1个条件才触发PFRA,那alloc_page函数就会等待很长时间。所以,linux会创建一个kswapd内核线程。会定期的被唤醒去调用PFRA算法,尽量去保证linux有较多的物理内存空间。不会低于high_wmark_pages。




那么去回收那些页呢?

PFRA会将“很久”不用的页reclaim到硬盘/flash上,然后释放这个页。对于页被reclaim到硬盘/flash的哪个位置?PFRA进行了分类:不可回收页,可交换页,可同步页,可丢弃页。




  1. 不可回收页


    ,包括:本来就是空闲的页,内核空间使用的页,临时锁定的页。内核空间的页linux认为会比较频繁,也比较重要,不会reclaim。



  2. 可同步页


    。如一些打开的文件,过mmap映射到内存的占用的页。这样的页本来在硬盘中就有备份。所以系统只需要查看这些页是否被修改过,如果被修改过,就将其写到硬盘上,然后reclaim,如果没有被修改过,那么直接reclaim。



  3. 可交换页


    。这些页是对应于这2种页的。包括:用户进行的malloc的页,或者和其他进程共享的页等。这些页在硬盘上没有对应的区域,所以要想将其写到硬盘上,就要有一个 交换区(swap区),内核会将其写到 这个交换区。然后再reclaim。



  4. 可丢弃页


    。如:slab机制保留的未使用的页,文件系统目录项等的一些缓存的页。这些页可以直接丢弃。



那么如何判断页是否“很久”没有用呢?

有个LRU()算法。




在zone这个结构体中,会有一些list:


  1. /* Fields commonly accessed by the page reclaim scanner */
  2. spinlock_t      lru_lock;

  3. struct

    zone_lru {

  4. struct

    list_head list;
  5. } lru[NR_LRU_LISTS];
/* Fields commonly accessed by the page reclaim scanner */
	spinlock_t		lru_lock;	
	struct zone_lru {
		struct list_head list;
	} lru[NR_LRU_LISTS];


用来连接这个zone中的各个不同状态的页。


  1. enum

    lru_list {
  2. LRU_INACTIVE_ANON = LRU_BASE,
  3. LRU_ACTIVE_ANON = LRU_BASE + LRU_ACTIVE,
  4. LRU_INACTIVE_FILE = LRU_BASE + LRU_FILE,
  5. LRU_ACTIVE_FILE = LRU_BASE + LRU_FILE + LRU_ACTIVE,
  6. LRU_UNEVICTABLE,
  7. NR_LRU_LISTS
  8. };
enum lru_list {
	LRU_INACTIVE_ANON = LRU_BASE,
	LRU_ACTIVE_ANON = LRU_BASE + LRU_ACTIVE,
	LRU_INACTIVE_FILE = LRU_BASE + LRU_FILE,
	LRU_ACTIVE_FILE = LRU_BASE + LRU_FILE + LRU_ACTIVE,
	LRU_UNEVICTABLE,
	NR_LRU_LISTS
};



LRU_INACTIVE_ANON  和LRU_ACTIVE_ANON对应于匿名页,就是上面说的可交换的页。 LRU_INACTIVE_FILE 和 LRU_ACTIVE_FILE对应于可同步页。LRU_UNEVICTABLE应该指不可回收的页。




讲解一下上图中相关的一些知识:


首先两个方框表示两个list,一个active,一个inactive。从了在zone的这个list中可以找到active的页,也可以从page这个结构体中检查。当page结构体中的 flag这个成员变量,flag&PG_active ==1时,page对应的页是active的,如flag&PG_active ==0,则是inactive的。


在每个方框中,也有两个部分,Ref0和Ref1。他代表页是否刚被访问过。如:当一个进程访问一个页时候,就会调用mark_page_accessed函数,这个函数将这个页的page结构的flag变量 flag |= PG_reference;这样标志页刚刚被访问过。但linux的kswap进程会扫描所有页,将 flag &= ~PG_reference;标志页有一段时间没有被访问过了。这样如果在Ref0中停留过长时间的话,就会调用shrink_active_list函数将这个页移动到inactive list中。



下面介绍回收的流程:




上图就是介绍的两个shrink memory的方法。一个是当alloc_page内存不够时,直接direct page reclaim。 一种是swap daemons。


当然他们shrink的强度是不一样的,direct page reclaim是紧急情况的,必须要回收到的。而swap daemons是尽量回收,保留更多的内存空间。所以在try_to_free_pages在调用shrink_zones函数中会传入个priority,这个priority代表shrink的强度,priority会不断减一,强度越来越大。直到释放了足够的alloc_page的空间。如果priority减到0了,还不能shrink足够的空间。那么就是调用out_of_memory函数,来kill 进程,释放空间。



在进行shrink_page_list中,会根据相关的页进行不同的设置

。如:


如当shrink  LRU_INACTIVE_ANON  里的page的时候,

就会将所有对应这个页的页表(PTE)

的present位和dirty位请0.但是其他位不为0. 其他位标志了这个页所在的swap区的位置(此时pte里的值已经不是页表信息了,而是表示这个页保存在磁盘的地址。具体意义可看《深入理解linux内核》的710页,换出页标识符)。


当shrink  LRU_INACTIVE_FILE  里的page的时候,就会

将所有对应这个页的页表(PTE)

的present位请0,但dirty位置一.由于这些页在磁盘上本来就是有对应的文件的,所以不需要记录他的具体问题,linux可以找到。



所以就有了上面讨论缺页异常时候,读页的那三个函数。和判断。。。。。。
吐舌头