Linux内存分析 | 反向映射RMAP

摘自 https://www.cnblogs.com/visayafan/archive/2011/12/24/2300758.html

为什么要使用反向映射

  物理内存的分页机制,一个 PTE(Page Table Entry)对应一个物理页,但一个物理页可以由多个PTE与之相对应,当该页要被回收时,Linux2.4的做法是遍历每个进程的所有PTE判断该PTE是否与该页建立了映射,如果建立则取消该映射,最后无PTE与该相关联后才回收该页。该方法显而易见效率极低,因为其为了查找某个页的关联PTE遍历了所有的PTE,我们不禁想:如果把每个页关联的PTE保存在页结构里面,每次只需要访问那些与之相关联的PTE不很方便吗?确实,2.4之后确实采用过此方法,为每个页结构(Page)维护一个链表,这样确实节省了时间,但此链表所占用的空间及维护此链表的代价很大,在2.6中弃之不用,但反向映射机制的思想不过如此,所以还是有参考价值的,可以参考:http://blog.csdn.net/dog250/article/details/5303581

这里写图片描述

  1. 每个进程有个进程描述符task_struct,其中有mm域指向该进程的内存描述符mm_struct。

  2. 每个进程都拥有一个内存描述符,其中有PGD域,指向该进程地址空间的全局页目录;mmap域指向第一个内存区域描述符vm_area_strut1。

  3. 进程通过内存区域描述符vm_area_struct管理内存区域,每个内存区域描述符都有vm_start和vm_end域指向该内存区域的在虚拟内存中的起始位置;vm_mm域指向该进程的内存描述符;每个vm_area_struct都有一个anon_vma域指向该进程的anon_vma;

  4. 每个进程都有一个anon_vma,是用于链接所有vm_area_struct的头结点,通过vm_area_struct的anon_vma_node构成双循环链表。


转自 https://www.cnblogs.com/arnoldlu/p/8335483.html

最直接的方式是直接看核心函数, 4 应用

所谓反向映射是相对于从虚拟地址到物理地址的映射,反向映射是从物理页面到虚拟地址空间VMA的反向映射。

RMAP能否实现的基础是通过struct anon_vma、struct anon_vma_chain和sturct vm_area_struct建立了联系,通过物理页面反向查找到VMA。

用户在使用虚拟内存过程中,PTE页表项中保留着虚拟内存页面映射到物理内存页面的记录。

一个物理页面可以同时被多个进程的虚拟地址内存映射,但一个虚拟页面同时只能有一个物理页面与之映射。

不同虚拟页面同时映射到同一物理页面是因为子进程克隆父进程VMA,和KSM机制的存在。

如果页面要被回收,就必须要找出哪些进程在使用这个页面,然后断开这些虚拟地址到物理页面的映射。

匿名页面实际的断开映射操作在rmap_walk_anon中进行的,可以看出从struct page、到struct anon_vma、到struct anon_vma_chain、到struct vm_area_struct的关系。

……

4 应用

内核中通过struct page找到所有映射到这个页面的VMA典型场景有:

  • kswapd内核线程回收页面需要断开所有映射了该匿名页面的用户PTE页表项。
  • 页面迁移时,需要断开所有映射到匿名页面的用户PTE页表项。

try_to_unmap()是反向映射的核心函数,内核中其他模块会调用此函数来断开一个页面的所有映射

/**
 * try_to_unmap - try to remove all page table mappings to a page
 * @page: the page to get unmapped
 * @flags: action and flags
 *
 * Tries to remove all the page table entries which are mapping this
 * page, used in the pageout path.  Caller must hold the page lock.
 * Return values are:
 *
 * SWAP_SUCCESS    - we succeeded in removing all mappings------------成功解除了所有映射的PTE。
 * SWAP_AGAIN    - we missed a mapping, try again later---------------可能错过了一个映射的PTE,需要重来一次。
 * SWAP_FAIL    - the page is unswappable-----------------------------失败
 * SWAP_MLOCK    - page is mlocked.-----------------------------------页面被锁住了
 */
int try_to_unmap(struct page *page, enum ttu_flags flags)
{
    int ret;
    struct rmap_walk_control rwc = {
        .rmap_one = try_to_unmap_one,--------------------------------具体断开某个VMA上映射的pte
        .arg = (void *)flags,
        .done = page_not_mapped,-------------------------------------判断一个页面是否断开成功的条件
        .anon_lock = page_lock_anon_vma_read,------------------------锁
    };

    VM_BUG_ON_PAGE(!PageHuge(page) && PageTransHuge(page), page);

    /*
     * During exec, a temporary VMA is setup and later moved.
     * The VMA is moved under the anon_vma lock but not the
     * page tables leading to a race where migration cannot
     * find the migration ptes. Rather than increasing the
     * locking requirements of exec(), migration skips
     * temporary VMAs until after exec() completes.
     */
    if ((flags & TTU_MIGRATION) && !PageKsm(page) && PageAnon(page))
        rwc.invalid_vma = invalid_migration_vma;

    ret = rmap_walk(page, &rwc);

    if (ret != SWAP_MLOCK && !page_mapped(page))
        ret = SWAP_SUCCESS;
    return ret;
}

内核中有三种页面需要unmap操作,即KSM页面、匿名页面、文件映射页面:

int rmap_walk(struct page *page, struct rmap_walk_control *rwc)
{
    if (unlikely(PageKsm(page)))
        return rmap_walk_ksm(page, rwc);
    else if (PageAnon(page))
        return rmap_walk_anon(page, rwc);
    else
        return rmap_walk_file(page, rwc);
}

下面以匿名页面的unmap为例:

static int rmap_walk_anon(struct page *page, struct rmap_walk_control *rwc)
{
    struct anon_vma *anon_vma;
    pgoff_t pgoff;
    struct anon_vma_chain *avc;
    int ret = SWAP_AGAIN;

    anon_vma = rmap_walk_anon_lock(page, rwc);-----------------------------------获取页面page->mapping指向的anon_vma数据结构,并申请一个读者锁。
    if (!anon_vma)
        return ret;

    pgoff = page_to_pgoff(page);
    anon_vma_interval_tree_foreach(avc, &anon_vma->rb_root, pgoff, pgoff) {------遍历anon_vma->rb_root红黑树中的AVC,从AVC得到相应的VMA。
        struct vm_area_struct *vma = avc->vma;
        unsigned long address = vma_address(page, vma);

        if (rwc->invalid_vma && rwc->invalid_vma(vma, rwc->arg))
            continue;

        ret = rwc->rmap_one(page, vma, address, rwc->arg);-----------------------实际的断开用户PTE页表项操作。
        if (ret != SWAP_AGAIN)
            break;
        if (rwc->done && rwc->done(page))
            break;
    }
    anon_vma_unlock_read(anon_vma);
    return ret;
}

struct rmap_walk_control中的rmap_one实现是try_to_unmap_one,最终调用page_remove_rmap()page_cache_release()来断开PTE映射关系

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值