首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【操作系统】考研408操作系统核心考点:深入理解系统调用机制

【操作系统】考研408操作系统核心考点:深入理解系统调用机制

作者头像
蒙奇D索隆
发布2025-09-28 08:07:21
发布2025-09-28 08:07:21
1K0
举报

(系统调用)

系统调用
系统调用

导读

大家好,很高兴又和大家见面啦!!!

在上一篇内容中我们介绍了操作系统中的中断与异常的相关内容:

  • 用户态:用户程序工作时的状态
  • 内核态:操作系统内核工作时的状态
  • 中断:也称外中断,是来自CPU执行指令外部的事件
    • 可屏蔽中断:通过INTR线发出的中断请求
    • 不可屏蔽中断:指通过NMI线发出的中断请求
  • 异常:也称内中断,是来自CPU执行指令内部的事件
    • 故障:由指令执行引起的异常
    • 自陷:也称陷入,是一种事先安排的“异常”事件
    • 终止:是指出现了使得CPU无法继续执行的硬件故障

在今天的内容中,我们会认识操作系统中的系统调用,下面我们进入今天的内容吧!!!

一、定义

在前面的内容中,我们有介绍过,操作系统是作为用户和计算机硬件之间的接口,需要向上提供一些简单易用的服务。主要包括命令接口和程序接口。其中,程序接口是由一组系统调用组成。

代码语言:javascript
复制
graph TB
A[用户]
B[应用程序]
C[操作系统]
D[硬件]
A--->B--->C--->D
A--->C
C---> |命令接口| A
C---> |程序接口|B

系统调用操作系统提供给应用程序使用的接口,可视为一种供应用程序调用的特殊函数应用程序可以通过系统调用请求获得操作系统内核的服务

既然系统调用可以设为特殊的函数,那么它又与库函数之间有哪些区别呢?

1.1 系统调用与库函数的区别

在我们学习的高级编程语言(如C语言、C++、JAVA……)中,这些高级语言会提供一些库函数供我们使用,如C语言中的标准输入函数:scanf,标准输出函数:printf,取绝对值函数:abs……,这些库函数有一些就会涉及到系统调用(如输入、输出函数),而另一些则不涉及(如取绝对值)。

因此,对比库函数而言,系统调用是实现某些库函数的更加底层的特殊函数

高级编程语言会通过将一些系统调用封装成库函数,用来隐藏一些系统调用的细节,以便为程序员提供更加方便的编程功能。

1.2 系统调用的必要性

系统调用(System Call)是用户应用程序(运行在“用户态”)请求操作系统内核(运行在“内核态”)为其提供服务或访问硬件资源的唯一通道和接口。​​

它的核心作用可以概括为:​​在保证系统安全、稳定和多任务高效运行的前提下,为应用程序提供必要的底层服务

设想一下,当不存在系统调用时,若多个应用程序要访问同一个共享资源,此时会出现什么情况呢?

如果没有系统调用,多个应用程序同时尝试使用一个共享资源(如CPU、内存、硬盘、打印机、网络等),整个系统将陷入混乱、冲突和不安全的状态。

具体会出现的问题可以分为以下几类:

  1. CPU 资源竞争:失去调度与控制

没有系统调用,操作系统就无法接管CPU的控制权。

  • 问题: 一个应用程序可以无限期地霸占CPU,永不释放。其他应用程序将永远得不到运行的机会,整个系统除了这一个程序外,看起来就像“死机”了一样。
  • 系统调用的作用: 操作系统通过时钟中断(一种硬件机制)和系统调用(如进程调度相关的调用),强制性地从当前应用程序收回CPU控制权,并根据调度算法公平地分配给下一个程序。这实现了“多任务”的错觉。
  1. 内存资源竞争:相互破坏、隐私泄露

没有系统调用,所有程序的内存空间将完全不设防。

  • 问题:
    • 无意覆盖: 程序A的一个指针错误可能轻易地写入程序B的内存空间,导致程序B崩溃或产生不可预知的行为,且很难调试。
    • 恶意窃取: 一个恶意程序可以随意读取其他程序内存中的所有数据,例如你的浏览器内存中保存的密码、银行账号等,隐私和安全荡然无存。
    • 空间耗尽: 一个程序可以无限制地申请和占用所有可用内存,导致其他程序无法运行。
  • 系统调用的作用: 操作系统通过内存管理单元(MMU)和系统调用,为每个进程提供独立的虚拟地址空间。一个程序无法直接访问其他程序或内核的内存。当它需要更多内存时(通过 brk 或 mmap 等系统调用),由内核作为中介来分配和映射物理内存,确保了隔离性和安全性。
  1. 硬件设备竞争:混乱与损坏

对于打印机、硬盘、声卡等硬件,如果没有协调,输出将会是混乱的。

  • 问题(打印机例子): 程序A和程序B同时向打印机发送数据。打印出的文件将是两份文档内容交织在一起的乱码,完全无法使用。
  • 问题(硬盘例子): 程序A和程序B同时要求硬盘在同一个位置写入数据。最终写入的数据可能是A的、可能是B的、也可能是损坏的混合体,导致数据完全丢失。
  • 系统调用的作用: 操作系统是唯一的仲裁者(Arbiter)。所有程序对设备的请求都必须通过系统调用发给内核。内核维护着请求队列(例如打印假脱机系统),串行化这些请求,确保一个设备在同一时刻只为一个进程服务,从而保证输出的完整性和正确性。
  1. 数据一致性问题

这与设备竞争类似,但更侧重于文件等数据资源。

  • 问题: 程序A和程序B同时打开并修改同一个文件。它们都在自己的内存中有一份文件副本。当它们保存时,后保存的程序会覆盖先保存的程序所做的所有更改,导致数据丢失。
  • 系统调用的作用: 操作系统通过文件系统提供并发控制机制,例如文件锁(fcntl 或 flock 系统调用)。当一个进程锁住文件时,其他进程要么等待,要么被告知无法访问,从而保护了数据的完整性。

也就是说,如果没有系统调用,计算环境将退回到无政府状态:

  1. 无隔离(No Isolation): 应用程序之间、应用程序与操作系统之间没有边界,可以相互窥探和破坏。
  2. 无协调(No Coordination): 对共享资源的访问是混乱的“抢着用”,而不是有秩序的“分着用”。
  3. 无特权(No Privilege): 应用程序可以执行任何指令(包括那些直接关闭硬件电源的危险指令),系统极其脆弱。
  4. 无多任务(No Multitasking): 无法实现多个程序同时稳定运行的体验。

因此,系统调用是操作系统为用户程序提供服务的唯一安全通道,同时也是管理和保护系统资源的最根本机制。它就像是城市中的交通信号灯和交通法规,如果没有它们,所有车辆(应用程序)在路口(共享资源)只会挤成一团,事故频发,谁也无法高效通行。

二、功能

系统中的各种共享资源都由操作系统同一掌管,因此凡事与共享资源相关的操作(如存储分配、I/O传输及文件管理等),都必须通过系统调用方式向操作系统提出服务请求,由操作系统代为完成,并将处理结果返回给应用程序。这样就可以保证系统的稳定性安全性,防止用户进行非法操作。

通常,一个操作系统提供的系统调用命令有几十条乃至上白条之多,每个系统调用都有唯一的系统调用号。这些系统调用按功能大致可以分为以下几类:

  • 设备管理:完成设备的请求或释放,以及设备的启动等功能
  • 文件管理:完成文件的读、写、创建及删除等功能
  • 进程控制:完成进程的创建、撤销、阻塞及唤醒等功能
  • 进程通信:完成进程之间的消息传递或信号传递等功能
  • 内存管理:完成内存的分配、回收以及获取作业占用内存区大小和起始地址等功能

显然,系统调用相关功能涉及系统资源管理、进程管理之类的操作,对整个系统的影响非常大,因此系统调用的处理需要由操作系统内核程序负责完成,要运行在内核态。

三、调用过程

在说明系统调用过程之前,我们需要先理解什么是陷入指令

陷入指令(Trap Instruction)​​ 是计算机体系结构和操作系统中一个非常关键的概念。它指的是​​一条特殊的机器指令,当 CPU 执行这条指令时,会主动、有意识地触发一个从用户态(User Mode)到内核态(Kernel Mode)的切换,并将控制权交给操作系统内核预先定义好的处理程序。​

下面我们再来看一下系统调用的整个过程:

  1. 用户程序首先将系统调用号和所需的参数压入堆栈;
代码语言:javascript
复制
graph TB
A[系统调用号1]
B[系统调用号2]
C[参数1]
D[参数2]
E[堆栈]
F[...]
A--->E
B--->E
F--->E
C--->E
D--->E
f[...]--->E
G[陷入指令]
  1. CPU调用实际的调用指令
代码语言:javascript
复制
graph TB
A[CPU]--->B[调用指令1]
A--->C[调用指令2]
A--->D[...]
  1. 执行陷入指令,将CPU状态从用户态转为内核态
代码语言:javascript
复制
graph LR
f[用户态CPU]--->e[陷入指令]--->a[内核态CPU]
  1. 硬件和操作系统内核程序保护被中断进程的现场,将程序计数器(PC)、程序状态字(PSW)以及通用寄存器内容等压入堆栈
代码语言:javascript
复制
graph TB
a[PC]
B[PSW]
C[通用寄存器存内容]
e[...]
f[堆栈]
a--->f
B--->f
C--->f
e--->f
  1. 分析系统调用类型,转入相应的系统调用处理子程序。
代码语言:javascript
复制
graph LR
a[系统调用1]--->b[CPU]--->c[系统调用1处理子程序]
d[系统调用2]--->b--->e[系统调用2处理子程序]
f[...]--->b--->g[...]

为了更好的分析系统调用类型,在系统中配置了一张系统调用入口表,表中的每个表项都对应一个系统调用,根据系统调用号可以找到该系统调用处理子程序的入口地址,如下所示:

系统调用号

子程序入口地址

000000

123456

000001

112233

……

……

  1. 在系统调用处理子程序执行结束后,恢复被中断的或设置新进程的CPU现场
代码语言:javascript
复制
graph LR
a[内核态CPU]--->B[用户态CPU]
  1. 返回被中断的进程或新进程,继续往下执行
代码语言:javascript
复制
graph TB
A[系统调用号3]
B[系统调用号4]
C[参数3]
D[参数4]
E[堆栈]
F[...]
A--->E
B--->E
F--->E
C--->E
D--->E
f[...]--->E

系统调用的整个过程,我们可以简单的理解为:

代码语言:javascript
复制
graph TB
A[用户态CPU执行用户程序进程]--->a[用户程序调用系统调用]--->b[用户态CPU执行陷入指令]--->c[内核态CPU执行系统调用]--->d[内核态CPU执行返回指令]
d--->e[用户态CPU执行后续进程]

结语

今天的内容到这里就结束了,通过本文的学习,我们系统地梳理了操作系统系统调用(System Call) 的核心概念与工作机制。让我们简单回顾一下重点:

  • 系统调用的本质:是应用程序向操作系统内核请求服务的唯一安全通道,运行于内核态,是一种特殊的、底层的接口。
  • 与库函数的区别:库函数是更高层次的封装,部分库函数基于系统调用实现,但并非所有库函数都涉及系统调用。
  • 存在的必要性:它是实现资源安全访问、多任务协调和系统稳定性的基石。没有系统调用,整个计算环境将陷入混乱、竞争和不安全的状态。
  • 主要功能:涵盖了设备管理、文件操作、进程控制、进程通信和内存管理等关键操作。
  • 调用过程的核心:通过陷入指令(Trap Instruction) 主动从用户态切换至内核态,由内核统一处理后再返回用户态。

理解系统调用,是理解操作系统如何管理资源、提供服务并保障安全的关键一步。

希望这篇内容对你有帮助!如果觉得有收获,欢迎点赞👍、收藏⭐️!有任何想法或问题,也欢迎在评论区💬留言交流。别忘了转发➡️给更多朋友,一起学习进步!

精彩继续,下一篇我们将学习《操作系统结构》,大家记得关注账号,不要错过更新!感谢大家的支持,我们下一篇再见!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-09-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 导读
  • 一、定义
    • 1.1 系统调用与库函数的区别
    • 1.2 系统调用的必要性
  • 二、功能
  • 三、调用过程
  • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档