5.1 与内核通信

系统调用在用户空间进程和硬件设备之间添加了一个中间层:

  • 为用户空间提供了硬件的抽象接口
  • 保证系统的稳定和安全
  • 每个进程都运行在虚拟系统中,如果应用程序可以随意访问硬件而内核对此一无所知的话,几乎无法实现多任务和虚拟内存。

5.2 API、POSIX和C库

image.png

5.3 系统调用

系统调用返回long类型表示成功或错误(long为了与64位硬件体系结构兼容)。系统调用出错时候,C库将错误码写入errmo全局变量通过调用perror(),可以将该变量翻译为人类可读的字符串

系统调用展开后的代码:

  1. asmlinkage long sus_getpid(void)

asmlinkage是编译指令,通知编译器仅从栈中提取该函数的参数,所有系统调用都使用它。系统调用返回long

5.3.1 系统调用号

Linux中每个系统调用都分配一个系统调用号,这样系统调用号关联系统调用。当用户进程执行系统调用时,使用系统调用号,而不提及系统调用名称。

奈何记录了系统调用表中所有已注册的系统调用的列表,存储在sys_call_table中,它为每一个系统调用指定了唯一的系统调用号。

5.4 系统调用处理程序

用户不能直接调用内核空间中的函数,内核驻留在受保护的地址空间上。应用进程通过软中断告知内核执行系统调用,x86上的中断号:128,通过指令int $80h触发该中断,系统切换至内核态
image.png

5.5 系统调用的实现

5.5.2 参数验证

最重要的参数检查就是针对指针的,内部必须保证:

  • 指针指向的内存区域属于用户空间。进程不能哄骗内核去读内核空间的数据。
  • 指针指向的内存区域在进程的地址空间里。进程不能哄骗内核去读其他进程的数据。
  • 如果是读,该内存应标记为只读;如果是写,标记为只写;如果是可执行,标记为可执行。进程不能绕过内存访问限制

内核提供copy_to_user()向用户空间写数据;copy_from_user()从用户空间读取数据。