blog:programming:linux_stack

Stack and Heap in linux

进程地址空间

Linux使用虚拟内存机制来管理计算机中的物理内存. 每个进程的内存空间都是独立的(在物理地址空间上),假如在32位系统中,进程虚拟地址的起始地址都是0x08048000h 对于同时运行的多个进程而言,就像运行在自己的内存小宇宙中,他们都认为其内存地址 开始于同一个地址。

内核接受任意程序发出的虚拟内存请求,并将这个虚拟内存转换到RAM中某处物理内存地址。

在32位系统中,进程的虚拟地址空间大小是4GB, 默认0~3G是用户空间,3~4G是内核空间。

32位系统,地址空间的分配如下图所示:

栈剖析

栈底位于高地址(不能高于0xBFFFFFFFh), 栈顶位于低地址(ESP指向栈顶),栈向下生长。

linux加载程序时,将大量的信息放入栈中, 包括可执行程序的完成路径,命令行参数和环境变量。

程序启动时,栈里面存放的内容按照下图的方式组织:

![linux stack info, when program starts][stack info] 图片来自于<<Assembly Language Step-By-Step: Programming with linux 3rd Edition>>

下面通过一个实验来检验:

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"
  • blog/programming/linux_stack.txt
  • 最后更改: 2022/01/09 22:43
  • 127.0.0.1