深入浅出系统调用的原理

        这里不进行系统调用源码的分析,因为那个太费时间了,而且很难在很快的时间里能够理解。但是我相信你看完下面的文章,你对系统调用的实现原理肯定有一个很深的理解。

一、系统调用的介绍

1.什么是系统调用

         系统调用就是应用程序与系统内核之间的接口。通过系统调用访问系统资源。

2.为什么需要系统调用

         由于系统有限的资源可能被多个不同的应用程序同时访问可因此,如果不加以保护,那么各个应用程序难免会产生冲突。所以一种解决方法是,不让应用程序直接访问系统的资源,所以增加了一层中间接口,那就是系统调用。
注:Linux使用0x80号中断作为系统调用的入口,windows采用0x2E号中断作为系统调用入口。

3.Linux系统调用

         在X86下系统调用由0x80中断完成,各个通用寄存器用于传递参数,EAX寄存器用于表示系统调用的接口号,比如EAX=1表示退出进程(exit);EAX=2表示创建进程(fork),每个系统调用都对应于内核源代码中的一个函数,都是以sys_开头,比如exit调用对应内核中的sys_exit函数。当系统调用返回时,EAX又作为调用结果的返回值。

4.系统调用弊端

●使用不便。操作系统提供的系统调用接口往往过于原始,程序员需要了解很多与操作系统相关的细节。
●各个操作系统之间的系统调用不兼容。首先windows系统和linux系统之间的系统调用即基本完全不同,虽然它们功能一样,但是定义和实现不一样。
         为了解决以上两个问题,我们通过中间层的方法----运行库来进行,这样的话使用起来就很方便,而且形式统一。运行库有它的标准,所以又叫标准库,凡是所有遵循这个标准的运行库理论上都是相互兼容的。
        运行时库讲不同的操作系统的系统调用包装为统一固定的接口,使得同样的代码,在不同的操作系统下都可以直接编译,并产生一致的结果。这就是源代码级上的可移植性。但运行库也有一定的缺陷,就是它只能去不同平台之间功能的交集。

二、系统调用原理

1.特权级与中断

         中断有两个属性,一个称为中断号(从0开始0),一个称为中断处理程序(Interrupt Service Routine, ISR).不同的中断具有不同的终端号,而同时一个中断处理程序一一对应一个中断号。在内核中,有一个数组称为中断向量表(Interrupt vector table),这个数组的第n项包含了指向第n号中断的中断处理程序的指针。当中断到来时,CPU会暂时中断当前执行的代码,根据中断的中断号,在中断向量表中找到对应的中断处理程序,并调用它。中断处理程序执行完成以后,CPU会继续执行之前的代码。
        通常意义上,中断有两种类型,一种称为硬件中断,这种中断来自于硬件的异常或其他事件的发生,如电源掉电、键盘被按下等;另一种称为软中断,软件中断通常是一条执行(i386下是int),带有一个参数记录中断号,使用这条指令可以手动触发某个中断并执行其中断处理函数。例如在i386下,int 0x80这条指令会调用第0x80号中断的处理程序。
         由于中断号是有限的,操作系统不舍得用一个中断号来对应一个系统调用,而倾向于用一个或少数几个中断号对应所有的系统调用。例如,i386下Windows里绝大多数系统调用都是由int 0x2e来触发的,而linux则使用int 0x80来触发所有的系统调用。那么问题来了,对于同一个中断号,操作系统如何知道是哪一个系统调用要被调用呢?
        和中断一样,系统调用都有一个系统调用号,每个系统调用号都唯一对应一个系统调用处理函数。例如Linux里fork的系统调用号是2,这个系统调用号在执行int指令前会被放置在某个固定的寄存器里,对应的中断代码会受到这个系统调用号,并且调用正确的函数。

2.基于int的Linux的系统调用实现

         以fork()的系统调用为例,其系统调用号为2,过程如下:


小结:用几句话简单总结一下,首先找到系统调用对应的中断号(Linux下是int 0x80),然后在中断向量表中找到对应的中断处理函数,再根据系统调用号,在中断处理函数找到对应系统调用函数进行执行。

猜你喜欢

转载自blog.csdn.net/kang___xi/article/details/80556633