1、什么是系统调用
计算机系统的各种硬件资源是有限的,在现代多任务操作系统上同时运行的多个进程都需要访问这些资源,为了更好的管理这些资源进程是不允许直接操作的,所有对这些资源的访问都必须有操作系统控制。也就是说操作系统是使用这些资源的唯一入口,而这个入口就是操作系统提供的系统调用(System Call)。
系统调用是属于操作系统内核的一部分的,必须以某种方式提供给进程让它们去调用。CPU可以在不同的特权级别下运行,而相应的操作系统也有不同的运行级别,用户态和内核态。运行在内核态的进程可以毫无限制的访问各种资源,而在用户态下的用户进程的各种操作都有着限制,比如不能随意的访问内存、不能开闭中断以及切换运行的特权级别。显然,属于内核的系统调用一定是运行在内核态下,但是如何切换到内核态呢?
操作系统一般是通过中断从用户态切换到内核态。中断就是一个硬件或软件请求,要求CPU暂停当前的工作,去处理更重要的事情。比如,在x86机器上可以通过int指令进行软件中断,而在磁盘完成读写操作后会向CPU发起硬件中断。
中断有两个重要的属性,中断号和中断处理程序。中断号用来标识不同的中断,不同的中断具有不同的中断处理程序。在操作系统内核中维护着一个中断向量表(Interrupt Vector Table),这个数组存储了所有中断处理程序的地址,而中断号就是相应中断在中断向量表中的偏移量。
一般地,系统调用都是通过中断实现的,比如,Linux下中断号0x80就是进行系统调用的。接下来就来看一下Linux下系统调用具体的实现过程。
2、linux下系统调用
系统调用是通过软中断指令 INT 0x80 实现的,而这条INT 0x80指令就被封装在C库的函数中。
INT 0x80 这条指令的运行会让系统跳转到一个预设的内核空间地址,它指向系统调用处理程序。即system_call函数。
(注意:!系统调用处理程序system_call 并非系统调用服务例程,系统调用服务例程是对一个详细的系统调用的内核实现函数。而系统调用处理程序是在运行系统调用服务例程之前的一个引导过程。是针对INT 0x80这条指令,面向全部的系统调用的。
简单来讲,运行不论什么系统调用。都是先通过调用C库中的函数,这个函数里面就会有软中断 INT 0x80 语句。然后转到运行系统调用处理程序 system_call ,
system_call 再依据详细的系统调用号转到运行详细的系统调用服务例程。)
system_call函数是怎么找到详细的系统调用服务例程的呢?通过系统调用号查找系统调用表sys_call_table!软中断指令INT 0x80运行时,系统调用号会被放入 eax 寄存器中,system_call函数能够读取eax寄存器获取,然后将其乘以4,生成偏移地址,然后以sys_call_table为基址。基址加上偏移地址,就能够得到详细的系统调用服务例程的地址了!
然后就到了系统调用服务例程了。
须要说明的是。系统调用服务例程仅仅会从堆栈里获取參数,所以在system_call运行前。会先将參数存放在寄存器中。system_call运行时会首先将这些寄存器压入堆栈。
system_call退出后。用户能够从寄存器中获得(被改动过的)參数。
另外:系统调用通过软中断INT 0x80陷入内核。跳转到系统调用处理程序system_call函数,然后运行对应的服务例程。可是因为是代表用户进程,所以这个运行过程并不属于中断上下文,而是进程上下文。因此。系统调用运行过程中,能够訪问用户进程的很多信息,能够被其它进程抢占,能够休眠。
当系统调用完毕后,把控制权交回到发起调用的用户进程前。内核会有一次调度。
假设发现有优先级更高的进程或当前进程的时间片用完,那么会选择优先级更高的进程或又一次选择进程运行。
3、系统调用意义
linux内核中设置了一组用于实现系统功能的子程序,称为系统调用。
系统调用和普通库函数调用很相似,仅仅是系统调用由操作系统核心提供,执行于核心态。而普通的函数调用由函数库或用户自己提供。执行于用户态。
一般的,进程是不能訪问内核的。它不能訪问内核所占内存空间也不能调用内核函数。CPU硬件决定了这些(这就是为什么它被称作"保护模式")。为了和用户空间上执行的进程进行交互,内核提供了一组接口。透过该接口,应用程序能够訪问硬件设备和其它操作系统资源。这组接口在应用程序和内核之间扮演了使者的角色,应用程序发送各种请求。而内核负责满足这些请求(或者让应用程序临时搁置)。
系统调用在用户空间进程和硬件设备之间加入了一个中间层。该层主要作用有三个:
(1) 它为用户空间提供了一种统一的硬件的抽象接口。
比方当须要读些文件的时候,应用程序就能够不去管磁盘类型和介质,甚至不用去管文件所在的文件系统究竟是哪种类型。
(2)系统调用保证了系统的稳定和安全。作为硬件设备和应用程序之间的中间人,内核能够基于权限和其它一些规则对须要进行的訪问进行裁决。
举例来说,这样能够避免应用程序不对地使用硬件设备,窃取其它进程的资源,或做出其它什么危害系统的事情。
(3) 每一个进程都执行在虚拟系统中,而在用户空间和系统的其余部分提供这样一层公共接口。也是出于这样的考虑。假设应用程序能够任意訪问硬件而内核又对此一无所知的话,差点儿就没法实现多任务和虚拟内存,当然也不可能实现良好的稳定性和安全性。