0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

系统调用与普通的函数调用之间的区别

jf_78858299 来源:码农的荒岛求生 作者:码农的荒岛求生 2023-02-15 11:47 次阅读

我们来聊聊系统调用与普通的函数调用之间的区别。

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

void funcB() {
}


void funcA() {
  funcB();
}

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

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

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

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

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

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

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函数,那么此时的内存布局就像这样:

图片

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

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 函数
    +关注

    关注

    3

    文章

    4303

    浏览量

    62409
  • 代码
    +关注

    关注

    30

    文章

    4741

    浏览量

    68324
  • 系统调用
    +关注

    关注

    0

    文章

    28

    浏览量

    8320
收藏 人收藏

    评论

    相关推荐

    Linux内核中系统调用详解

    Linux内核中设置了一组用于实现各种系统功能的子程序,称为系统调用。用户可以通过系统调用命令在自己的应用程序中
    发表于 08-23 10:37 762次阅读
    Linux内核中<b class='flag-5'>系统</b><b class='flag-5'>调用</b>详解

    如何查看及更改函数/函数块的调用环境

    模块化设计的思想是把一些相似的功能(比如电机控制、阀控制)设计成函数函数块,这样就可以反复调用。其优点是:使程序架构更加清晰,避免重复编写相似功能的代码。不过可能会产生一个疑惑:既然PLC的程序
    的头像 发表于 11-17 09:08 880次阅读
    如何查看及更改<b class='flag-5'>函数</b>/<b class='flag-5'>函数</b>块的<b class='flag-5'>调用</b>环境

    linux内核系统调用之参数传递

    普通函数一样,系统调用通常需要一些输入/输出参数,这些参数可能包括实际值(即数字)、用户模式进程地址空间中的变量地址,甚至包括指向用户模式函数
    的头像 发表于 12-20 09:32 1418次阅读

    如何确保在调用之间保留它的值?

    大家好。一个非常基本的问题让我困惑。一个变量在ISR()文件和3个非中断文件中更新(读和写),并且我希望它的生存期延长,这意味着它在函数/文件调用之间保留它的值。它将被限定为“volatile”。但是,它是否还需要具备“静态”的资格呢?如何确保在
    发表于 03-31 07:48

    C++教程之函数的递归调用

    C++教程之函数的递归调用 在执行函数 f 的过程中,又要调用 f 函数本身,称为函数的递归
    发表于 05-15 18:00 35次下载

    系统调用函数库分析及实例

    作为用户我们极少接触系统调用,但是我们熟悉C 语言,对库函数调用并不陌生。C语言支持一系列库函数调用
    发表于 06-23 16:46 46次下载
    <b class='flag-5'>系统</b><b class='flag-5'>调用</b><b class='flag-5'>函数</b>库分析及实例

    类成员函数普通函数区别研究

    本文的目的是研究类成员函数普通函数区别,以及不同调用方式之间
    发表于 09-15 14:52 32次下载

    详解python普通函数创建与调用

    函数是一种仅在调用时运行的代码块。您可以将数据(称为参数)传递到函数中,然后由函数可以把数据作为结果返回。
    的头像 发表于 03-01 16:32 1835次阅读

    Linux中的系统调用是怎样实现

    系统调用普通函数调用没有本质区别普通
    的头像 发表于 02-15 11:40 1206次阅读
    Linux中的<b class='flag-5'>系统</b><b class='flag-5'>调用</b>是怎样实现

    C语言函数调用的形式及过程

    C语言函数调用时的数据传递 在调用有参函数时,主调函数和被调函数
    的头像 发表于 03-10 14:28 1604次阅读

    什么是函数调用

    函数调用,就是使用我们已经定义好的函数,或者C语言自带的库函数
    的头像 发表于 04-04 17:21 5642次阅读

    SCL中调用函数的示例

    在此,可插入函数 (FC) 调用函数块 (FB) 调用函数块可作为单实例、多重实例或参数实例进行调用
    的头像 发表于 06-06 10:18 2080次阅读

    网络系统调用网络套接字入口函数

    网络套接字入口函数 //所有的网络套接字系统调用函数(socket bind listen connect )都使用一个共同的入口函数:sy
    的头像 发表于 07-24 11:02 446次阅读

    python定义函数调用函数的顺序

    定义函数调用函数的顺序 函数被定义后,本身是不会自动执行的,只有在被调用后,函数才会被执行,得
    的头像 发表于 10-04 17:17 1267次阅读

    python函数函数之间调用

    函数函数之间调用 3.1 第一种情况 程序代码如下: def x ( f ): def y (): print ( 1 ) return y def f (): print ( 2
    的头像 发表于 10-04 17:17 564次阅读