2017-05-20

聚会回来一如既往的看了会羽毛球比赛,然后想到前几天和朋友讨论的逆向映射的问题,还是简要总结下,免得以后再忘记了!可是当我添加时间……这就有点尴尬了……520还在写技术博客……


闲话不多说,之前一个问题是想要根据物理页框号得到映射的虚拟地址,一时间不知道如何下手了,在群里和一个朋友讨论了一番,记得之前看swap机制的交换缓存时,记载说系统当要换出一个页面时,可以很容易找到使用该页面的所有进程,然后撤销映射。这一点也就成了我的突破口。经过对源码的一番研究结合相关书籍,便有了今天这篇文章。重点就是逆向映射机制。

顾名思义,有一个虚拟地址经过页面转换得到物理地址的过程为正向映射,那么根据物理地址推导虚拟地址呢?自然成了逆向映射。众所周知,Linux下每个物理页面对应一个page结构,物理页框号可以很容易的转化到page结构,不妨看下内核是怎么转化的。

#define __pfn_to_page(pfn)    (mem_map + ((pfn) - ARCH_PFN_OFFSET))
#define __page_to_pfn(page) ((unsigned long)((page) - mem_map)+ ARCH_PFN_OFFSET)

这里有点像windows 的pfn数据库了,mem_map是一个page指针,作为pfn数据库(实际上是一个大的数组的起始),ARCH_PFN_OFFSET是物理起始地址的pfn。所以差值实际就是有效pfn。通过page转化成pfn也是同样的思路。那么这和逆向映射什么关系呢?下面要说的就是至关重要的page结构,该结构比较庞大,我们只说和逆向映射有关系的部分。

page结构中有两个字段:

struct page{
struct address_space *mapping;
union {
pgoff_t index; /* Our offset within mapping. */
void *freelist; /* slub/slob first free object */
bool pfmemalloc; /* If set by the page allocator,
* ALLOC_NO_WATERMARKS was set
* and the low watermark was not
* met implying that the system
* is under some pressure. The
* caller should try ensure
* this page is only used to
* free other pages.
*/
};
struct { union {
/*
* Count of ptes mapped in
* mms, to show when page is
* mapped & limit reverse map
* searches.
*
* Used also for tail pages
* refcounting instead of
* _count. Tail pages cannot
* be mapped and keeping the
* tail page _count zero at
* all times guarantees
* get_page_unless_zero() will
* never succeed on tail
* pages.
*/
atomic_t _mapcount; struct { /* SLUB */
unsigned inuse:;
unsigned objects:;
unsigned frozen:;
};
int units; /* SLOB */
};
atomic_t _count; /* Usage count, see below. */
};
};
}; }

其实这里想说的就三个字段,mapping,在映射匿名页面的时候指向一个anon_vma结构,在映射文件页面的时候指向inode节点的address-space;index,表示对应的虚拟页面在vma中的线性索引;_mapcount,共享该页面的进程的数目;注意该值默认是-1,当有一个进程使用时为0,所以其值表明除了当前进程还有多少进程在使用,便于撤销。了解了这三个字段,接下来就好解释多了。通过一个函数page_referenced来解释。

int page_referenced(struct page *page, int is_locked,struct mem_cgroup *memcg, unsigned long *vm_flags)

原版解释如下:Quick test_and_clear_referenced for all mappings to a page,returns the number of ptes which referenced the page.就是快速的检查并清除一个页面的所有引用(不同页表当中),返回引用这个page页面的pte数量。简单走一下流程

int page_referenced(struct page *page,
int is_locked,
struct mem_cgroup *memcg,
unsigned long *vm_flags)
{
int referenced = ;
int we_locked = ; *vm_flags = ;
if (page_mapped(page) && page_rmapping(page)) {
if (!is_locked && (!PageAnon(page) || PageKsm(page))) {
we_locked = trylock_page(page);
if (!we_locked) {
referenced++;
goto out;
}
}
if (unlikely(PageKsm(page)))
referenced += page_referenced_ksm(page, memcg,
vm_flags);
else if (PageAnon(page))
referenced += page_referenced_anon(page, memcg,
vm_flags);
else if (page->mapping)
referenced += page_referenced_file(page, memcg,
vm_flags);
if (we_locked)
unlock_page(page); if (page_test_and_clear_young(page_to_pfn(page)))
referenced++;
}
out:
return referenced;
}

首先检查正向和逆向映射是否都存在,如果没有锁定该页面并且页面是KSM 页面或者文件映射页面,则需要trylock,如果加锁失败,则直接out.接下来就是对不同情况的处理。如果是KSM页面走page_referenced_ksm。如果是匿名映射页,走page_referenced_anon,如果是文件映射页,走page_referenced_file。KSM是内核页面共享的一种机制,主要用在KVM中,但是其他地方也可以引用,由于其需要计算页面是否相同,所以在重复率不高的场合,大部分选择关掉KSM,关于KSM在另一篇文章已经介绍。

如果是匿名映射页面,进入page_referenced_anonstatic int page_referenced_anon(struct page *page,struct mem_cgroup *memcg,unsigned long *vm_flags)函数

static int page_referenced_anon(struct page *page,
struct mem_cgroup *memcg,
unsigned long *vm_flags)
{
unsigned int mapcount;
struct anon_vma *anon_vma;
pgoff_t pgoff;
struct anon_vma_chain *avc;
int referenced = ; anon_vma = page_lock_anon_vma_read(page);
if (!anon_vma)
return referenced; mapcount = page_mapcount(page);
pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT);
anon_vma_interval_tree_foreach(avc, &anon_vma->rb_root, pgoff, pgoff) {
struct vm_area_struct *vma = avc->vma;
unsigned long address = vma_address(page, vma);
/*
* If we are reclaiming on behalf of a cgroup, skip
* counting on behalf of references from different
* cgroups
*/
if (memcg && !mm_match_cgroup(vma->vm_mm, memcg))
continue;
referenced += page_referenced_one(page, vma, address,
&mapcount, vm_flags);
if (!mapcount)
break;
} page_unlock_anon_vma_read(anon_vma);
return referenced;
}

要查看页面的访问情况,肯定要定位到具体的PTE,而PTE只能根据虚拟地址查找页表获得,所以当务之急还是找到虚拟地址和页表。这里首先获得page对应的anon_vma,前面提到,在匿名映射情况下,page->mapping指向anon_vma结构。然后获取了page的共享计数mapcount,获取page对应的虚拟页框在vma中对应的线性索引index,接下来就开始遍历interval-tree了。每个anon_vma_chain关联一个进程的vma,通过vma_address(page, vma)便可以获取在当前vma对应的进程的虚拟地址。暂且忽略cgroup相关的内容。接下来调用page_referenced_one解除映射。前面已经提到,目前已经有了虚拟地址,有了vma,根据vma可以获取对应的mm_struct,进而获取页基址,OK,流程走通了。该函数就不在列举了,函数中有两种情况,如果是大页面(2M页面),需要获得是pmd;如果是普通页面,需要获取pte;之后检查_PAGE_ACCESSED位。如果被设置,则清除,然后++引用计数器,否则,不变。所以经常访问的页面,引用计数器高,就更容易被定义成活跃页面,常驻活跃LRU链表,就不容易被换出。

回顾下最初的问题,通过物理地址找到虚拟地址,在获取了vma和index后,一个函数就解决问题,但是笔者这里有一个疑问,代码显示这里根据page结构中的index对所有的vma进行索引,这点令我很困惑,理论上将不能保证page映射的虚拟页框在所有的vma中都是同样的偏移吧?如果有知道的老师,还请告知!!

static inline unsigned long
__vma_address(struct page *page, struct vm_area_struct *vma)
{
pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT); if (unlikely(is_vm_hugetlb_page(vma)))
pgoff = page->index << huge_page_order(page_hstate(page)); return vma->vm_start + ((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
}

代码到这里就不需要多解释了吧,关于anon_vma结构的组织,以后凑空在分析;

感谢主!

参考:

linux 3.10.1源码

《深入linux内核架构》

linux 逆向映射机制浅析的更多相关文章

  1. KVm中EPT逆向映射机制分析

    2017-05-30 前几天简要分析了linux remap机制,虽然还有些许瑕疵,但总算大致分析的比较清楚.今天分析下EPT下的逆向映射机制.EPT具体的工作流程可参考前面博文,本文对于EPT以及其 ...

  2. linux 逆向映射

    逆向映射用于建立物理内存页和使用该页的进程的对应页表项之间的联系,在换出页时以便更新所有涉及的进程.得到物理页基址后,根据pfn_to_page可以将页框转换为page实例,page实例中的mappi ...

  3. Linux模块机制浅析

    Linux模块机制浅析   Linux允许用户通过插入模块,实现干预内核的目的.一直以来,对linux的模块机制都不够清晰,因此本文对内核模块的加载机制进行简单地分析. 模块的Hello World! ...

  4. Linux模块机制浅析_转

    Linux模块机制浅析 转自:http://www.cnblogs.com/fanzhidongyzby/p/3730131.htmlLinux允许用户通过插入模块,实现干预内核的目的.一直以来,对l ...

  5. 【ARM-Linux开发】Linux模块机制浅析

    Linux模块机制浅析   Linux允许用户通过插入模块,实现干预内核的目的.一直以来,对linux的模块机制都不够清晰,因此本文对内核模块的加载机制进行简单地分析. 模块的Hello World! ...

  6. [内核同步]浅析Linux内核同步机制

    转自:http://blog.csdn.net/fzubbsc/article/details/37736683?utm_source=tuicool&utm_medium=referral ...

  7. 浅析Linux内核同步机制

    非常早之前就接触过同步这个概念了,可是一直都非常模糊.没有深入地学习了解过,最近有时间了,就花时间研习了一下<linux内核标准教程>和<深入linux设备驱动程序内核机制>这 ...

  8. 【Linux开发】Linux模块机制浅析

    Linux允许用户通过插入模块,实现干预内核的目的.一直以来,对linux的模块机制都不够清晰,因此本文对内核模块的加载机制进行简单地分析. 模块的Hello World! 我们通过创建一个简单的模块 ...

  9. linux内存回收机制

    无论计算机上有多少内存都是不够的,因而linux kernel需要回收一些很少使用的内存页面来保证系统持续有内存使用.页面回收的方式有页回写.页交换和页丢弃三种方式:如果一个很少使用的页的后备存储器是 ...

随机推荐

  1. SNF框架及机器人2018年1-9月份升级内容

    1月 增加评星控件.年月选择控件 完善表格弹框的封装,增加多选弹框 的封装 增加表格 单元格合并.列头必填与可填写的标识 4月 关于分页查询和排序的各种修改(扶额) 导入excel优化 bs计算合计的 ...

  2. (6) MySQL慢查询日志的使用

    一. 设置方法 使用慢查询日志里捕获 启用之前需要先进行一些设置 方法一:全局变量设置 设置慢查询日志的日志文件位置 set global slow_query_log_file = "D: ...

  3. vultr搭建ss

    我在Ubuntu1604上运行的sslocal,但是发现firefox无法链接ss代理,后来用的chromium才成功连接上, ---------------------------- 下面是正文 - ...

  4. 阿里java代码检测工具p3c

    阿里在杭州云栖大会上,正式发布众所期待的<阿里巴巴Java开发规约>扫描插件! 该插件由阿里巴巴P3C项目组研发.这个项目组是阿里巴巴开发爱好者自发组织形成的虚拟项目组,把<阿里巴巴 ...

  5. hdoj:2056

    #include <iostream> #include <iomanip> #include <cstdlib> using namespace std; str ...

  6. 如何在云端部署SAP HANA实战, Azure 上的 SAP HANA(大型实例)概述和体系结构

    什么是 Azure 上的 SAP HANA(大型实例)? Azure 上的 SAP HANA(大型实例)是一种针对 Azure 的独特解决方案. 除了提供 Azure 虚拟机以用于部署和运行 SAP ...

  7. Validation failed for one or more entities. See ‘EntityValidationErrors’解决方法;关于如何查看 EntityValidationErrors 详细信息的解决方法

    我们在 EF 的编程中,有时候会遇到这样一个错误: 但是,按照他的提示 "See 'EntityValidationErrors' property for more details.&qu ...

  8. Linux 正则表达式_010

    Linux 正则表达式 标注:本教程只针对linux运维的三剑客命令awk,sed,grep正则表达式 什么是正则表达式? 简单的说,正则表达式就是为处理大量的字符串而定义的一套规则和方法通过定义的这 ...

  9. shell脚本 切换用户

    如下: #!/usr/bin/expect -f spawn su root expect "Password:" send "mypasswd\r" inte ...

  10. jsp 连接access数据库

    参考笔记:https://www.cnblogs.com/mthoutai/p/7278995.html