news 2026/2/13 13:03:11

[从零构建操作系统]08 函数调用时栈的底层行为解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
[从零构建操作系统]08 函数调用时栈的底层行为解析


第一步:在kernel_init里安家(黄色区域)

代码位置: 右上角 kernel_init 函数开头

栈图对应: 左侧黄色的 “局部变量和数据”

  1. sub $0x10, %esp

    • 动作:也就是把栈顶指针esp往下拉 16 个字节,给kernel_init这个函数划出一块地盘(栈帧)。

  2. movl $0x1, -0x4(%ebp)

    • 动作:把数值1放入ebp往下 4 字节的地方。

    • 对应:这就是代码里的int a = 1。图中黄色区域的a=1就存在这。

  3. movl $0x2, -0x8(%ebp)

    • 动作:把数值2放入ebp往下 8 字节的地方。

    • 对应:这就是代码里的int b = 2。图中黄色区域的b=2就存在这。


第二步:准备调用test,打包行李(橙色区域)

代码位置: 右上角 call 指令上面的两行 push

栈图对应: 左侧橙色的 “参数传递”

这里有一个关键知识点:C语言函数参数入栈顺序是从右往左。

我们要调用的是 test(a, b),所以先压 b,再压 a。

  1. push -0x8(%ebp)(对应代码push b)

    • 动作:把变量b(也就是2)压入栈中。

    • 对应:图中橙色区域上方的“参数:b”

  2. push -0x4(%ebp)(对应代码push a)

    • 动作:把变量a(也就是1)压入栈中。

    • 对应:图中橙色区域下方的“参数:a”

注意:此时栈顶(esp)已经指到了“参数 a”的位置。


第三步:跳过去执行(蓝色区域)

代码位置: 右上角 10033: call 1000c <test>

栈图对应: 左侧蓝色的 “返回地址”

  1. call ...

    • 动作:CPU要去别的地方执行代码了,但它得记得回来之后该从哪接着干。所以 CPU 会自动把“下一条指令的地址”(也就是add $0x8, %esp这一行的地址)压入栈。

    • 对应:图中蓝色的“返回地址”。此时esp指向这里(图中标注的esp1)。


第四步:进入test函数,建立新家(绿色区域)

代码位置: 右下角 test 函数的开头

栈图对应: 左侧绿色的 “之前ebp”

  1. push %ebp(test函数的第一行代码)

    • 动作:test函数说:“我也要用ebp来定位我的地盘,但我不能把kernel_initebp弄丢了。” 所以先把kernel_initebp值压入栈保存起来。

    • 对应:图中绿色的“之前的ebp”

  2. mov %esp, %ebp

    • 动作:把当前的栈顶位置赋值给ebp

    • 对应:此时ebpesp都指向了绿色格子的最下沿(图中标注的ebp / esp2)。


终极解密:为什么要这么折腾?

请看右下角test函数如何取参数:

现在的ebp指向绿色的“之前ebp”。

  • 往上(高地址)走 4 个字节,是蓝色的“返回地址”。

  • 再往上走 4 个字节(+8),就是橙色的“参数 a”

  • 再往上走 4 个字节(+120xc),就是橙色的“参数 b”

对照右下角代码:

  • mov 0x8(%ebp), %edx-> 取出了a

  • mov 0xc(%ebp), %eax-> 取出了b

总结

这张图画的就是:

  1. 黄色:调用者自己存的私房钱(局部变量)。

  2. 橙色:调用者打包给被调用者的礼物(参数)。

  3. 蓝色:回家的路标(返回地址)。

  4. 绿色:被调用者用来定位的基准桩(旧 ebp)。

被调用者(test)站在绿色的位置,往回伸手(ebp + 偏移),就能拿到别人传给它的参数。

补充:x86编译器对各字段的分类

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/11 17:44:55

【JavaWeb】ServletConfig为Servlet提供配置参数

ServletConfig的使用 ServletConfig是什么&#xff1f; 为Servlet提供初始配置参数的一种对象&#xff0c;每个Servlet都有自己独立唯一的ServletConfig对象容器会为每个Servlet实例化一个ServletConfig对象&#xff0c;并通过Servlet生命周期的init方法传入给Servlet作为属性 …

作者头像 李华
网站建设 2026/2/8 12:26:23

Flutter 测试驱动开发的基本流程

欢迎大家加入开源鸿蒙跨平台开发者社区&#xff0c;一起共建开源鸿蒙跨平台生态。### Flutter 测试驱动开发&#xff08;TDD&#xff09;实践指南 测试驱动开发&#xff08;TDD&#xff09;是一种软件开发方法&#xff0c;强调在编写功能代码之前先编写测试用例。通过这种方式…

作者头像 李华
网站建设 2026/2/6 1:17:17

[UUCTF 2022 新生赛]ezpop

1.打开先看代码<?php //flag in flag.php error_reporting(0); class UUCTF{public $name;public $key;public $basedata;public $ob;function __construct($str){$this->name$str;}function __wakeup(){if($this->key"UUCTF"){$this->obunserialize(ba…

作者头像 李华
网站建设 2026/2/10 16:11:47

盛水最多的容器(滑动窗口 双指针)

这道题当然可以暴力求解&#xff0c;O(N^2),但是有时候并不会通过&#xff0c;因此要想一个时间复杂度为O&#xff08;N&#xff09;的方法。如果说用滑动窗口肯定会有人会有疑问&#xff0c;这怎么用&#xff1f;下面直接说解法&#xff1a;首先left与right分别指向数组的两边…

作者头像 李华
网站建设 2026/2/4 18:00:58

深度探究Span:.NET内存布局与零拷贝原理及实践

深度探究Span&#xff1a;.NET内存布局与零拷贝原理及实践 在.NET开发中&#xff0c;高效的内存管理至关重要&#xff0c;尤其在处理高性能、低延迟的应用场景时。Span<T> 类型应运而生&#xff0c;它为开发者提供了一种灵活且高效的内存操作方式&#xff0c;能够显著提升…

作者头像 李华
网站建设 2026/2/11 6:37:27

helm 部署 elasticsearch 栈

说明:本文使用的 chart 仓库名字为 elastic/cloud-on-k8s 地址为 elastic/cloud-on-k8s。 1、添加 repo 源 helm repo add elastic https://helm.elastic.co helm repo update2、安装 eck-operator ⚠️说明:ECK Operator(Elastic Cloud on Kubernetes)本身安装的东西其…

作者头像 李华