您的位置:首页 > 运维架构 > Linux

Linux系统调用接口、 系统调用例程 和 内核服务例程之间的关系

2012-09-21 10:41 387 查看


Linux系统调用接口、 系统调用例程 和 内核服务例程之间的关系

转自:维库电子开发网  http://data.weeqoo.com/2009/2/2009217113941152763.html
系统调用接口的主要任务是把进程从用户态切换到内核态。在具有保护机制的计算机系统中,用户必须通过软件中断或陷阱,才能使进程从用户态切换为内核态。

在i386体系中,Linux的系统调用接口是通过调用软中断指令“int $ Ox80”使进程从用户态进入内核态的,这个过程也叫做“陷入”。当系统调用接口调用软中断指令“int $ Ox80”时,这个指令会发生一个中断向量码为128的中断请求,并在中断响应过程中将进程由用户态切换为内核态。

因为Linux只允许系统调用接口使用128这一个软中断向量,这也就意味着所有的系统调用接口必须共享这一个中断通道,并在同一个中断服务例程中(这里的中断服务例程就是对应于中断号为128的中断服务例程,通过查中断向量表得到)调用不同的内核服务例程,所以,系统调用接口除了要引发“int
$ Ox80”软中断之外,为了进人内核后能调用不同的内核服务例程,还要提供识别内核服务例程的参数,这个参数叫做“系统调用号”。也就是说,所有可为进程提供服务的内核服务例程都应具有一个唯一的系统调用号。当然,系统调用接口还应为内核服务例程准各必要的参数。

  综上所述,系统调用接口需要完成以下几个任务:

  ●要保护用户态的现场,即把处理器的用户态运行环境保护到进程的内核堆栈。

  ●为内核服务例程准备参数(包括“系统调用号”--linux中规定用寄存器EAX传递),并定义返回值的存储位置。

  ●用软中断指令“int $ Ox80”发生一个中断向量码为128的中断请求,以使进程进入内核态。

  ●跳转到系统调用例程。

  ●系统调用例程结束后返回。

  系统调用例程是系统提供的一个通用的汇编语言程序.其实它是一个中断向量为128的中断服务程序,其入口为system_call。它应完成的任务有:

  ●接受系统调用接口的参数。

  ●根据系统调用号,转向对应的内核服务例程,并将相关参数传递给内核服务例程。

  ●在内核服务例程结束后,自中断返田到系统凋甩接口.

  系统调用的过程如图所示。

  从图中可以看到,系统调用接口是用高级语言来编写的,而通过调用中断指令陷入内核后的系统调用例程(即中断向量为128的中断服务程序)则是用汇编语言编写的。

  为了通过系统调用号来调用不同的内核服务例程,系统必须维护一个系统调用表,这个表实质上就是系统调用号与内核服务函数的对照表。Linux是用数组sys_call_tabl来作为这个表的,在这个表的每个表项中存放着对应内核服务例程的指针,而该表项的下标就是该内核服务例程的系统调用号。Linux规定,在i386体系中,系统调用号由处理器的寄存器eax来传递。下图中的系统调用例程,通过查EAX中的系统调用号与sys_call_tabl来确定相应的内核服务函数。

                                                                                       图1



                                                       

  系统调用表Sys_call_table的部分内容列举如下:

                                                                                           图二



老吴曰:  以上所说的是我们在用户程序中直接调用系统调用的执行步骤,比如说,我们在程序中调用 fork()这个系统调用来产生一个子进程,那么执行过程就是上面图一的几个步骤,而如果我们在程序中调用的是 比如C语言的 库函数printf(),那么执行过程又是怎样的呢? 这个问题要牵扯到编译器的事。比如说,在linux上,我们用gcc命令编译一个程序,printf()这个库函数最终会被编译成调用write()这个系统调用,我们也可以用strace命令跟踪程序,也会发现printf()函数最终是通过write()系统调用来实现它的功能的。        
而如果我们在windows上编译printf(),那么printf()就会被编译成windows上相应的系统
调用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息