什么是系统调用
现代操作系统中,用户进程与内核进程进行交互的接口。
为什么要用系统调用/系统调用的意义
-
为用户空间提供了一种硬件的抽像接口。如读写文件时,可以不用管硬盘的介质和类型,均使用open、write、read实现文件的打开、读写操作。
-
保证了系统的稳定和安全。Linux中,系统调用是用户态访问内核的唯一合法手段,内核可以基于此进行权限、用户类型等必要的合法性检查,防止用户不正确的访问硬件、使用资源,导致产生危害系统的操作。
-
通过系统调用,内核可以对来自用户态的操作进行感知。如果用户态可以随意访问硬件但内核却一无所知,几乎没法实现多任务、虚拟内存等。本质上还是为了保证系统的稳定和安全。
如用户态可以随意创建进程、线程,内核却又不知道,就会导致进程、线程混乱。
如用户态可以随意使用内存,内核不知道哪被使用,哪没被使用,就无法进行内存管理。
系统调用是如何实现的
系统调用如何陷入到内核
通知内核的机制靠软中断实现:通过引发一个异常来促使系统切换到内核态去执行异常处理程序。
在x86架构中,预定义的软中断是中断号128,通过int $0x80指令触发该中断。在arm架构中,通过svc(Supervisor Call)指令来触发中断。
这种指令会触发一个异常,导致系统切换到内核态,执行系统调用处理程序(这个处理程序为软中断的中断处理程序,即系统调用处理程序,该程序会根据传参去查找真正要执行的系统调用)。
如何实现自己的系统调用
-
将系统调用加入系统调用表。
内核记录了系统调用表中所有已经注册过的系统调用的列表,存储在sys_call_table中。每一种体系结构中,都明确定义了这个表。
对于大多数体系结构来说,该表位于entry.S文件。以2.6.34版本的Linux内核,x86架构中该表位于syscall_table_32.S中,arm架构中,该表位于arch/arm/kernel/calls.S中。

- 对于所支持的不同体系结构,在<asm/unistd.h>中添加系统调用号。
linux2.6.34版本内核,arm架构中位于arch/arm/include/asm/unistd.h中,x86架构位于arch/x86/include/asm/unistd_32.h或unistd_64.h中。

- 在内核中实现系统调用,如kernel/sys.c中,该文件包含了各种系统调用的实现。
系统调用必须被编译进内核,不能被编译成模块。
通过以上三步,实现了在内核中注册自己的系统调用。在用户空间访问系统调用,通常靠C库支持,通过包含头文件和C库链接,可以直接或间接使用系统调用。
我们自行在内核中注册的系统调用,C库没有提供支持。可以使用linux提供的_syscalln()宏,来直接调用系统调用。
以 long open(const char *filename, int flags, int mode)为例,直接访问系统调用的写法如下:
#define NR_open 5
_syscall3(long, open, const char*, filename, int, flags, int, mode)
即n为系统调用参数个数,该宏参数为2+2*n个
本文地址: 【操作系统-内核-驱动学习笔记】——系统调用