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

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

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

  • 系统调用和普通函数有何区别?
  • 什么是内核态用户态
  • 操作系统如何让CPU切换状态?
  • 内中断、外中断、软中断、硬中断是什么意思?
  • 库函数和系统调用有何区别?
#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由用户态转变为内核态。如果没有中断机制,就无法切回内核程序,进程调度无法执行也就没有并发技术了。

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

3. 中断机制的基本原理

image.png

如图不同的中断信号,需要用不同的中断处理程序来处理。 当CPU检测到中断信号后,会根据中断信号的类型去查询“中断向量表”,以此来找到相应的中断处理程序在内存中的存放位置。

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

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

我们拆成两个步骤来介绍进程系统调用的细节:

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

总结一下:
image.png

应用程序可以通过系统调用来请求获得操作系统内核的服务,Java程序员可以理解为操作系统提供的API。

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

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

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

image.png

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

目录
相关文章
|
安全 Oracle 网络协议
第4章 数据库安全性——4.1 数据库安全性概述
第4章 数据库安全性——4.1 数据库安全性概述
|
C语言
设备树知识小全(六):设备节点及label的命名
设备树知识小全(六):设备节点及label的命名
511 0
【计算机网络】第三章 数据链路层(可靠传输)
【计算机网络】第三章 数据链路层(可靠传输)
|
分布式数据库 Hbase 存储
带你读《HBase原理与实践》之一:HBase概述
Apache HBase是基于Apache Hadoop构建的一个高可用、高性能、多版本的分布式NoSQL数据库,是Google BigTable的开源实现,通过在廉价服务器上搭建大规模结构化存储集群,提供海量数据高性能的随机读写能力。
|
Shell Linux Android开发
【Linux】【编译相关】execvp: /bin/sh: Argument list too long问题处理小结
【Linux】【编译相关】execvp: /bin/sh: Argument list too long问题处理小结
2057 0
|
存储 C语言
用加法器实现补码的加/减运算
用加法器实现补码的加/减运算
543 0
|
存储 关系型数据库 MySQL
认识MySQL数据库中的事件
认识MySQL数据库中的事件。
341 4
|
机器学习/深度学习 运维 监控
智能化运维:未来数据中心的守护者
【6月更文挑战第11天】在数字化浪潮不断推进的今天,数据中心作为企业信息架构的核心,其稳定性和高效性对企业运营至关重要。本文将探讨智能化运维如何通过先进的技术手段,实现对数据中心的实时监控、自动化管理与故障预防,从而确保企业IT基础设施的高可用性和性能优化。
|
数据中心 云计算 网络架构
|
网络协议 算法 网络虚拟化
【计算机网络】数据链路层(超多图详析)
之前学习了原理体系结构中的物理层,也写了非常详细文章。如果没有看可以先去学习一下,这样有助于我们对数据链路层的学习。
【计算机网络】数据链路层(超多图详析)