#Stack and Heap in linux [Process mem 32bit]: http://data.linuxtoy.cn/image/Process_Mem_32bit.png "linux Process mem" [Stack info]: http://data.linuxtoy.cn/image/Stack_Info_on_start.png "linux stack info" # 进程地址空间 Linux使用虚拟内存机制来管理计算机中的物理内存. 每个进程的内存空间都是独立的(在物理地址空间上),假如在32位系统中,进程虚拟地址的起始地址都是0x08048000h 对于同时运行的多个进程而言,就像运行在自己的内存小宇宙中,他们都认为其内存地址 开始于同一个地址。 内核接受任意程序发出的虚拟内存请求,并将这个虚拟内存转换到RAM中某处物理内存地址。 在32位系统中,进程的虚拟地址空间大小是4GB, 默认0~3G是用户空间,3~4G是内核空间。 32位系统,地址空间的分配如下图所示: ![linux process memory][Process mem 32bit] # 栈剖析 栈底位于高地址(不能高于0xBFFFFFFFh), 栈顶位于低地址(ESP指向栈顶),栈向下生长。 linux加载程序时,将大量的信息放入栈中, 包括可执行程序的完成路径,命令行参数和环境变量。 程序启动时,栈里面存放的内容按照下图的方式组织: ![linux stack info, when program starts][stack info] 图片来自于`<>` 下面通过一个实验来检验: test.c ``` int main(int argc, char *argv[]) { return 0; } ``` 通过gdb查看程序启动时的栈信息 ``` $ gdb ./test GNU gdb (GDB) 7.11.1 ... ... Reading symbols from ./test...(no debugging symbols found)...done. (gdb) set args a b c arg1 arg2 arg3 (gdb) b _start Breakpoint 1 at 0x4003b0 (gdb) r Starting program: /home/caodan/test a b c arg1 arg2 arg3 Breakpoint 1, 0x00000000004003b0 in _start () (gdb) info registers rsp rsp 0x7fffffffe780 0x7fffffffe780 ``` 由于在64bit系统上,所以栈顶寄存器是rsp,而不是esp, rsp的值为:0x7fffffffe780 dump当前的栈信息, x/70xg的含义: x表示eXamine memory, 70表示需要显示70个单元的内容 x表示以16进制显示, g表示每个单元的长度为8个字节 ``` (gdb) x/70xg 0x7fffffffe780 0x7fffffffe780: 0x0000000000000007 0x00007fffffffeaab 0x7fffffffe790: 0x00007fffffffeabd 0x00007fffffffeabf 0x7fffffffe7a0: 0x00007fffffffeac1 0x00007fffffffeac3 0x7fffffffe7b0: 0x00007fffffffeac8 0x00007fffffffeacd 0x7fffffffe7c0: 0x0000000000000000 0x00007fffffffead2 0x7fffffffe7d0: 0x00007fffffffeadd 0x00007fffffffeaef 0x7fffffffe7e0: 0x00007fffffffeb2d 0x00007fffffffeb44 0x7fffffffe7f0: 0x00007fffffffeb68 0x00007fffffffeb7f 0x7fffffffe800: 0x00007fffffffeb8f 0x00007fffffffeb9a 0x7fffffffe810: 0x00007fffffffebb2 0x00007fffffffebc4 0x7fffffffe820: 0x00007fffffffebd6 0x00007fffffffebf7 0x7fffffffe830: 0x00007fffffffec03 0x00007fffffffec2c 0x7fffffffe840: 0x00007fffffffec3c 0x00007fffffffec98 0x7fffffffe850: 0x00007fffffffeca4 0x00007fffffffeccd 0x7fffffffe860: 0x00007fffffffed12 0x00007fffffffed28 0x7fffffffe870: 0x00007fffffffed44 0x00007fffffffedac 0x7fffffffe880: 0x00007fffffffedb6 0x00007fffffffedc5 0x7fffffffe890: 0x00007fffffffede9 0x00007fffffffedfc 0x7fffffffe8a0: 0x00007fffffffee0d 0x00007fffffffee22 0x7fffffffe8b0: 0x00007fffffffee37 0x00007fffffffee48 0x7fffffffe8c0: 0x00007fffffffee5d 0x00007fffffffee66 0x7fffffffe8d0: 0x00007fffffffee77 0x00007fffffffee7f 0x7fffffffe8e0: 0x00007fffffffee91 0x00007fffffffeea0 0x7fffffffe8f0: 0x00007fffffffeecc 0x00007fffffffeedb 0x7fffffffe900: 0x00007fffffffeef5 0x00007fffffffef09 0x7fffffffe910: 0x00007fffffffef3f 0x00007fffffffef4c 0x7fffffffe920: 0x00007fffffffef57 0x00007fffffffef76 0x7fffffffe930: 0x00007fffffffef8a 0x00007fffffffefa4 0x7fffffffe940: 0x00007fffffffefcd 0x0000000000000000 0x7fffffffe950: 0x0000000000000021 0x00007ffff7ffa000 0x7fffffffe960: 0x0000000000000010 0x00000000bfebfbff 0x7fffffffe970: 0x0000000000000006 0x0000000000001000 0x7fffffffe980: 0x0000000000000011 0x0000000000000064 0x7fffffffe990: 0x0000000000000003 0x0000000000400040 0x7fffffffe9a0: 0x0000000000000004 0x0000000000000038 ``` **命令行参数的个数:** 第一个内存单元中的值是7, 表示命令行参数的个数为7. **命令行参数的地址信息:** ``` (gdb) x/s 0x00007fffffffeaab 0x7fffffffeaab: "/home/caodan/test" (gdb) x/s 0x00007fffffffeabd 0x7fffffffeabd: "a" (gdb) x/s 0x00007fffffffeabf 0x7fffffffeabf: "b" (gdb) x/s 0x00007fffffffeac1 0x7fffffffeac1: "c" ``` 最后一个命令行参数地址的下一个内存单元中的值是一个空指针,用来分隔命令行参数与环境变量 **环境变量:** ``` (gdb) x/s 0x00007fffffffead2 0x7fffffffead2: "XDG_VTNR=2" (gdb) x/s 0x00007fffffffeadd 0x7fffffffeadd: "XDG_SESSION_ID=c2" (gdb) x/s 0x00007fffffffeaef 0x7fffffffeaef: "TERMINATOR_UUID=urn:uuid:ccfa742b-66a3-4731-afba-07604b68d670" ```