深入理解任务堆栈

2023-05-16

先来看这一个小函数,猜猜他的运行结果(VC6环境)?

#include <stdio.h>

void  b()
{
    int data[10];
    printf("helloworld!/r/n");
    data[11]-=5;
}

int main()
{
    b();
    return 0;
}

堆栈溢出,肯定不正常,马上有人叫起来了。

没错, 那么结果是什么呢,为什么会不停打印helloworld呢,我们将用堆栈揭开他的奥秘。

且看main函数汇编代码。  

很简单,  L12  调用b函数,   L13对返回值赋0.

这里有个很关键的东东: call

call包含2部分操作,call的下一条指令地址入栈,跳转,也就是从效果来说,包含push  0040108D 和 jmp  00401005两条操作。 假如,你打开内存窗口,你会看到,堆栈里已经有0040108D 这个值了。

10:   int main()
11:   {
         ...........
12:       b();
00401088   call        @ILT+0(b) (00401005)
13:       return 0;
0040108D   xor         eax,eax
14:   }

再来看函数b

当你把  printf("helloworld!/r/n"); 替换为 printf("%08x!/r/n",data[11]);时,你会发现,程序在不停的打印0040108D!, 显而易见,你修改的data[11]其实就是函数b的返回值地址,而data[11] -= 5;更是巧妙的利用 call    00401005 这条指令正好是5个字节的特点,将返回地址正好修改到了 0040108D ,也就是说函数返回时会再次调用函数b。每次b()都会把返回值改为b返回的地址,导致b()被不停的调用。

 

 

为什么data[11]正好是函数的返回值呢,让我们来看堆栈和任务有和关系
 

    任务(线程)都有一个堆栈,任务创建时创建,任务撤销时撤销。 任务的创建本质上包含2点。

    1  任务资源的分配(任务TCB和任务堆栈),很多嵌入式操作系统把TCB和堆栈是分配在一起的,比如Vxworks操作系统,其任务ID,堆栈基地址,TCB指针其实指向同一块内存。 创建任务时要指定任务大小,分配堆栈空间其实是一个特殊的malloc函数,他从堆栈空间分配,而不是从系统空间分配内存。任务堆栈windows下默认比较大,嵌入式OS则比较小,经常64k左右。 而局部变量就保存在堆栈中,当访问局部变量越界时,就发生了我们常说的"堆栈被踩了",堆栈被踩得话后果严重,轻则导致某次运行结果不对(这种问题很难定位),重则导致程序崩溃,例如把上面程序改为data[11]-=4,则程序直接崩溃。

 

    2  任务的初始化,包含2部分,任务TCB的初始化,并且把TCB和操作系统关联。

        TCB中包含任务的很多东西,   比如任务拥有的信号量的链表,文件描述符的链表,CPU寄存器的值(任务切换时用的),任务优先级,堆栈地址,任务名称等等,这些都需要初始化。初始化完成之后,操作系统会把这个任务TCB假如调度队列,如果加入调度队列时任务状态是就绪,那么当他拿到CPU时就可以直接运行了。

 

    堆栈中包含任务的栈帧,也就是说在函数调用链(A call B,B call C,C call D,D call E),那么堆栈中,ABCDE函数分别对应自己的一段栈帧。以E为例  E的栈帧包含A函数的传入参数,函数返回值,局部变量和临时保存的寄存器值。

 

     函数栈帧在主调函数和被掉函数中分配,在函数返回时释放,这就是为什么局部变量地址在函数返回后其值可能失效。

     例如 下面代码FuncB分配的函数栈帧在FuncB执行完后又被分配给FuncC,FuncC中很可能会踩到FuncB曾经的局部变量。

      FuncA{

         FuncB();

         FuncC();

      }

    任务(线程)的栈以及上面函数b的栈为下图。

函数栈帧分布 

函数b的栈帧分布

    *debug版本的函数b其实除了data[10],还在局部变量位置分配了一部分内存用来做调试,不过我们不用关系他。

    *为什么是data[11],而不是data[10]/data[12]或者其他? x86下编译器函数入口一般会有2条指令。

      push  ebp

      move ebp,esp

      其实就是将ebp作为帧指针来用(函数帧即为栈中一个函数所拥有的一段内存)。

      而这样就可以在函数中采用ebp-XXX表示局部变量,用ebp+XXX来表示传入参数。 函数中经常会有一些push操作,

      采用esp对局部变量和参数寻址远不如用ebp来的省事了,因为esp是经常变化的,而ebp是相对横的的。

 

     

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

深入理解任务堆栈 的相关文章

随机推荐

  • PCB布线走直角或锐角问题研究

    首先需要说明的一点 xff0c 在正常布线的过程中还是要尽量避免布线走直角和锐角 这里只是研究深入下布线走直角或锐角会有多大的危害性 xff0c 结尾给出答案 无论是教科书还是公司的技术规范都会强调布线避免出现走直角和锐角 xff0c 不过
  • 磁珠基本原理

    概念 磁珠的全称为铁氧体磁珠滤波器 xff0c 是一种抗干扰元件 xff0c 主要功能是滤除高频噪声 xff0c 消除存在于传输线结构 xff08 电路 xff09 中的噪声 工作原理 磁珠通过阻抗吸收并以发热的形式将不需要频段的能量耗散掉
  • BUCK型DC-DC变换器

    前述 DCDC从控制手段上来说分为PWM式 谐振式以及他们的结合式 每 一种方式中从输入与输出之间是否有变压器隔离又可以分为有隔离 无隔离两类 每一类有六种拓扑结构 BUCK Boost BUCK Boost Cuk Sepic和Zeat
  • dubbo服务超时导致的异常org.apache.dubbo.remoting.TimeoutException

    1 dubbo服务超时异常提示信息如下 xff1a cause org apache dubbo remoting TimeoutException Waiting server side response timeout by scan
  • 基于TCP/IP实现串口到网络的通讯转换

    工作模式 通过串口服务器 xff0c 采集到天平的称量值发送到PC端 操作步骤 1 软件测试 测试工具 xff1a USR M0 V2 2 5 8 基础设置 xff1a 模块静态IP 设置成服务器IP xff0c HTTP服务端口 设置成4
  • 结构体的对其规则以及为什么要对其

    结构体的内存对齐规则以及为什么要对齐 内存对齐规则 span class token number 1 span 第一个成员在与结构体变量偏移量为 span class token number 0 span 的地址处 span class
  • 宏定义参数

    宏定义的参数以逗号 xff08 作为分隔符 span class token macro property span class token directive keyword include span span class token s
  • [STM32]关于环形队列的实现

    在程序中使用环形队列判断接收数据格式 xff0c 避免在中断中处理造成程序响应速度慢的问题 直接贴代码 xff1a LoopRxCommu h ifndef LOOPRXCOMMU H define LOOPRXCOMMU H includ
  • C#旅程——串口发送数据

    串口发送数据时可以一个byte一个byte的发送数据 xff0c 也可以一次性丢出 xff0c 分多次丢出的话会导致一段数据被分成多段发出 xff0c 中间的延时可能会超过2ms xff0c 与FW通讯时会出现异常 span class t
  • 【记录】一次51单片机串口乱码问题排查

    记录 一次51单片机串口乱码问题排查 项目场景问题描述原因分析解决方案结语 项目场景 在51串口收发仿真实验中使用两个单片机互相通信 xff0c 程序设定A上电1s后通过串口以16进制给B发送AA 直到B收到AA后回复BB xff0c 当A
  • IO流java基础

    二十四 IO流 24 1 File 1 1 File 类概述和构造方法 File 它是文件和目录路径名的抽象表示 文件和目录是可以通过File封装成对象的 对于File而言 其封装的并不是一个真正存在的文件 仅仅是一个路径名而已 它可以是存
  • TX2上布置vins_fusion_gpu指南

    1 参考链接 如果初次安装 xff0c 新的TX2环境 xff0c 请参考文档 https github com arjunskumar vins fusion gpu tx2 nano 2 问题记录 1 xff0c 自己的环境情况 我的环
  • Ubuntu下安装cmake

    Ubuntu下安装cmake 今天因为项目的原因需要将cmake升级一下 xff0c 原来我是按照链接没有卸载旧版本 xff0c 直接升级 但是出现一些问题 xff0c 然后我全部卸载后 xff0c 重新安装 以下就是我的安装步骤 第一步
  • AUTH:basic认证和digest认证

    Http authentication BASIC In the context of an HTTP transaction basic access authentication is a method for a web browse
  • Quick Audience组织和工作空间功能解读

    简介 xff1a Quick Audience完成了权限系统全面升级 xff0c 可以解决集团企业不同品牌 不同运营组织 xff0c 不同消费者运营的诉求 xff0c 精细化保障企业数据访问安全 xff0c 提升管控的灵活度 更多关于数智化
  • Socket编程基础总结,全网最全

    IP地址 xff1a 可以在网络环境中 xff0c 唯一标识一台主机 端口号 xff1a 可以定位网络的一台主机上 xff0c 唯一标识一个进程 ip地址 43 端口号 xff1a 可以在网络环境中 xff0c 唯一标识一个进程 在TCP
  • 嵌入式开发--RS-485通讯的问题

    嵌入式开发 RS 485通讯的问题 RS 485说明接口芯片硬件连接CubeMX设置代码编写引脚定义使能串口中断函数发送数据接收数据 有一个问题 xff0c 多收了一个数数据线上的波形问题分析问题解决 RS 485说明 RS 485一般简称
  • UNIX网络编程卷1(第三版)字节排序函数测试

    内存中存储多字节有两种方法 xff0c 即小端字节序和大端字节序 xff0c Ubuntu10 04 是小端字节序 xff0c 网际协议所用的字节序为大端字节序 内存地址增长方向 低序字节 gt 高序字节 小端字节序 高序字节 gt 低序字
  • android非常好的在线视频播放器源码(包含在线音频播放源码)

    一 在线音频播放器 lt xml version 61 34 1 0 34 encoding 61 34 utf 8 34 gt lt LinearLayout xmlns android 61 34 http schemas androi
  • 深入理解任务堆栈

    先来看这一个小函数 xff0c 猜猜他的运行结果 VC6环境 xff1f include lt stdio h gt void b int data 10 printf 34 helloworld r n 34 data 11 61 5 i