日期:2014-05-16  浏览次数:20761 次

linux内核--系统调用(一)

    UNIX系统通过向内核发出系统调用(system call)实现了用户态进程和硬件设备之间的大部分接口。

一、POSIX API和系统调用

    从编程者的观点看,API和系统调用之间的差别是没有关系的:唯一相关的事情就是函数名、 参数类型以及返回代码的含义。然而,从内核设计者的观点看,这种差别确实有关系,因为系统调用属于内核,而用户态的库函数不属于内核。

二、系统调用处理程序及服务例程

   当用户态的进程调用一个系统调用时,CPU切换到内核态并开始执行一个内核函数。调用系统调用的最终结果都是跳转到所谓系统调用处理程序的汇编语言函数。因为内核实现了很多不同的系统调用,进程必须传递一个名为系统调用号的参数来识别所需的系统调用,eax寄存器就用此目的。

    系统调用处理程序与其他异常处理程序的结构类似,执行如下操作:

  • 在内核态栈保存大多数寄存器的内容(这个操作队所有系统调用都通用,并用汇编语言编写)
  • 调用名为系统调用服务例程的相应的C函数来处理系统调用。
  • 退出系统调用处理程序:用保存在内核栈中的值加载寄存器,CPU从内核态切换回到用户态(所有的系统调用都要执行这一相同操作,该操作用汇编语言代码实现)

   下图展示了调用系统调用的应用程序、相应的封装例程、系统调用处理程序以及系统调用服务例程之间的关系,箭头表示函数之间的执行流。把CPU从用户态切换到内核态和从内核态切换到用户态。

    为了把系统调用与相应的服务例程关联起来,内核利用了一个系统调用分派表,这个表存放在sys_call_table数组中,有NR_syscalls个表项:第n个表项包含系统调用号为nd的服务例程的地址。

三、进入和退出系统调用

    可以通过两种不同的方式嗲用系统调用:

  • 执行int $0x80汇编语言指令,在linux内核的老版本中,这是从用户态切换到内核态的唯一方式。
  • 执行sysenter汇编语言指令。

    同样内核可以通过两种方式从系统调用退出,从而使CPU切换回到用户态:

  • 执行iret汇编语言指令
  • 执行sysexit汇编语言指令,它和sysenter指令同时在Intel Pentium 2微处理器中引入

    但是进入内核的两种范式并不像看起来那么简单,因为:

  • 内核必须既支持只使用int $0x80指令的旧函数库,同时支持也可以用sysenter指令的新函数库。
  • 使用sysenter指令的标准库必须能处理仅支持int $0x80指令的旧内核
  • 内核和标准库必须既能运行在不包含sysrenter指令的旧处理器上,也能运行在包含它的新处理器上。

  1)通过int $0x80指令发出系统调用

    调用系统调用的传统方法是使用汇编语言指令int。向量128(0x80)对应于内核入口点,在内核初始化期间调用的函数trap_init(),用下面的方式建立对应于向量128的中断描述符表表项:

    set_system_gate(0x80,&system_call);