关注了就能看到更多这么棒的文章哦~
printk() indexing
By Jonathan Corbet
May 27, 2021
DeepL assisted translation
https://lwn.net/Articles/857148/
当内核开发者想要看到关于运行中的内核状态的一些信息时,他们喜欢使用 printk()。这个操作就会生成一条 log entry 给人们阅读,但是有些成功有些不成功。其实,这些信息的最终处理者往往不是人类,内核的 log 会被那些希望发现问题的自动监控系统(monitoring system)读取。而实际情况经常工作得不太好,会出现监控系统错过重要信息的情况。printk() format indexing patch set 就是目前用来改善这种情况的众多尝试中最新的一种做法。
监控系统是由管理员安装的,用来在自己管理的系统出现问题时能及时发现。因此,比如说 CPU 突然起火了,而系统管理员并不在旁边,那么他们希望至少收到警报通知,从而他们知道应该先后给硬件供应商和消防部门打电话了。要发出这个警报的话,监控系统就需要观察内核日志中的相关代码所打印的 "CPU on fire" 这条消息。如果一切顺利的话,该信息将在 CPU 熔化之前就跳出来,管理员也可以及时开始订购系统来替换这个损坏的 CPU 了。
然后,有一天一位善意的小改动开发者出现了,他认为把消息的内容改成 "CPU in flames" 的话就会更有美感。在经过严格的 patch review 过程后,维护者正式合并了这个改动,并在 stable kernel update 中发布了出来。系统管理员在看到改进后的信息时,也会被 kernel 最新的这句话的美感深深地折服了。
但是,只有在管理员本人亲自看到这条消息时才会感受到这个美妙的改动。而不幸的是,监控系统仍然在寻找以前 "CPU on fire" 这条信息,它不仅不会被新的措辞所折服,而且还会完全错过这条消息。因此,它完全不会发出警报,系统管理员也就继续专心浏览他最喜欢的社交网站来进行美其名曰网络延迟测试的工作了,一直到喷淋器触发,一片混乱。到那时,已经来不及马上订购一个替换系统了,服务就会中断。
系统管理员非常讨厌看到这种情况。
问题是,虽然通过 printk() (以及其他基于 printk 的函数) 发出的信息会被用户空间的工具拿来分析,但这些消息从来不被认为是内核 ABI 的一部分,所以内核开发者认为可以随时改变(或删除)这些消息。这给那些创建了大量的正则表达式来检测感兴趣的信息的用户带来了麻烦,同时也阻碍了将提示信息翻译成其他语言的工作。
多年来,已经尝试过许多方法来解决这个问题。在 2011 年的内核峰会上,有一个激烈的讨论,是围绕着是否给每条内核消息添加一个 128 位的二进制 tag。这个想法跟其他所有类似的解决方案一样,都没能进入 mainline。很简单,内核社区从未觉得有必要对于通过 printk() 发布的 log 消息采用什么 struct 管理起来,这么做会耗费的额外功夫似乎完全不值得。
Chris Down 的最新提案中并未试图强制要求采用什么结构,相反,它只是收集了内核中所有的 printk()格式字符串,并通过 debugfs 中的一个文件把它们全部暴露出来。实现细节上来说,会把 printk() 改成一个 wrapper macro,其中会声明一个新的 structure 来保存格式信息。
struct pi_entry {
const char *fmt;
const char *func;
const char *file;
unsigned int line;
const char *level;
const char *pre_fmt;
const char *post_fmt;
};
这个 structure 里面保存了传递给 printk() 的格式化字符串,以及 log level、位于源码中哪个位置,以及由各种 wrapper 函数(比如 dev_printk()函数)添加的 "pre" 和 "post "数据。这个结构会被放在编译好的 kernel 中的一个特殊 p(.printk_index)中。当然,这个 wrapper macro 还会调用原有的 printk()函数(现在叫_printk())来将消息打印输出。
在编译内核时,所有这些 pi_entry structure 都被放在了 .printk_index p 里。在 mount 了 debugfs 之后,管理员可以查看 printk/index/vmlinux 这个文件,其中包含了全部的格式。printk/index 下还针对每个 loadable module 也有对应的文件节点。查看这些包含数千个格式化字符串的文件,可能会像管理员的社交媒体新闻一样有趣,但这并不是正确的使用方法。相反,监控系统可以利用这些信息来确保他们所有的检查条件仍然与内核实际会生成的消息是相匹配的。如果 "CPU on fire" 这个检测条件不再匹配得上,那么要么 CPU 已经具备了防火能力,要么就是这条 log 信息已经被改过了。如果事实证明原因是后者的话,就可以相应地更新这个检查条件了。
截至本文写作时,在这个补丁集所经历的少量 review 中还没有被提出来多少问题,尤其是这几个我们比较关心的问题。第一个是:为什么这些信息需要放在内核中?它可以被放在一个单独的文件中,不需要占用系统运行时的内存。也许原因是这种机制可以使得内核在跨系统部署时也能保证提供了正确的格式字符串列表。
另一个问题是,debugfs 明确定义为不适合用在生产系统(production system)中,但这个功能看起来是要用于 production kernel 的。如果这个机制被纳入 mainline 的话,它可能必须得另找一个更加适合的位置,比如/sys。
不过,这个改动是否会被接受还有待观察。由于这个机制不需要内核开发者做任何额外工作,而且如果把相应的 config 关闭掉的话就完全不占用任何资源,因此可能比起以前那些为了改善 kernel log message 解析的方案遇到更少阻力。如果是这样的话,监控系统仍然会面临 kernel log message 发生意外变化的情况,但至少在这种时候能知道需要修改检测条件了。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~