linux 进程地址空间,操作系统原理:进程地址空间与调用栈

Linux 进程虚拟地址空间

先回忆一下ELF文件的组织结构,可以看这篇文章:Linux 链接与ELF文件。程序执行后进程地址空间布局则和操作系统密切相关。在将应用程序加载到内存空间执行时,操作系统负责代码段与数据段的加载,并在内存中为这些段分配空间。Linux的进程地址空间大致如下:

0818b9ca8b590ca3270a3433284dd417.png

栈 (stack)

函数调用借助的就是栈。Linux中ulimit -s命令可查看和设置堆栈最大值,调高堆栈容量可能会增加内存开销和启动时间。

内存映射段 (mmmap)

内核将硬盘文件的内容直接映射到内存,借助Linux的mmap()系统调用。这里也用来映射ELF文件用到的动态链接库。

堆 (heap)

分配的堆内存是经过字节对齐的空间,以适合原子操作。堆管理器通过链表管理每个申请的内存,由于堆申请和释放是无序的,最终会产生内存碎片。

堆的末端由break指针标识,当堆管理器需要更多内存时,可通过系统调用brk()和sbrk()来移动break指针以扩张堆,一般由系统自动调用。

数据段 (bss + data)

主要用来放全局变量和静态局部变量,地址编译期确定。

代码段 (text)

放的函数指令,地址编译期确定。

Linux 函数调用栈帧结构

根据反汇编可知,每个函数第一个指令都是push $rbp。当caller者调callee时,指令callq 把callee返回后的下一个指令地址压入栈帧,然后保存更新rbp寄存器,这样层层调用构成函数调用栈,每个栈帧开始就是存的上一个栈帧的rbp, 结尾就是调用callee后的下一个执行令地址。函数调用详细介绍可以看这篇文章从汇编看Linux C函数的调用约定和参数传递的细节。调用栈的结构图大致如下:

0818b9ca8b590ca3270a3433284dd417.png

下面用一个例子来说明栈帧结构的细节。

int fun3(int a3)

{

int a = 3;

int re = a3;

while(1);

return re;

}

int fun2(int a2)

{

int a = 2;

int re = fun3(a2);

return re;

}

int fun1(int a1)

{

int a = 1;

int re = fun2(a1);

return re;

}

int main()

{

int a = 7;

int b = fun1(a);

return 0;

}

利用gdb对上面里进行详细分析,编译gcc -g main.c, 执行./a.out, 然后新开一个shell窗口ps afxu | grep a.out查找进程PID,gdb - 进行调试。可以很容易分析出函数的栈帧结构组织关系,详细如下:

fun3 (a3=7) at main.c:5

5 while(1);

(gdb) bt

#0 fun3 (a3=7) at main.c:5

#1 0x000000000040051f in fun2 (a2=7) at main.c:12

#2 0x0000000000400543 in fun1 (a1=7) at main.c:19

#3 0x0000000000400564 in main () at main.c:26

(gdb) p $rbp // 当前栈帧0 rbp

$1 = (void *) 0x7fff1c4d0ce0

(gdb) x/i *(long*)($1 + 8) // 栈帧1 返回地址

0x40051f : mov %eax,-0x4(%rbp)

(gdb) p/x *(unsigned long*)$rbp // 栈帧1 rbp地址

$2 = 0x7fff1c4d0d08

(gdb) x/i *(long*)($2 + 8) // 栈帧2 返回地址

0x400543 : mov %eax,-0x4(%rbp)

(gdb) p/x *(unsigned long*)$2 // 栈帧3 rbp

$3 = 0x7fff1c4d0d30

(gdb) x/i *(long*)($3 + 8) // 栈帧3 返回地址

0x400564 : mov %eax,-0x4(%rbp)

(gdb) x/24x $rsp // 从rsp开始24 DWORD的栈内容

0x7fff1c4d0ce0: 0x1c4d0d08 0x00007fff // rbp

0x0040051f 0x00000000 // return addr

0x7fff1c4d0cf0: 0xa68271a8 0x00000007 0xa6dfe4c0 0x00007fe4

0x7fff1c4d0d00: 0x00000002 0x00007fe4

0x1c4d0d30 0x00007fff // rbp

0x7fff1c4d0d10: 0x00400543 0x00000000 // return addr

0x004005bd 0x00000007

0x7fff1c4d0d20: 0x1c4d0d50 0x00007fff 0x00000001 0x00000000

0x7fff1c4d0d30: 0x1c4d0d50 0x00007fff // rbp

0x00400564 0x00000000 // return addr

还可以用pmap命令看布局:

root@ubuntu:~# pmap -p 23620

23620: ./a.out

0000000000400000 4K r-x-- /root/a.out

0000000000600000 4K r---- /root/a.out

0000000000601000 4K rw--- /root/a.out

00007fe4a6817000 1776K r-x-- /lib/x86_64-linux-gnu/libc-2.19.so

00007fe4a69d3000 2044K ----- /lib/x86_64-linux-gnu/libc-2.19.so

00007fe4a6bd2000 16K r---- /lib/x86_64-linux-gnu/libc-2.19.so

00007fe4a6bd6000 8K rw--- /lib/x86_64-linux-gnu/libc-2.19.so

00007fe4a6bd8000 20K rw--- [ anon ]

00007fe4a6bdd000 140K r-x-- /lib/x86_64-linux-gnu/ld-2.19.so

00007fe4a6de6000 12K rw--- [ anon ]

00007fe4a6dfd000 8K rw--- [ anon ]

00007fe4a6dff000 4K r---- /lib/x86_64-linux-gnu/ld-2.19.so

00007fe4a6e00000 4K rw--- /lib/x86_64-linux-gnu/ld-2.19.so

00007fe4a6e01000 4K rw--- [ anon ]

00007fff1c4b2000 132K rw--- [ stack ]

00007fff1c5dd000 8K r-x-- [ anon ]

ffffffffff600000 4K r-x-- [ anon ]

更多有意思的细节可以看后面参考文献。

Reference

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值