【Linux】系统调用过程及意义

系统调用

Linux内核中设置了一组用于实现各种系统功能的接口,称为系统调用。为了方便使用操作系统,操作系统预留出了一些接口,这些接口就是系统调用函数。用户可以通过系统调用命令在自己的应用程序中调用它们。从某种角度来看,系统调用和普通的函数调用非常相似。区别仅仅在于,系统调用由操作系统核心提供,运行于核心态;而普通的函数调用由函数库或用户自己提供,运行于用户态。

系统调用号

在Linux中,每个系统调用被赋予一个系统调用号。这样,通过这个独一无二的号就可以关联系统调用。当用户空间的进程执行一个系统调用的时候,这个系统调用号就用来指明到底是要执行哪个系统调用;进程不会提及系统调用的名称。
系统调用号相当重要,一旦分配就不能再有任何变更,否则编译好的应用程序就会崩溃。此外,如果一个系统调用被删除,它所占用的系统调用号也不允许被回收利用,否则,以前编译过的代码会调用这个系统调用,但事实上却调用的是另一个系统调用。
Linux有一个“未实现”系统调用sys_ ni syscall(), 它除了返回ENOSYS外不做任何其他工作,这个错误号就是专门针对无效的系统调用而设的。虽然很罕见,但如果一个系统调用被删除,或者变得不可用,这个函数就要负责“填补空缺”。

系统调用的过程

应用程序以某种方式通知系统,告诉内核自己需要执行一个系统调用,希望系统切换到内核态,这样内核就可以代表应用程序在内核空间执行系统调用。通知内核的机制是靠软中断实现的:通过引发一个异常来促使系统切换到内核态去执行异常处理程序。此时的异常处理程序实际上就是系统调用处理程序。在x86系统上预定义的软中断是中断号128,通过init $0x80指令触发该中断。这条指令会触发一个异常导致系统切换到内核态并执行第128号异常处理程序,而该程序正是系统调用处理程序。这个处理程序名字起得很贴切,叫system_ call()。最近,x86 处理器增加了一条叫做sysenter的指令。与init中断指令相比,这条指令提供了更快、更专业的陷入内核执行系统调用的方式。

  1. 保存上下文(用户态执行)
  2. 用eax保存系统调用号用其他寄存器保存传递的参数,在x86-32系统上,ebx、 ecx、 edx、 esi 和edi按照顺序存放前五个参数。(内核态执行)
  3. 系统调用必须仔细检查它们所有的参数是否合法有效。与进程相关的函数必须检查提供的PID是否有效。必须检查每个参数,保证它们不但合法有效,而且正确。进程不应当让内核去访问那些它无权访问的资源。然后触发0x80中断,首先执行system_call 函数,然后通过eax寄存器将系统调用号传递给内核,内核通过eax的系统调用号在系统调用表里找到相应的系统调用函数
  4. 执行该系统调用函数。(内核态执行)
  5. 执行完毕后,转入ret_from_sys_call 例程,从系统调用返回到用户态。

系统调用的意义

系统调用在用户空间进程和硬件设备之间添加了一个中间层。该层主要作用有三个。首先,它为用户空间提供了一种硬件的抽象接口。举例来说,当需要读写文件的时候,应用程序就可以不去管磁盘类型和介质,甚至不用去管文件所在的文件系统到底是哪种类型。第二,系统调用保证了系统的稳定和安全。作为硬件设备和应用程序之间的中间人,内核可以基于权限、用户类型和其他一些规则对需要进行的访问进行裁决。举例来说,这样可以避免应用程序不正确地使用硬件设备,窃取其他进程的资源,或做出其他危害系统的事情。第三,每个进程都运行在虚拟系统中,而在用户空间和系统的其余部分提供这样一层公共接口,也是出于这种考虑。如果应用程序可以随意访问硬件而内核又对此一无所知的话,几乎就没法实现多任务和虚拟内存,当然也不可能实现良好的稳定性和安全性。在Linux中,系统调用是用户空间访问内核的唯一手段;除异常和陷入外,它们是内核唯一的合法入口

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章