系统调用
- 与内核通信
- API、POSIX和C库
- 系统调用
- 系统调用号
- 系统调用的性能
- 系统调用处理程序
- 指定恰当的系统调用
- 参数传递
- 系统调用的实现
- 实现系统调用
- 参数验证
- 系统调用上下文
本系列博客追寻《Linux内核设计与实现-Robert Love》,各个Linux机中的内核源代码不一,因此直接下载官网内核源码
参考书籍:《Linux内核设计与实现-Robert Love》
目录 | 描述 |
arch | 特定体系结构的源码 |
block | 块设备I/O层 |
crypty | 加密API |
Documentation | 内核源码稳文档 |
drivers | 设备驱动程序 |
firmware | 使用某些驱动程序而需要的设备固件 |
fs | VFS和各种文件系统 |
include | 内核头文件 |
init | 内核引导和初始化 |
ipc | 进程间通信代码 |
kernel | 像调度程序这样的核心子系统 |
lib | 通用内核函数 |
mm | 内存管理子系统和VM |
net | 网络子系统 |
samples | 示例,示范代码 |
script | 编译内核所需脚本 |
security | Linux安全模块 |
sound | 语音子系统 |
usr | 早期用户空间代码 |
tools | 在Linux开发中有用的工具 |
virt | 虚拟化基础结构 |
与内核通信
系统调用在用户空间和硬件设备之间添加了一层中间层,该层主要作用有三个:
- 为用户空间提供了一层硬件的抽象接口(不用管磁盘类型和文件系统类型)
- 系统调用保证了系统的稳定和安全(权限,用户类型进行裁决)
- 实现虚拟内存。
系统调用是用户空间访问内核的唯一手段,除异常和陷入外,他们是内核唯一的合法入口
Linux的系统调用比大部分OS少得多
API、POSIX和C库
API:一组API应用编程接口定义了一组应用程序使用的编程接口,他们可以实现一个系统调用,或者多个系统调用。
POSIX:在UNIX中,最流行的应用编程接口是基于POSIX的
C库:C库实现了UNIX系统的主要API,包括标准C库函数和系统调用接口,所有C库程序都可以使用C库,此外,C库提供了POSIX的大部分API
系统调用
要访问系统调用(syscall),通常通过C库中定义的函数调用来实现。此外,系统调用还会通过一个long类型的返回值表示成果或者错误(负为错误,0为正确)
系统调用在出现错误的时候C库会把错误码写入error全局变量,通过perror()库函数,可以把该变量翻译成用户可以理解的错误字符串
命名规则:用户态的xxx函数到达内核态会变成sys_xxx函数,这是Linux中所有系统调用都应该遵守的命名规则。
系统调用号
在Linux中,每个系统调用都被赋予一个系统调用号。当用户空间的进程执行一个系统调用的时候,这个系统调用号就会指明执行哪个系统调用号(进程不会题记系统调用的名称)
内核记录了系统调用表的所有已注册的系统调用的列表
系统调用的性能
Linux系统调用比其他许多操作执行的要快
系统调用处理程序
用户空间不能直接执行内核代码,由用户态到内核态的机制是软中断实现的:通过引发一个异常来促使系统切换到内核态去执行异常处理程序(此时的异常处理程序实际上就是系统调用处理程序)
在x86是通过int $0x80
指定恰当的系统调用
因为所有的系统调用陷入内核的方式都一样,所有必须把系统调用号一并传给内核。(在x86上,系统调用号是通过eax寄存器传递给内核的)
参数传递
有时候还需要把参数传递给内核,最简单的方法就是和系统调用号一样用寄存器,下x86-32系统上,ebx、ecx、edx、esi和edi按照顺序存放前五个参数
返回值放在eax寄存器
系统调用的实现
实现系统调用
用途、调用参数、返回值和错误码、多样接口
参数验证
检查参数是否合法有效
系统调用上下文
内核在执行系统调用的时候处于进程上下文,current指向前任务,即引发系统调用的那个进程。
在进程上下文中,内核可以休眠并且可以被抢占
继续加油!