第五章 系统调用

系统调用

内核提供给用户进程与内核交互的接口

1. 与内核通信

系统调用是用户空间进程与硬件设备间的中间人,它为用户空间提供了硬件的抽象接口;保证系统的稳定和安全;帮助实现多任务和虚拟内存。在Linux中,系统调用是用户空间访问内核的唯一手段(除了异常和trap)

  • 应用编程接口API
  • POSIX
  • libc

2. 系统调用

系统调用syscall,通常通过C库中定义的函数调用进行

函数getpid()的一个实现:

1
2
3
4
5
6
7
SYSCALL_DEFINE0(getpid)
{
return task_tgid_vnr(current); // return current->tgid
}
//宏展开为:
asmlinkage long sys_getpid(void)

编译指令asmlinkage限定词在定义系统调用时使用。

系统调用号
在Linux中,每个系统调用都有一个系统调用号,通过这个号就可以联系系统调用,进程实际上用这个号来指明执行哪个系统调用。

内核会记录一张sys_call_table。

用户空间程序无法直接执行内核代码,因此需要一种方式通知系统,自己需要执行一个系统调用,这种机制靠软中断实现:通过引发一个异常促使系统切换到内核态去执行异常处理程序,此时的异常处理程序实际上就是系统调用处理程序。

3. 系统调用的实现

  • 实现系统调用:明确用途,系统调用的语义和行为,考虑兼容和未来。”Provide mechanism, not policy”
  • 参数验证:系统调用必须仔细检查参数的合法和有效,最重要的检查就是指针:
    • 指针指向的内存区域属于用户空间
    • 指针指向的内存区域在进程的地址空间
    • 进程不能绕过内存访问限制,必须要有相关的rwx权限

绑定一个系统调用的步骤:

  • 1.在系统调用表最后加上一个表项
  • 2.在中定义系统调用号
  • 3.系统调用必须编译进内核(不能是编译成模块),这需要把它放进kernel/下的一个相关文件中。

在用户空间访问系统调用:
Linux提供一组宏,用于直接访问系统调用,他们会设置好寄存器并调用陷入指令,这些宏是_syscalln(),其中n的范围是0-6。

x86可以用 INT 0x80执行系统调用,这就是一个中断

Linux System Calls
Linux System Call Table