【计算子系统】内存管理之四:内存交换

  本节将讨论内存交换,计算子系统相关内容目录点此进入

什么是内存交换?为什么需要它?

  当系统内存使用压力较大时,内核会将访问率不好的匿名页暂时写到磁盘(换出)以释放这部分匿名页供系统使用;当换出的页再次被访问时,内核重新将其读入(换入)。

  内存交换是回收匿名页的唯一手段。我们知道,应用程序使用的内存页分为文件映射页和匿名页两种。对于文件映射页,回收时可以将它的内容回刷到映射文件中。而对于匿名页(也包括私有的文件映射页),默认情况下没有持久化文件和它对应。如果要回收这部分内存,必然要寻找可以暂时存放内容的存储对象。因此就出现了交换分区和内存交换的概念。

如何实现?

交换分区

  我们可以通过设置交换分区来打开内存交换功能,具体可参考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;
}

  至此,快速分配流程中涉及的回收和交换过程已分析完毕,后续我们将分析慢速分配流程中的内存压缩(迁移)功能。


转载请注明:吴斌的博客 » 【计算子系统】内存管理之四:内存交换