知识全景图/ 软件工程与系统/ 如何设计系统/ 如何设计操作系统/ 资源限制与故障恢复

如何设计资源限制与故障恢复

让坏程序、坏设备和失败路径只能局部破坏,不能拖垮整台机器。

定位:这页收住 ToyOS 第一版的最后一层:资源不是无限的,失败不是异常情况,而是系统每天都会遇到的路径。OS 必须知道谁用了多少,失败时释放什么,恢复时信任什么。
一、坏场景:一个程序耗尽整台机器
失败:一个程序不断 fork、打开文件、申请内存、创建 socket、写满磁盘。没有限制时,它不是“自己的程序坏了”,而是把整个系统拖死。
资源失控方式被迫设计
进程槽无限创建子进程每用户 / 每进程树进程数上限
内存页无限 malloc 或 mmap地址空间和物理页限额
fd不断 open / socket 不 close每进程 fd 上限
磁盘块写满文件系统配额、预留空间、写入失败语义
CPU 时间死循环消耗计算资源时间片、优先级、CPU 配额
网络缓冲连接和收包占满内存连接数、缓冲区、背压和丢弃策略
二、资源账本对象
对象字段负责什么
ResourceLimitsmax_processes、max_memory、max_fds、max_sockets定义上限
ResourceUsageprocesses、pages、fds、sockets、cpu_ticks记录当前占用
Owneruid、pid、process_group资源归属
CleanupListallocated_pages、open_files、locks、children失败或退出时释放资源
FailureRecordtype、owner、resource、action记录失败和处理结果
三、失败路径必须先设计
spawn(path): proc = alloc_process_slot() if proc == null: return -EAGAIN if !charge(current.owner, PROCESS_LIMIT): free_process_slot(proc) return -EAGAIN if !load_program(proc, path): cleanup_process(proc) uncharge(current.owner, PROCESS_LIMIT) return -ENOENT run_queue.push(proc) return proc.pid
失败点必须释放什么不能留下什么
分配进程槽后加载失败进程槽、页、fd、计数半初始化进程
写文件空间不足已分配但未提交的数据块坏 inode 或坏目录项
socket 建立中断socket 对象、端口表占用、缓冲区占住端口的幽灵连接
驱动返回错误等待队列、IO 请求对象永远 blocked 的进程
四、恢复不等于重启
失败恢复动作验收问题
进程崩溃关闭 fd、释放页、唤醒父进程资源是否全部回收
内存耗尽拒绝新分配,保留内核预留页内核是否还能继续处理错误
磁盘写一半journal recovery重启后元数据是否一致
设备错误返回错误码,重置设备或隔离请求等待进程是否被唤醒
网络缓冲爆满丢弃、背压或拒绝连接是否只影响相关 socket
五、验收实验
实验通过标准证明了什么
fork bomb超过进程上限后返回错误,shell 仍可用进程限额成立
fd 泄漏超过 fd 上限后 open 失败,其他进程不受影响fd 账本成立
内存耗尽用户分配失败,但内核仍能调度和打印错误内核预留和失败路径成立
磁盘满write 返回错误,不产生坏目录项文件系统失败语义成立
崩溃清理进程崩溃后 fd、页、锁全部释放cleanup list 成立
六、回到操作系统主线
上一步:网络与 Socket
如果你想先看网络连接怎样变成受内核管理的 fd,回到 如何设计网络与 Socket
回总页
回系统设计总入口
如果你想把 OS 和数据库、编译器放到一起比较,回到 如何设计系统
一句话:资源限制与故障恢复让 OS 从“能跑”变成“坏了也不全坏”:每个资源有主人、有上限、有释放路径,失败能返回错误而不是拖垮系统。