在运行时存储程序的机器指令的位置

Where machine instructions of a program stored during runtime?

本文关键字:机器指令 位置 程序 运行时 存储      更新时间:2023-10-16

我所知,每当我们运行任何程序时,该程序的机器指令都会加载到 RAM 中。同样,内存有两个区域:堆栈和堆。

我的问题是:机器指令存储在哪个内存区域?堆栈还是堆?

我了解到以下程序给出了运行时错误,尽管函数内没有声明变量。这背后的原因是堆栈溢出。那么我应该假设函数的机器指令存储在堆栈中吗?

int func()
    {
            return func();
    }

两者都不是,因为它不是像堆栈和堆那样动态分配的。

可执行加载程序将可执行文件 (.text) 及其包含的任何静态数据(如全局变量 (.data/.rodata) 的初始值)加载到未使用的 RAM 区域中。然后,它会设置可执行文件请求的任何零初始化内存 (.bss)。

只有这样,才会为main()设置堆栈。如果您输入另一个函数,则堆栈内存将在堆栈上分配,该函数包含返回地址、函数参数和任何本地声明的变量以及通过 alloca() 分配的任何内存。[1] 当您从函数返回时,内存会释放。

堆内存按 malloc()calloc()realloc() 分配。当您free()realloc()它时,它会释放。

在进程终止之前,用于可执行文件及其静态数据的 RAM 不会释放。

因此,堆栈和堆基本上处于应用程序的控制之下。可执行文件本身的内存由可执行加载程序/操作系统控制。在适当装备的操作系统中,您甚至没有对该内存的写入访问权限。


关于您编辑的问题,没有。(糟糕的风格,编辑问题以给它一个全新的角度。

可执行代码保留在加载时的位置。调用函数不会将机器指令放在堆栈上。您的func()(一个不带参数的函数)在堆栈上放置的唯一内容是返回地址,该指针指示当前函数返回后应继续执行的位置。

由于没有任何调用返回,因此程序会不断在堆栈上添加返回地址,直到该地址不再存在。这与机器代码指令完全无关。


[1]:请注意,这些实际上都不是 C 语言标准的一部分,而是实现定义的,并且可能有所不同 - 我提出了一个简化版本的事务。例如,函数参数可能在 CPU 寄存器中传递,而不是在堆栈上传递。

既不是非此即彼。

程序的图像包含代码和静态数据(即所有字符串常量、静态数组和结构等)。它们将被加载到 RAM 的不同段中。

堆栈和堆是存储数据的动态结构,它们将在程序开始时创建。堆栈是硬件支持的解决方案,而堆是标准库支持的解决方案。

因此,您的代码将位于代码段中,

静态数据和堆将位于数据段中,堆栈将位于堆栈段中。

程序的机器指令加载到RAM中

适用于托管的"类似 PC"的系统。在嵌入式系统上,代码通常直接从闪存执行

同样,有两个内存区域:堆栈和堆。

不,这是一些过度简化,太多了,太糟糕的编程老师教了。

还有很多其他区域:.data.bss所有具有静态存储的变量都去哪里,常量去哪里.rodata等等。

存储程序代码的段通常称为 .text

同样,有两个内存区域:堆栈和堆。

事情没那么简单。通常在主流操作系统上,会有更多事情发生:

  • 每个正在运行的线程有一个堆栈;
  • 有多少堆,就有多少堆(实际上,就内存管理器而言,
  • 您要求在虚拟地址空间中"启用"一些内存页,"堆"的东西源于这样一个事实,即通常使用某种堆管理器代码在分配之间有效地分配这些内存部分);
  • 可以有内存映射文件和共享内存;
  • 最重要的是,可执行文件(和动态库)被映射在进程的内存中,通常代码区(所谓的"文本"段)以只读模式映射,其他区域(通常与初始化的全局和静态变量以及加载器修复的内容有关)在写入时复制。

因此,代码存储在可执行文件的相关部分中,该部分映射到内存中。

它们通常位于名为 .text 的部分。

在 Linux 上,您可以使用 core-utils size命令列出 ELF 对象或可执行文件的部分,例如在 tst ELF 可执行文件上:

$ size -Ax tst | grep "^.text"
.text                0x1e8   0x4003b0
$

除了堆栈和堆之外,还有多个内存段。 下面是如何在内存中布局程序的示例:

              +------------------------+
high address  | Command line arguments |   
              | and environment vars   |  
              +------------------------+
              |         stack          |
              | - - - - - - - - - - -  |
              |           |            |
              |           V            |
              |                        |
              |           ^            |
              |           |            |
              | - - - - - - - - - - -  |
              |          heap          |
              +------------------------+
              |    global and read-    |
              |       only data        |
              +------------------------+
              |     program text       |
 low address  |    (machine code)      |
              +------------------------+   

细节因平台而异,但这种布局对于基于 x86 的系统来说很常见。 机器码占用自己的内存段(以ELF格式标记为.text),全局只读数据将存储在另一个段(.rdata.rodata),未初始化的全局存储在另一个段(.bss)等。 有些段是只读的,有些是可写的。

既不是堆也不是堆栈。

通常,可执行指令存在于代码段中。

引用维基百科文章

在计算中,代码段(也称为文本段或简称为文本)是目标文件的一部分或包含可执行指令的程序虚拟地址空间的相应部分。

当加载程序将程序放入内存以便可以执行时,会分配各种内存区域(特别是作为页面)

在运行时,目标文件的代码段将加载到内存中的相应代码段中。特别是,它与堆栈或堆无关。


编辑:

在上面的代码片段中,您遇到的称为无限递归

即使你的函数不需要局部变量的堆栈空间,它仍然需要在调用内部函数之前推送外部函数的返回地址,从而要求堆栈空间,只是永远不要return[将地址弹出堆栈][就像在不返回点],从而耗尽堆栈空间, 导致堆栈溢出。