让不可信用户程序安全地请求内核服务,而不是直接控制机器资源。
| 用户想做什么 | 不能直接做的原因 | 被迫设计 |
|---|---|---|
| 打印文字 | 控制台设备是共享资源,不能让每个程序直接写设备寄存器 | write(fd, buf, len) |
| 读文件 | 路径、权限、缓存、文件偏移都归内核管理 | open/read/close |
| 创建进程 | 进程表、地址空间、资源上限不能由用户随意改 | spawn/exit/wait |
| 等待事件 | 等待和唤醒必须和调度器状态机一致 | sleep/wait/poll |
| 普通函数调用 | 系统调用 | 为什么不同 |
|---|---|---|
| 同一权限、同一地址空间 | 从用户态切到内核态 | 内核要获得特权,用户程序不能获得特权 |
| 参数默认来自可信调用者 | 参数全部来自不可信用户输入 | 指针、长度、fd、路径都要检查 |
| 失败多半是局部错误 | 失败可能破坏全局资源 | 必须有统一错误码和回滚路径 |
| 调用者和被调用者同属一个程序 | 用户程序请求内核代表它操作资源 | 需要权限、配额和审计边界 |
| 系统调用 | 职责 | 必须检查 |
|---|---|---|
spawn(path, argv) | 创建新进程 | 路径权限、参数地址、进程数上限 |
exit(code) | 结束当前进程 | 资源释放、父进程唤醒、退出码保存 |
wait(pid) | 等待子进程 | 父子关系、目标状态、阻塞条件 |
open(path, flags) | 打开文件或设备 | 路径合法、权限允许、fd 上限 |
read(fd, buf, len) | 读取资源 | fd 有效、buf 可写、len 合理 |
write(fd, buf, len) | 写入资源 | fd 可写、buf 可读、设备是否可用 |
close(fd) | 释放 fd 引用 | fd 是否属于当前进程、引用计数是否归零 |
sleep(ms) | 让出 CPU 等待时间 | 时间参数、唤醒队列、取消条件 |
| 检查 | 防什么 | 失败返回 |
|---|---|---|
| fd 是否有效 | 用户伪造资源编号 | EBADF |
| 用户指针是否可读写 | 读内核地址、写非法页、跨越未映射区域 | EFAULT |
| 长度是否合理 | 整数溢出、超大复制、资源耗尽 | EINVAL |
| 权限是否允许 | 越权读写文件或设备 | EACCES |
| 资源是否足够 | fd、内存、进程槽耗尽 | ENOMEM/EMFILE |
| 实验 | 通过标准 | 证明了什么 |
|---|---|---|
| 非法指针 | write(1, kernel_addr, len) 返回错误,系统不崩 | copy_from_user 边界成立 |
| 非法 fd | read(999, buf, len) 返回 EBADF | fd 表检查成立 |
| 无权限路径 | 普通用户打开受限文件失败 | 权限检查在内核边界执行 |
| 资源上限 | 打开超过 fd 上限时返回错误 | 系统调用不绕过资源账本 |
| 失败清理 | spawn 中途失败不会泄漏进程槽和页 | 系统调用有回滚路径 |