新博客:http://sparkandshine.net/
分类: LINUX
2011-06-20 09:59:27
摘要:
本文先回答了为什么需要系统调用,为什么增加系统调用,以及如何编写系统调用。紧接着给出一个简单实例,最后详尽分析系统调用整个过程。
一、基础知识
1.1 为什么需要系统调用[2]
系统调用作为沟通用户空间进程和硬件设备之间的中间层,有其必要性,主要体现在:
① 提高系统安全性和稳定性,内核可以基于权限和其他一些规则对访问进行裁决
② 使得编程更加容易,从硬件设备的低级编程解放出来
③ 增强可移植性,只要内核所提供的一组相同的接口,皆可正确编译和执行相同的程序
总的来说,系统调用主要用途有:控制硬件、设置系统状态或者读取内核数据、进程管理[3]。
1.2 系统命令、API、C库、POSIX、系统调用、内核函数
Linux的系统命令格式遵循系统V的传统,多数放在/bin和/sbin下。在Shell窗口下运行命令ls、pwd、chmod,都是系统命令,它是内部引用API的可执行程序。另,可用命令strace ls跟踪系统命令ls用到的系统调用,其他命令类似[3]。
API(应用编程接口)是一些函数定义,说明了如何获得一个给定的服务。POSIX标准定义了一系列的API,Linux的API遵循了该标准。
C库可以理解成API的具体实现,其实现了POSIX绝大部分API,并且提供了一套封装例程将系统调用在用户空间包装后供用户编程使用。
除了异常和陷入外,系统调用是用户空间访问内核的惟一手段(事实上,通过软中断int 0x80引发一个异常来促使系统切换到内核态去执行异常处理程序,即系统调用处理程序),为用户提供进入内核的一些接口,而内核函数对应于其实现(进入内核后,不同的系统调用会找到其对应的内核函数)。可以通过命令cat /proc/kallsyms查看内核公开的内核函数。
1.3 为什么增加系统调用
有些服务可以选择在内核完成,也可以选择在用户空间完成。选择在内核完成通常基于以下考虑[3]:
① 服务必须获得内核数据,比如一些服务必须获得中断或系统时间等内核数据
② 安全性。在内核提供服务比用户空间更安全,防止被非法访问
③ 性能。在内核提供服务,有效避免陷入/返回和系统调用处理程序带来的花销,提高效率(适合于一些对性能要求非常高的应用或者非常频繁使用的应用)
④ 如果内核和用户空间都需要使用该服务,那么最好实现在内核空间,比如随机数产生
1.4 编写系统调用
(1) 内核开发不同于用户空间应用程序开发[1]
① 没有libc库,内核不链接使用标准C函数库,主要原因在于速度和大小(对于内核,完整C库太大了)
② 内核开发使用的C语言涵盖了ISO 99标准和GNU C扩展特性,比如内联函数、内联汇编、分支声明(likely及unlikely)
③ 没有内存保护机制,此外内核中的内存都不分页
④ 内核不能完美支持浮点操作,因为其本身不能陷入
⑤ 容积小且固定的栈,新版本的内核把内核栈和中断栈分开,32位机器栈大小只有4KB
⑥ 同步和并发,内核的许多特性都要求能够并发的访问共享数据
⑦ 可移植性的重要性
(2) 实现系统调用
① 明确用途、确定接口
首先明确其确切用途,不提倡一个系统调用实现多种用途(ioctl除外),另外还需确定新的系统调用的参数(接口应该简洁,参数应尽可能少)、返回值和错误码。还应考虑系统调用的通用性和可移植性。
② 参数验证
系统调用必须检查所有参数的合法正确。最重要的一项检查是检查用户提供的指针是否有效(属于用户空间、指向的内存区域属于进程的地址空间、内存访问权限)。另,内核提供了两种方法copy_to_user()和copy_from_user()实现用户空间与内核空间的数据拷贝。还应检查对资源访问的权限,可以调用函数capable()检查是否有权访问特定资源。
二、增加系统调用实例
假设系统调用名为foo(),取《Linux内核设计与实现》的例子。
在没有实现该系统功能调用的内核版本运行该测试用例将得到-1。
三、系统调用整个过程
通过实例对系统调用有了感性认识,现在来深入分析整个系统调用过程。
这个得过段时间再整理了,我的思路是用GDB调试上述的例子,一路跟踪下去,阅读源代码。等搞清楚了再整理。由此给您带不便,敬请谅解!欢迎交流。
参考文献:
[1]Robert Love著.陈莉君译.Linux内核设计与实现(第二版)[M].北京:机械工业出版社.2006.1
[2]DANIEL P.BOVET & MARCO CESATI著.陈莉君译.深入理解LINUX内核(第三版)[M].北京:中国电力出版社.2007.9
[3]博文《Linux系统调用-Printf从函数库到OS跟踪流程》