【问题标题】:How the function calls works in terms of Stack and Code memory?函数调用在堆栈和代码内存方面如何工作?
【发布时间】:2021-10-23 01:05:07
【问题描述】:

我一直试图了解内存是如何工作的,在内存方面,特别是在嵌入式系统中,在应用程序执行过程中一步一步发生了什么。更多 C/C++ 中的上下文

  • 应用程序的堆栈外、堆、静态和代码内存,存储在 RAM 或易失性内存中,哪些部分存储在非易失性内存中?还是在执行应用程序时,将整个应用程序复制到 RAM 或易失性内存中?
  • 调用函数时,是将该函数的所有汇编指令都复制到堆栈中还是只为函数分配内存?
  • 如果只为函数实时分配内存,这意味着这些变量的地址必须添加到函数的汇编代码中,这是怎么回事?
  • 当我们在 C 中为嵌入式系统编写代码时,谁在嵌入式系统堆栈内存分配等中执行所有这些操作? MCU 中没有 OS 为我们做内存管理,所以在 MCU 中的函数调用期间谁来管理这个内存分配

【问题讨论】:

标签: c compiler-construction stack embedded dynamic-memory-allocation


【解决方案1】:
  • 应用程序的堆栈、堆、静态和代码内存不足,即 存储在 RAM 或易失性存储器中以及存储在哪个部分 非易失性存储器?或者当一个应用程序被执行时,整个 应用程序被复制到 RAM 或易失性内存中?
  • 当一个函数被调用时,是否执行该函数的所有汇编指令 函数被复制到堆栈或仅分配内存 功能?
  • 如果只为函数实时分配内存,这意味着 这些变量的地址必须添加到汇编代码中 函数,这是怎么发生的?

为了全面而正确地理解裸机(无操作系统和操作系统环境(嵌入式系统)、不同内存结构及其所有内部结构)中的代码执行,我建议您阅读这本书 - Extreme C (Auth: Kamran Amini),尤其是这些部分:

  1. 第 2 章:从源代码到二进制文件
  2. 第 4 章:进程内存结构
  3. 第 5 章:堆栈和堆

根据我的经验,异端和随机 cmets 反而会损害您的理解,您将几乎无法理解它。查阅真实的已发布内容。

【讨论】:

    【解决方案2】:

    当我们用 C 为嵌入式系统编写代码时,谁在嵌入式系统堆栈内存分配等中完成所有这些工作? MCU 中没有 OS 为我们做内存管理,所以在 MCU 中的函数调用期间谁来管理这个内存分配

    C中的程序员接口是main.c文件:

    int main (){
    
    // User code here
    
    return 0;
    }
    

    但从技术上讲,您的 IDE(Keil uVision 或 Code Composer Studio 等)连同它的 RTE(运行时环境)组件,会在之前放置初始化和反初始化文件的编译代码(通常称为 MCU 启动代码)在此 main.c 文件之后,在为特定目标硬件(例如 Tiva C TM4C123GH6PM 或 STM32F400 等)构建(编译)源代码阶段。

    这些启动/初始化文件中的代码初始化堆栈和堆内存、MCU 的时钟门控控制、设置不同的外围设备(gpio、串行、i2c 等)、中断向量表、PC(程序计数器)、 SP(堆栈指针)等以及更多其他内容。

    您可以为您的目标 MCU 打开一个简单程序的调试会话,同时将其配置为“在第一个汇编指令/启动时停止”,然后执行步进操作以遍历所有代码,直到您到达 main.c 文件。

    研究结果非常棒。

    【讨论】:

      【解决方案3】:

      在嵌入式系统中

      我假设系统没有MMU,假设有一些小型微控制器。总体而言,请注意答案很大程度上取决于特定系统的特定配置。最后,您可以根据需要将所有内容存储在易失性存储器中,并且可以通过这种方式配置微控制器。

      应用程序的栈外、堆外、静态和代码内存,哪些存储在RAM或易失性内存中,哪些部分存储在非易失性内存中?

      有部分,见https://www.embeddedtutor.com/2019/07/memory-layout-executable-in-embedded.html。 TBH 这真的是微不足道的——如果有什么变化,它就是易失的,如果什么没有变化并且在断电之间持续存在,它就是非易失的。比方说:

      what where
      stack volatile
      heap volatile
      static initialization of static variables is stored in non-volatile memory, static variables themselves are stored in volatile memory. I.e. data segment
      Code We live on Harvard architectures and assembly instructions do not change, so they go to non-volatile memory

      或者当一个应用程序被执行时,整个应用程序被复制到 RAM 或易失性内存中?

      不,它没有被复制。

      但是,无论如何,您可以配置微控制器以执行来自易失性存储器的指令,例如 STM32 code_in_ram。仍然没有副本(可以有,您可以为此编写代码......),说明在那里。 IE。这一切都取决于具体的配置。

      如果只为函数实时分配内存,那意味着这些变量的地址必须添加到函数的汇编代码中,这是怎么发生的?

      有一个stack pointer 包含堆栈顶部的位置。它会根据是否从堆栈中添加或删除数据而增加或减少(请注意,例如在 x86 堆栈上,内存地址会向 减少 内存地址增长,就像倒置一样)。函数本身管理堆栈,而函数由 C 编译器创建,C 编译器创建代码来管理堆栈。

      有一些标准指定了它在特定架构上的具体工作方式——这些标准指定了架构ABI。例如,x86-64 ABIARM ABI。它们指定使用哪些寄存器、如何使用和传递堆栈等。然后编译器“实现该 ABI”——创建符合 ABI 标准中给定规则的汇编代码。

      当我们用 C 为嵌入式系统编写代码时,谁在嵌入式系统堆栈内存分配等中完成所有这些工作?

      C 代码自己管理内存。编译器将它们粘合在一起。

      那么谁在 MCU 中的函数调用期间管理此内存分配

      我猜是代码本身? (现在)C 标准库是用 C 编写的,有些是开源的。 Newlib 是用于嵌入式系统的流行的免费 C 标准库实现 - 您可以检查 it's malloc implementation

      【讨论】:

        猜你喜欢
        • 2014-12-23
        • 2014-08-30
        • 2011-02-10
        • 2020-04-24
        • 1970-01-01
        • 2022-11-22
        • 1970-01-01
        • 2023-03-04
        • 2019-02-25
        相关资源
        最近更新 更多