面板卖 128、256、512 MiB 的容器,每个容器首次启动都要用 apt 装 openssh-server。以前在 256 MiB 里 fork 失败;现在 128 MiB 全部通过,64 MiB 和 Linux 的 LXC 表现一样。
程序用 mmap 私有映射一个文件时,内核当场把整个文件复制进这个进程;程序本身(bash、perl、apt)的各段也是整个复制。apt 装包时同时有 apt-get、debconf、apt-extracttemplates、dpkg 等多个进程,每个都把 libc、libapt-pkg、包缓存各复制一份,256 MiB 的容器就 fork 失败了。
| 容器内存 | 首次启动装 openssh-server、SSH | Go 静态 / cgo | apt 装 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"。
在测试机上用同一个镜像,cgroup v2 内存上限、不开 swap(LXC 的做法):
| Linux memcg | apt update | 装 openssh-server | 装 python3 |
|---|---|---|---|
| 64 MiB | 通过 | 通过 | 内存不够被终止 |
| 32 MiB | 被终止 | 被终止 | 被终止 |
所以 64 MiB 时 RustROS 和 Linux 一样:能装 openssh、能登录,装 python3 不够。apt-get 本身建包缓存要约 46 MiB 匿名内存,64 MiB 下两边都很紧。32 MiB 两边都跑不了 apt。
disk_gib / disk_free_gib 以前报文件分区当前的大小(新装的机器从 1 GiB 起、自动每次长 1 GiB),面板会以为母鸡只有 1 GiB。现在报文件分区能自动长到的容量(到磁盘末尾)和其中的可用部分。
CT_MEM,默认 256 MiB);Go 测试程序的大块内存按容器大小给;SSH 超时算一项失败、保留控制台日志。mprotect 保留 CPU 标的"已写"位(以前会让共享文件映射漏写回);mremap 扩大文件映射时还是同一个文件、用原来的权限。设计见 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 以上卖。
ppoll 和 pselect6 忽略了信号掩码参数,sshd 收不到子进程退出和 SIGTERM。对照测试:关掉修复时 28 次登录卡住 2 次,打开后 500 次没有卡住。node_test 加了连续 100 次登录的检查。