本节将讨论内存交换,计算子系统相关内容目录点此进入。
什么是内存交换?为什么需要它?
当系统内存使用压力较大时,内核会将访问率不好的匿名页暂时写到磁盘(换出)以释放这部分匿名页供系统使用;当换出的页再次被访问时,内核重新将其读入(换入)。
内存交换是回收匿名页的唯一手段。我们知道,应用程序使用的内存页分为文件映射页和匿名页两种。对于文件映射页,回收时可以将它的内容回刷到映射文件中。而对于匿名页(也包括私有的文件映射页),默认情况下没有持久化文件和它对应。如果要回收这部分内存,必然要寻找可以暂时存放内容的存储对象。因此就出现了交换分区和内存交换的概念。
如何实现?
交换分区
我们可以通过设置交换分区来打开内存交换功能,具体可参考swapon命令的使用方法。交换分区可以是一个物理磁盘分区,也可以是一个普通文件,在内核中只要是一个文件对象就可以。
交换分区以页为单位进行管理分配,内核通过一个引用数组记录交换分区中的每个页被进程的引用次数。每次换出同一页时,该页对应的引用数加一;换入时则相反,每换入一次就减一。
换出时机
在内存回收快速分配流程中,如果选择回收匿名页,或者在慢速流程中唤醒kswapd内核服务进程回收匿名页时,都会将匿名页换出。
换出流程
在shrink_page_list函数中,我们关注其中和匿名页相关的部分就可以了解整个换出流程。代码片断参考内存回收,更深入的细节需要进一步深入pageout函数进行分析。
整个过程可以参考下图:匿名页换出前(图a),首先要把它加到交换分区文件对应的交换缓存(Swap Cache)中(图b)。交换缓存其实就是交换分区对应的文件缓存,和普通文件缓存一样,它也是通过一棵radix树进行数据组织。交换缓存把交换过程和文件系统关联了起来,我们可以通过文件系统抽象接口完成交换动作。另外,交换缓存也成为换出和换入过程需要使用的共享资源,通过锁机制可以有效达到同步效果。
接着内核便通过反向映射找到匿名页所有的映射页表,解除页表映射(图c),并在页表中填入匿名页在交换分区中对应的位置信息。之所以在页表中填入匿名页在交换分区的存放位置,是便于在换入时重新读取页内容。
映射关系全部解除后,如果脏页内容已回刷完成,内核就可以将匿名页从缓存分区中移除并回收该页(图e)。图d表示解除页表映射后,进程B再次访问该匿名页的场景。
换入流程
换入发生在被换出的匿名页再次被访问时,此时首先会进入页异常处理过程。内核通过判断页表内容确认需要进行换入操作时,将调用do_swap_page完成换入动作。
该函数整体思路比较明确:先查找交换缓存看是否存在期望的匿名页,如果存在则重新映射并更新页表;否则就分配新页加到交换缓存中,在读取页内容完成后即可映射给页表。每次发生换入操作时,交换分区对应页的引用计数减一,当发现交换分区中对应页的引用为零时,表示没有进程引用该页,便可将交换分区中的页回收。
linux/mm/memory.c:
static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long address, pte_t *page_table, pmd_t *pmd,
unsigned int flags, pte_t orig_pte)
{
spinlock_t *ptl;
struct page *page, *swapcache;
swp_entry_t entry;
pte_t pte;
int locked;
int exclusive = 0;
int ret = 0;
entry = pte_to_swp_entry(orig_pte); /*从页表项中找出换出页在交换分区中的位置*/
...
page = lookup_swap_cache(entry); /*查找交换缓冲区*/
if (!page) {
/*如果交换缓冲区未缓存当前交换位置,则重新分配内存页并从交换分区中读入内容*/
page = swapin_readahead(entry, GFP_HIGHUSER_MOVABLE, vma, address);
...
} else if (PageHWPoison(page)) {
...
}
swapcache = page;
locked = lock_page_or_retry(page, mm, flags);
...
/*重新在页表中添加映射*/
page_table = pte_offset_map_lock(mm, pmd, address, &ptl);
...
pte = mk_pte(page, vma->vm_page_prot);
...
set_pte_at(mm, address, page_table, pte);
if (page == swapcache)
do_page_add_anon_rmap(page, vma, address, exclusive);
else /* ksm created a completely new copy */
page_add_new_anon_rmap(page, vma, address);
...
swap_free(entry); /*减少当前交换位置的引用计数*/
...
return ret;
}
至此,快速分配流程中涉及的回收和交换过程已分析完毕,后续我们将分析慢速分配流程中的内存压缩(迁移)功能。
转载请注明:吴斌的博客 » 【计算子系统】内存管理之四:内存交换