RustROS · 2026-10-26 · 主线

内核评估:为什么每次都要重启

内核做小是为了热更新、几乎不用重启。评估发现:现在每次发布都重启,主要原因不是内核的大小,而是模块接口的指纹覆盖了整个接口 crate(连注释),以及版本号编进了内核。下面是现状、原因和建议;集中瘦身放在 M7a 之后。

目的:内核做小是为了热更新、几乎不用重启(用户,2026-10-26)。所以衡量标准是 多久必须换一次内核,字节数其次。这份评估不改代码,列出现状、原因和建议; 集中瘦身放在 M7a 之后(用户已同意的顺序),之前的里程碑只顺路做。

1. 现状

内核槽里的文件 340 KiB(LZ4 压缩、4 KiB 对齐):

部分未压缩压缩后
内核本体(代码 196 KiB + 只读数据 34 KiB)230 KiB142 KiB
引导模块(引导用协议栈、virtio、存储、模块管理、救援、DHCP、更新服务)300 KiB181 KiB
PE 引导代码、引导模块目录、对齐—约 17 KiB

内核本体按部分(代码和只读数据,字节;改动次数是 2026-10-22 以来碰过该文件的提交数):

部分大小改动是什么结论
proc、proc/host.rs、proc/image.rs34.8 K22进程表、clone/线程、信号投递、共享映射与页缓存、exec 和 ELF 加载、定时器、/proc 的数据机制留下;ELF 加载、clone 标志的语义、/proc 数据整理可以移到 linux 模块(内核只映射段、建线程)
kmain(main.rs)31.9 K40主循环,很多东西被内联进来改得最勤:先拆开看清楚内联了什么,再把非循环的部分移走
sys24.0 K30给应用的接口(Sys/Net/Kv/Admin 的实现):文件列表、模块列表、spawn、分区增减第二勤:Admin、Kv 的文件辅助大多是策略,可以放到一个"管理"模块,内核只给最小的原语
modules14.7 K15模块安装、启动顺序、状态(modules::apply 一个函数 12 K)策略部分移到 modman(已经在引导模块里),内核只留加载和替换
uefi + UEFI 入口 + bootstate19.8 K1启动项、BootNext、引导状态(只在开机、退出固件服务前跑一次)适合移到 efiboot 加载器;uefi.rs 是用户写的,由用户决定
vm10.0 K6地址空间、缺页、写时复制机制,留下
services、datapath、loader14.1 K11服务替换、最底层收发包、模块加载机制,留下
perf、font、console、acpi6.9 K10性能统计、控制台输出、ACPI输出留下;性能报告的格式化可以移出
库代码(core、alloc、util)约 35 K—格式化、集合随调用者移走而变小

另外 bss 388 KiB(64 KiB 启动堆、陷阱栈、控制台缓冲区)不在文件里。

2. 为什么现在每次发布都要重启

到目前为止每个版本都换了内核、重启了一次。原因按影响排序:

  1. 模块接口的指纹是整个 api crate 源码的哈希(crates/api/build.rs,连注释 一起),模块和内核指纹不同就不加载。所以任何接口改动,包括只在两个模块之间用的 接口(比如 linux 模块和 vfs 模块之间的 Vfs),甚至改一行注释,都要换内核。 M2、M3、M4 都改了接口,这一条单独就足以让每次发布都重启。
  2. 版本号编进内核(rustros_api::BUILD):内核代码没变,文件也变了,部署流程 就照样上传内核、重启。
  3. 内核真的改了:每个里程碑都给内核加了机制(M1 进程,M2 VFS 宿主,M3 线程信号 共享映射,M4 套接字事件唤醒)。功能还在搭建,这一条暂时免不了。

字节数影响的只是第 3 条的一部分;前两条和内核大小无关,却是现在重启的主要原因。

3. 建议

现在就定的规则

入口、模块加载与替换、最底层收发包)的才进内核。

小而值得的改动(建议在 N 之前做,需要用户同意)

  1. 指纹只看接口本身:哈希去掉注释和空白(改注释、改说明不再要求换内核)。
  2. 内核没变就不换内核:内核文件不带版本号(版本号由引导状态或模块告诉内核),发布 工具比较内核本身有没有变化;没变时清单里沿用上一个内核,部署只换模块,不重启。
  3. 按接口分开指纹:内核和模块之间的宿主接口一个指纹,各服务接口(Vfs、 Filesystem、NetStack、Syscalls……)各一个;加载时只核对用到的。之后改 Vfs 只要求用 Vfs 的一方匹配。内核本身也调用这些服务(它是中枢),所以这一条单独 还不能让接口变化免重启,但能看清每次到底是哪个接口逼着换内核。

M7a 之后的集中瘦身

  1. 稳定的服务接口:内核调用的服务接口改成显式、带版本、只追加的函数表(不用 Rust 的 trait 对象,它的布局没有保证)。这是"接口变化也不用重启"的前提,工作量 最大,等进程、容器、网络的机制定型后做。
  2. 按第 1 节的表移出策略代码:sys 的管理接口、modules::apply 的策略、proc 里的 ELF 加载和 /proc 数据、kmain 里内联进来的非循环部分、性能报告。
  3. uefi.rs 移到 efiboot(用户决定)。

顺路做