什么是系统调用

现代操作系统中,用户进程与内核进程进行交互的接口。

为什么要用系统调用/系统调用的意义

  1. 为用户空间提供了一种硬件的抽像接口。如读写文件时,可以不用管硬盘的介质和类型,均使用open、write、read实现文件的打开、读写操作。

  2. 保证了系统的稳定和安全。Linux中,系统调用是用户态访问内核的唯一合法手段,内核可以基于此进行权限、用户类型等必要的合法性检查,防止用户不正确的访问硬件、使用资源,导致产生危害系统的操作。

  3. 通过系统调用,内核可以对来自用户态的操作进行感知。如果用户态可以随意访问硬件但内核却一无所知,几乎没法实现多任务、虚拟内存等。本质上还是为了保证系统的稳定和安全。

    如用户态可以随意创建进程、线程,内核却又不知道,就会导致进程、线程混乱。

    如用户态可以随意使用内存,内核不知道哪被使用,哪没被使用,就无法进行内存管理。

系统调用是如何实现的

系统调用如何陷入到内核

通知内核的机制靠软中断实现:通过引发一个异常来促使系统切换到内核态去执行异常处理程序。

在x86架构中,预定义的软中断是中断号128,通过int $0x80指令触发该中断。在arm架构中,通过svc(Supervisor Call)指令来触发中断。

这种指令会触发一个异常,导致系统切换到内核态,执行系统调用处理程序(这个处理程序为软中断的中断处理程序,即系统调用处理程序,该程序会根据传参去查找真正要执行的系统调用)。

如何实现自己的系统调用

  1. 将系统调用加入系统调用表。

    内核记录了系统调用表中所有已经注册过的系统调用的列表,存储在sys_call_table中。每一种体系结构中,都明确定义了这个表。

    对于大多数体系结构来说,该表位于entry.S文件。以2.6.34版本的Linux内核,x86架构中该表位于syscall_table_32.S中,arm架构中,该表位于arch/arm/kernel/calls.S中。

  1. 对于所支持的不同体系结构,在<asm/unistd.h>中添加系统调用号。

linux2.6.34版本内核,arm架构中位于arch/arm/include/asm/unistd.h中,x86架构位于arch/x86/include/asm/unistd_32.h或unistd_64.h中。

  1. 在内核中实现系统调用,如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个

说点什么
请文明发言!
支持Markdown语法
好耶,沙发还空着ヾ(≧▽≦*)o
Loading...