linux系统调用部分下面的连接已经讲述的很全面详细了,本文的部分内容也连引用自接文章。本篇文章主要依据《linux内核设计与实现》一书,力求简洁,基础而全面,如有不正确之处,请不惜赐教。
1 系统调用是什么?为什么需要系统调用?
系统调用是内核中用户空间进程和硬件设备之间的中间层,其内容是提供给用户空间使用的一些硬件的抽象接口。主要作用有:
保证了系统的稳定和安全,因为内核可以根据权限等规则对这些访问(用户空间进程发起的系统调用)进行裁决;
提供了用户空间合法访问内核的唯一的方法(由用户态转为内核态);
使用户程序对硬件的访问对内核可见,是实现虚拟内存和多任务的必要条件;
2.系统调用与C库 API POSIX 各有什么关系?
POSIX 可以理解为UNIX 系统中一套应用编程接口的标准规范,POSIX 定义的API 函数和系统调用一般存在着直接调用关系。
linux中,C库实现或者包含了大多数的POSIX 的AP1(即标准C库函数有直接对应的系统调用接口)。
3 系统调用的格式规范
返回值类型:内核中为long ,用户空间中为int;必须以sys_开头;
声明时必须使用:asmlinkage限定词(用于通知编译器仅从栈中提取该函数的参数)
ps:用户空间将系统调用需要的一些参数及返回值,例如系统调用号,函数参数等通过寄存器传递的方式传给内核(系统调用处理程序),与上文所述并不矛盾,不可混为一谈。因为asmlinkage方式是在用户参数已经从用户空间传给内核空间(系统调用处理程序)后,编译时提取参数采用的方式。
例如:asmlinkage long sys_getpid(void)
4 系统调用的实现(引用自:点击打开链接)
Linux中实现系统调用利用了0x86体系结构中的软件中断。软件中断和我们常说的中断(硬件中断)不同之处在于,它是通过软件指令触发而并非外设引发的中断,也就是说,又是编程人员开发出的一种异常(该异常为正常的异常),具体的讲就是调用int $0x80汇编指令,这条汇编指令将产生向量为0x80的编程异常。
更详细地解释一下这个过程。int $0x80指令的目的是产生一个编号为0x80的编程异常,这个编程异常对应的是中断描述符表IDT中的第128项——也就是对应的系统门描述符。门描述符中含有一个预设的内核空间地址,它指向了系统调用处理程序:system_call()(别和系统调用服务程序混淆,这个程序在entry.S文件中用汇编语言编写)。
很显然,所有的系统调用都会统一地转到这个地址,但Linux一共有2、3百个系统调用都从这里进入内核后又该如何派发到它们到各自的服务程序去呢?解决这个问题的方法非常简单:首先Linux为每个系统调用都进行了编号(0—NR_syscall),同时内核中保存了一张系统调用表,该表中保存了系统调用编号和其对应的服务例程,因此在系统调入通过系统门陷入内核前,需要把系统调用号一并传入内核,在x86上,这个传递动作是通过在执行int0x80前把调用号装入eax寄存器实现的。这样系统调用处理程序一旦运行,就可以从eax中得到数据,然后再去系统调用表中寻找相应服务例程了。
除了需要传递系统调用号以外,许多系统调用还需要传递一些参数到内核,比如sys_write(unsigned int fd, const char * buf, size_t count)调用就需要传递文件描述符fd、要写入的内容buf、以及写入字节数count等几个内容到内核。碰到这种情况,Linux会有6个寄存器可被用来传递这些参数:eax (存放系统调用号)、 ebx、ecx、edx、esi及edi来存放这些额外的参数(以字母递增的顺序)。具体做法是在system_call( )中使用SAVE_ALL宏把这些寄存器的值保存在内核态堆栈中.PS:
- 系统调用其实很简单,就是操作系统的API都是通过软件中断动态的调用,通过调用int $0x80 触发软件中断,然后通过一些寄存器将参数传入,实现对API的调用。
- 在嵌入式操作系统中有软中断的概念,该软中断是指将硬中断中次优先级的任务(软中断是中断处理下半部的三种实现方式之一,另外两种是工作队列和tashlet)交给软中断处理,其运行于系统栈中,优先级高于任务,和本章所提及的软件中断有很大的区别,软件中断处理和硬中断处理流程相同,只是该中断由软件触发。
- 指针指向的内存区域是否是属于用户空间;
- 指针指向的内存区域是否在该用户进程的地址空间里;
- 该内存是否是可读,可写;
- 最后检查用户是否有合法权限;