LWN:数据类型的命名差异!
关注了就能看到更多这么棒的文章哦~
What's in a (type) name?
By Jonathan Corbet
September 2, 2022
DeepL assisted translation
https://lwn.net/Articles/906496/
内核的 manual pages (https://www.kernel.org/doc/man-pages/ )项目的地位比较特别。它是作为一个独立的项目来管理的,没有跟内核的文档放在一起,它的任务是记录内核中系统调用的接口,以及由 C 库为这些接口所提供的 wrapper API。有时这两个目的会有一些冲突,这可以从去年的一个关于是否使用 C 标准类型名称(standard type names)来描述内核定义的结构的讨论中就可以看到。
C 语言的 <stdint.h> 头文件为那些需要准确规定如何表示整型变量的开发者定义了一些 type。例如,int16_t 是一个 16 位的有符号类型,而 uint64_t 是一个 64 位、无符号类型。在定义那些硬件实现的、通过通信协议交换的、或在用户和内核空间之间传递的数据结构时,需要控制到这种程度。
然而,内核并不使用这些类型来定义其系统调用接口。相反,内核有自己的内部定义的类型。例如,内核的 API 定义不是使用 uint64_t,而是使用 __u64。这种情况已经存在了很久,实际上在标准 C 类型出现之前就已经存在了,这是 kernel 项目日常工作方式的一部分。
一般来说,man page 需要展示出内核中对数据类型的定义。因此,例如,bpf()手册中对 bpf_attr union 中的一部分的定义是:
struct { /* Used by BPF_MAP_*_ELEM and BPF_MAP_GET_NEXT_KEY
commands */
__u32 map_fd;
__aligned_u64 key;
union {
__aligned_u64 value;
__aligned_u64 next_key;
};
__u64 flags;
};
这些类型对于内核开发者来说会非常熟悉,但对于用户空间开发者来说,它们看起来就可能有点怪了。早在 2021 年 4 月,man-pages 的共同维护者 Alejandro Colomar 就决定来改写 man pages,使用标准的 C 类型,让这些内容看起来更熟悉和正常一些。也许是因为他喜欢挑战,Colomar 是从 bpf() man page 开始的;在打上了他的 patch 后,上述结构被定义为:
struct { /* Used by BPF_MAP_*_ELEM and BPF_MAP_GET_NEXT_KEY commands */
uint32_t map_fd;
uint64_t [[gnu::aligned(8)]] key;
union {
uint64_t [[gnu::aligned(8)]] value;
uint64_t [[gnu::aligned(8)]] next_key;
};
uint64_t flags;
};
这个 patch 立即被 BPF 维护者 Alexei Starovoitov 否决了,他说:"man page 应该按照.h 文件中的方式来描述内核 api"。Colomar 回答说,无论哪种方式,实际使用的类型都是一样的,而且他改动之后对用户来说更好:
如果我们有一个固定宽度的整数类型的标准语法(实际上其他类型也一样),那么 man page 就应该尽可能地遵循这个标准。任何背离标准(无论是 C 还是 POSIX)的做法都应该有一个强大的理由,否则就只会造成混乱。
但 Starovoitov 坚决反对这个做法,他说,man page 应该按照代码所 include 的相关内核头文件里的定义来描述这些类型。
2021 年 5 月,Colomar 又回来提供了新版本的 patch,跟前一版本相比变化不大。同样没有变化的是人们对它的态度。这一次,Greg Kroah-Hartman 也表达了他的反对意见,他说所涉及的类型 "不一样,它们生活在不同的命名空间和世界中,不可能在所有场景下都是可以直接交换的"。不过,GNU C 库的开发者 Zack Weinberg 并不同意这个观点:
C 结构的手册文档 不应该 跟头文件中的实际声明一致。文档中的字段类型通常是跟实际使用的类型在赋值时兼容(assignment-compatible),但并非总是如此。我们不能保证字段的顺序与头文件相同,也不能保证列出的字段的集合是完整的。
这个论点未能说服内核社区,他们仍然强烈反对这一改动。这场讨论后来平息了一年多。
2022 年 8 月,Colomar 又回来了,同样是带着一个新的 patch,修改了更多的文件;他提供了从三个不同的开发者那里得到的 Nacked-by tag。不出所料,这些开发者在风平浪静的这一年间并没有改变态度来同情这个做法。Starovoitov 重申了他的反对意见,并要求 Colomar 不要再发送 patch 了:
Colomar 的回应是继续推进,并将该 patch 合入到 man-pages 仓库中了。如果有一个内核补丁遇到了这样的反对,几乎可以肯定永远不会被合入,但 man-pages 不是 kernel 项目。Colomar 似乎是目前唯一活跃的 man-pages 维护者;自从 2021 年 8 月 man pages 5.13 发布后,长期维护者 Michael Kerrisk 似乎已经从相关活动中消失了。因此,当涉及到这个领域的决定时,没有人能够推翻 Colomar 的决定。
这次讨论的大部分内容都跟之前类似,但这次 Linus Torvalds 也加入了讨论。他指出,内核的类型不可能与标准的 C 类型相同,否则会产生命名空间(namespaces)问题:内核不能包括 <stdint.h> 来定义这些类型,但也不能在用户空间使用的文件中定义这些类型且不产生冲突。Torvalds 同意其他人的意见,即文档应该与实际使用的类型相匹配:
老实说,我不认为这有 很大 的区别,但是跟文档来源不匹配的文档最终只会让人产生困惑。有人会说 "这不对",甚至可能修改 struct 的定义来跟文档相符。
这条信息,加上 Kroah-Hartman 提出的 revert 这个改动的要求,说服 Colomar 退让了。他的总结语是:
你说服了我。手册将完全按照内核中的类型来进行记录。这样更简单。
由于这个 patch 在 Greg 要求我这么做的情况下最近被我 revert 掉了,所以我将保持这种方式。我想这可以结束 man-pages 的讨论。
当然,有趣的是,内核确实在内部定义了许多标准类型(standard types),而且有成千上万的变量都是用这些类型定义的。在内核中使用标准 C 类型本身不是问题,只有在用户空间的 API 定义中使用它们时,才会有问题。如果有足够强的意愿,很可能是一个可以解决的问题,但这不是个不费吹灰之力的小工作。所以看起来 man-page 将继续按照内核的用户空间 API 头文件中实际使用的类型来进行记录。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~