Skip to content

导引:点亮什么、为什么需要它

004 我们让内核跑起来了,可它只会往 debugcon 吐单字符(PL===CPP),既不能打印一个数字,也没法把 BootInfo 里那张内存图好好 dump 出来。更要命的是——我们改一行代码,除了"重跑看它崩不崩"之外没有任何验证手段。这一章,我们要给内核装上真正的输出(串口 + kprintf)和真正的测试(host 单测 + QEMU 内核测试双轨)。从这以后,内核才算"会说话",我们也才算有底气继续往上堆功能。

这一章我们要点亮什么

三件事,一件比一件实在。

第一,写一个串口驱动:让内核能往 COM1(端口 0x3F8)一个字节一个字节地输出文本,QEMU 的 -serial stdio 直接接住,终端里就能看到内核在说什么。

第二,写一个 kprintf——内核版的 printf。给它一个格式串和几个参数,它能把 %d%x%p 这些格式化好地打到串口上(顺带还支持 %b 二进制,这是 Cinux 自己加的)。

第三,搭一套双轨测试:一边是 host 上跑的单元测试(用宿主机 g++ 编,CTest 驱动),另一边是 QEMU 里跑的内核测试(和真内核同样的编译选项,跑完自动退出)。两边共享同一份纯算法代码。

做完之后,make test 会先在 host 上跑格式化的边界测试,再在 QEMU 里跑一遍内核,串口打印出 === All tests completed === 后干净退出。整个内核第一次有了"改完能自动验证"的能力。

为什么现在需要它

004 的内核有个很尴尬的处境:它已经能跑 C++ 了,可一旦想确认"我的 BootInfo 读对了没"、"那张 E820 内存图有几条、各多大",你毫无办法。debugcon 只能吐单字符,想打个 42 都得自己拆位。没有结构化输出,后面的内存管理、进程这些东西根本没法调试——你连"分配了多少"都打印不出来。

但更深的问题不是输出,是测试。到目前为止,我们验证内核的方式只有一种:重编、重跑、看它崩不崩。这种验证粗糙得可怕——一个边界 bug(比如 format_decimal 遇到 INT64_MIN 直接溢出)可能要等到很久以后某个偶然的场景才暴露。我们需要的,是把内核里那些和硬件无关的纯算法(比如"把一个整数转成字符串")单独拎出来,在 host 上用正常的测试框架去磨它。

这正好串起了这一章的核心设计。串口和 kprintf 解决"怎么输出";而 kprintf 之所以能做到 host 可测,是因为我们把"格式化算法"(format.cpp)和"输出到哪"(serial 还是 debugcon)解耦了——那个算法是纯函数,既能编进内核,也能编进 host 测试。这就是这一章最值钱的一个架构决定。

外部依据:OSDev 的 Serial Ports 页描述了 16550 UART 的寄存器布局与 LSR 状态位;PC 的 COM 端口标准(COM1 基址 0x3F8)是 IBM PC 定下的约定。

035_multi_terminal-45-gf25de18 · f25de18 · 2026-08-04