Home Kernel Samepage Merging (KSM)
Post
Cancel

Kernel Samepage Merging (KSM)

一、KSM机制

1
2
3
4
https://zhuanlan.zhihu.com/p/102469328
详细的代码流程图
https://www.kernelnote.com/entry/linux-ksm-merge
详细的代码分析

​ 内核采用虚拟内存管理技术为每个进程分配独立的虚拟内存地址空间。而物理内存的分配是由进程去访问虚拟地址时产生缺页异常 (Page Fault) 来 触发。一个进程的虚拟地址空间在内核中用内存描述符struct mm_struct进行表示,而进程的虚拟地址空间又被划分为多个虚拟内存区域struct vm_area_struct,简称vma。另外,进程描述符由struct task_struct中的mm域记录。

KSM后常驻一个名叫ksmd非实时线程。它会执行 ksm.c 源码里的ksm_do_scan接口定时扫描被标记为MMF_VM_MERGEABLEmm_struct[内 存描述符],调用cmp_and_merge_page识别并合并内容完全一样的物理页,扫描的间隔和每次扫描的页数分别由/sys/kernel/mm/ksm/ pages_to_scan/sys/kernel/mm/ksm/sleep_millisecs控制。用户层可以通过系统调用madvise(addr,length,MADV_MERGEABLE)对一块页 对齐的内存标记为可用于 KSM 合并。此外由于madvise系统调用会通过内核源码 madvise.c 里的madvise_behavior接口对内存区域 vma 中的 内存进行标记,如果该区间和周围的内存区间标记不同,那么会分配新的vma,而内核对进程持有的vma是有限制的,分配的 vma 数目必须小于/ proc/sys/vm/max_map_count里的限制,一旦超出,那么会引发OOM Killer导致进程crash如果调大max_map_count产生过多的vma会 导致系统的性能下降,我们应该根据自身业务的规模进行合理调整,同时有必要加上监控预警。

1
https://maggie262.github.io/2020/11/20/ksm/

KSM数据结构关系图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
https://www.cnblogs.com/arnoldlu/p/8335541.html
ksm_do_scan
    scan_get_next_rmap_item---------------------选取合适的匿名页面
    cmp_and_merge_page--------------------------将页面与root_stable_tree/root_unstable_tree中页面进行比较,判断是否能合并
        stable_tree_search----------------------搜索stable红黑树并查找是否有和page内容一致的节点
        try_to_merge_with_ksm_page--------------尝试将候选页合并到KSM页面中
        stable_tree_append
        unstable_tree_search_insert-------------搜索unstable红黑树中是否有和该页内容相同的节点
        try_to_merge_two_pages------------------若在unstable红黑树中找到和当前页内容相同节点,尝试合并这两页面成为一个KSM页面
        stable_tree_append----------------------将合并的两个页面对应rmap_item添加到stable节点哈希表中
        break_cow
    
KSM页面和匿名页面的区别
分两种情况,匿名页是父子进程VMA共享同一个匿名页面;ksm是不相干的进程VMA共享同一个匿名页面。

二、反向映射

1
https://os.51cto.com/art/202011/632380.htm

目的:为了找到一个物理页面对应的页表项.

用途:内存回收,内存碎片整理,CMA, 巨型页,页迁移等各个场景中都能发现反向映射所做的关键性的工作。

1 反向映射发展

2.反向映射分类据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1fork流程中会存在反向映射的建立,可以熟悉建立流程
SYSCALL_DEFINE0(fork)
kernel_clone
copy_process
copy_mm
dump_mm
dup_mmap
	->|anon_vma_fork 匿名映射反向映射创建
	->|vma_interval_tree_insert_after	文件映射反向映射创建
2、内存回收或者页表迁移,会通过反向映射查找对应的pte,可以熟悉查找流程.
    try_to_unmap
    ->|rmap_walk
    	->|rmap_walk_ksm if PageKsm(page)  ksm
    	->|rmap_walk_anon if PageAnon(page) 匿名页
    	->|rmap_walk_file page cache
(1)匿名反向映射

​ 匿名页的共享主要发生在父进程fork子进程的时候,父fork子进程时,会复制所有vma给子进程,并通过调用dup_mmap->anon_vma_fork 建立子进程的rmap以及和长辈进程rmap关系结构:

主要通过anon_vma这个数据结构体中的红黑树将共享父进程的页的所有子进程的vma联系起来(通过anon_vma_chain 来联系对应的vma和av), 当然这个关系建立比较复杂,涉及到vma,avc和av这些数据结构体。

而在缺页异常do_anonymous_page的时候将page和vma相关联

1
2
3
4
5
6
try_to_unmap //mm/rmap.c 
->rmap_walk 
  ->rmap_walk_anon 
     ->anon_vma_interval_tree_foreach(avc, &anon_vma->rb_root,pgoff_start, pgoff_end) 
         ->rwc->rmap_one 
            ->try_to_unmap_one 

对于候选页,会拿到候选页相关联的anon_vma,然后从anon_vma的红黑树中遍历到所有共享这个页的vma,然后对于每一个vma通过 try_to_unmap_one来处理相对应的页表项,将映射关系解除。

(2)文件反向映射

​ 文件页的共享主要发生在多个进程共享libc库,同一个库文件可以只需要读取到page cache一次,然后通过各个进程的页表映射到各个进程的vma 中。管理共享文件页的所以vma是通过address_space的区间树来管理,在mmap或者fork的时候将vma加入到这颗区间树中:

发生文件映射缺页异常的时候,将page和address_space相关联。

1
2
3
4
5
try_to_unmap //mm/rmap.c 
->rmap_walk 
  ->rmap_walk_file 
    ->vma_interval_tree_foreach(vma, &mapping>i_mmap,pgoff_start, pgoff_end)  
         ->rwc->rmap_one

对于每一个候选的文件页,如果是映射页,就会遍历page所对应的address_space的区间树,对于每一个满足条件的vma, 调用try_to_unmap_one来找到pte并解除映射关系。

(3)KSM反向映射

​ 遍历ksm机制中两课红黑树,一棵是stable tree,一棵是unstable tree, 然后通过匿名映射的反向映射查找对应的vma。

1
2
3
4
5
6
try_to_unmap //mm/rmap.c 
->rmap_walk 
   ->rmap_walk_ksm  //mm/ksm.c 
        -> hlist_for_each_entry(rmap_item, &stable_node->hlist, hlist) 
    ->anon_vma_interval_tree_foreach(vmac, &anon_vma->rb_root,0, ULONG_MAX)  
      ->rwc->rmap_one 

3.总结

匿名页,文件页和ksm页的反向映射,分别通过page所对应的的vma, address_space, stable_node结构来查找vma

三、额外知识:
1
2
https://blog.csdn.net/Z_Stand/article/details/102222612 
文件IO与内存映射:page cache页高速缓存
i page cache level0视图

ii. page cache作用

​ 提高文件系统性能,内核利用一部分物理内存分配出缓冲区,用于缓存系统操作和数据文件,当内核收到读写的请求时,内核先去缓存区找是否有请 求的数据,有就直接返回,如果没有则通过驱动程序直接操作磁盘。

​ 缓存机制优点:减少系统调用次数,降低CPU上下文切换和磁盘访问频率。

iii. 文件读写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
读文件流程如下
数据结构关联:inode -> i_mapping 指向address_space对象,address_space->host指向inode
数据结构关联:page->mapping 指向页缓存owneraddress_space
系统调用传参:文件描述符+文件偏移地址
操作系统找到文件address_space,根据偏移量到页缓存中查找page
若查找到,返回数据到用户空间
否则,内核新建一个page并加入到页缓存,数据从磁盘载入该项
调用readpage方法将数据返回给用户空间
    
写文件流程如下:
数据结构关联:inode -> i_mapping 指向address_space对象,address_space->host指向inode
数据结构关联:page->mapping 指向页缓存owneraddress_space
系统调用传参:文件描述符+文件偏移地址
操作系统找到文件address_space,根据偏移量到页缓存中查找page
若查找到,将数据写入到该缓存中,该页成为脏页
若没有查找到,向缓存的计数树管理的页面中添加一个新的页面,并将用户空间的数据写入到该页面
当数据满足页缓存的时间或空间原理时,使用pdflush后台回写例程来将脏页数据会写到磁盘

1
2
3
https://xgwang0.github.io/2018/12/24/Linux-FileSystem_File-Read_Write-Process/
https://blog.csdn.net/Frecon/article/details/80153535?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169106404316800182147077%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=169106404316800182147077&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-80153535-null-null.268^v1^koosearch&utm_term=%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F&spm=1018.2226.3001.4450
文件读写

iv. page cache控制

​ sysctl 参数: dirty_ratiodirty_background_ratio

v. 文件读写 与 文件映射
1
https://www.cnblogs.com/charlesblc/p/6263665.html

而使用mmap操作文件中,创建新的虚拟内存区域和建立文件磁盘地址和虚拟内存区域映射这两步,没有任何文件拷贝操作。而之后访 问数据时发现内存中并无数据而发起的缺页异常过程,可以通过已经建立好的映射关系,只使用一次数据拷贝,就从磁盘中将数据传 入内存的用户空间中,供进程使用。

总而言之,常规文件操作需要从磁盘到页缓存再到用户主存的两次数据拷贝。而mmap操控文件,只需要从磁盘到用 户主存的一次数据拷贝过程。说白了,mmap的关键点是实现了用户空间和内核空间的数据直接交互而省去了空间不同** 数据不通的繁琐过程。因此mmap效率更高。

1
2
3
4
5
6
7
8
9
10
11
12
13
mmap优点总结
由上文讨论可知,mmap优点共有一下几点:
1、对文件的读取操作跨过了页缓存,减少了数据的拷贝次数,用内存读写取代I/O读写,提高了文件读取效率。
2、实现了用户空间和内核空间的高效交互方式。两空间的各自修改操作可以直接反映在映射的区域内,从而被
对方空间及时捕捉。
3、提供进程间共享内存及相互通信的方式。不管是父子进程还是无亲缘关系的进程,都可以将自身用户空间映射
到同一个文件或匿名映射到同一片区域。从而通过各自对映射区域的改动,达到进程间通信和进程间共享的目的。
     同时,如果进程A和进程B都映射了区域C,当A第一次读取C时通过缺页从磁盘复制文件页到内存中;但当B
     再读C的相同页面时,虽然也会产生缺页异常,但是不再需要从磁盘中复制文件过来,而可直接使用已经保
     存在内存中的文件数据。
4、可用于实现高效的大规模数据传输。内存空间不足,是制约大数据操作的一个方面,解决方案往往是借助硬盘
空间协助操作,补充内存的不足。但是进一步会造成大量的文件I/O操作,极大影响效率。这个问题可以通过mmap映射
很好的解决。换句话说,但凡是需要用磁盘空间代替内存的时候,mmap都可以发挥其功效。
This post is licensed under CC BY 4.0 by the author.