一文讲解系统调用与函数调用有什么区别?

news2025/2/23 23:09:34

作为程序员你肯定写过无数的函数,假设有这样两个函数:

void funcB() {
}


void funcA() {
  funcB();
}

函数之间是可以相互调用的,这很简单很happy有没有。

要知道是代码、是函数就可以相互调用,不管你用什么语言写的。

假设funcB是内核中的函数,funcA是你自己写的函数,就像这样:

// Linux内核中的函数
void funcB() {
}


// 你的函数
void funcA() {
  funcB();
}

那么funcA应该也能调用funcB(如果funcB可以供外界调用的话)。

 资料直通车:Linux内核源码技术学习路线+视频教程内核源码

学习直通车:Linux内核源码内存调优文件系统进程管理设备驱动/网络协议栈

有的同学可能会惊呼,我们可以自己编写代码调用操作系统的函数,那岂不是可以直接控制操作系统了?

too yong too simple!

如果我们编写的代码可以直接调用所有的操作系统函数那么从某种程度上讲的确可以说是能控制操作系统,但如果操作系统只允许你调用内核中的有限的几个函数 呢?

怎么样,你(应用程序)是不是就被限制住了。

你又会问,操作系统是怎样限制应用程序能调用哪些内核中的函数呢?

实际上单靠操作系统这种软件是没有办法限制应用程序能调用哪些以及多少个内核函数的,因此为施加这种限制必须依靠——硬件。

这里的硬件指的就是CPU。

那么CPU又是怎么施加这种限制的呢?

我们先来看看普通的函数调用,函数调用对应的机器指令是call指令,就像这样:

call 0x400410

call指令后的这个地址0x400410就是被调函数的第一条机器指令所在的内存地址。

当CPU执行到这条机器指令时直接跳转到对应的地址继续执行指令,从程序员的角度看就是函数调用。

而如果是我们程序的函数调用操作系统的函数就不允许使用call指令了,而是syscall机器指令(x86_64)。

使用syscall指令调用操作系统函数时也是把相应函数的第一条指令的地址放到syscall之后吗?

显然不是的,因为操作系统系统代码和你的代码都是单独编译以及运行的,你根本就不知道操作系统的某个函数存放在内存的什么位置上,也不应该让你知道,因此使用syscall调用操作系统的函数时我们只能附加一个序号,比如序号0对应操作系统中的A函数、序号1对应操作系统中的B函数等等,这样使用syscall指令时只需要将该序号写入rax寄存器即可,CPU在执行syscall指令时通过读取rax寄存器的值就能知道到底该调用操作系统中的哪个函数了。

可以看到,利用这种机制操作系统限制了应用程序可以调用哪些内核中的函数。

有的同学可能会有疑问,如果一个call指令因为种种原因后面跟上的地址”无意“中指向了一个内核函数的地址,那么CPU执行call指令时会怎样呢?就像这样:

call 0x400410

这里假设0x400410这个地址指向了一个内核函数地址。

很简单,CPU在执行这条指令时会判断出当前进程没有权限访问0x400410这个地址,因此CPU在执行这条指令时会产生异常,该进程会被直接kill掉。

这里列举了Linux在各种处理器上怎样进行系统调用。

看到了吧,syscall和call在使用方法上还是有很大不同的,可以看到call是直接 调用的,也就是说应用程序这一层中的函数调用是直接调用的,而syscall其实是间接 调用的,即我们调用操作系统中的函数时其实是间接调用的。

除此之外,CPU在执行call指令以及syscall指令时另外一个不同点在于模式的切换。

当CPU执行普通函数时其实是运行在用户态,user mode,在这种模式下CPU不能执行某些特权指令,这也就意味着我们的程序其实是受限的;而当CPU执行syscall开始执行操作系统的代码时会切换到内核态,kernel mode,在这种模式下CPU可以执行任何特权指令,不受任何限制,操作系统才是真正的管理计算机的大boss。

可以看到,当在普通程序中进行函数调用时就是函数调用,而普通函数调用操作系统中的函数时才叫系统调用。

最后再说一点,普通的函数调用所使用的栈全部位于进程的栈区,假设main函数调用funcA函数,funcA调用funcB函数,那么此时的进程内存布局就像这样:

而进行系统调用时当CPU开始执行操作系统的代码时不再基于进程栈区而是会跳转到操作系统某个特定内存区域,该区域作为进程在内核中的栈区,因此也叫做内核栈,每个进程在内核中都有自己的内核栈,因此我们可以看到一个进程其实有两个栈区,一个在用户态一个在内核态。

假设main函数调用funcA,funcA进行系统调用,调用内核中的funcB函数,funcB函数调用内核中的funcC函数,那么此时的内存布局就像这样:

好啦,这个话题就到这里,希望对大家理解操作系统有所帮助。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/362226.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023/02/21 事件循环-eventloop 宏任务 微任务 讲解

1 JS是单线程 js是单线程的。也就是说,同一个时间只能做一件事。作为浏览器脚本语言,与它的用途有关。JavaScript的主要用途是和用户互动,以及操作DOM,这决定了它只能是单线程。 js是单线程的。也就是说,同一个时间只…

如何使用 API 工具做 Websocket 测试

在 API 测试中,对 Websocket 协议的支持呼声越来越高,今天给大家推荐一款 开源的 API 管理工具——Postcat,以及教教大家,如何利用 API 管理工具做 Websocket 测试。 在线 Demo 链接:Postcat - Open Source API Ecosys…

17 个短代码,检验你 Python 基本功

Python 是一门非常优美的语言,其简洁易用令人不得不感概人生苦短。在本文中,蛋糕将带大家回顾 17个非常有用的 Python 技巧,例如查找、分割和合并列表等。这 17 个技巧都非常简单,但它们都很常用且能激发不一样的思路。 人生苦短&…

来一波骚操作,Java内存模型

文章整理自 博学谷狂野架构师 什么是JMM 并发编程领域的关键问题 线程之间的通信 线程的通信是指线程之间以何种机制来交换信息。在编程中,线程之间的通信机制有两种,共享内存和消息传递。 ​ 在共享内存的并发模型里,线程之间共享程序的公共…

项目管理从需求管理开始--不懂需求管理还敢带项目?

分析报告指出,多达76%的项目失败是因为差劲的需求管理,这个是项目失败的最主要原因,比技术、进度失控或者混乱的变更管理还要关键。很多PMO和PM却没有把需求管理重视起来,甚至认为这只是产品经理的事情,自己只做交付即…

Spark RDD及内存计算

文章目录Spark RDD及内存计算性能调优RDD 的核心特征和属性内存计算Spark RDD及内存计算 性能调优 性能调优的本质: 性能调优不是一锤子买卖,补齐一个短板,其他板子可能会成为新的短板。因此,它是 一个动态、持续不断的过程&…

第51篇-某彩网登录参数分析-webpack【2023-02-21】

声明:该专栏涉及的所有案例均为学习使用,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!如有侵权,请私信联系本人删帖! 文章目录 一、前言二、网站分析一、前言 今天我们看一个webpack的网站 aHR0cHM6Ly8xMGNhaTUwMC5jYy9sb2dpbg==二、网站分析 首先…

Springboot 全局异常处理类

全局异常处理 在开发过程中,不管是Dao、Servie、Controller,层都有可能发生异常,对于异常处理,通常是try-catch或者直接throw,这会让try-catch的代码在代码中任意出现,系统的代码耦合度高&…

Elasticsearch7.8.0版本进阶——数据更新流程

目录一、数据更新流程概述二、数据更新流程步骤2.1、数据更新流程图解2.2、部分更新一个文档的步骤2.3、数据更新流程注意事项一、数据更新流程概述 部分更新一个文档需要结合数据读取和写入流程。 二、数据更新流程步骤 2.1、数据更新流程图解 2.2、部分更新一个文档的步骤…

经典文献阅读之--MSC-VO(曼哈顿和结构约束VIO)

0. 简介 对于视觉里程计而言,在面对低纹理场景时,往往会出现退化的问题,究其原因是人造环境往往很难找到足够数量的点特征。而其他的几何视觉线索则是比较容易找到,在城市等场景中,通常表现出结构规律,如平…

程序中的日期使用问题-格式转化:SimpleDateFormat、org.apache.commons.lang3.time.DateUtils

前言 日期使用问题主要是格式转换的问题 场景:通过excel导入数据,其中一个字段为出生日期,需要对字段值进行合法性校验 博客地址:芒果橙的个人博客 【http://mangocheng.com】 一、个人浅谈日期 时间日期作为一个基础的标识和维度…

Windows下使用git提交代码到gitee

Windows下使用git提交代码到gitee 1、下载安装git 2、或者参考Gitee初始仓库里面的配置代码 上传本地代码到gitee上 1、 在本要上传的代码文件夹,右键 选择:Git Bash Here 2、执行:git init 3、然后执行:git add 文件名。&…

深入Spring底层透析bean生命周期及循环引用的醍醐灌顶篇

目录前言一.Bean的生命周期1.1 Bean的实例化阶段1.2 Bean的初始化阶段(重点)1.3 Bean的完成阶段二.循环引用问题(面试常问题)三.Spring的三级缓存(重点来了)四.完整的Spring IoC整体总结前言 本篇是接着bean的创建基本…

人工智能详细笔记 :卷积神经网络(CNN)

卷积神经网络和深度学习1.卷积神经网络的结构2.卷积神经网络的卷积运算3.卷积神经网络中的关键技术BP神经网络存在的问题: 输入类型限制:BP神经网络以数值作为输入。如果需要计算图像相关的信息的话,首先需要从图像中提取特征。隐层数量限制…

直播预告 | 嵌入式BI如何将数据分析真正融入业务流程

在信息化高速发展的今天,数据成为企业最有价值的资产之一。而数据本身很难直接传递有价值的信息,只有通过对数据进行挖掘、分析,才能让数据真正成为生产力。 商业智能(BI)应运而生,可以帮助企业更好地从数…

ASP.NET MVC | 简介

目录 前提 1.教程 2.MVC 编程模式 最后 前提 在学习学过很多课程,但是最主要学的还是ASP.NET MVC这门课程,工作也是用的ASP.NET MVC,所以写一点ASP.NET MVC的东西,大家可以来看看,我自己不会的时候也不用找别的地方…

django项目实战六(django+bootstrap实现增删改查)进阶优化modelform类与视图

目录 一、将视图里面的modelform类分离出去 二、按模块迁移视图 1、新建views视图文件夹 2、views下新建对应模块文件,并从views.py当中迁移代码 三、删除views.py 四、修改url路由 接上一篇《django项目实战五(djangobootstrap实现增删改查&#xf…

数据结构-考研难点代码突破(C++实现树型查找 - 平衡二叉树(AVL树)的基本操作(增删))

文章目录1. 平衡二叉树的概念AVL树的插入AVL树查找效率AVL树的删除(了解)2. C代码3. 考研数据结构代码仓库1. 平衡二叉树的概念 二叉搜索树虽然可以提高搜索效率,但如果数据接近有序的话搜索二叉树的效率退化为链表了。为了解决这个问题&…

跨境群店安全运营神器—超级浏览器

有点跨境电商经验的跨境人都知道,物理隔离是比较安全稳妥的防关联办法。但是多台电脑多条网络的办公方式,不仅设备成本高,人员的费用也高得吓人。后来大家开始使用VPS来防关联。VPS是一种虚拟专用服务器,它是一种将一台服务器分割…

企业微信机器人发送消息

前言 随着科技的发展,各企业公司的业务不断发展,那么就需要强有力的沟通软件,其中企业微信、钉钉的能力得到了大众的认可,今天这篇文章就讲其中的一个功能-调用企业微信机器人(下文简称应用)进行消息传递。它的好处有哪些呢?自然是可以让相关人员及时追踪任务进度。 一、…