Linux中页面的定期换出

来源:互联网 发布:古典舞演出服淘宝网 编辑:程序博客网 时间:2024/06/15 10:41
Linux内核中设置了一个专门定期将页面换出的“守护神”kswapd。
它在系统相对空闲的时候来运行。使用的是内核的空间。

线程kswapd的源代码基本上都在mm/vmscan.c中。kswapd_init()

函数kswapd_init()是在系统初始化期间收到调用的。
第一件是在swap_setup()中根据物理内存的大小设定一个全局量page_cluster;
第二件事是创建线程kswapd,由kernel_thread()完成。还创建了另一个线程kreclaimd。

在简单的初始化操作以后,程序便进入一个无限循环。
在每次循环的末尾一般都会调用interruptible_sleep_on_timeout()进入睡眠,让内核自由地调度别的进程运行。
内核在一定时间以后会唤醒并调度kswapd继续运行,这个时间就是常数HZ。

HZ决定了内核中每秒钟有多少次时钟中断。用户可以在编译内核前的系统配置阶段改变其数值。

kswapd在例行路线中做:
第一部分是在发现物理页面已经短缺的情况下,预先找出如果页面,且将这些页面的映射断开,使这些物理页面从活跃状态转入不活跃状态,为页面的换出作好准备。
第二部分是每次都要执行的,把已经处于不活跃状态的“”页面写入交换设备,使它们成为不活跃“干净”页面继续缓冲,或进一步回收这样的页面成为空闲页面。

首先,检查内存中可供分配或周转的物理页面是否短缺。inactive_shortage()
系统中应该维持的物理页面供应量由两个全局量确定,freepages.high和inactive_target,分别为空闲页面的数量和不活跃页面的数量,二者之和为正常情况下潜在的供应量。

当前尚存的空闲页面,其数量由nr_free_pages()加以统计;
不活跃“干净”页面,其数量由nr_inactive_clean_pages()加以统计。
现有的不活跃“脏”页面,这种页面全在同一个队列中,内核中的全局量nr_inactive_dirty_pages记录着当前此类页面的数量。


通过free_shortage()检查是否有某个具体管理区中有严重的短缺。


释放和换出若干页面是通过do_try_to_free_pages()完成。在此之前,需要调用waitqueue_active()查看kswapd_done队列中是否有函数在等待执行,并把查看的结果作为参数传递给do_try_to_free_pages()。


内核中有几个特殊的队列,各部分(主要是设备驱动)可以把一些底层函数挂入这样的队列,使得这些函数在某些事件发生时就能得到执行。


函数waitqueue_active()就是查看是否有函数在这个队列中等待执行,其定义在include/linux/wait.h中。


task_struct结构中的need_resched是为强制调度而设置的,每当CPU结束了一次系统调用或中断服务、从系统空间返回用户空间时就会检查这个标志。
kswapd是个内核线程,永远不会“返回用户空间”,这样就有可能绕过这个机制而站住CPU不放,所以只能靠它“自律”,可能需要较长时间的操作才检查这个标志并调用schedule()。


循环中做的事情:
1. 通过refill_inactive_scan()扫描活跃页面队列,试图从中找到可以转入不活跃状态的页面;
2. 通过swap_out()找出一个进程,然后扫描其映射表,从中找出可以转入不活跃状态的页面;
3. 试试用于dentry结构和inode结构的页面。


swap_out()断开一个页面的映射使其转入不活跃状态时,已经将页面转入不活跃页面队列。


每个进程都有其自身的虚存空间,空间中已经分配并建立了映射的页面构成一个集合。但在任何一个给定的时刻,该集合中的每一个页面所对应的物理页面不一定都在内存中,在内存中的往往只是一个子集,这个子集称为“驻内页面集合”(resident set),其大小称为rss。在存储管理结构mm_struct中有一个成分就是rss。




mm->rss反映了一个进程占用的内存页面数量;
mm->wap_cnt反映了该进程在一轮换出内行页面的努力中尚未受到考察的页面数量。


就每个进程的角度而言,对内存页面的占用存在着两个方向上的运动:
一个方向是因页面异常而有更多的页面建立起或恢复起映射;
另一方面则是周期性地受到swap_out()的考察而被切断若干页面的映射。
这两个运动的结合决定了一个进程在特定时间内对内存页面的占用。


页面的换出具体是由swap_out_mm()来完成的。


内存页面的page结构中,字段flags中的各种标志位反映着页面的当前状态,其中的PG_active标志位表示当前这个页面是否“活跃”,即是否仍在active_list队列中。


一个可交换的物理页面一定在某个LRU队列中,不在active_list队列中就说明一定在inactive_dirty_list中或某个inactive_clean_list中。


一个映射中的物理页面是否应该换出,取决于这个页面最近是否受到了访问。这是通过inline函数ptep_test_and_clear_young()测试(并清0)的,其定义在include/asm-i386/pgtable.h中。


页表项中的_PAGE_ACCESSED标志位,当i386 CPU的内存映射机制在通过一个页面表项将一个虚存地址映射成一个物理地址,进而访问这个物理地址时,就会自动将该表项的_PAGE_ACCESSED标志位设成1.


如果页面还活跃,就通过SetPageReferenced()将page数据结构中的PG_referenced标志位置成1。将页面表项中表示受到过访问的信息转移至页面的数据结构中。


既然页面有映射(否则不会出现在目标进程的映射表中并且在内存中)却又不再活跃页面队列中:
在do_swap_page()中可以看到,当因页面异常而恢复一个不活跃页间的映射时,并不立即把它转入活跃页面队列,而把这项工作留给page_launder(),让其在系统比较空闲时再来处理,所以这样的页面有可能不在活跃队列中。


页面的寿命由page->age表示。
如果页面不再活跃队列中要先通过age_page_down_ageonly()减少其寿命。
只要page->age尚未达到0,就还不能将此页面换出。


将一个page结构从活跃队列拖链由宏操作del_page_from_active_list()完成,其定义在include/linux/swap.h中。


通过mmap()建立起的文件映射,其page结构中的指针mapping指向相应的address_space数据结构。如果决定解除映射,而页面表项中的_PAGE_DIRTY标志位为1,就要在转到drop_pte处之前,先把page结构中的PG_dirty标志位设为1,并把页面转移到该文件映射的“脏”页面队列中。


swap_out_mm() -> swap_out() -> refill_inactive() -> do_try_to_free_pages() -> kswapd()


页面的寿命实际上以扫描的次数为单位。
0 0
原创粉丝点击