首先说明一下,本文只是一个理解过程,很多表述不是很严谨,且可能存在错误,这里只是个人的理解过程,希望不要误导过客。
说明系统调用先简单白话一下用户态内核态、中断的概念
一、用户态内核态
简单来说,操作系统的是对硬件资源的管理,并将这些资源提供给运行在系统上的程序。
为避免任意程序随意操作硬件资源,对硬件资源的访问会按照权限进行区分,CPU等很多硬件在硬件基本就镜像权限级别的划分。以cpu为例,通常划分为0~3三个等级,0最高可以执行大部分CPU指令访问硬件资源,3最底只能执行部分CPU指令。但系统上目前通常只用了两个等级0级和3级,程序运行在0级的时称其运行在内核态,程序运行在3级时称其运行在用户态。
二、中断
中断就是让cpu先暂时停下手头的工作A,去处理更为重要的工作B,处理完B后再继续A(暂不考虑嵌套中断等情况)
至于实现这个过程,涉及到以下几个问题
- 1、如何让cpu暂停下来?
两种方式,一种是硬件中断,硬件上信号触发cpu暂停;
还有一种是软件中断,cpu执行过程中,执行到了特殊指令(int 0x80)。 - 2、cpu暂停后,去做更重要的事情前需要做什么?
由于需要在完成B之后,恢复之前的工作流,所以必须先保存当前的工作流环境,在处理完B后,恢复A环境然后继续工作A - 3、cpu如何知道更重要的事情在哪?
系统通常是采用中断号,中断向量表实现的。具体而言是,系统存着一张表,表中存放着编号和中断处理函数的地址的对应关系,当触发中断时,cpu会收到一个中断号(对应表中的编号),然后拿这个号码去表中查找函数地址,然后跳转到对应函数即可。
最后还要说明一点是:中断处理函数的在内核态执行的
三、系统调用
在了解了上述概念后,系统调用也就比较好理解了
系统调用是系统提供给程序的一种服务接口,通过这些接口用户态程序可以间接的访问硬件资源等系统资源。
这个过程也有几个问题需说明一下
- 1、系统调用过程发生了什么?
访问硬件等资源通常需要在内核态,因此可以说系统调用是系统提供的主动从用户态切换到内核态的一种方式(还有其他方式,异常、外围设备中断等)。 - 2、系统如何实现这个功能呢?
中断,通常系统调用采用软中断的方式实现,软中断对应的中断号0x80,对应的中断处理函数system_call内部的实现过程通常是一个分支调用过程,即根据中断处理函数的输入参数(系统调用号)选择调用哪一个系统封装好的功能函数(即系统调用的真正功能执行函数sys_***)
明确上述问题后,系统调用可以表述为:通过向内核发送软中断请求,从而获得内核提供的某些服务。
实例过程如下图,图片来自使用 Linux 系统调用的内核命令,该文章还介绍了如何添加系统调用,过程比较简单,这里不赘述了
四、系统调用关键字
- 系统调用表-sys_call_table-./kernel/entry.S
- 系统调用号-寄存器参数传递-eax-参数权限验证
- 返回值-寄存器传递-0成功-负数失败-错误码errno
- 未实现系统调用-sys_ni_syscall()-return ENOSYS
- 系统调用总入口-system_call-./kernel/entry.S
- 系统调用执行函数地址-sys_call_table + (系统调用号 * word_size)
- 系统调用返回控制枢纽-ret_from_sys
- 直接使用系统调用-_syscalln()
五、asmlinkage关键字
内核代码中系统调用的执行函数sys_***定义前面都带有asmlinkage这个关键字。查询到的定义、作用、原因如下:
- 1、定义
#define fastcall __attribute__((regparm(3)))
#define asmlinkage __attribute__((regparm(0)))
- 1、作用
__attribute__((regparm(0)))声明这个函数的输入参数采用栈传递(非寄存器传递,c编译器默认是采用寄存器传参);
__attribute__((regparm(3)))声明输入参数采用寄存器传递。 - 2、原因
entry.S中的汇编代码system_call是将参数先压入栈,然后调用的sys_***,所以要显示声明为栈参数传递。至于entry.S中为什么这么写,原因还未查到。。。
网友评论