系统调用
内核提供给用户进程与内核交互的接口
1. 与内核通信
系统调用是用户空间进程与硬件设备间的中间人,它为用户空间提供了硬件的抽象接口;保证系统的稳定和安全;帮助实现多任务和虚拟内存。在Linux中,系统调用是用户空间访问内核的唯一手段(除了异常和trap)
- 应用编程接口API
- POSIX
- libc
2. 系统调用
系统调用syscall,通常通过C库中定义的函数调用进行
函数getpid()的一个实现:1234567SYSCALL_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执行系统调用,这就是一个中断