浅析Linux下的堆内存管理
2016-06-20 10:20
447 查看
最近在看一本叫《程序员的自我修养-链接.装载域库》(俞甲子,石凡,潘爱民 著)这本书,不得不像大家安利这本书,从最基本的EFL文件到链接,再到装载,虽然现在还没看完,但是对程序的可执行文件和虚拟内存的布局有了进一步的了解,不得不说是一本好书。言归正传,本文讨论的话题是堆内存的管理,首先让我们来了解一下进程的虚拟地址空间的组成部分(如下图):
首先来讨论一下这个program break,手册上说brk和sbrk会改变program break的位置,program break被定义为程序data segment的结束位置。感觉program break被定义成程序data segment的结束位置理解有点模糊,下面我们就用最基本的程序来验证。
1.程序代码如下:
首先我们将程序在后台运行(./a.out &),得到的结果是:
然后,执行命令cat /proc/18092/maps就可以得到进程堆的地址范围,如下图:
可以看到我们进程堆的范围是09c2b000-09c4d000 rw-p 00000000 00:00 0 [heap],堆的起始地址和p1的地址一样,说明程序brk()在堆上申请空间,也就是说刚开始的时候program break和堆的起始地址是一样的。
2.我们对上面的程序进行修改,在这个基础上继续分配空间,如下:
运行结果为:
那么问题来了,我们在调用brk()之后,program break的值为0x95f6010,刚好是从0x95f6000地址开始的16个字节,但当我们对空间进行赋值之后,program break的值就变成0x9618000,一下子就变大了很多,这是疑问?
3.我们继续修改程序:
输出结果为:
这个时候我们看到program break的位置本来已经在一个足够大的位置上(0x962f000),但是在追加申请空间之后又变回到0x960d018(这个偏移量刚好为24个字节,是6个int型变量的大小),为什么program break从0x962f000回退到了0x960d018,这又是个疑问?
4.下面来看看第二个程序:
我们可以看到ptr的当前地址为0x991f000,这个地址就是堆空间的起始地址,也是program break的位置,后来我们调用malloc()在堆上分配100字节的空间,这100字节空间起始地址为0x991f410(至于为什么和0x991f000相差这么多还没搞明白),如果是正真分配100字节,那么program break的位置应该是(0x991f410+100字节=0x991f474),但是我们显示program break当前位置(0x9940000)的时候却比0x991f474大很多,说明系统一次性就分配了比100字节大很多的堆空间,至于为什么?我想如果每次分配几字节或几十字节的空间都调用sbrk()函数的话,系统开销太大,所以索性一次分配足够大的空间。当进程需要另外申请空间时,系统可能的工作就是在这个一次性分配足够大的剩余空间里再分配。至于为什么不从0x991f000直接开始分配,初步设想是0x991f000-0x991f410这段堆空间肯能用作记录目前堆的分配情况。
针对以上三个疑问,目前还在探索中,希望已经深入理解这一块的大牛指点!!!!!!
首先来讨论一下这个program break,手册上说brk和sbrk会改变program break的位置,program break被定义为程序data segment的结束位置。感觉program break被定义成程序data segment的结束位置理解有点模糊,下面我们就用最基本的程序来验证。
1.程序代码如下:
#include <stdio.h> #include <unistd.h> #include <apue.h> int main(){ void* p = sbrk(0); //获取当前program break的当前位置(此时是heap的起始地址) int* p1 = (int *)p; // 将当前空指针转化成int型指针 brk(p1+4); //分配了16个字节的空间 p1[0] = 10; p1[1] = 20; p1[2] = 30; p1[3] = 40; p1[4] = 50; printf("%p\n",p1); printf("%p\n",p1+1); printf("%p\n",p1+2); printf("%p\n",p1+3); printf("%p\n",sbrk(0));//这个时候program break的值比较大,因为一次性分配了较大的空间,而不必每次都调用sbrk()来分配堆空间 while(1){ sleep(1); } }
首先我们将程序在后台运行(./a.out &),得到的结果是:
然后,执行命令cat /proc/18092/maps就可以得到进程堆的地址范围,如下图:
可以看到我们进程堆的范围是09c2b000-09c4d000 rw-p 00000000 00:00 0 [heap],堆的起始地址和p1的地址一样,说明程序brk()在堆上申请空间,也就是说刚开始的时候program break和堆的起始地址是一样的。
2.我们对上面的程序进行修改,在这个基础上继续分配空间,如下:
#include <stdio.h> #include <unistd.h> #include <apue.h> int main(){ void* p = sbrk(0); //获取当前program break的当前位置(此时是heap的起始地址) int* p1 = (int *)p; // 将当前空指针转化成int型指针 brk(p1+4); printf("program break is :%p\n",sbrk(0));//这个时候program break的值比较大,因为一次性分配了较大的空间,而不必每次都调用sbrk()来分配堆空间 p1[0] = 10; printf("program break is %p\n",sbrk(0)); p1[1] = 20; p1[2] = 30; printf("program break is %p\n",sbrk(0)); p1[3] = 40; p1[4] = 50; printf("%d,%p\n",*p1,p1); printf("%d,%p\n",*(p1+1),p1+1); printf("%d,%p\n",*(p1+2),p1+2); printf("%d,%p\n",*(p1+3),p1+3); printf("program break is %p\n",sbrk(0));//这个时候program break的值比较大,因为一次性分配了较大的空间,而不必每次都调用sbrk()来分配堆空间 }
运行结果为:
那么问题来了,我们在调用brk()之后,program break的值为0x95f6010,刚好是从0x95f6000地址开始的16个字节,但当我们对空间进行赋值之后,program break的值就变成0x9618000,一下子就变大了很多,这是疑问?
3.我们继续修改程序:
#include <stdio.h> #include <unistd.h> #include <apue.h> int main(){ void* p = sbrk(0); //获取当前program break的当前位置(此时是heap的起始地址) int* p1 = (int *)p; // 将当前空指针转化成int型指针 int *p2,*temp; brk(p1+4); printf("program break is :%p\n",sbrk(0)); p1[0] = 10; printf("program break is :%p\n",sbrk(0)); p1[1] = 20; p1[2] = 30; p1[3] = 40; p1[4] = 50; printf("%p\n",&p1[0]); printf("%p\n",&p1[1]); printf("%p\n",&p1[2]); printf("%p\n",&p1[3]); temp=p1+4; brk(temp+2); temp[0]=60; temp[1]=70; printf("%p\n",&temp[0]); printf("%p\n",&temp[1]); printf("program break is :%p\n",sbrk(0)); }
输出结果为:
这个时候我们看到program break的位置本来已经在一个足够大的位置上(0x962f000),但是在追加申请空间之后又变回到0x960d018(这个偏移量刚好为24个字节,是6个int型变量的大小),为什么program break从0x962f000回退到了0x960d018,这又是个疑问?
4.下面来看看第二个程序:
#include <stdio.h> #include <stdlib.h> #include<unistd.h> int main(){ void* ptr, *ptr1,*ptr2; ptr = sbrk(0); printf("sbrk:%p\n", ptr); ptr1 = malloc(100); ptr = sbrk(0); printf("sbrk:%p, ptr1:%p\n", ptr, ptr1); free(ptr1); ptr2=malloc(50); ptr = sbrk(0); printf("sbrk:%p,ptr2:%p\n",ptr,ptr2); }输出结果为:
我们可以看到ptr的当前地址为0x991f000,这个地址就是堆空间的起始地址,也是program break的位置,后来我们调用malloc()在堆上分配100字节的空间,这100字节空间起始地址为0x991f410(至于为什么和0x991f000相差这么多还没搞明白),如果是正真分配100字节,那么program break的位置应该是(0x991f410+100字节=0x991f474),但是我们显示program break当前位置(0x9940000)的时候却比0x991f474大很多,说明系统一次性就分配了比100字节大很多的堆空间,至于为什么?我想如果每次分配几字节或几十字节的空间都调用sbrk()函数的话,系统开销太大,所以索性一次分配足够大的空间。当进程需要另外申请空间时,系统可能的工作就是在这个一次性分配足够大的剩余空间里再分配。至于为什么不从0x991f000直接开始分配,初步设想是0x991f000-0x991f410这段堆空间肯能用作记录目前堆的分配情况。
针对以上三个疑问,目前还在探索中,希望已经深入理解这一块的大牛指点!!!!!!
相关文章推荐
- linux简单命令
- 嵌入式Linux系统启动过程
- LINUX常用命令
- 树莓派上基于图形界面的安装程序-synaptic
- 安装交叉编译器后,无法执行arm-linux-gcc -v
- Linux下修改文件创建时间(修改文件更改时间)
- linux内核部件分析之——设备驱动模型之class
- Linux内核日志开关
- Linux操作系统的权限代码分析【转】
- Linux 指令
- (转)资源监控工具Spotlight监测LINUX
- 使用screen将程序放到背景运行
- VMware虚拟机的安装
- 嵌入式 Linux C语言(十三)——双链表
- 嵌入式 Linux C语言(十二)——单链表
- linux 查看磁盘空间大小
- Linux下的文件查找命令——find
- Linux Server(一)搭建Java、PHP生产环境
- Linux网卡绑定(bond)
- Linux网桥配置