https://lwn.net/Articles/737822/

@齐江

当内核开发者想要向用户空间传递消息时,如调试或者报告一个严重的系统问题,printk()常常是不错的选择。但是,在2017 Kernel Summit上,Steve Rostedt(同时还有Petr Mladek和Sergey Senozhatsky)指出printk()并不是很成熟。特别地,它将影响系统的整体性能。针对该问题的根因以及可能的解决方案已经有讨论,但切实的解决方案依然需要等待实现。

Rostedt说,这个问题与console lock的管理有关,该锁用于序列化访问消息打印的控制台设备。深入到printk()的实现细节,大家会发现有趣的调用序列,如:

if (console_trylock()) console_unlock();

	第一条调用尝试获取console lock,但可能不会成功;第二条,看上去是在获取到锁后又立即释放。而正是释放console lock的过程可能造成系统的问题。

	无论console lock是否可用,printk()都得继续;由于printk()在kernel的所有地方都有调用,等待任意类型的锁都将带来系统死锁的风险。因此,如果一个特定的调用无法获取console lock,则将简单地将输出缓冲起来并返回,以期望其他进程再将输出刷回到控制台。持有console lock的线程将负责该项任务,在释放锁的时候期望将所有已缓冲的输出刷回。

	在一个有着很多CPU的大型系统中,在任何时间可能存在多个线程同时调用printk()。这将导致持有console lock的线程多出很多工作。实际上,最坏的情况是,在缓冲刷回的同时输出持续堆积,使得锁持有者一直无法结束。这对系统性能以及需要运行在受影响CPU上的任务时延来说是非常糟糕的。

	Peter Zijlstra说,一旦这个问题出现,他只是移除printk()调用直到恢复。Andrew Morton对起初创建这个机制感到抱歉,他说,这是他凌晨3点的产物:) Rostedt继续提到,最坏的情况下,刷printk()的缓冲输出可能占用很长时间,以至于触发watchdog和系统崩溃。如果系统中有100个CPU,其中一个可能永远在刷printk()的缓冲输出。

	他说,针对该问题存在几个可能的解决方案。一个是如Zijlstra建议的移除printk(),但这如打地鼠(whack-a-mole)游戏一样并没有实际解决该问题。另一个备选是一种新的锁机制,当第二个线程尝试获取console lock时,自旋等待其可用。当前锁持有者将看到有个等待者并释放锁;然后第二个线程将获取到锁,并接着刷输出缓冲。如果多个CPU正在产生输出,锁将在他们中间逐个传递,而不至于存在某一个CPU持续长时间打印输出。

	Jan Kara说他曾经尝试过实现类似的机制,但遇到各种特殊的场景并最终放弃。Mathieu Desnoyers建议延迟打印工作到一个工作队列来执行,而不是立即输出;Ben Herrenschmidt同意这个观点,说并没有立即输出的切实需求。但Rostedt回答到,Linus Torvalds坚持要求crash dump必须立即输出,因此延迟输出并非适用任意场景。entireprintk()缓冲必须尽快打印。

	还有一些这对新方案的细节讨论,但并没有达成实质结论。在新机制实现并发布出来后,该讨论仍将继续。