知识全景图/ 软件工程与系统/ 如何设计系统/ 如何设计操作系统/ 系统调用与内核边界

如何设计系统调用与内核边界

让不可信用户程序安全地请求内核服务,而不是直接控制机器资源。

定位:虚拟内存把用户程序隔离开了,但用户程序仍然要打印、读文件、创建进程、等待 IO。系统调用就是边界:用户程序不能直接碰资源,但可以向内核申请资源。
一、坏场景:用户程序想做事,但不能直接碰硬件
失败:如果用户程序能直接写磁盘寄存器、改页表、关中断或访问任意内核对象,它就可以绕过权限、破坏全局状态,甚至接管机器。
用户想做什么不能直接做的原因被迫设计
打印文字控制台设备是共享资源,不能让每个程序直接写设备寄存器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 等待时间时间参数、唤醒队列、取消条件
四、边界上的最小检查
sys_write(fd, user_buf, len): file = current.open_files.get(fd) if file == null or !file.writable: return -EBADF if !user_memory.readable(user_buf, len): return -EFAULT if len > MAX_IO_SIZE: return -EINVAL kernel_buf = copy_from_user(user_buf, len) return file.write(kernel_buf)
检查防什么失败返回
fd 是否有效用户伪造资源编号EBADF
用户指针是否可读写读内核地址、写非法页、跨越未映射区域EFAULT
长度是否合理整数溢出、超大复制、资源耗尽EINVAL
权限是否允许越权读写文件或设备EACCES
资源是否足够fd、内存、进程槽耗尽ENOMEM/EMFILE
五、验收实验
实验通过标准证明了什么
非法指针write(1, kernel_addr, len) 返回错误,系统不崩copy_from_user 边界成立
非法 fdread(999, buf, len) 返回 EBADFfd 表检查成立
无权限路径普通用户打开受限文件失败权限检查在内核边界执行
资源上限打开超过 fd 上限时返回错误系统调用不绕过资源账本
失败清理spawn 中途失败不会泄漏进程槽和页系统调用有回滚路径
六、回到操作系统主线
下一页:文件系统
系统调用建立边界后,下一步是把磁盘块组织成文件、目录和 fd。继续看 如何设计文件系统
回总页
一句话:系统调用的本质不是“进入内核”,而是在不信任用户程序的前提下,让它安全地请求内核代管资源。