西城杨柳弄春柔————系统调用

什么是系统调用?

系统调用是应用程序与操作系统内核之间的接口,它决定了应用程序是如何与内核打交道的,无论程序是直接进行系统调用,还是通过运行库,最终还是会到达系统调用这个层面上,系统调用实现在内核中。

系统调用的意义?

在现代的操作系统里,程序运行的时候,本身是没有权利访问多少系统资源的,由于系统有限的资源有可能被多个不同的的应用程序同时访问,因此,如果不加以保护,那么各个应用程序难免产生冲突。所以,现代操作系统将可能产生冲突的系统资源给保护起来,阻止应用程序直接访问,所有的操作都必须经由操作系统所规定的方式来进行,如果没有操作系统的帮助帮助,应用程序的执行可谓是相当艰难。

为了让应用程序有能力访问系统资源,也为了让程序借助操作系统做一些必须由操作系统支持的行为,每个操作系统都会提供一套接口(系统调用),以供应用程序使用。这些接口往往通过中断来实现,比如Linux使用0x80号中断作为系统调用的入口

关于中断(Interrupt)?

现代的CPU常常可以在多种截然不同的特权级别下执行指令,在现代的操作系统中,通常也据此有两种特权级别,分别为用户模式(User Mode)和内核模式(Kernel Mode),也别成为用户态和内核态。由于有多种特权模式的存在,操作系统就可以让不同的代码运行在不同的模式上,以限制他们的权利,提高稳定性和安全性。

系统调用是运行在内核态的,而应用程序基本都是运行在用户态的,用户态的程序如何运行内核态的代码呢?操作系统一般是通过中断来从用户态切换到内核态

那什么是中断呢?中断是一个硬件或者软件发出的请求,要求CPU暂停当前的工作转手去处理更加重要的事情。

中断一般具有两个属性,一个称为中断号(从0开始),一个称为中断处理程序。不同的中断具有不同的中断号。在内核中,有一个数组称为中断向量表,这个数组的第n项包含了指向第n号中断的中断处理程序指针。当中断到来时,CPU会暂停当前的执行代码,根据中断的中断号,在中断向量表中招到对应的中断处理程序,并调用它。中断处理程序执行完成后,CPU会继续执行之前的代码。 (如下简单示意图)

通常意义上,中断有两种类型,一种被称为硬件中断,这种中断来自于硬件的异常或者其他事件的发生,如:电源掉电、按下键盘等。另一种被称为软件中断,软件中断通常是一条指令,带有一个参数记录中断号,使用这条指令用户可以手动出发某个中断并执行其中的中断处理程序。

由于中断号是有限的,操作系统不会舍得用一个中断号来对应一个系统调用,而更倾向于用一个或者少数几个中断号来对应所有的系统调用。例如,Linux使用int 0x80 来触发所有的系统调用。

那么对于同一个中断号,操作系统如何知道是哪一个系统调用要被调用呢?和中断一样,系统调用都有各自的一个系统调用号,就像身份标识一样来标明是哪一个系统调用,这个系统调用号通常就是系统调用在系统调用表中的位置,例如,Linux中fork的系统调用号是2.

系统调用号在执行int指令前会被放置在某个固定的寄存器里,对应的中断代码会取得这个系统调用号,并且调用正确的函数。以Linux的int 0x80 为例,系统调用号是由寄存器eax来传入的,用户将系统调用号放入eax,然后使用int 0x80 调用中断,中断服务程序就可以从eax中取得系统调用号,进而调用对应的函数,当系统调用返回时,eax又作为调用结果的返回值。(以fork为例,简单示意图如下)