LWN:madvise()只能对本进程做,没想过控制别的进程吗?

640点击上方蓝色字关注我们~



New system calls for memory management

By Jonathan Corbet
May 24, 2019


社区提出一些新的system call,希望能加入后续kernel版本里。其中一部分是专门用于内存管理进程的。下面会介绍用于进程间zero copy的process_vm_mmap(),还有两个新的API用于通知kernel更改其他进程里的内存处理策略的。


process_vm_mmap()

很多情况下需要快速把数据从一个进程搬到另一个进程去,例如消息传递的应用程序都是如此,这样的例子非常多。在Linux 3.2开发周期里已经加入了一对专门为这种情况定制的system call,不过目前知道的人还不多。


这两个syscall都会把当前进程地址空间(参数lvec数组)的数据复制到对方进程空间(参数rvec),他们实现了不经过kernel space来做跨进程的数据搬移。对某些特定场景下,这两个API会非常高效,不过有一些例外情况,特别是需要复制的数据如果特别多的情况下。


Kirill Tkhai提交了一组patch set,其中最开始他就描述了一些用这两个API碰到的问题:做copy时必须要真正做数据传递,且要能访问到所有数据。如果要复制的数据碰巧被换出了(swapped out),就会被自动换入回RAM。对copy的目标地址也是一样的道理,此外,目标地址空间如果尚未分配过真正的page的话,那么就需要先分配内存来放置这份copy的数据。最终所有数据都要从CPU这边绕一道,会把CPU本地cache里的其他有用数据都挤没了。具体出现的现象是:在线迁移很大的container(容器)的时候,每当container的空间加倍的时候,迁移时间会增大100倍。整个系统都会被大量的IO操作拖累,很多有价值的CPU cache内容都不再驻留在cache里了。


Tkhai提出的方案是引入一个新的system call,避免数据的copy:


    int process_vm_mmap(pid_t pid, unsigned long src_addr, unsigned long len,
unsigned long dst_addr, unsigned long flags);

这个函数很像mmap(),会在dst_addr(目标地址空间)位置,以len长度先在调用者进程的内存空间里创建一个新的内存映射(memory mapping),最终数据内容就是pid指定的进程空间里src_addr位置的数据。API包含若干flag:PVMMAP_FIXED说明是精确地址;PVMMAP_FIXED_NOREPLACE则要求避免这个特定的地址映射替换了此地址原有的的地址映射。


最终这个函数的效果很像process_vm_readv(),不过他们的区别是:新函数并不会复制所有数据到目标地址,而是把源进程的页表项复制过去,事实上只是对原始数据做了一个共享映射(shared mapping)。不用copy数据的话,也就不用分配对应的内存空间,能极大提高效率。同时也不再怕这些数据从内存被换出(swapping)到外存去。


大家对这组patch set进行了非常细致的讨论。Andy Lutomirski不认为它能真正解决问题,相反“既危险又复杂”。他的一些担心后来讨论中被解决了,不过他还是觉得这些问题应该可以用splice()来解决。Kirill Shutemov担心用splice()的话可能跟内核的reverse-maping(反向映射)代码配合不好,会引入一些很难debug的问题。目前讨论还在进行;process_vm_mmap()可能最终还是有机会能合入kernel的,不过肯定需要先经过更多讨论。


Remote madvise()

有时候,一个进程会要通过madvise()调用,来让kernel改变对其他进程的内存处理。在Oleksandr Natalenko描述的一个例子里面,希望能让某个进程来加入kernel same-page merging (KSM,内核让相同内容的page避免创建多份copy)从而优化内存空间的使用。KSM是一个优化共呢过,需要用madvise()来申请加入的。如果这个目标进程没有自己调用madvise(),那外界就没有什么好方法能让它加入KSM。

Natalenko的方案是在/proc目录下每个进程的子目录里增加一个新的文件节点( 名字就是madvise)。这样只要对这个文件节点写入“merge”,就相当于这个进程主动调用madvise(MADV_MERGEABLE),保证本简称的全部地址空间都加入KSM;相应的,写入“unmerge”就可以关闭此进程的KSM。将来,可能可以继续优化,能允许此进程地址空间的一部分(而不是全部)来加入KSM,或者支持其他的madvise()功能。


对这组patch set的反应也不是非常乐观。Alexey Dobriyan觉得还不如增加一个新的system call来实现这个功能。Michal Hocko表示赞同,认为本次2019 LSFMM Summit上介绍的"remote madvise()",是一个更加合理的解决方案。


process_madvise()

与此同时,Minchan Kim按照remote madvise()的想法实现了一组patch set,具体来说实现了如下system call:


    int process_madvise(int pidfd, void *addr, size_t length, int advice);

这个函数的效果就好像这个pidfd(pidfd file descriptor,不是通常说的process ID)对应的进程调用了madvise()一样。此API本身很容易理解,不过不久之后这组改动的下一个patch就提供了一个很复杂的API:


    struct pr_madvise_param {
    int size;
    const struct iovec *vec;
   }

   int process_madvise(int pidfd, ssize_t nr_elem,
    int *behavior,
    struct pr_madvise_param *results,
    struct pr_madvise_param *ranges,
    unsigned long flags);

这个改动的本来目的是希望能让一次process_madvise()调用就能更改目标进程的地址空间里的多处区域。具体来说,behavior, results, 和ranges数组这几个参数,每个都是nr_elem多项。具体到每一个参数,behavior就是要调用的madvise() flag;ranges就是一组vec数组指定的内存空间,每个range处理过后的返回结果,都放在results数组里面。


这组patch set还增加了一些其他的madvise()操作,MADV_COOL可以让指定的那些page都被移到inactive list的头部,也就是在系统内存紧张的时候,让这些page能在不久之后就被回收(reclaim),肯定比active list里面的page更加早的被回收。

MADV_COLD,相应的会把这些page移动到inactive list的尾部,这样可能会让他们能立刻就被回收。这两个API都是Android运行时能够利用的功能。


社区对这组patch set的回应更加正面一点。绝大多数的意见都是关于一些命名上的小问题,似乎预示着它没有什么基本性的问题。Christian Brauner,此前完成了大多数pidfd的工作,他希望所以使用pidfd的system call都能够以"pidfd_"来开头,也就是希望API被命名为pidfd_madvise()。不过这里并不是所有人都赞同这个观点,所以最终用哪个名字还没有决定。还有一些人反对MADV_COOL和MADV_COLD这两个名字,不过同样也没有达成一致该用什么名字比较好。


Hocko比较疑惑为何需要这个多区域操作的API,毕竟调用madvise()的时候通常不需要很快的能完成。Kim回答说他今后会提供一些benchmark数据来证明这个API的必要性。


本文提到的这3个system call,process_madvise()是最有希望能推进的。看起来有明显的需求,需要让一个进程能修改其他进程的memory的处理策略。接下来就是慢慢夯实API的细节,定义清楚它应该如何工作了。


全文完

LWN文章遵循CC BY-SA 4.0许可协议。

极度欢迎将文章分享到朋友圈 
热烈欢迎转载以及基于现有协议上的修改再创作~


长按下面二维码关注:Linux News搬运工,希望每周的深度文章以及开源社区的各种新近言论,能够让大家满意~


640?wx_fmt=jpeg

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值