系统调用的概念及原理

2023-05-16

系统调用与内核函数

内核函数与普通函数形式上没有什么区别,只不过前者在内核实现,因此要满足一些内核编程的要求。
系统调用是用户进程进入内核的接口层,它本身并非内核函数,但它是由内核函数实现的,进入内核后,不同的系统调用会找到相应的内核函数,这些内核函数被称为系统调用的“服务例程”。
比如系统调用getpid()实际调用的是服务例程sys_getpid(),也可以说,系统调用getpid()是服务例程sys_getpid()的“封装例程”。

系统调用基本概念

计算机的各种硬件资源是有限的,为了更好的管理这些资源,用户进程是不允许直接操作的,所有对这些资源的访问都必须由操作系统控制。为此操作系统为用户态运行的进程与硬件设备之间进行交互提供了一组接口,这组接口就是所谓的系统调用。

那么在应用程序和硬件之间设置这样一个接口层有什么优点呢?
1. 把用户从学习硬件设备的低级编程特性中解放出来。
2. 提高了系统的安全性,内核在满足某个请求之前就可以在接口级检查这个请求的正确性。
3. 最重要的是,这些接口使得程序更具有可移植性,因为只要不同操作系统所提供的一组接口相同,那么在这些操作
系统上就可以正确的编译和执行相同的程序。

系统调用实质上就是函数调用,只不过调用的是系统函数,处于内核态而已。 用户在调用系统调用时会向内核传递一个系统调用号,然后系统调用处理程序通过此号从系统调用表中找到相应的内核函数执行,最后返回。

系统调用号

Linux系统有几百个系统调用,为了唯一的标识每一个系统调用,Linux为每一个系统调用定义了一个唯一的编号,这个编号就是系统调用号。(在我的电脑上,它是在 /usr/include/x86_64-linux-gnu/asm/unistd_32.h, 可以通过 find / -name unistd_32.h -print 查找)。
在这里插入图片描述
系统调用号的另一个目的是作为系统调用表的下标,当用户空间的进程执行一个系统调用的时侯,这个系统调用号就被用来指明到底是要执行那个系统调用。

系统调用表

为了把系统调用号与相应的服务例程关联起来,内核利用了一个系统调用表,存放在sys_call_table数组中,它是一个函数指针数组,每一个函数指针都指向其系统调用的封装例程,有NR_syscalls个表项,第n个表项包含系统调用号为n的服务例程的地址。

系统调用处理函数

系统调用最终还是会由内核函数完成,那么为什么不直接调用内核函数呢?
这是因为用户空间的程序无法直接执行内核代码,因为内核驻留在受保护的地址空间上,不允许用户进程在内核地址空间上读写。所以,应用程序会以某种方式通知系统,告诉内核需要执行一个函数调用,这种通知机制是靠软中断来实现的,通过引发一个异常来促使系统切换到内核态去执行异常处理程序。此时的异常处理程序就是所谓的系统调用处理程序。

系统调用是属于操作系统内核的一部分,必须以某种方式提供给进程让它们去调用。CPU可以在不同的特权级别下运行,
而相应的操作系统也有不同的运行级别,用户态和内核态。运行在内核态的进程可以毫无限制的访问各种资源,
而在用户态下的用户进程的各种操作都有着限制,比如不能随意的访问内存、不能开闭中断以及切换运行的特权级别。
所以操作系统通过中断从用户态切换到内核态。

补充——中断是什么

中断控制是计算机发展中一种重要的技术。 最初它是为克服对I/O接口控制采用程序查询所带来的处理器效率低而产生的。
中断控制的主要优点是只有在I/O需要服务时才能得到处理器的响应,而不需要处理器不断地进行查询。
由此,最初的中断全部是对外部设备而言的,称为外部中断(或硬件中断)。
但随着计算机系统结构的不断改进以及应用技术的日益提高,中断的适用范围也随之扩大,出现了所谓的内部中断(或叫异常),
它是为解决机器运行时所出现的某些随机事件及编程方便而出现的。因而形成了一个完整的中断系统。

中断可分为两大类:异常中断
异常分为故障陷阱特点是既不使用中断控制器,也不能被屏蔽(异常实际上是CPU发出的中断信号)。
中断分为外部可屏蔽中断和外部非屏蔽中断,所有I/O设备产生的中断请求均引起屏蔽中断,而紧急事件(如硬件故障)引起的故障产生非屏蔽中断。

Intel x86系列微机共支持256中向量中断,Linux对256个向量的分配如下:
(1) 从0~31的向量对应异常和非屏蔽中断。
(2) 从32~47的向量分配给屏蔽中断。
(3) 从48~255的向量用来标识软中断。Linux只用了其中一个(128向量即0x80向量)用来实现系统调用。
注:Linux为什么只使用一个中断号来对应所有的系统调用,而不是一个中断号对应一个系统调用?
因为中断号是有限的,而系统调用又太多了。

Linux对系统调用的调用必须通过执行int $0x80汇编指令,这条指令会产生向量为128的编程异常。

中断有两个重要的属性,中断号中断处理程序。中断号用来标识不同的中断,不同的中断具有不同的中断处理程序。在操作系统内核中维护着一个中断向量表,这个数组存储了所有中断处理程序的地址,而中断号就是相应中断在中断向量表中的偏移量。

在实地址模式中,CPU把内存中从0开始的1KB用来存储中断向量表。表中的每个表项占4个字节,
由两个字节的段地址和两个字节的偏移量组成,这样构成的地址便是相应的中断处理程序的入口地址。
但是,在保护模式下,由4字节的表项构成的中断向量表显然满足不了要求。这是因为:
<1>除了两个字节的段描述符,偏移量必须用4字节来表示;
<2>要有反映模式切换的信息。
因此,在保护模式下,中断向量表中的表项由8个字节组成,中断向量表也改叫做中断描述符表
IDT(Interrupt Descriptor Table)。其中的每个表项叫做一个门描述符(Gate Descriptor),
“门”的含义是当中断发生时必须先通过这些门,然后才能进人相应的处理程序。

Linux下系统调用原理

根据上文,我们知道Linux使用一个中断号来对应所有的系统调用,那么只有一个中断号,操作系统怎么知道是哪一个系统调用要被调用呢?
上文也提到过,系统调用拥有自己的系统调用号,系统调用表以及系统调用处理函数。一个调用号对应一个处理函数,通过处理函数和调用号在调用表中找到相应的系统调用。

在Linux中,EAX寄存器是负责传递系统调用号的。
以fork()为例,fork()的系统调用号为2,在执行int $0x80指令前,调用号会被存放在eax寄存器中,系统调用处理函数(中断处理函数)最终会通过系统调用号,调用正确的系统调用。

伪代码:
movl eax,2
int 0x80

在这里插入图片描述系统调用除了需要传递系统调用号以外,还是需要传递参数,并且具有返回值的,那么参数是怎么传递的呢?
对于参数传递,Linux也是通过寄存器完成的。Linux最多允许向系统调用传递6个参数,
分别依次由%ebx,%ecx,%edx,%esi,%edi和%ebp这个6个寄存器完成。

以调用exit(1)为例

伪代码:
movl eax,2
movl ebx,1
int 0x80

因为exit需要一个参数1,所以这里只需要使用ebx。这6个寄存器可能已经被使用,所以在传参前必须把当前寄存器的状态保存下来,待系统调用返回后再恢复。

Linux中,在用户态和内核态运行的进程使用的栈是不同的,分别叫做用户栈和内核栈, 两者各自负责相应特权级别状态下的函数调用。当进行系统调用时,进程不仅要从用户态切换到内核态,同时也要完成栈切换, 这样处于内核态的系统调用才能在内核栈上完成调用。系统调用返回时,还要切换回用户栈,继续完成用户态下的函数调用。

寄存器%esp(栈指针,指向栈顶)所在的内存空间叫做当前栈, 比如%esp在用户空间则当前栈就是用户栈,否则是内核栈。栈切换主要就是%esp在用户空间和内核空间间的来回赋值。 在Linux中,每个进程都有一个私有的内核栈,当从用户栈切换到内核栈时,需完成保存%esp以及相关寄存器的值(%ebx,%ecx…)并将%esp设置成内核栈的相应值。而从内核栈切换会用户栈时,需要恢复用户栈的%esp及相关寄存器的值以及保存内核栈的信息。

之前总是听说系统调用很费时,在学习了系统调用的原理后,我们应该也知道了其中的原因

  1. 系统调用通过中断实现,需要从用户态切换到内核态,也就是要完成栈切换。
  2. 会使用寄存器传参,需要额外的保存和恢复的过程。

以上关于系统调用的总结,都是本人从书本以及网上的资料,博客学习到的,如果由什么不对的地方,欢迎指正。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

系统调用的概念及原理 的相关文章

  • MOT:A Higher Order Metric for Evaluating Multi-object Tracking

    简介 HOTA A Higher Order Metric for Evaluating Multi object Tracking是IJCV 2020的paper xff0c 在此之前以MOTChallenge为主的多目标跟踪benchm
  • 如何更新Ubuntu系统、调整多系统启动顺序

    如何更新Ubuntu系统 调整多系统启动顺序 安装包 升级前系统前 xff0c 第一个命令或者说动作是 xff1a 以下命令需要在Ubuntu终端窗口内执行 xff0c 打开终端的快捷键是 xff1a CTRL 43 alt 43 T sp
  • git新建仓库,本地分支由master变为main

    由于一些众所周知的原因 xff0c github上传代码的默认分支由master变为了main 还是我昨天新建仓库的时候发现的 xff08 以前的仓库并不受影响 xff09 但本地分支仍旧为master xff0c 这就导致上传之后仓库有两
  • 调试笔记2:SPI+DMA

    一 内容简介 说明 xff1a 关于DMA xff0c SPI的基本知识这里不做介绍 本文只讲述SPI 43 DMA的实现 这里仅实现从外设到内存 从内存到外设也可以参考修改 目的 xff1a 使用STM32作为SPI从机接收数据 xff0
  • ANO匿名上位机V7协议&STM32

    ANO匿名上位机V7协议 amp STM32 说明 xff1a 以下程序为自己编写 xff0c 若有误欢迎各位指出 基于ANO匿名V7上位机的通信协议编写的代码 文章目录 ANO匿名上位机V7协议 amp STM32前言一 Ano V7上位
  • Makefile教程

    1 Makefile 简介 Makefile 是和 make 命令一起配合使用的 很多大型项目的编译都是通过 Makefile 来组织的 如果没有 Makefile 那很多项目中各种库和代码之间的依赖关系不知会多复杂 Makefile的组织
  • centos8安装docker错误解决

    安装出现 Problem problem with installed package buildah Last metadata expiration check 0 08 17 ago on Sat 20 Feb 2021 12 43
  • 深度学习环境配置记录——RTX3050

    一 下载 首先需要先了解一下深度学习环境需要的各个软件之间的关系 xff1a 从源代码构建 TensorFlow google cn 然后了解自己的电脑 NVIDIA控制面板中查看显卡驱动 xff0c 注意这个只是显卡驱动的版本 xff0c
  • RT-Thread— 知识点总结(RTT认证+面试题汇总)

    RT Thread 知识点总结 内核 RO xff1a 只读数据段 xff0c 存放程序中定义的常量 RO Size xff1a code 43 RO Data gt 占用flash大小 RW xff1a 读写数据段 xff0c 存放非0全
  • 建立本地分支与远程分支关联

    文章目录 全过程使用的指令1 1 更新 remote 版本1 2 建立一个新的分支与远程分支对应1 3 关联远程仓库分支 全过程使用的指令 span class token function git span fetch span clas
  • 遥感卫星飞行控制系统设计

    文章目录 1 卫星姿态控制模块组成2 转动惯量和地球自转角速度3 初始姿态和目标姿态4 欧拉角转四元数及四元数转欧拉角5 仿真6 绘图分析 1 卫星姿态控制模块组成 其中执行机构为零动量反作用飞轮 xff0c 此处略去 xff1b 传感器测
  • Objects Track Benchmarks

    MOT 2D MOT MOT challengeTAOCaltech Roadside PedestriansBDD100KWaymoAOTPANDAArgoVerseHiEve Multi person Motion TrackingUA
  • 树莓派4B全40管脚对应功能示意图

    以下两图中 xff0c 图1是树莓派引脚功能图 xff0c 其对应图2红框标注的部分 xff0c 黄色数字标注了对应的管脚 谢谢评论区指正 xff0c 实在抱歉 xff0c 实物图部分误用了树莓派1代的实物图 xff0c 但树莓派4b整体布
  • 【数据结构与算法】车辆路径问题(Vehicle Routing Problem,VRP)

    车辆路径问题 xff08 Vehicle Routing Problem VRP xff09 什么是车辆路径问题 车辆路线问题 xff08 VRP xff09 是指一定数量的客户 xff0c 各自有不同数量的货物需求 xff0c 配送中心向
  • 护士实习自我鉴定

    医院实习护士的评价 篇一 短短一个月的泌尿外科实习生活已接近尾声 xff0c 回顾这段时间的实习生活 xff0c 我感受很深 泌尿外科是我实习的第二站 xff0c 相对肝胆外科就不是那么的忙碌 在泌尿外科经历的业务学习是让我印象最深刻的 x
  • uni-app 实现照片水印并上传照片

    话不多说 xff0c 直接代码 lt template gt lt view class 61 34 wrap 34 gt lt u form model 61 34 model 34 ref 61 34 uForm 34 gt lt u
  • 文档服务器minio 可通过文件路径进行访问

    方法一 登陆服务器 xff0c 直接修改桶的权限 xff1a 1 2 3 4 方法二 创建桶的时候设置桶的策略 xff08 注 xff1a minio版本过高 xff0c 可能没有此方法 xff09 创建一个名为managertest的文件
  • JAVA 视频压缩

    项目依赖 开发引入Windows依赖 xff0c 生产引入linux依赖 xff1b 建议一次都引入 lt 视频压缩 gt lt dependency gt lt groupId gt ws schild lt groupId gt lt
  • Elasticsearch 7.X以上依赖自带jdk

    将 elasticsearch env 中的环境依赖修改为 set JAVA 61 ES HOME jdk bin java exe
  • JPA时间注解的使用

    JPA时间注解 64 Temporal注解 格式化时间日期 xff0c 页面直接得到格式化类型的值 64 Temporal TemporalType DATE 只代表年月日 xff0c 没有时分秒 在页面端取值 xff1a 2019 03

随机推荐

  • python 无人机、飞机轨迹(含姿态角)可视化方法

    无人机 飞机轨迹 含姿态角 可视化方法 目标 xff1a 在三维直角坐标系中画出包含无人机位置pos 偏航角yaw 俯仰角pitch 滚转角roll等姿态的飞行轨迹 思路 xff1a 同时建立机体坐标系和直角坐标系 xff0c 飞机的所有点
  • 【报错记录】import keras时出现AlreadyExistsError: Another metric with the same name already exists.

    AlreadyExistsError Another metric with the same name already exists AlreadyExistError 已存在另一个同名度量 keras版本与tensorflow版本不对应
  • vscode 选择python解释器

    当python环境不止一个时 xff0c vscode可以选择指定的python解释器 xff0c 具体为 xff1a vscode设置中打开Command Palette 键入 Python Select Interpreter
  • 统计代码量方法

    文章目录 方法一 xff1a 直接使用正则表达式方法二 xff1a 使用 96 cloc 96 文件2 1 在Windows下使用代码量统计工具 不设置环境变量 设置环境变量 2 2 96 Linux 96 下使用代码量统计工具 方法三 x
  • Makefile教程(掌握这里足够)

    众所周知 xff0c 在Linux环境下进行项目开发那就少了使用make来构建和管理自己的工程 如果想要更加深入的学习 xff0c 我在这里推荐一本书 https www jianguoyun com p DZWKrLIQjKL5Bxi0z
  • Docker:Docker的安装与镜像加速

    文章目录 一 Docker教程1 1 Docker的优点1 2 Docker容器技术和传统虚拟机技术的性能比较1 3 Docker的相关链接 二 Docker的安装2 1 使用官方安装脚本自动安装2 2 使用Docker仓库进行安装 三 D
  • 字节的高低位交换

    文章目录 一 字节的高低位交换1 移位操作2 蝶式交换法3 查表法 一 字节的高低位交换 问题 xff1a 对一字节的数据 xff0c 进行逐个高低位交换 例如0xCF 11001111B 经过0 7 1 6 2 5 3 4对应位置的交换
  • Typora中的emoji图标标签

    转载自 xff1a https www cnblogs com wangjs jacky p 12011208 html People x1f604 smile x1f606 laughing x1f60a blush x1f603 smi
  • [MFC]使用编辑框来设置IP地址

    我们除了使用IP控件来设置控件之外还可以使用编辑框来设置IP xff0c 这样的话 xff0c 就需要来进行判断我们输入的IP是否合法 判断IP地址合法的标准 xff1a 字符串中必须包含3个符号 被符号 分隔的4个字符串的长度必须小于或等
  • [MFC控件]IP地址控件

    文章目录 使用场景 xff1a 96 CIPAddressCtrl 96 类的成员的属性 xff1a 1 空内容判断 96 CIPAddressCtrl IsBlank 96 2 清空控件 96 CIPAddressCtrl ClearAd
  • VS项目字符集

    在使用VS进行编码过程中 xff0c 查看项目属性看到项目的默认值下有一个字符集选项 xff0c 看下图 xff1a 多字节字符集 在最初的Internet上只有一种字符集 那就是ASCII字符集 xff0c 它相信大家都知道 xff0c
  • 查看当前自己电脑的线程数

    我们在学习线程 xff0c 那我们知道自己电脑中的CPU的线程数吗 xff1f 方法一 xff1a 任务管理器 启动任务管理器点击任务管理器的 性能 选项如下图所示 xff0c 我的电脑是双核四线程的 如果上图看的不明确 xff0c 可以看
  • [MFC控件]获取Edit编辑框内容

    文章目录 一 设置编辑框变量二 通过ID获取 一 设置编辑框变量 1 为编辑框控件添加一个类型为CEdit的变量m Edit CString str span class token punctuation span m Edit span
  • python pip

    简介 pip 是最常用的Python包管理工具 xff0c 该工具提供了对Python 包的查找 下载 安装 卸载的功能 目前Python 2 7 9 43 或 Python 3 4 43 以上版本都自带 pip 工具 xff0c 或者co
  • Julia入门-0、在Windows下安装Julia

    文章目录 0 前言1 相关网站2 Windows 系统下安装Julia3 Julia 交互式命令窗口 0 前言 Julia 是一个面向科学计算的高性能动态高级程序设计语言 Julia 最初是为了满足高性能数值分析和计算科学的需要而设计的 x
  • Julia入门-2、Julia中的全局模块对象

    在 Julia 中 xff0c 有几个关键的全局模块对象 xff1a jl main module 表示当前正在执行的模块 xff0c 也称为 顶层模块 xff08 top level module xff09 或 主模块 xff08 ma
  • Julia入门-3、Julia包管理工具

    文章目录 0 Julia 的包管理工具是 96 Pkg 96 1 使用Julia包管理工具过慢 0 Julia 的包管理工具是Pkg Julia 的包管理工具是Pkg xff0c 可以用于安装 更新 卸载和管理 Julia 中的软件包 以下
  • 华三snmp3配置

    snmp agent 开启SNMP协议 snmp agent local engineid 0000000000 系统自动生成 xff0c 无需配置 snmp agent community read h3c acl 2001 只读属性为h
  • 如何合理的选择加密软件?驱动层加密与应用层加密那个更具优势?

    合理的使用文档加密软件至关重要 站在信息安全的角度来看 xff0c 目前要做的是 xff0c 人员需要正确的对待加密软件为基础 xff0c 然后依据企业的实际办公需求 xff0c 去合理的使用文档加密软件产品来帮助企业达到数据安全保护的要求
  • 系统调用的概念及原理

    系统调用与内核函数 内核函数与普通函数形式上没有什么区别 xff0c 只不过前者在内核实现 xff0c 因此要满足一些内核编程的要求 系统调用是用户进程进入内核的接口层 xff0c 它本身并非内核函数 xff0c 但它是由内核函数实现的 x