返回介绍

8. 内存布局

发布于 2024-10-12 21:58:07 字数 2762 浏览 0 评论 0 收藏 0

进程内存布局与编码息息相关的是堆(heap)和栈(stack)。

  • stack : 用于维护函数调用上下文。
  • heap : 动态分配内存区。

进程内存模型示意图

                 |              |
        0x400000 +--------------+
                 | .init, .text |
                 +--------------+
                 | .rodata      |
                 +--------------+
                 | .data, .bss  |
                 +--------------+
                 | heap         |  v
                 +--------------+
                 | ...          |  mmap
                 +--------------+
                 | dynlib       |
                 +--------------+
                 | ...          |
                 +--------------+
                 | stack        |  ^
  0x7ffffffff000 +--------------+
                 | vsyscall     |
  

栈由高位向低位扩展,通常以 SP 寄存器定位栈顶。每个函数调用,都会在栈划出一个区域,称做栈帧(stack frame),以 [esp, ebp] 确定范围,BP 也被称做帧指针(frame pointer)。

默认栈大小受系统限制,超出会导致段错误。使用 ulimit -s 查看或修改。

栈帧保存如下内容:

  • 上下文。(如寄存器数据,用于结束时恢复现场)
  • 当前函数局部变量。(非静态)
  • 调用其他函数是的参数。(register, cdecl)
  SP +-------------+---------
     | ...         |
     +-------------+
     | local       |
     +-------------+
     | context     |
  BP +-------------+---------
     | old BP      |  调用方帧指针
     +-------------+
     | PC          |  调用方返回地址
     +-------------+
     
#include <stdio.h>
#include <stdlib.h>

int add(int x, int y)
{
    int z = x + y;
    return z;
}

int main (int argc, char *argv[])
{
    int x = 0x11;
    int y = 0x22;
    int z = add(x, y);

    printf("%d\n", z);
    return EXIT_SUCCESS;
}
$ objdump -d -M intel ./test | less -p "<main>:"

参数 -fomit-frame-pointer 可以取消 BP 寄存器使用,但不推荐。指令 leave = pop sp, bp; pop bp ,清除当前栈帧,然后 ret 再 pop 恢复 PC/IP 回到调用方。

目前 Linux 标准发行版使用 glibc/ptmalloc2 堆分配器。

  • 向操作系统申请大块内存,交由分配器管理。
  • 短生命周期使用 sbrk。
  • 分配和释放时,合并小块内存。
  • 释放时可能被放回内存池。
  • 长生命周期大内存使用 mmap。
  • 特大块内存申请使用 mmap。

google tcmalloc, freebsd jemalloc

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>

int main (int argc, char *argv[])
{
    char *p = malloc(1024);
    *p = 0x22;

    char *p2 = mmap(NULL, 1<<20, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    *p2 = 0x33;

    printf("%p, %p\n", p, p2);

    getchar();
    free(p);
    munmap(p2, 1<<20);

    return EXIT_SUCCESS;
}
$ gcc -g -O0 -Wall -std=gnu11 -o test test.c    # 使用 gnu,否则 MAP_ANONYMOUS 未定义。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文