LWN:系统调用怎样才能做到扩展性最佳?

news/2024/7/7 14:35:43 标签: 编程语言, python, java, 人工智能, 区块链

关注了就能看到更多这么棒的文章哦~

Conventions for extensible system calls

By Jonathan Corbet
September 8, 2020
LPC
原文来自:https://lwn.net/Articles/830666/
DeepL assisted translation

内核其实并不只有一个系统调用可以用来重命名文件,而是有三个:rename()、renameat()和 renameat2()。每一个调用都是在前一个调用被证明无法支持某个新功能时添加的。类似的事情在其他一些系统调用中也发生过,都是因为需要一个功能,但是当前接口无法实现,于是又再一次地创建了一个新的类似接口。在 2020 年的 Linux Plumbers Conference 上,Christian Brauner 和 Aleksa Sarai 举办了两场会议,重点讨论了如何创建面向未来(future-proof)的系统调用,当需要新功能时,总是可以扩展来支持。

Brauner 首先指出,系统调用的可扩展性在邮件列表中已经经过了反复讨论。对于每一个新的系统调用来说,通常都会出现类似争论。一般来说,开发人员试图从两种模式中选一种:一个是完整的多路复用方式(multiplexer),使用一个系统调用来处理多个功能;或者创建一系列新的系统调用,每个都是单一用途(single-purpose)。他说,我们已经用这两种方式把咱们自己和用户空间开发者都弄得很不爽了。这里一直没有很好的准则可供遵循,最好是能建立一些约定方式,大家能就未来内核 API 应该如何设计达成一致。

对系统调用的要求应该更严一些,而且要在文档中记录好。每个新调用都应该有最低限度的可扩展性,这样就不会再需要创建一个 renameat2()。他说,最起码可以基于提供一个 flags 参数这种方式,这可以说是今天新系统调用都在遵循的方式。这里产生了一个简短的偏题讨论,为什么 flags 参数的类型应该是 unsigned int,回答简要来说是因为 signed 类型可以被符号扩展,可能会导致许多没打算设置的 flag bit 被设置上。

Sarai 接替进行演讲,讨论了现在存在的各种处理系统调用扩展性的方法。其中之一,就是直接添加一个新的系统调用,这很简单直接,但它给用户空间代码带来了很大的负担,它们必须修改代码才能使用这个新的调用 API。并且还需要检查新的调用在当前系统上是否有支持(在不支持新调用的情况下就要退回到其他的解决方案中)。他说,走另一个极端的方案就是多路复用函数(multiplexer),这个方案本身有很大的问题。

还有另一种方法是设置一个 flag(当然是要在支持 flag 的系统调用才行),并让系统调用采取可变的参数数量;其中,可变的调用(variadic call)很难在 C 库中正确支持。因为必须传递所有可能的参数,导致也会将 stack 上的垃圾数据传递给内核。系统调用可以设计成使用固定大小的结构作为参数,目标是让这些结构包含足够的 padding 区域,以支持未来的任何需求。他说,这种方法的问题是,它需要有预测未来的能力,而事实证明这很难做到。

最后,他说,可以通过造一台时间机器来解决这个问题。当然这个解决方案,也是比较困难的。。。

Extensible structs

Brauner 和 Sarai 正在推行一种新的解决方案,他们称之为 "extensible structs",这是在设计 openat2()系统调用时使用的方法。这种机制的工作原理是将系统调用的参数汇集到一个单一的 C struct 中,系统调用传递的参数中就包含指向该结构的指针,以及该结构的 size。这个 size 参数相当于是一个版本号一样。当需要向内核传递新数据的方式,从而导致需要扩展系统调用时,新的字段会被添加到 struct 的末尾,从而增加 struct size。这些新的字段设计时很小心,值为 0 就意味着行为跟添加这个字段之前的行为保持一致。

当系统调用从用户空间发起时,内核会将传入的结构体大小与它自己认为的结构体应该有多大进行比较。如果来自用户空间的大小较小,这表明调用者期望使用旧版本的系统调用,然后用户空间没有提供的字段就会被填入零,系统调用可以照常进行,行为不会有异常。如果,内核的大小反而小于从用户空间传递过来的结构,那么内核就是老版本的一方。在这种情况下,所有多余的字段(从内核的角度来看)都会被检查一下,如果发现它们都为零,调用可以继续进行。否则,调用失败,返回 E2BIG 错误值,因为用户空间请求的功能是内核不支持的。

H. Peter Anvin 问, E2BIG 这个返回值有什么用。Sarai 回答说,它的含义是 "参数列表太长",他也承认这有点奇怪,但 "it makes sense if you squint(斜着眼看的话这个意义很合适)"(大概是在讲笑话吧)。

[Aleksa Sarai, Arnd Bergmann、Christian Brauner 和 Florian Weimer]

Brauner 强调说,规则上应该禁止今后新增的 multiplexer system call 这种多路复用系统调用。这个原则基本上现在大家都赞同了。bpf()并不是一个可扩展的系统调用,它是一个多路复用接口,恰好使用了可扩展的结构。所以在将来设计新的系统调用时, bpf()并不是很好的参考例子。GNU C 库的开发者也明确表示,他们不希望再看到任何多路复用系统调用,因为在 C libary 这个层面上很难处理。Arnd Bergmann 说,一些开发者已经在阻止多路复用系统调用合入了,但很难及时发现所有的这种例子。Brauner 回答说,这正是为什么应该把这个规则写到文档中去的原因。

Anvin 问道,为什么结构大小要作为一个单独的参数传递,而不是嵌入结构本身。Brauner 回答说,在参数列表中传递大小感觉更 "像 C",但这并不那么重要啦。不过社区应该选择一种约定俗成的方式。Anvin 说,如果 struct 被传递给不同的用户空间 layer,而这些用户空间 layer 对它的正确大小可能有不同的想法,那么单独使用 size 参数就会产生问题:可能会导致向内核传递 bad data。

Kees Cook 则表示,用户空间的混乱比内核的混乱要好。如果内核从 struct 内部读取 size,然后用这个 size 把 struct 本身读到内核空间,那就会有问题:这个 size 可能在两次读取之间发生了变化,从而导致内核内部的漏洞。因此,他说,size 最好作为一个单独的元素来传递。当被问及哪种约定对 C library 更好时,Florian Weimer 回答说,他对这两种方式都没有强烈偏好。

Brauner 说,过去,Al Viro 把 extensible structs 称为 "crap insertion vector",说它们可以被用来在没有审查的情况下,把新功能偷偷塞进内核。Brauner 说,这种观点并不成立。举例来说,当时引起人们这种看法的那个新功能,确实被 review 过程给揪出来了。他觉得这里出问题的可能性并不比其他类型的系统调用更大。

这部分讨论的最后,Brauner 说,新的约定需要添加到文档中。目前关于添加系统调用的文档并没有及时更新。他过去曾试图更新这个文档,但 "基本上没有人注意到"。他一直在努力让更多人能赞同 extensiblestructs。同时,他已经用它来成功提交了两个新的系统调用。他将致力于编写新版本的文档 patch,会好好介绍一下他所得到的当前最佳方案。

Probing feature support

extensible structs 可能可以解决在系统调用中添加新功能的问题,但它们本身并不能解决问题的另一面:帮助用户空间弄清楚在一个给定的系统上到底支持哪些功能。正如 Brauner 所指出的那样,程序通常不得不采取一种试错(trial-and-error)的方法,即尝试执行每一个可以用的 API,看看它是否真的有效。这个过程很痛苦的,而且会变得很浪费时间,特别是如果这个代码实现在 library 或者一些需要短暂运行的程序中。我们应该可以有更好的方案的。关于如何做得更好的对话就这么开始了,,然后在后面的 birds-of-a-feather 环节继续进行。

Sarai 提到了一个一直有人在提的建议:在系统调用中增加一个 "no-op"标志。内核会返回 extensible struct 的一个副本,并将 kernel 所有支持的 flag bit 都置位,那些不是 flag 的字段则用最高支持值来填充。如果所查询的操作完全不支持,则会返回一个唯一的错误代码。Bergmann 很快就指出,这个标志会把系统调用变成一种具有多个函数的多路复用调用,但他也承认,他可以理解这种做法的好处。

另一种方案是创建一个新的系统调用,专门用于检查其他系统调用支持哪些功能。这在实现上并不是很简单,因为它需要在内核中增加一个新的 infrastructure 来定义系统调用的功能。Brauner 指出,有两种特定类型的可扩展性需要支持:添加一个新的 flag,以及增加 struct 的 size。

Bergmann 建议了一个改动最小的解决方案,在内核输出到用户空间的 VDSO 区域中增加一个 counter。每次增加一个新功能,counter 就会增加。Cook 认为这有点太简陋了。用户空间更希望能直接查询到某个特定功能是不是可用。Weimer 说,这将使编写可移植的软件(portable software)变得更加困难,因为需要测试所有功能的各种组合,但 Cook 回答说,这个问题早就已经存在,查询功能的能力只是把它暴露出来而已,反而会让它更快得到解决。Mark Rutland 建议先挑一个系统调用来开始,定义一下到底哪些信息会需要暴露给 user space。Brauner 认为 clone3()可以作为一个很好的开始选项。

大家似乎一致认为,解决这个问题的正确方法是使用一个新增的系统调用,所以讨论接下来转到了这个系统调用应该是什么样的。Brauner 首先建议,这个新的调用可以接受所关注的系统调用的编号作为参数,返回当前的有效 flag 的集合,以及 struct size。Sarai 指出,openat2()有两个 flag 参数,这样情况就变得复杂。Weimer 则表示,他希望能够查询 vfork()是否可用,但那里根本没有 flags 参数。

Mark Rutland 建议使用类似这样的 API:

int sys_features(int syscall_no, u32 *map, size_t mapsize);

内核会接受 map 参数作为一个 bitmap,其中指明了所请求的系统调用的可用功能。每个系统调用都会有一组常量,用于指代每个可能存在或不存在的功能,每个常量对应一个 bit,如果该特征是可用的,就会在 map 中设置为 1。这个建议在讨论中似乎获得了一些支持。

Brauner 说,还需要将向内核添加系统调用的过程自动化,最好别用 Perl 编写的。这样它可以执行一些检查,包括 struct size 的描述是否正确,sys_features()信息是否正确。Bergmann 建议扩展内核中的 DECLARE_SYSCALL()宏,把支持的功能的 bitmap 作为一个额外的参数。

交流的时间稍微有点长,但结果已经很清楚了。对于 extensible structs 部分,主要需要做的事情是更新文档来反映大家关于如何处理可扩展性的新共识(如果大家确实都赞同这个共识的话)。对于功能查询(feature-query)的系统调用,要做的是写一些代码来展示实际的工作方式。一旦这个概念进入邮件列表,最终可能会变得面目全非。我们只能希望最终的结果是一个正确方案,这样我们将来就不再需要 sys_features2()了~

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

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~


http://www.niftyadmin.cn/n/733659.html

相关文章

pg相关命令

1、修改pg数量: ceph osd pool set test pg_num 40 ceph osd pool set test pgp_num 40 2、查看pg和osd的映射关系: ceph pg dump | grep ^4. |awk ‘{print $1 “\t” $17 “\t” $10}’ 3、查看pg状态: ceph pg dump pgs_brief 4、获取pg数…

AndroidPullToRefresh拉动效果配置

为什么80%的码农都做不了架构师?>>> 最近用了 开源的 AndroidPullToRefresh 库,但是发现拉动时的效果有个很奇怪的地方,无论上下拉动,当列表滚动到顶部或底部时,会瞬间弹出半个列表高度的拉动提示&#xf…

LWN:Lua in the kernel!

关注了就能看到更多这么棒的文章哦~Lua in the kernel?By Jake EdgeSeptember 9, 2020Netdev原文来自:https://lwn.net/Articles/830154/DeepL assisted translationBPF,是 Linux 内核中用于网络(和其他一些)部分的专门定制的语言&#xff0…

SQL server2005链接Access linked servers providers 没有 microsoft.ACE.oledb.12.0

SQL server2005链接Access linked servers providers 没有 microsoft.ACE.oledb.12.0, 需要安装 Microsoft Access Database Engine 2007 注意:Microsoft Access Database Engine 2007的位数要与操作系统一致 下载地址 https://www.microsoft.com/en-us/download/d…

ceph balancer相关命令

ceph mgr module ls ceph mgr module enable balancer ceph balancer on ceph balancer mode crush-compat ceph config-key set “mgr/balancer/max_misplaced”: “0.01” ceph config-key dump ceph balancer status ceph balancer eval ceph balancer optimize myplan ceph…

LWN:LPC 2020上安卓内核相关讨论!

关注了就能看到更多这么棒的文章哦~Android kernel notes from LPC 2020By Jonathan CorbetSeptember 10, 2020LPC原文来自:https://lwn.net/Articles/830979/DeepL assisted translation在 Android 项目的早期,它的 kernel 代码跟 kernel co…

北京欢乐谷一日游

首先附上欢乐谷地图,右下角入园 主要游玩路线 欢乐时光 》甜品王国》香格里拉》失落玛雅》爱琴海》亚特兰蒂斯》华侨城大剧院 欢乐时光必玩项目: 先在欢乐时光玩两个项目:欢乐风火轮和极速飞车 欢乐风火轮 极速飞车 甜品王国必玩项目 尖…

LWN:GCC也支持BPF了!

关注了就能看到更多这么棒的文章哦~BPF in GCCBy Jake EdgeSeptember 15, 2020LPC原文来自:https://lwn.net/Articles/831402/DeepL assisted translationBPF virtual machine 在内核中的应用越来越广泛,但直到最近 GCC 才在支持这个 target …