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. 物联网全景动态图谱2.0|PaaS物联网平台汇总(上篇)

    物联网智库 原创 物联网智库 整理发布 转载请注明来源和出处 ------   [导读]   ------ 毫无疑问,2018年物联网对行业的深度变革才刚刚开启. 物联网产业链企业的质与量将进入全面爆 ...

  2. mysql视图详解 mysql视图

    目录 22.1. ALTER VIEW语法 22.2. CREATE VIEW语法 22.3. DROP VIEW语法 22.4. SHOW CREATE VIEW语法 本章讨论了下述主题: ·    ...

  3. SQL2008R2的 遍历所有表更新统计信息 和 索引重建

    [2.以下是更新统计信息] DECLARE UpdateStatisticsTables CURSOR READ_ONLY FOR SELECT sst.name, Schema_name(sst.s ...

  4. 【原创 Hadoop&Spark 动手实践 6】Spark 编程实例与案例演示

     [原创 Hadoop&Spark 动手实践 6]Spark 编程实例与案例演示 Spark 编程实例和简易电影分析系统的编写 目标: 1. 掌握理论:了解Spark编程的理论基础 2. 搭建 ...

  5. 基于jQuery 3D旋转明星人物展示特效

    分享一款基于jQuery 3D旋转明星人物展示特效.这是一款来自百度换肤活动的明星旋转展示效果.效果图如下: 在线预览   源码下载 实现的代码. html代码: <div class=&quo ...

  6. 程序员自己编写的类和JDK类是一种合作关系。

    封装类: JAVA为每一个简单数据类型提供了一个封装类,使每个简单数据类型可以被Object来装载. 除了int和char,其余类型首字母大写即成封装类. 转换字符的方式: int I=10; Str ...

  7. linux java 安装

    对于java的开发,有openJDK 和 orcale jdk两种,大多数的linux 系统都会内置openjdk的安装包,但是大多数java项目的开发都是基于orcale jdk的,所以安装orca ...

  8. Jquery EasyUI Combotree根据选中的值展开所有父节点

    Jquery EasyUI Combotree根据选中的值展开所有父节点  Jquery EasyUI Combotree 展开父节点, Jquery EasyUI Combotree根据子节点选中的 ...

  9. 【linux】在宝塔上 同ip 不同端口 设置一个端口对应一个网站

    准备工作: ip一个 , 例如:192.168.1.666 服务器一台,放行所需端口 假想一个域名 www.test.com ps:默认你已经装好宝塔面板了 实现效果: 192.168.1.666:6 ...

  10. 【Static Program Analysis - Chapter 1】 Introduction

    Regarding correctness, programmers routinely use testing to gain confidence that their programs work ...