【Linux环境编程】内存管理初探

一.内存管理层次

硬件层次:内存结构管理

内核层次:内存映射,堆扩展

语言层次: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?

  1. 使用命令”ps aue“ 可查看当前正在执行的所有进程的信息。

  2. 在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/test2
08049000-0804a000 r--p 00000000 08:0a 8128164    /home/jiange/linuxprogram/day1/test2
0804a000-0804b000 rw-p 00001000 08:0a 8128164    /home/jiange/linuxprogram/day1/test2
b75a6000-b75a7000 rw-p 00000000 00:00 0 
b75a7000-b774f000 r-xp 00000000 08:07 263264     /lib/i386-linux-gnu/libc-2.19.so
b774f000-b7751000 r--p 001a8000 08:07 263264     /lib/i386-linux-gnu/libc-2.19.so
b7751000-b7752000 rw-p 001aa000 08:07 263264     /lib/i386-linux-gnu/libc-2.19.so
b7752000-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.so b7792000-b7793000 r--p 0001f000 08:07 263240 /lib/i386-linux-gnu/ld-2.19.so b7793000-b7794000 rw-p 00020000 08:07 263240 /lib/i386-linux-gnu/ld-2.19.so bfd18000-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 0x804a034
a2 0x804a028
a3 0x8048620
b1 0xbf919150
b2 0x804a02c
b3 0xbf919154
p1 0x807a008
main 0x804848a
f 0x804847d
3633

我们看一下/proc/3633/maps:

08048000-08049000 r-xp 00000000 08:0a 8128164    /home/jiange/linuxprogram/day1/test2
08049000-0804a000 r--p 00000000 08:0a 8128164    /home/jiange/linuxprogram/day1/test2
0804a000-0804b000 rw-p 00001000 08:0a 8128164    /home/jiange/linuxprogram/day1/test2
0807a000-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.so
b7784000-b7786000 r--p 001a8000 08:07 263264     /lib/i386-linux-gnu/libc-2.19.so
b7786000-b7787000 rw-p 001aa000 08:07 263264     /lib/i386-linux-gnu/libc-2.19.so
b7787000-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.so
b77c7000-b77c8000 r--p 0001f000 08:07 263240     /lib/i386-linux-gnu/ld-2.19.so
b77c8000-b77c9000 rw-p 00020000 08:07 263240     /lib/i386-linux-gnu/ld-2.19.so
bf8fa000-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 0xbf8eab48
a2 0xbf8eab4c
a3 0xbf8eab50
p1 0x937d008
p2 0x937d018
p3 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,C语言,内存管理)