LWN: 让RLIMIT_MEMLOCK改用一个合适的缺省值!
关注了就能看到更多这么棒的文章哦~
In search of an appropriate RLIMIT_MEMLOCK default
By Jonathan Corbet
November 19, 2021
DeepL assisted translation
https://lwn.net/Articles/876288/
通常来说,一个 13 行的、实际上只调整了一行代码的 patch 不会有多少争议。但是,这样的 patch 有时候还是会暴露出对如何管理内核行为的不同观点。Drew DeVault 的 patch 就是一个很好的例子,他显然正在从 npm 社区中抽身出来。这个 patch 给予大家重新思考内核社区应该如何为 resource limit 等可配置参数选择默认值。
内核实现了一套适用于每个运行进程(特指非特权进程)运行进程的资源限制(resource limit)。它们可以用来调节一个进程能够使用多少 CPU 时间、可以打开多少文件等等。setrlimit() 函数手册中就记录了全部的限制条件。本文中牵涉到的 RLIMIT_MEMLOCK,是对进程可以把多少内存锁定在 RAM 中进行了限制,默认值是 64KB。系统管理员可以提高它,但没有特权的进程则不能调整。
很久以前,锁定内存(locking memory)是一种特权操作,这个功能会阻止内存被换出,但它也给内核带来了资源管理问题。也就是说如果有太多的内存被 lock 在 RAM 里,那么就不会有足够的内存让系统的其他部分继续正常工作了。不过,因为 GnuPG 之类的加密工具开始广泛使用,最终导致这一功能被提供给了所有进程。GnuPG 里需要把包含敏感数据(例如密钥和口令)的内存 lock 在 RAM 里,从而防止这些数据被写入交换设备或 core-dump 文件。为了实现这种额外安全功能,内核社区向所有用户都提供了 mlock()系统调用,但将可以 lock 的 page 数量限制在一个相对较低的数值。
内存的使用随着时间的推移发生了变化。GnuPG 比起几年前来,并不需要更多的内容 lock 在 RAM,但现在其他场景中用户会触碰到 locked-memory 的上限。例如 BPF 程序就被存储在不可 swap 的内核内存中,它所使用的空间也被统计到这个限制数值里了。这些程序往往都比较小,但 64KB 对许多应用来说还是不太够。目前看来 locked memory 的最大的新出现消费者是 io_uring。
每当内核为 I/O 准备了一个用户空间的 buffer 时,该 buffer 必须在整个操作期间都要 lock 在内存中。这种 lock 状态是相对短暂的,不会从用户的限额中扣除相应的数字。然而,配置 I/O buffer 并将其 lock 在内存中的动作涉及到相当多的工作。如果该 buffer 被频繁用来进行 I/O 操作,那么这个配置和释放的开销占比就不可以忽略了,应用程序的速度可能会因此而大大降低。要想消除这种开销,其中一种方式就是让 io_uring 子系统允许用户 "注册(register)" 一些 buffer 进来。这种做法可以为 I/O 配置好一些 buffer,并将它们保留住,可以重复使用。
I/O buffer 的 size 可能会很大,所以将它们都一直锁定在内存中的话会消耗大量的 RAM。因此,对以这种方式 lock 下来的内存数量就应该进行限制了。因此,当 buffer 被注册进来时,内核会根据上述的 locked-memory limit 来进行统计以及把关。这样一来 64KB 的这个限制就真的变成了 io_uring 场景下的一个束缚。为了使 io_uring 更有实用价值,人们几乎必定会希望提高这个 limit 上限。因此,64KB 的默认限制有可能会导致用户无法使用 io_uring,除非发行版提供方或管理员来专门调整并增加这个数值——但是通常他们并没有调整过这个值。
为了避免这个问题,DeVault 希望将这个限制提高到 8MB。他认为期望这个问题在其他地方来解决,是一个不切实际的想法。
这个责任应该由内核承担。在这里解决这个问题,比起在数百个发行版中来解决这个问题要容易得多。而且,只有在这个值在几乎所有地方都被增大的情况下,最终用户的软件才真正敢于开始依靠这个设置。
Matthew Wilcox 指出,有很多其他方法可以让恶意用户至少 lock 8MB 的内存,所以他认为这一改动没有增加什么危险性,但他还是有一些保留意见。他认为也许按比例来扩大限制会更好,这样在内存较小的机器上配置更加恰当。他还想知道 8MB 是否是新的 limit 的合适值,io_uring 用户是否还需要更多内存 lock。io_uring 的维护者 Jens Axboe 回答说,"8MB 对于大多数普通应用场景来说已经足够了",而且就是这类应该应用应该能够“开箱即用”,也就是不希望需要管理员干预。
不过,Andrew Morton 不赞同使用这个数值(或者任何其他数值):
我们永远无法设置一个绝对正确的值,对吧?唯一能决定一个系统中的恰当的设置的人是这个系统的运营者。每隔几年来胡乱提高一下限额,反而会减少了人们正确对待这个问题的动力。
DeVault 回答说,"perfect is the enemy of good",他没有时间去说服所有的发行版提供方来配置一个更加符合现实的默认值。Morton 进一步建议说,这个 limit 应该从一开始就被设置为零,这样就可以迫使用户空间这边一定会提供一个解决方案,但这一建议没有得到多少回应。到了这时,基本上谈话就结束了。
这里的一个想法似乎是,内核社区不应该为 RLIMIT_MEMLOCK 这样的参数提出一个合适的默认值,从而能迫使下游的发行版提供商可以考虑他们的用户需要什么,并相应地进行配置。但这似乎会导致绝大多数情况下人们会在 kernel 层面选择维持现状,因为今后哪怕有一个有价值的新功能,它大概率也是在在大多数系统上都不会需要用到的。对合理的默认值进行一些思考,是人们通常期望每一个软件项目中都要做到的事情。不清楚为什么内核在这方面就应该有所不同。因此,这种改动很可能最终会被 merge,但也许要等到邮件回复讨论更加多一些才行吧。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~