Linux内存管理之反向映射RMAP

1. Linux反向映射有三个常用数据结构,可以简称AV, VMA,AVC

struct anon_vma {
    struct anon_vma *root;//指向红黑树最顶层AV,可以理解为祖宗进程的AV
    unsigned degree;//树深度,表示是第几代进程,也可以理解为在红黑树的层级
    struct anon_vma *parent;    //指向父进程AV
    struct rb_root rb_root;//所有子孙进程的AVC红黑树,有多少个子孙进程,就有多少个AVC通过rb插入到此红黑树
};

struct anon_vma_chain {
    struct vm_area_struct *vma;//指向same_vam链入的VMA,其实也就是当前进程的VMA
    struct anon_vma *anon_vma;//指向rb节点插入的AV
    struct list_head same_vma; //链入VMA的anon_vma_chain链表
    struct rb_node rb; //插入AV的rb_root红黑树.
};

一个VMA表示进程的一段虚拟地址空间.

struct vm_area_struct {

/*VMA的AVC链表,比如第三代进程(连续fork两次),那么就会有三个AVC在这个链表,表示三个进程有相同的VMA,

前两个AVC通过rb结点依次插入父进程和父父进程AV的rb_root节点.而第三个AVC插入到当前进程AV的rb_root节点

*/
struct list_head anon_vma_chain;
struct anon_vma *anon_vma;  //VMA的AV,只有私有的匿名映射才有一个AV指针
}

结论:

  1. 假如进程有N个子孙进程,那么VMA->AV的rb_root红黑树,就有N个AVC插入红黑树(包含自身的AVC)

  2.假如VMA的anon_vma_chain链表有N个AVC,那么当前进程就属于第N代进程(连续forkN次)

2.反向映射应用场景

 2.1 内核回收时,需要断开page的所有映射

 2.2 页面迁移时,也要断开所有页面的映射

3. 断开映射处理流程

这里只分析匿名映射unmap过程

try_to_unmap->rmap_walk->rmap_walk_anon

static int rmap_walk_anon(struct page *page, struct rmap_walk_control *rwc)
{
    /*获取page->maping的AV */
    anon_vma = rmap_walk_anon_lock(page, rwc);
    if (!anon_vma)
        return ret;
/*计算页面在map中的偏移 */
    pgoff = page_to_pgoff(page);
/*遍历AV的rb_root记录着所有子进程的AVC */
    anon_vma_interval_tree_foreach(avc, &anon_vma->rb_root, pgoff, pgoff) {
        struct vm_area_struct *vma = avc->vma;/*取子进程的VMA */
        unsigned long address = vma_address(page, vma);

    /*这里有直接调用rmap_one和done函数
    rmap_one: try_to_unmap_one :解除一个映射
        done:page_not_mapped 检查page的mapcount是否大于0,

        */

        ret = rwc->rmap_one(page, vma, address, rwc->arg);
        if (ret != SWAP_AGAIN)
            break;
        /*如果mapcount为-1,则直接结束unmap流程 */
        if (rwc->done && rwc->done(page))
            break;
    }
    anon_vma_unlock_read(anon_vma);
    return ret;
}

主要做了三件事
1. 检查address是否指向物理页面page(PFN相同)
2. 重新设置page的pte页表项
3. 减少page的mapcount和count计数
static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
             unsigned long address, void *arg)
{
    /*对比address和page是否指向同一个物理页面,如果发生过COW,PTE内容肯定不一样,这里会返回NULL,如果没有发生COW,这里检查通过 */
    pte = page_check_address(page, mm, address, &ptl, 0);
    if (!pte)
        goto out;
        /*重新设置页表项 */
        swp_entry_t entry = { .val = page_private(page) };
        pte_t swp_pte;
        swp_pte = swp_entry_to_pte(entry);
        if (pte_soft_dirty(pteval))
            swp_pte = pte_swp_mksoft_dirty(swp_pte);
        set_pte_at(mm, address, pte, swp_pte);
    /*减少mapcount和count引用计数 */
    page_remove_rmap(page);
    page_cache_release(page);

    return ret;
}


4. mapcout计数

do_anonymous_page :缺页异常

page->_mapcount //初始化为-1,第一次建立映射时设置为0(page_add_new_anon_rmap)


fork后
page->_mapcount++(page_dup_rmap),page->_count++(get_page),并设置PTE为写保护pte_wrprotect


发生了COW
对old page的mapcount--

unmap时
1.如果page还没有发生COW,则会对page所有的映射进行unmap操作
try_to_unmap_one执行page->_mapcount--

2. COW发生后,page的mapcount相应的减少(page_remove_rmap)

  

猜你喜欢

转载自blog.csdn.net/bin_linux96/article/details/84069375
今日推荐