“系统调用”究竟是不是个函数?

举报
程序饲养员 发表于 2024/01/02 09:36:30 2024/01/02
【摘要】 主要差异就体现在**系统调用**过程中CPU发生了由**用户态->内核态->用户态**的状态转换,而我们应用程序写的函数自始至终都是用户态运行。

老规矩,如果能回答上这些问题,就没必要看这篇文章了,做些更有意义的事情吧。

- **系统调用**和普通**函数**有何区别?
- 什么是**内核态** 和 **用户态**?
- 操作系统如何让CPU切换状态?
- 内中断、外中断、软中断、硬中断是什么意思?
- 库函数和系统调用有何区别?

```c
#include <fcntl.h>
int open(const char *path, int oflag, .../* mode_t mode */)
```
这是一个系统调用,看起来跟我们写的C函数签名一模一样,由此可以得出结论,系统调用就是一个函数。

这个结论是不是有点肤浅,哈哈。我们来看看这个结论是否靠谱。

这个“函数”与我们写的函数有什么差异呢?

主要差异就体现在**系统调用**过程中CPU发生了由**用户态->内核态->用户态**的状态转换,而我们应用程序写的函数自始至终都是用户态运行。

下面我们就来解密这个过程。

## “内核态” 和 “用户态”
在CPU设计生产时就划分了特权指令和非特权指令,因此CPU执行一条指令前就能判断出其类型。

一些危险指令只能由操作系统来执行,例如清空内存指令,如果让某个用户程序执行那么会影响整个系统。所以CPU需要一种机制来避免这种事情发生,因此划分了这两种状态:内核态、用户态。

CPU 中有个寄存器叫 **程序状态字寄存器(PSW)**,其中有二进制位1表示“内核态”、0表示“用户态”(有些CPU可能相反)。

1. 处于**内核态**时,说明此时正在运行的是**内核程序**,此时**可以执行特权指令**
2. 处于**用户态**时,说明此时正在运行的是**用户程序**,此时**只能执行非特权指令**

> 别名:
内核态=核心态=管态;
用户态=目态

### 操作系统如何让CPU切换状态?
我们通过一个案例来说明CPU切换状态的几种情况。

电脑开机后,CPU处于**内核态**。需要启动应用程序时,操作系统会主动出让CPU,让应用程序在CPU上执行。在出让CPU前先**执行一条特权指令**把PSW置为**用户态**,之后应用程序就能正常执行了。假如有黑客让**应用程序**中包含一条只有在CPU内核态才能运行的**特权指令**,就会触发**中断**(由CPU自己发出中断信号),此时操作系统收到**中断信号**接管CPU,开始执行操作系统内核的中断处理程序(这种情况中断程序通常会直接kill用户进程)。

总结一下这个案例中的状态切换:
1. 内核态 --> 用户态:**执行一条特权指令**把PSW置为**用户态**。
2. 用户态 --> 内核态:用户态执行特权指令引发**中断**,导致PSW置为**内核态**。

引发状态切换的有特权指令和中断,特权指令我们知道是CPU的一些危险指令集,那**中断**是什么呢?

### 中断和异常

#### 1. 内中断(也称为”异常“或软中断)
与CPU当前执行的指令有关,中断信号来源于CPU内部。
例如,
1. 应用程序内非法执行一条特权指令,CPU正处于用户态(不允许执行特权指令),便会触发内中断。
2. 正常指令也会导致内中断,如除法指令遇到除数为零。
3. 还有一种情况是应用程序需要请求操作系统内核的服务,此时会执行一条特殊的指令**陷入指令**(也称为“trap指令”或“访管指令”),**陷入指令是一个普通指令,并不是特权指令**。**系统调用**就是陷入指令实现的。

#### 2. 外中断(也称为硬中断)
与CPU当前执行的指令无关,中断信号来源于CPU外部。
CPU在执行完每条指令后,都会例行检查有没有外中断信号。
例如,

1. 时钟中断,由时钟部件发出来的中断信号,当CPU检测到有中断信号时,会暂停正在运行的用户程序,转而执行时钟中断的内核程序,这段程序一般就是进程调度程序。并发就是基于此实现的。
2. IO中断,例如打印机完成打印任务后,触发IO中断信号,CPU就会执行操作系统内核的IO中断处理程序。

无论是**内中断**还是**外中断**,让操作系统夺回CPU使用权的唯一途径是”中断“,使CPU由用户态转变为内核态。如果没有中断机制,就无法切回内核程序,进程调度无法执行也就没有并发技术了。

下面的图总结了中断的几种情况:
Pasted image 20231221151051.png

#### 3. 中断机制的基本原理
Pasted image 20231221125205.png
如图**不同的中断信号,需要用不同的中断处理程序来处理。** 当CPU检测到中断信号后,会根据中断信号的类型去查询“中断向量表”,以此来找到相应的中断处理程序在内存中的存放位置。



铺垫完这部分知识,说回系统调用。

现在我们就明白了之前讨论的,当发生**系统调用**时CPU由**用户态->内核态->用户态**的状态转换的原理。
Pasted image 20231221151820.png

我们拆成两个步骤来介绍**进程**与**系统调用**的细节:
1. 用户态->内核态
当函数执行系统调用时,首先执行**传参指令**将程序参数放到CPU寄存器上,例如系统调用的类型(如fork)以及其他参数。**CPU陷入指令**会导致中断,因此内核接管CPU。那么此时执行这个函数的进程就被阻塞了,紧接着CPU根据中断向量表执行中断处理程序,发现是系统调用就执行这个系统调用相关的代码指令。
2. 内核态->用户态
执行完系统调用相关的代码指令,将结果写入内存地址,以备进程恢复后读取结果。再之后执行特权指令**把CPU设置为用户态**,进程恢复**读取系统调用的结果**继续执行函数内其它指令。


总结一下:
Pasted image 20231219212342.png
应用程序可以通过系统调用来请求获得操作系统内核的服务,Java程序员可以理解为操作系统提供的API。

库函数的执行过程与我们自己写的函数并无不同,它们是由标准组织定义实现,方便开发者使用。但是因为库函数需要考虑各种边界情况,实际性能未必有我们自己实现的性能好,所以不要盲目认为库函数性能一定很强。对于Java程序员可以对标 **Java标准库** 理解。

下面这个图能更直观的表现出库函数、系统调用、用户程序之间的关系:
Pasted image 20231219212753.png


如下图就是操作系统提供的各种共享资源 ,用户进程想要使用共享资源,只能通过系统调用向操作系统内核发出请求。内核会对各个请求进行协调处理,这样可以保证系统的稳定性和安全性,防止用户进行非法操作。

Pasted image 20231219213829.png

这篇文章是学习《王道考研》的操作系统课整理而来。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。