很简单,只需将“ printk.synchronous=1 ”添加到内核命令行。
printk() 现在已经完全异步了。您可以添加 printk.synchronous=1 让 printk() 同步。
请参阅补丁“printk:使 printk() 完全异步”:
目前, printk() 有时会等待将消息打印到控制台,有时则不会(当其他进程持有 console_sem 时)。如果 printk() 抓取 console_sem 并开始打印到控制台,它会从内核 printk 缓冲区打印消息,直到缓冲区为空。当连接串行控制台时,打印速度很慢,因此系统中的其他 CPU 有足够的时间在一个 CPU 打印时将新消息附加到缓冲区。因此,CPU 可以花费无限的时间在 console_unlock() 中进行打印。如果在禁用中断的情况下调用 printk() 调用 console_unlock(),这将是一个特别严重的问题。
在实践中,用户观察到 CPU 可能会在 console_unlock() 中花费数十秒打印(通常在发现数百个 SCSI 设备的引导期间)导致 RCU 停顿(CPU 进行打印很长时间没有达到静止状态),软锁定报告(打印 CPU 的 IPI 没有得到服务,因此其他 CPU 正在旋转等待打印 CPU 处理 IPI),并最终导致机器死亡(因为来自停顿和锁定的消息比我们能够更快地附加到 printk 缓冲区打印)。因此,这些机器无法在连接了串行控制台的情况下启动。另一个观察到的问题是,由于 printk 速度慢,硬件发现速度很慢,并且 udev 在内核设法发现所有附加的硬件之前超时。
这个补丁使 printk() 完全异步(类似于 printk_deferred() 到目前为止所做的)。它将消息附加到内核 printk 缓冲区和 wake_up() 一个特殊的专用 kthread 以执行打印到控制台。这样做的好处是打印总是发生在可调度的上下文中,因此我们不会锁定任何特定的 CPU 甚至中断。它还具有 printk() 速度快的优点,因此内核启动不会因慢速串行控制台而减慢。这种方法的缺点是在崩溃的情况下,重要消息不会出现在控制台输出中的可能性更高(我们可能需要工作调度才能将消息打印到控制台)。如果 oops 正在进行中,我们通过将 printk 切换到立即打印到控制台的原始方法来在一定程度上减轻这种风险。
暂时,异步 printk 被认为不如同步的可靠,所以默认情况下我们保持 printk 在同步模式下运行。有一个 printk.synchronous 内核参数允许选择同步/异步模式作为启动参数,或者稍后通过 sysfs 旋钮从用户空间选择。
printk() 预计在不同的条件和不同的场景下工作,包括当所有工作人员都忙(例如分配内存)时 OOM 的极端情况,因此 printk() 使用自己的专用打印 kthread,而不是依赖于工作队列(即使设置了 WQ_MEM_RECLAIM 位,我们也可能会收到打印延迟,直到工作队列声明 ->mayday,如 Tetsuo Handa 所述)。