zoukankan      html  css  js  c++  java
  • linux memory lock浅析

    linux内核提供了用于锁定内存的系统调用,如:

    mlock:lock一段地址范围内已map的内存

    mlockall:lock进程虚拟地址空间内已map的内存,还可以选择对于此后新map的空间是否自动lock

    mmap+MAP_LOCKED选项:在mmap的同时,对相应地址范围进行mlock


    利用这些系统调用,用户进程可以对自己需要使用的内存进行lock。lock,则意味着相应的数据在unlock之前将一直存在于物理内存中,不会被回收。

    对于应用程序来说,可以将内存中一些对程序性能影响较大的数据lock起来,避免非预期的页面回收引起性能波动。


    lock


    memory lock的实现思想说起来很简单。在《linux内存管理浅析》一文中说到,用户进程的地址空间由一组vma结构来管理,每一个vma代表一个已映射的、连续的、且属性相同的内存空间。

    而memory lock要做的事情就是给相应的vma置一个VM_LOCKED标记,然后这个标记会影响到内存回收策略。

    当然,memory lock时指定的地址范围可能跟现有的某个vma并不完全重合,所以lock操作可能导致现有的vma被合并或分割。(因为要满足同一个vma的内存属性一致,而“lock”也是其属性之一。)


    在《linux页面回收浅析》一文中又说到,给用户空间使用的内存(包括用户分配的匿名内存和page cache中的内存)会由LRU来管理,然后会有内核线程扫描LRU,并从中回收page。

    当page reclaim流程扫描到一个“最近最少访问”(非精确的)的page时,会试图回收它。在回收之前,需要通过反向映射,找到那些包含了它的vma,从而找到那些映射了它的page table,然后将映射取消掉。

    而如果映射了这个page的某个vma带有VM_LOCKED标记呢?说明这个page已被某个进程lock(尽管不一定被所有映射它的进程都lock),这时就应该放弃回收。(直到所有映射了这个page的vma都unlock,这个page才有可能被回收。)


    给相应的vma加VM_LOCKED标记,以及page reclaim流程通过反向映射检查该标记,就是memory lock最核心的处理逻辑。这两个动作就保证了被lock的page不会被回收。

    除此之外,memory lock还会有一些附加动作:


    1、分配并映射page。

    内存空间的map并不代表物理内存页被分配并映射。而就算物理内存已映射过,也有被回收掉的时候。

    memory lock会将lock vma区域内的page都安排就位。所要做的事情也就是遍历一下整个区域,对没有映射上page的地方手动触发一下page fault。

    而如果page fault失败(比如page分配失败),除了mlock系统调用,其他情况下都是会忽略的。毕竟lock主要还是保证page不被回收。


    2、将page移动到unevictable_list。

    在page reclaim流程中,如果扫描了一堆page,都在试图回收之时,费尽气力走完反向映射,然后发现page被lock过,以至于无法回收,那实在就太悲剧了。

    所以内核在LRU中新增了一个unevictable_list(除原有的active_list、inactive_list之外)。被lock的page将放到unevictable_list中,然后不再被page reclaim流程所扫描。

    同时也给page置一个PG_mlocked标记,以表示该page已被lock,并将被放入unevictable_list。

    跟前一个附加动作一样,这里并不一定保证成功。所以page reclaim流程通过反向映射检查VM_LOCKED标记的逻辑一定是需要存在的,那才是根本。


    unlock

    与lock相对应,munlock、munlockall用于对进程的某段内存空间进行解锁。

    此外,当vma消失时(比如munmap、exit、等),也会自动解锁。


    memory unlock是memory lock的逆过程。相应vma的VM_LOCKED标记会被去掉。然后还要通过反向映射去检查该page是否已经不再被其他的vma所lock,若是,则进一步,将page上的PG_mlocked标记去掉,并从unevictable_list中移走;否则说明该page尚未unlock完全,不需要进一步动作。

    memory unlock并不立刻将解锁的page回收,而是让其走上自然回收的过程(放回active_list或inactive_list,然后交由page reclaim流程去处理)。


    相比之下,memory lock只需要确保vma上的VM_LOCKED标记打上了,其他的都好说。就算page未分配成功、或者未放入unevictable_list,page reclaim流程总是能通过反向映射检查出page被lock的事实。

    然而memory unlock就没有这样的补救措施。如果unlock之后,page还没从unevictable_list中移走,就再没有人会发现并回收它了(page reclaim流程不会去看unevictable_list)。所以memory unlock一定会确保成功。

    不过话说回来,memory lock涉及到page分配,的确有失败的可能。而memory unlock是释放page,也的确是可以确保成功的。就好比malloc会失败、而free不会失败一样。


    其他


    限制

    进程能够lock的page数目是有限制的,通过ulimit -l命令就能看到。当然你有权将其调整为unlimited。


    关于fork操作的影响

    fork系统调用会创建一个子进程,并拷贝父进程的整个地址空间,包括对所有vma的拷贝。

    不过子进程的vma并不继承VM_LOCKED标记。

    也就是说,一次memory lock只需要一次memory unlock来解锁,不会因为fork而把问题搞得复杂。


    关于一些强制page cache回收的操作

    madvise+MADV_DONTNEED

    这里其实并不会触发page cache的强制回收,仅仅是取消本进程到page cache中相应page的内存映射。

    从逻辑上讲,page cache是全局的,而虚拟内存则是进程私有的,对私有的memory进行advise显然不应该直接影响全局的page cache;


    fadvise+POSIX_FADV_DONTNEED

    与madvise不同,fadvise是对file的advise,而file正是全局的概念。所以fadvise确实会直接影响page cache。

    对于指定的page,如果没有映射且不是脏页,fadvise+POSIX_FADV_DONTNEED会直接将其丢弃掉。

    否则,fadvise+POSIX_FADV_DONTNEED会检查page是否有PG_mlocked标记,是则不做处理,否则如果page未被映射,试图将page移动到inactive_list的尾部,以便page reclaim流程优先将其回收。(因为回收涉及到走反向映射并取消映射、以及脏页写回等操作,直接在这里回收会把问题搞复杂,所以还是交给page reclaim。)(被映射的page其实也是不接受fadvise的。)

    对于page被lock的情况,一般是有映射且有PG_mlocked标记,fadvise不会对其做处理。

    极端情况下,memory lock时只设置了vma的VM_LOCKED标记,其他的都失败了,可能会导致应该被lock的page未被映射,从而被fadvise+POSIX_FADV_DONTNEED丢弃。但是这种情况其实跟page未分配成功是类似的。


    /proc/sys/vm/drop_caches

    试图清除所有文件的所有page cache,对于单个page的处理跟fadvise+POSIX_FADV_DONTNEED是一致的。


    shmem lock

    除了前面提到的系统调用之外,对于ipc shmem还有另一种方法实现类似memory lock的功能,即使用shmctl系统调用的SHM_LOCK/SHM_UNLOCK操作。

    我们知道,shmem可以通过shmat系统调用attach到用户进程空间,那么也就可以对attach的空间进行memory lock。而如果你不打算attach,也可以从全局范围内对一块shmem执行shmctl+SHM_LOCK。后者会在这个shmem的page cache所对应的address_space结构上加AS_UNEVICTABLE标记,而page reclaim流程也同样会检查该标记以判断page是否被lock。

    类似的,能不能在不mmap一个file的情况下,对file的page cache所对应的address_space结构加AS_UNEVICTABLE标记呢?目前貌似还没有这样的操作。


    关于unevictable_list

    其实unevictable_list并不是专为memory lock服务的。所有不希望被回收的page都可以往里面放,memory lock的page只是其中一例(其实从字面意思就能看出来)。

    前面提到的shmem lock严格来说就不属于memory lock一路。另外ramfs所用到的page也是不可回收的,内核会自动给相应inode所对应的address_space结构上加AS_UNEVICTABLE标记。

  • 相关阅读:
    构建之法阅读笔记02
    4.7-4.13 第八周总结
    构建之法阅读笔记01
    顶会热词统计
    结对作业-四则运算升级版
    3.31-4.5 第七周总结
    大道至简阅读笔记03
    3.23-3.30 第六周总结
    第7周总结
    人月神话阅读笔记之三
  • 原文地址:https://www.cnblogs.com/wangfengju/p/6173078.html
Copyright © 2011-2022 走看看