关注了就能看到更多这么棒的文章哦~
By Jonathan Corbet
March 16, 2020
原文来自:https://lwn.net/Articles/815118/
过去十多年里人们开始大量采用同一种做法:就是在各个新增的系统调用里面加上了"flags"参数,甚至哪怕并没有真正用起来,也会先加上再说。好的方面,这确实有助于增强API的可扩展性,不过我们我也看到一些相关系统调用里增加了许多各种各样的flags。尤其是在file和filesystem相关的系统调用里,flags太多了,许多调用甚至需要使用3个参数位置来设置flags。
了解Unix系统调用API的朋友都很熟悉一组用在文件系统上的flags:o_flags,用在open()等调用中。这些flags会影响系统调用的具体行为,例如O_CREAT会在文件不存在的时候创建并打开,O_NOFOLLOW发现文件名指向的是个符号链接的时候就直接返回错误,O_NONBLOCK要求这个操作不能阻塞,等等。其中一些flag会影响文件查找过程(比如O_NOFOLLOW),一些flag例如O_NONBLOCK则会影响拿到的文件描述符今后的具体行为。这些都定义在同一个flag namespace里,所有open()类似的系统调用都会支持。
要在目录中创建新的项目的话,open()只是其中一种方式,link()也可以做到。当人们要给link()增加flags的时候,就创建了linkat()系统调用,它采用了一种新式方法来拿到目录对应的文件描述符。linkat()有一组跟此前不同的flag(AT_flags),比如AT_SYMLINK_FOLLOW,这个含义其实刚好跟O_NOFOLLOW相反。还有一个AT_SYMLINK_NOFOLLOW,不过linkat()并不支持这个flag,只有fchmodat()和execveat()这种API才支持。还有许多其他的AT_flags,比如AT_NO_AUTOMOUNT,用在新近的statx()系统调用上。
后来又有了openat2(),这是在5.6 kernel中的API。它没有用一个单独的flag参数,而是要求提供一个指向open_how结构的指针:
struct open_how {
__u64 flags;
__u64 mode;
__u64 resolve;
};
其中的flags就是包括那些open()系列函数所用的O_flags,而resolve则是另一组flags(称为RESOLVE_flags),包括RESOLVE_BENEATH,限制只能查找指定目录之下的文件,RESOLVE_NO_SYMLINKS则是有点类似O_NOFOLLOW或者AT_SYMLINK_NOFOLLOW的,不过有区别,它会在解析路径节点的过程中任何阶段都会阻止展开符号链接,而不是只看最后一个节点是不是符号链接。
LWN此前介绍过fsinfo()系统调用的来龙去脉。它是用来提供mount上来的文件系统的信息的。这个新API也包括一个结构指针作为参数:
struct fsinfo_params {
__u32 at_flags;
__u32 flags;
__u32 request;
__u32 Nth;
__u32 Mth;
__u64 __reserved[3];
};
这里的at_flags就是指前面讲过的AT_flags,而flags则是此API专用的另一组flag。最近fsinfo()的作者David Howells说有人告诉他在新的系统调用上不应该用AT_flags,而是应该用RESOLVE_flags,所以他文是不是AT_flags已经算作deprecated(即将废弃)状态了。后来他提了一个patch把AT_flags标为deprecated,然后增加了一些RESOLVE_flags来覆盖一些暂时未有支持的AT_flags功能。比如,增加了RESOLVE_NO_TERMINAL_SYMLINKS(后来又改名为RESOLVE_NO_TRAILING_SYMLINKS),实现了AT_SYMLINK_NOFOLLOW的相同作用。
Christian Brauner也比较喜欢RESOLVE_flags,认为除了openat()之外今后其他地方可能也会用到一些RESOLVE_flags特有的含义。不过他也认为“我们需要避免再让userspace看到一组新的flags感到混淆了”,不过其他人觉得现在担心这个可能有点晚了。
不过Linux Torvalds不太喜欢把AT_flags标为deprecated的注意。他认为软件会继续使用O_NOFOLLOW或者AT_SYMLINK_NOFOLLOW,不能简单把它们删掉。他补充说:
And yes, the fact that we then have three different user-visible namespaces (O_xyz flags for open(), AT_xyz flags for linkat(), and now RESOLVE_xyz flags for openat2()) is sad and messy. But it's an inherent messiness from just how the world works. We can't get rid of it.
(大意:现在我们有3组flags用在各种API上,确实有点太乱了,不过这是现实世界里的继承问题,我们没法简单删掉)
他说,用多种flag来实现类似的一组功能,这会导致许多复杂性,让人们搞不清楚该怎么用。我们可以认为他的意思是所有这类patch今后都无法合入mainline。后来他又说,如果fsinfo()既需要AT_flags也需要RESOLVE_flags的功能,那么fsinfo()就应该能接受这两种flags。这样再加上它自己特有的flag,就是一个API带有3种flags了。可以预料,按这种做法的话,今后如果要实现个openat2()来使用AT_flag的话,那么它也需要接受第三种flag了。
这种情况确实比较“sad and messy”,看起来短时间内也不会怎么变好。这个API其中最乱的一点,就是它对这些flags没有进行类型检查。开发者只有靠自己小心检查来避免自己把flag填错位置了。这一点在今后也很难改变了,哪怕有人愿意修改也没用,因为要保证向后兼容。这还不是咱们这么多API里面设计的最乱的一个,只能先继续带病生存了。
全文完
LWN文章遵循CC BY-SA 4.0许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注LWN深度文章以及开源社区的各种新近言论~