Linux系统调用

来源:互联网 发布:西安电子大学网络教育 编辑:程序博客网 时间:2024/06/06 20:12

Linux系统调用
系统调用(system call)是用户空间訪问内核的唯一手段,除异常和陷入外,他们是内核唯一的合法入口。通常情况下应用程序是通过应用编程接口API来访问函数,而不是直接使用系统调用来编程。
操作系统通常是通过中断从用户态切换到内核态。中断就是一个硬件或软件请求,要求CPU暂停当前的工作,去处理更重要的事情。
比方。在x86机器上能够通过int指令进行软件中断。而在磁盘完毕读写操作后会向CPU发起硬件中断。
这里写图片描述
中断有两个重要的属性,中断号和中断处理程序。中断号用来标识不同的中断,不同的中断具有不同的中断处理程序。在操作系统内核中维护着一个中断向量表(Interrupt Vector Table)。这个数组存储了全部中断处理程序的地址,而中断号就是对应中断在中断向量表中的偏移量。
一般地,系统调用都是通过软件中断实现的,x86系统上的软件中断由int $0x80指令产生。
系统调用时会进行用户态到内核态的切换(Linux下的系统调用达到319个),在操作系统中,无论用户级进程还是内核级进程,本质上都属于同一个进程,只是运行在内核态的进程可以毫无限制的访问各种资源,而在用户态下的用户进程的各种操作都有着限制,比如不能随意的访问内存、不能开闭中断以及切换运行的特权级别。所以它们属于同一块PCB,各自拥有用户栈和内核栈。
系统调用需要提供参数及返回值,所以就涉及到:
1、系统调用的函数名转换;
2、系统调用的参数传递。
(一)、函数名的转换
每个系统调用都有一个系统调用号作为唯一的标识符,在内核中有一张内核系统调用表,表中的元素是系统调用函数的起始地址,系统调用号是系统调用函数在表中的偏移量。所以只要指定系统调用号,就可以确定相应的系统调用,从而完成函数名的转换。
举例来说,exit的调用号为1号(不同版本之间存在差异)
这里写图片描述
通过eax寄存器传递系统调用号,将1给eax,然后用int指令进行系统调用。
这里写图片描述

(二)、参数传递
系统调用的参数传递使用寄存器,Linux最多可以向系统调用传递6个参数,分别使用%ebx、%ecx、%edx、%esi、%edi和%epb来完成。
当进行系统调用时,进程不仅要从用户态切换到内核态,同时也要完成栈切换,这样处于内核态的系统调用才能在内核栈上完成调用。系统调用返回时,还要切换回用户栈,继续完成用户态下的函数调用。
寄存器%esp(栈指针,指向栈顶)所在的内存空间叫做当前栈,比如%esp在用户空间则当前栈就是用户栈,否则是内核栈。栈切换主要就是%esp在用户空间和内核空间间的来回赋值。在Linux中,每个进程都有一个私有的内核栈,当从用户栈切换到内核栈时,需完成保存%esp以及相关寄存器的值(%ebx,%ecx…)并将%esp设置成内核栈的相应值。而从内核栈切换会用户栈时,需要恢复用户栈的%esp及相关寄存器的值以及保存内核栈的信息。在调用int指令机型系统调用后会把用户栈的%esp的值及相关寄存器压入内核栈中,系统调用通过iret指令返回,在返回之前会从内核栈弹出用户栈的%esp和寄存器的状态,然后进行恢复。
系统调用很耗时,要尽量少用。第一,系统调用通过中断实现,需要完成栈切换。第二,使用寄存器传参,这需要额外的保存和恢复的过程。

原创粉丝点击