5.1 与内核通信
系统调用在用户空间进程和硬件设备之间添加了一个中间层:
- 为用户空间提供了硬件的抽象接口
- 保证系统的稳定和安全
- 每个进程都运行在虚拟系统中,如果应用程序可以随意访问硬件而内核对此一无所知的话,几乎无法实现多任务和虚拟内存。
5.2 API、POSIX和C库
5.3 系统调用
系统调用返回long类型表示成功或错误(long
为了与64位硬件体系结构兼容)。系统调用出错时候,C库将错误码写入errmo全局变量。通过调用perror(),可以将该变量翻译为人类可读的字符串。
系统调用展开后的代码:
asmlinkage long sus_getpid(void)
asmlinkage
是编译指令,通知编译器仅从栈中提取该函数的参数,所有系统调用都使用它。系统调用返回long
。
5.3.1 系统调用号
Linux中每个系统调用都分配一个系统调用号,这样系统调用号关联系统调用。当用户进程执行系统调用时,使用系统调用号,而不提及系统调用名称。
奈何记录了系统调用表中所有已注册的系统调用的列表,存储在sys_call_table
中,它为每一个系统调用指定了唯一的系统调用号。
5.4 系统调用处理程序
用户不能直接调用内核空间中的函数,内核驻留在受保护的地址空间上。应用进程通过软中断告知内核执行系统调用,x86上的中断号:128,通过指令int $80h
触发该中断,系统切换至内核态。
5.5 系统调用的实现
5.5.2 参数验证
最重要的参数检查就是针对指针的,内部必须保证:
- 指针指向的内存区域属于用户空间。进程不能哄骗内核去读内核空间的数据。
- 指针指向的内存区域在进程的地址空间里。进程不能哄骗内核去读其他进程的数据。
- 如果是读,该内存应标记为只读;如果是写,标记为只写;如果是可执行,标记为可执行。进程不能绕过内存访问限制。
内核提供copy_to_user()
向用户空间写数据;copy_from_user()
从用户空间读取数据。