【问题标题】:Variables and calls in a C program and its corresponding location in the Linux process address spaceC 程序中的变量和调用及其在 Linux 进程地址空间中的对应位置
【发布时间】:2019-01-11 11:16:23
【问题描述】:

我目前正在学习 Linux 进程地址空间,我不确定这些 C 变量在进程地址空间中对应的位置。

我知道当一个函数被调用时,会创建一个新框架,它会包含局部变量和其他函数调用等。

我不确定的是框架中的指针:

我有这个功能:

int main(){
    char *pointer1 = NULL;
    char *pointer2 = (void *)0xDDDDDDDD;
    pointer1 = malloc(80);
    strcpy(pointer1, "Testing..");
    return(0);
}

调用 main 时,会创建一个新框架。

变量被初始化。

我不确定这些是指针,在哪里:

  • *pointer1对应在进程地址空间-数据还是文本部分?

  • *pointer2对应于进程地址空间-数据还是文本部分?

  • NULL0xDDDDDDDD 是否属于数据或文本部分?

  • 由于pointer1 = malloc(80),是否属于栈段?

【问题讨论】:

    标签: c linux linux-kernel


    【解决方案1】:

    首先应该注意的是,C 规范实际上并不要求将局部变量存储在堆栈中,它根本没有指定 自动 变量的位置。

    话虽如此,变量pointer1pointer2 本身的存储很可能会被编译器放入堆栈中。当调用main 函数时,它们的内存将成为编译器创建的堆栈帧的一部分。

    继续,在现代类似 PC 的系统上,指针实际上只不过是一个简单的无符号整数,它的值是它指向的地址。用于初始化的值(NULL0xDDDDDDDD)只是简单的整数值。初始化的完成与普通的int 变量相同。因此,用于初始化的值并不真正作为“数据”存在,而是可以直接在机器代码中编码,因此将存储在“文本”(代码)段中。

    最后,对于动态分配,pointer1 的存储位置不会改变。它只是为pointer1 分配一个新值是什么?正在分配的内存位于与任何程序部分分开的“堆”上(即它既不在代码、数据或堆栈段中)。

    【讨论】:

      【解决方案2】:

      正如一些程序员花花公子刚才所说,C 规范没有规定必须放置自动变量的区域。但是编译器通常会增加堆栈以适应它们。但是,它们可能会在 .data 区域结束,如果它们被定义为 static char *pointer1,它们就会结束。

      初始化值可能存在也可能不存在于程序区域中。在您的情况下,由于值的类型是int,如果具有适当内联运算符的指令可用,大多数架构会将初始化内联为适当的机器指令。例如,在 x86_64 中,将发出单个 mov/movq 操作以将 0 (NULL) 或其他 int 放入堆栈上的适当内存位置。

      但是,使用全局范围初始化的变量,例如 static char string[40] = "Hello world" 或其他已初始化的全局变量最终会出现在 .data 区域并占用其中的空间。编译器可能会将已声明但未定义的全局范围变量放在 .bss 区域中。

      问题既然pointer1 = malloc(80),它是否属于堆栈部分?这个问题定义不明确,因为它包含两个东西。

      pointer1 是一个将保存在&pointer1 的值。考虑到上述考虑,编译器可能已放入堆栈的地址。

      malloc(80) 的结果是一个值,它引用堆上的一个区域,一个不同的区域,在映射的程序空间之外动态分配。 在 Linux 上,调用 malloc 的结果甚至可能会创建一个新的 NULL 支持的内存区域(即不是永久存储在文件中的临时区域;尽管它可以被内核交换)。

      本质上,您可以将 malloc(80) 的行为方式想象成类似(不考虑 free(),所以 这是过于简单化了):

      int space_left = 0; void *last_mapping = NULL;
      void *malloc(int req) {
          void *result;
          if (space_left < req) {
              last_mapping = mmap(NULL, MALLOC_CHUNK_LENGTH, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
              space_left = MALLOC_CHUNK_LENGTH;
          }
          space_left -= req;
          result = last_mapping;
          last_mapping += req;
          return result;
      }
      

      使用 MAP_PRIVATE 调用 mallocmmap 的巨大区别在于 mmap 是一个 Linux 系统调用,它必须进行内核上下文切换,分配新的内存映射并为每个分配的内存块重置 MMU 层,而 malloc 可以更智能,将单个大区域用作“堆”,并在堆初始化后管理用户空间中的不同 malloc 和 free (直到堆空间用完,它可能必须管理多个堆)。

      【讨论】:

        【解决方案3】:

        你疑惑的最后一段,即“由于pointer1 = malloc(80),它是否属于堆栈部分?”,我可以告诉你

        在 C 中,动态内存是使用一些标准库函数从堆中分配的。两个关键的动态内存函数是 malloc() 和 free()。

        malloc() 函数采用单个参数,即所请求内存区域的大小(以字节为单位)。它返回一个指向已分配内存的指针。如果分配失败,则返回 NULL。标准库函数的原型是这样的:

              void *malloc(size_t size);
        

        free() 函数获取 malloc() 返回的指针并释放内存。不返回成功或失败的指示。函数原型是这样的:

              void free(void *pointer);
        

        您可以参考文档 https://www.design-reuse.com/articles/25090/dynamic-memory-allocation-fragmentation-c.html

        【讨论】:

          猜你喜欢
          • 2012-08-07
          • 2014-07-28
          • 2012-01-22
          • 1970-01-01
          • 1970-01-01
          • 2014-09-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多