Linux进程地址空间详解
来源:互联网 发布:淘宝运费险漏洞 编辑:程序博客网 时间:2024/06/08 18:39
之前写的一篇文章《a.out分段及运行时内存结构》简要介绍了Linux下的可执行文件格式和运行时的内存布局,这篇文章将更为详细得讨论Linux下进程的虚拟地址空间的布局。如下所述的内容都是基于32位系统的。
Linux传统内存布局进程的线性地址空间分为两部分:
1、从0×00000000到0xbfffffff的线性地址,无论进程运行在用户态还是内核态都可以寻址。
2、从0xc0000000到0xffffffff的线性地址,只有内核态的进程才能寻址。
进程的栈从地址0xc0000000向低地址发展,同时内存映射区域从0×40000000向高地址发展,因为栈所用内存相对较小(通常小于100MB),因此约有2GB左右的映射空间;可执行文件的正文段从0×8048000开始,然后依次是数据段,BSS段,进程堆的起始点大于BSS段的结束点,并向高地址发展,因为0×40000000以上已用作内存映射用,因此堆的大小只有约1G,这有点太小了。
为了验证传统内存布局方式,使用以下测试程序,测试环境为Slackware 13.37 (Kernel 2.6.37)。
为了使内核切换到传统内存布局,执行命令#sysctl -w vm.legacy_va_layout=1(因为Linux 2.6.7及以后版本内核已经默认使用新的内存布局方式了)。
运行此程序,查看其输出及相应/proc/pid/maps内容:
首先分析程序的输出:
1、因为main()函数和printf()函数位于代码段中,而代码段是从0×08048000开始的,所以符合表中所述。
2、first是第一个临时变量,由于在first之前还有一些环境变量,它的值并非0xbfffffff,而是0xbfcd1264,这是正常的。
3、p0是在堆中分配的,其地址小于0×4000 0000,这也是正常的。
4、但p1和p2也是在堆中分配的,而其地址竟大于0×4000 0000,与表一描述不符。
原因在于:运行时堆的位置与内存管理算法相关,也就是与malloc的实现相关。关于内存管理算法的问题,我们在后继文章中有详细描述,这里只作简要说明。在glibc实现的内存管理算法中,malloc小块内存是在小于0×4000 0000的内存中分配的,通过brk/sbrk不断向上扩展,而分配大块内存,malloc直接通过系统调用mmap实现,分配得到的地址在文件映射区,所以其地址大于0×40000000。
所以,p0是在堆上分配的,而p1~p3则是通过mmap实现的,这表现在maps文件的倒数第三行(40189000-a0290000)。
然后分析maps文件内容:
08048000-08049000 代码段
08049000-0804a000 数据段
(注意本程序无BSS段)
0804a000-0806b000 堆
40000000-40189000 内存映射区,本程序映射了ld和libc动态链接库
40189000-a0290000 内存映射区,malloc用其为p1~p3分配内存
bf841000-bf862000 栈
ffffe000-fffff000 内核为我们映射的系统调用入口代码
鉴于以上传统内存布局的限制,Linux 2.6.7及以后版本已经默认使用另一种新的内存布局方式,如下图所示:
从上图可以看到,mmap 映射区域至顶向下扩展, mmap 映射区域和堆相对扩展,直至耗尽虚拟地址空间中的剩余区域,弥补了经典内存布局方式的不足。
为了使用此新的内存布局,执行命令#sysctl -w vm.legacy_va_layout=0,然后重新编译运行程序并查看其输出及maps文件内容:
对比上一次maps文件内容,发现现在的内存映射区域已经从b7857000开始了,并且向下发展。p1~p3现在位于575c8000-b76cc000区域内。
新的内存布局图还显示栈底和Kernel Space之间、栈顶和映射区域之间、堆和BSS段之间都有一个随机的offset,每次运行程序时的值都不一样,这样会使得使用缓冲区溢出进行攻击更加困难。如果需要,当然也可以让程序的栈和映射区域从一个固定位置开始,只需要设置全局变量randomize_va_space值为0即可(默认值为 1)。
不明白offset的作用?让我们再执行一次程序,然后观察其输出和maps文件:
和之前一次执行的输出相比较,发现程序输出中first变量的地址已经变了,而且maps文件中栈和内存映射区域的地址也变了。
那么,禁用offset会怎么样呢?执行命令#sysctl -w kernel.randomize_va_space=0,运行程序两次,再观察其输出:
第一次的输出:
第二次的输出:
正如所预料的,这两次运行的输出一模一样,其实再多执行几次也是一样的。
多线程内存空间布局以上所述的都是只有主线程情况下的内存布局,那多线程情况下的布局是怎样的呢?
这里也使用一个程序来测试:
编译此程序gcc thread.c -lpthread,运行后其输出如下:
主线程与第一个线程的栈之间的距离:0xbfbaf648 – 0xb771d384 = 132M
第一个线程与第二个线程的栈之间的距离:0xb771d384 – 0xb6f1d384= 8M
其它几个线程的栈之间距离均为8M。
也就是说,主线程的栈空间最大为132M,而普通线程的栈空间仅为8M,超这个范围就会造成栈溢出(后果很严重)。
上一篇:机器周期,指令周期,时钟周期,节拍与晶振
下一篇:bss,data,text,rodata,堆,栈,常量段
- 欢迎letitbe413在ChinaUnix博...
- 欢迎linminchao在ChinaUnix博...
- 欢迎sagatagintoki在ChinaUnix...
- 欢迎abc16156在ChinaUnix博客...
- 欢迎yuliangguiguzi在ChinaUni...
- linux 常见服务端口
- xmanager 2.0 for linux配置
- 【ROOTFS搭建】busybox的httpd...
- openwrt中luci学习笔记
- 什么是shell
- linux dhcp peizhi roc
- 关于Unix文件的软链接
- 求教这个命令什么意思,我是新...
- sed -e "/grep/d" 是什么意思...
- 谁能够帮我解决LINUX 2.6 10...
- Linux进程地址空间详解
- Linux进程地址空间详解
- Linux进程地址空间详解
- Linux进程地址空间详解 转载
- Linux进程地址空间
- Linux进程地址空间
- LINUX进程地址空间
- linux 进程地址空间
- Linux进程地址空间
- Linux进程地址空间
- Linux进程地址空间
- linux 进程地址空间
- LINUX进程地址空间
- linux进程地址空间
- linux进程地址空间
- Linux进程地址空间
- linux进程地址空间
- linux 进程地址空间
- C++实现string存取二进制数据的方法
- JavaScript内存泄露
- 机器周期,指令周期,时钟周期,节拍与晶振
- MySQL数据库SYS CPU高的可能性分析
- binder的机制和原理
- Linux进程地址空间详解
- The application's PagerAdapter changed the adapter's contents without calling PagerAdapter#notifyDat
- 第五周项目1.3-三角形类雏形
- bss,data,text,rodata,堆,栈,常量段
- ubuntu14.04 + opencv3.1 + contrib modules
- linux 分析 目标文件 的命令
- 淘宝API开发系列---淘宝API的测试及使用
- reactNative小demo讲解 对比iOS
- 项目打release时遇到的错误