【Linux环境编程】内存管理初探
来源:互联网 发布:淘宝卖孕婴用品怎么样 编辑:程序博客网 时间:2024/06/06 01:19
一.内存管理层次
硬件层次:内存结构管理
内核层次:内存映射,堆扩展
语言层次:C的malloc以及C++的new等
数据结构层次:智能指针,STL
二.问题的提出
#include <stdio.h>main(){ int *p1 = malloc(4); int *p2 = malloc(4); int *p3 = malloc(4); int *p4 = malloc(4); printf("p1: %p\n", p1); printf("p2: %p\n", p2); printf("p3: %p\n", p3); printf("p4: %p\n", p4);}
编译运行:
gcc test1.c -o test1./test1
结果:
p1: 0x845f008
p2: 0x845f018
p3: 0x845f028
p4: 0x845f038
问题:我们明明申请的是4个字节的空间,但是我们可以看到,相邻的两个变量之间相差16个字节,这是为什么?
三.linux对内存的结构描述
我们先看看linux对进程内存的结构描述!
在linux中,当我们运行一个进程时,/proc/${pid}中会存放pid对应的进程的运行信息,包括内存结构,一旦进程结束,该文件夹就会被删除。
1.如何获取pid?
使用命令”ps aue“ 可查看当前正在执行的所有进程的信息。
在C中,我们可以如下获取:
#include <stdio.h>#include <unistd.h> //unix标准库main(){ int pid = getpid(); printf("pid: %d\n", pid);}
2.观察进程内存
好了,接下来我们执行下面这个代码,写个死循环让进程保持活动状态,然后看看其内存结构:
#include <stdio.h>#include <unistd.h> //unix标准库main(){ int pid = getpid(); printf("pid: %d\n", pid); while(1);}
得到pid: 3467
cd /proc/3467
该文件夹下是该进程的所有信息,其内存信息存放在maps中:
cat maps
结果:
08048000-08049000 r-xp 00000000 08:0a 8128164 /home/jiange/linuxprogram/day1/test208049000-0804a000 r--p 00000000 08:0a 8128164 /home/jiange/linuxprogram/day1/test20804a000-0804b000 rw-p 00001000 08:0a 8128164 /home/jiange/linuxprogram/day1/test2b75a6000-b75a7000 rw-p 00000000 00:00 0 b75a7000-b774f000 r-xp 00000000 08:07 263264 /lib/i386-linux-gnu/libc-2.19.sob774f000-b7751000 r--p 001a8000 08:07 263264 /lib/i386-linux-gnu/libc-2.19.sob7751000-b7752000 rw-p 001aa000 08:07 263264 /lib/i386-linux-gnu/libc-2.19.sob7752000-b7755000 rw-p 00000000 00:00 0 b776b000-b776e000 rw-p 00000000 00:00 0 b776e000-b7770000 r--p 00000000 00:00 0 [vvar]b7770000-b7772000 r-xp 00000000 00:00 0 [vdso]b7772000-b7792000 r-xp 00000000 08:07 263240 /lib/i386-linux-gnu/ld-2.19.sob7792000-b7793000 r--p 0001f000 08:07 263240 /lib/i386-linux-gnu/ld-2.19.sob7793000-b7794000 rw-p 00020000 08:07 263240 /lib/i386-linux-gnu/ld-2.19.sobfd18000-bfd39000 rw-p 00000000 00:00 0 [stack]
里面其他部分我们先不做探究,我们只看4个部分:
代码空间
堆
全局栈
局部栈
而这4个部分对应的是上面的哪些呢?可以自己写代码测试一下!
#include<unistd.h>#include<malloc.h>int a1;static int a2=2;const int a3=2;int add(int a,int b){ return a+b;}int main(){ int b1; static int b2=2; const int b3=2; int *p1 = (int *)malloc(4); printf("a1 %p\n",&a1); printf("a2 %p\n",&a2); printf("a3 %p\n",&a3); printf("b1 %p\n",&b1); printf("b2 %p\n",&b2); printf("b3 %p\n",&b3); printf("p1 %p\n",p1); printf("main %p\n",main); printf("f %p\n",add); printf("%d\n",getpid()); while(1); return 0;}
结果:
a1 0x804a034a2 0x804a028a3 0x8048620b1 0xbf919150b2 0x804a02cb3 0xbf919154p1 0x807a008main 0x804848af 0x804847d3633
我们看一下/proc/3633/maps:
08048000-08049000 r-xp 00000000 08:0a 8128164 /home/jiange/linuxprogram/day1/test208049000-0804a000 r--p 00000000 08:0a 8128164 /home/jiange/linuxprogram/day1/test20804a000-0804b000 rw-p 00001000 08:0a 8128164 /home/jiange/linuxprogram/day1/test20807a000-0809b000 rw-p 00000000 00:00 0 [heap]b75db000-b75dc000 rw-p 00000000 00:00 0 b75dc000-b7784000 r-xp 00000000 08:07 263264 /lib/i386-linux-gnu/libc-2.19.sob7784000-b7786000 r--p 001a8000 08:07 263264 /lib/i386-linux-gnu/libc-2.19.sob7786000-b7787000 rw-p 001aa000 08:07 263264 /lib/i386-linux-gnu/libc-2.19.sob7787000-b778a000 rw-p 00000000 00:00 0 b77a0000-b77a3000 rw-p 00000000 00:00 0 b77a3000-b77a5000 r--p 00000000 00:00 0 [vvar]b77a5000-b77a7000 r-xp 00000000 00:00 0 [vdso]b77a7000-b77c7000 r-xp 00000000 08:07 263240 /lib/i386-linux-gnu/ld-2.19.sob77c7000-b77c8000 r--p 0001f000 08:07 263240 /lib/i386-linux-gnu/ld-2.19.sob77c8000-b77c9000 rw-p 00020000 08:07 263240 /lib/i386-linux-gnu/ld-2.19.sobf8fa000-bf91b000 rw-p 00000000 00:00 0 [stack]
a3,main,add存放在08048000-08049000,为代码空间;(拥有x权限)
a1,a2,b2存放在0804a000-0804b000这一段,为全局栈;
p1存放在0807a000-0809b000,为堆;
b1,b3存放在bf8fa000-bf91b000,为局部栈;
于是可以知道:
全局的普通变量、静态变量放在全局栈中,全局 const变量放在代码区 。
局部普通变量 和const变量都放在局部栈中,static变量放在全局栈中。
malloc申请的空间放在堆中。
main函数 和 自己写的函数 都是放在代码区的。
我们的代码是怎么加载到代码空间的呢?
ldd ./test2 linux-gate.so.1 => (0xb77bd000) libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75f3000) /lib/ld-linux.so.2 (0xb77bf000)
上面有一个/lib/ld-linux.so.2,实际上,我们的程序就是通过它来加载到代码空间的!
所以我们想执行程序可以通过以下方式:
/lib/ld-linux.so.2 ./test2
3.堆和栈的不同
#include<unistd.h>#include <malloc.h>int main(int argc, const char *argv[]){ int a1 = 10; int a2 = 10; int a3 = 10; int *p1 = (int*)malloc(4); int *p2 = (int*)malloc(4); int *p3 = (int*)malloc(4); printf("a1 %p\n",&a1); printf("a2 %p\n",&a2); printf("a3 %p\n",&a3); printf("p1 %p\n",p1); printf("p2 %p\n",p2); printf("p3 %p\n",p3); printf("%d\n",getpid()); while(1); return 0;}
结果:
a1 0xbf8eab48a2 0xbf8eab4ca3 0xbf8eab50p1 0x937d008p2 0x937d018p3 0x937d028
奇怪的问题来了!a放在栈中,用了4个字节,p放在堆中,用了16个字节!那么,为什么堆多花去了12个字节呢?
我们做以下测试:
#include<malloc.h>int main(int argc, const char *argv[]){ int *p1 = malloc(4); int *p2 = malloc(4); int *p3 = malloc(4); *p1 = 1; *(p1+1) = 2; *(p1+2) = 3; *(p1+3) = 4; *(p1+4) = 5; *(p1+5) = 6; *(p1+6) = 7; *(p1+7) = 8; *(p1+8) = 10; *(p1+9) = 11; printf("%d\n",*p2); return 0;}结果:5
没有报错,结果也貌似正确,(p1+4)的地址刚好是p2的地址,答案是5。
我们在结束之前加上一句:
free(p1);
再次执行,程序崩溃了!!
原因:堆其实是由链表管理的,malloc 生成的空间,先存放本身的数据,接下来存放前一个元素的地址,再是后一个元素的地址,再后一个是数据本身的长度。而上面修改了p1+1, p1+2, p1+3,破坏了链表的结构,于是在free的时候,链表无法正确地连接起来!
4.new delete跟 malloc free的区别
new是用malloc实现的,delete是用free实现的,只是new 在生成时会初始化(基本类型默认值,UDT调用构造函数),delete在删除前会先清0(或者调用析构函数)。
new []与new: new[]会循环依次调用构造器,new只调用一次。
其他知识点
1.定位分配
char p[20];int *p1 = new(p) int;//p1定位分配到p的位置,c预言中对应的有realloc函数。
2.
c语言是弱类型语言,c++是强类型语言
int *p = malloc(4);
在c是对的,在c++是错的,c++一定要(int*)malloc(4);
gcc xxx.cpp 是按照c++ 编译的
gcc xxx.c 才会按照c编译
- 【Linux环境编程】内存管理初探
- Linux内存管理初探
- Linux内存管理初探
- Linux内存管理初探
- 初探linux内存管理
- Linux环境下C编程指南(第二版) -- 内存管理
- linux网络编程-内存管理
- 内存管理初探
- 初探内存管理
- 初探 iOS 内存管理
- Linux C的开发环境 Linux下C++编程初探
- 【Linux环境编程】内存管理:函数栈空间,虚拟内存及其分配
- linux内存模型初探
- 初探JAVA的内存管理
- 【初探】内存管理知多少
- linux高级编程01 内存管理
- [Linux高级编程]内存管理&映射&分配
- 【linux编程】C++内存管理详解
- Deep Learning with Torch
- git常用命令
- 呃呃呃
- 【内存】内存泄露
- 要想富,找好友
- 【Linux环境编程】内存管理初探
- 非root用户启动tomcat遇到的环境变量问题
- hdu 2918 Tobo or not Tobo(IDA*算法)
- C内存对齐
- 2015 伦敦最美酒吧
- 【黑马程序员】C语言基础-C语言语法
- js正则表达式之$1$2$3$4$5$6$7$8$9属性,返回子匹配的结果
- northwind数据库导出为excel文件下载
- Java集合综述