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

浅谈Linux中系统调用代码相关

一、系统调用简介

操作系统为用户态的进程与硬件设备进行交互提供了一组接口,在应用程序和硬件之间设置一个额外的层有诸多优点:

1. 使得编程更容易,不需要用户学习硬件设备的低级编程

2. 提高了系统的安全性,内核在满足某个请求之前可以检查请求的正确性

3. 添加的接口使得程序具有可移植性

Unix系统通过向内核发送系统调用(system call)实现用户空间进程和硬件设备之间的大部分接口。 这里使用wiki的一幅图片简单说明:


从图中我们可以看到,无论是在用户空间中调用GNU C库函数后由GNU 的C库与系统调用通信还是应用程序直接调用函数触发系统调用,Linux的内核部分总是通过系统调用接口与外部交流。在当初学习操作系统课程时,我们也知道系统调用是应用程序访问内核空间的唯一手段。从逻辑上来说,系统调用可被看成是一个内核与用户空间程序交互的接口——它好比一个中间人,把用户进程的请求传达给内核,待内核把请求处理完毕后再将处理结果送回给用户空间。比如,用户程序中使用 open() 函数打开一个文件,gnu C库 glibc 将会调用系统调用sys_open(这也是系统调用名称的一般规律,xyz() 函数对应的系统调用名字一般为 sys_xyz ,而在较新内核比如3.10上,绝大多数系统调用的名字都改以“SyS_”开头,也即 open() 函数对应的为 SyS_open 系统调用 )。

换句话说,用户访问内核的路径是事先规定好的,只能从规定位置进入内核,而不准许肆意跳入内核。有了这样的陷入内核的统一访问路径限制才能保证内核安全无虞。我们可以形象地描述这种机制:作为一个游客,你可以买票要求进入野生动物园,但你必须老老实实地坐在观光车上,按照规定的路线观光游览。当然,不准下车,因为那样太危险,不是让你丢掉小命,就是让你吓坏了野生动物。


二、系统调用实现方法

实现系统调用需要涉及包括架构相关的特性的一个控制传输方法,典型的实现就是使用软中断(software interrupt)或者陷入(trap)。
Linux 系统调用的可以通过两种不同的方式调用:
*    第一种实现机制是一个多路汇聚以及分解的过程,该汇聚点就是 0x80 中断这个入口点(X86 系统结构)。也就是说,所有系统调用都从用户空间中汇聚到 0x80 中断点,同时保存具体的系统调用号。当 0x80 中断处理程序运行时,将根据系统调用号对不同的系统调用分别处理(调用不同的内核函数处理)。 
*    第二种实现为SYSCALL / SYSRET ,SYSENTER / SYSEXIT 汇编语言指令(独立于AMD和intel建立,本质上一样的)。


三、Linux中的定义及相关解释

如果我们使用 ctags 或者 cscope 在Linux 内核源代码中查找系统调用的话(如 “ vim -t sys_open ”或者在 cscope界面下查找 sys_open 的函数定义),插件会告诉我们源代码中并没有对于 sys_open 这个系统调用的定义,所以我们只能简介查找。

我们都知道 sys_open 系统调用的实际操作基本上都是在 do_sys_open 函数中完成的,当然,如果你不知道,你可以使用 ftrace 追踪一会系统调用,然后在追踪结果中进行筛选并找出 sys_open 系统调用的大致调用路径,下面是我的