RustROS · 2026-10-30 · 内存

小内存容器:文件页共享,写时复制

面板卖 128、256、512 MiB 的容器,每个容器首次启动都要用 apt 装 openssh-server。以前在 256 MiB 里 fork 失败;现在 128 MiB 全部通过,64 MiB 和 Linux 的 LXC 表现一样。

内核改动有,需重启一次PE 文件 405,504 → 421,888 字节
接口改动有进程的文件映射、exec、存储容量;所有模块重装
回归测试通过260 个单元测试;file_test 修正后单独重跑通过
转发18.6 Gbit/s · 383 kppsTCP 单流 / 64 字节单流,和之前持平

问题以前为什么不够用

程序用 mmap 私有映射一个文件时,内核当场把整个文件复制进这个进程;程序本身(bash、perl、apt)的各段也是整个复制。apt 装包时同时有 apt-get、debconf、apt-extracttemplates、dpkg 等多个进程,每个都把 libc、libapt-pkg、包缓存各复制一份,256 MiB 的容器就 fork 失败了。

做法和 Linux 一样的三层

验收真实的 LXC Debian 13 cloud 容器(路由方式,独立 IP)

容器内存首次启动装 openssh-server、SSHGo 静态 / cgoapt 装 python3、导入、pip
256 MiB通过通过通过
128 MiB通过通过通过
64 MiB通过通过apt-get 内存不够被终止

另外 Debian 13 在只有 256 MiB 的虚拟机里(进程可用约 150 MiB):Go、apt 装 curl/python3/pip、LTP 288 项通过,没有 "Failed to fork"。

对照Linux 在同样限制下

在测试机上用同一个镜像,cgroup v2 内存上限、不开 swap(LXC 的做法):

Linux memcgapt update装 openssh-server装 python3
64 MiB通过通过内存不够被终止
32 MiB被终止被终止被终止

所以 64 MiB 时 RustROS 和 Linux 一样:能装 openssh、能登录,装 python3 不够。apt-get 本身建包缓存要约 46 MiB 匿名内存,64 MiB 下两边都很紧。32 MiB 两边都跑不了 apt。

接口node.info 的磁盘

disk_gib / disk_free_gib 以前报文件分区当前的大小(新装的机器从 1 GiB 起、自动每次长 1 GiB),面板会以为母鸡只有 1 GiB。现在报文件分区能自动长到的容量(到磁盘末尾)和其中的可用部分。

测试改动和修正

之后内存超开(2026-10-30 补充)

设计见 docs/memory-overcommit-design.md。已做:全机回收文件页;模块 swap 把冷的私有页压缩留在内存(默认最多占四分之一内存);放不下或压缩不了的页可以写到磁盘上的换页文件,用每次启动随机生成的密钥加密(默认关闭,cfg swap disk_mib);cfg node overcommit 和 node.info 的内存指标。

测试结果
约 150 MiB 可用内存里 300 MiB 私有内存(容易压缩的页),加 fork,加压缩期间替换模块内容全对;181 MiB 压到 11 MiB(测试页面几乎全是零,真实程序的压缩率低得多)
250 MiB 压缩不了的数据最多 210 MiB 在磁盘上,内容全对
固定 150 kpps 转发,同时持续往磁盘换页(最差情况)转发约 131 kpps(不换页时 150.0),少量丢包

没有做:异步磁盘读写(上面那 13% 的代价就是它造成的)、容器之间按公平份额挑选被换出的页(现在是最大的进程先换)。所以磁盘换页默认关闭,只作最后手段;在这两项和真实负载测试之前,不建议把超开比例调到 1.0 以上卖。

修正容器里 SSH 偶尔卡住

ppoll 和 pselect6 忽略了信号掩码参数,sshd 收不到子进程退出和 SIGTERM。对照测试:关掉修复时 28 次登录卡住 2 次,打开后 500 次没有卡住。node_test 加了连续 100 次登录的检查。