老规矩,如果能回答上这些问题,就没必要看这篇文章了,做些更有意义的事情吧。
#include <fcntl.h>
int open(const char *path, int oflag, .../* mode_t mode */)
这是一个系统调用,看起来跟我们写的C函数签名一模一样,由此可以得出结论,系统调用就是一个函数。
这个结论是不是有点肤浅,哈哈。我们来看看这个结论是否靠谱。
这个“函数”与我们写的函数有什么差异呢?
主要差异就体现在系统调用过程中CPU发生了由用户态->内核态->用户态的状态转换,而我们应用程序写的函数自始至终都是用户态运行。
下面我们就来解密这个过程。
在CPU设计生产时就划分了特权指令和非特权指令,因此CPU执行一条指令前就能判断出其类型。
一些危险指令只能由操作系统来执行,例如清空内存指令,如果让某个用户程序执行那么会影响整个系统。所以CPU需要一种机制来避免这种事情发生,因此划分了这两种状态:内核态、用户态。
CPU 中有个寄存器叫 程序状态字寄存器(PSW),其中有二进制位1表示“内核态”、0表示“用户态”(有些CPU可能相反)。
别名:
内核态=核心态=管态;
用户态=目态
我们通过一个案例来说明CPU切换状态的几种情况。
电脑开机后,CPU处于内核态。需要启动应用程序时,操作系统会主动出让CPU,让应用程序在CPU上执行。在出让CPU前先执行一条特权指令把PSW置为用户态,之后应用程序就能正常执行了。假如有黑客让应用程序中包含一条只有在CPU内核态才能运行的特权指令,就会触发中断(由CPU自己发出中断信号),此时操作系统收到中断信号接管CPU,开始执行操作系统内核的中断处理程序(这种情况中断程序通常会直接kill用户进程)。
总结一下这个案例中的状态切换:
引发状态切换的有特权指令和中断,特权指令我们知道是CPU的一些危险指令集,那中断是什么呢?
与CPU当前执行的指令有关,中断信号来源于CPU内部。
例如,
与CPU当前执行的指令无关,中断信号来源于CPU外部。
CPU在执行完每条指令后,都会例行检查有没有外中断信号。
例如,
无论是内中断还是外中断,让操作系统夺回CPU使用权的唯一途径是”中断“,使CPU由用户态转变为内核态。如果没有中断机制,就无法切回内核程序,进程调度无法执行也就没有并发技术了。
下面的图总结了中断的几种情况:
如图不同的中断信号,需要用不同的中断处理程序来处理。 当CPU检测到中断信号后,会根据中断信号的类型去查询“中断向量表”,以此来找到相应的中断处理程序在内存中的存放位置。
铺垫完这部分知识,说回系统调用。
现在我们就明白了之前讨论的,当发生系统调用时CPU由用户态->内核态->用户态的状态转换的原理。
我们拆成两个步骤来介绍进程与系统调用的细节:
总结一下:
应用程序可以通过系统调用来请求获得操作系统内核的服务,Java程序员可以理解为操作系统提供的API。
库函数的执行过程与我们自己写的函数并无不同,它们是由标准组织定义实现,方便开发者使用。但是因为库函数需要考虑各种边界情况,实际性能未必有我们自己实现的性能好,所以不要盲目认为库函数性能一定很强。对于Java程序员可以对标 Java标准库 理解。
下面这个图能更直观的表现出库函数、系统调用、用户程序之间的关系:
如下图就是操作系统提供的各种共享资源 ,用户进程想要使用共享资源,只能通过系统调用向操作系统内核发出请求。内核会对各个请求进行协调处理,这样可以保证系统的稳定性和安全性,防止用户进行非法操作。
这篇文章是学习《王道考研》的操作系统课整理而来。