malloc的系统调用

TL;DR

  • 在读这篇文章的时候你应该知道c语言的malloc是使用系统调用从操作系统申请内存的,这两个系统调用分别是brkmmap

    /prco/$pid/maps文件

  • 先来简单看下这个文件,这个文件会显示整个进程的地址空间分布情况。

    brk

  • brk从内核获取内存是通过增加程序中断地址方式的,开始于start_brk,结束于brk,初始的时候两者都指向的是同一个位置。
  • 当ASLR关闭的时候,start_brk和brk都是指向bss段的尾部的
  • 当ASLR开启的时候,start_brk和brk初始位置是bss段的尾部加一个随机的偏移。

    nice pic,a ha :D
  • 如上图虚拟内存地址空间分布图所示,start_brk即是堆空间的开始,brk即是堆空间的结束。
  • !注意,后面的测试均是关闭ASLR后测试输出,此外测试系统使用的是ubuntu14.04.1X64操作系统。

    1
    2
    关闭ASLR的方法
    #echo 0 > /proc/sys/kernel/randomize_va_space
  • 示例代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    /* sbrk and brk example */
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    int main()
    {
    void *curr_brk, *tmp_brk = NULL;
    printf("Welcome to sbrk example:%d\n", getpid());
    /* sbrk(0) gives current program break location */
    tmp_brk = curr_brk = sbrk(0);
    printf("Program Break Location1:%p\n", curr_brk);
    getchar();
    /* brk(addr) increments/decrements program break location */
    brk(curr_brk+4096);
    curr_brk = sbrk(0);
    printf("Program break Location2:%p\n", curr_brk);
    getchar();
    brk(tmp_brk);
    curr_brk = sbrk(0);
    printf("Program Break Location3:%p\n", curr_brk);
    getchar();
    return 0;
    }
  • 输出分析:

  • 在brk之前,我们可以看到进程中是没有堆块的,因此 start_brk=brk=end_data=0x602000
  • 当程序执行brk调用的时候,如下的输出,我们看到有了堆块,因此 start_brk=end_data=0x602000,此时
  • start_brk=end_data=0x602000
  • brk = 0x603000
  • 解释一下新增加的一行:这里602000-603000就是动态分配的地址空间,对应的操作权限标志是rw-p即可读可写不可执行,私有的
  • 000000的文件偏移是因为没有映射任何的文件,00:00 是主要/次要的设备数-由于没有映射任何文件,所以这里也是0,最后的0是inode number,还是由于没有映射任何的文件,所以这里也还是0.

    mmap

  • malloc使用mmap来创建一个私有的匿名映射块,私有的匿名映射块主要的目的就是分配新的内存。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    /* Private anonymous mapping example using mmap syscall */
    #include <stdio.h>
    #include <sys/mman.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdlib.h>
    void static inline errExit(const char* msg)
    {
    printf("%s failed. Exiting the process\n", msg);
    exit(-1);
    }
    int main()
    {
    int ret = -1;
    printf("Welcome to private anonymous mapping example::PID:%d\n", getpid());
    printf("Before mmap\n");
    getchar();
    char* addr = NULL;
    addr = mmap(NULL, (size_t)132*1024, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (addr == MAP_FAILED)
    errExit("mmap");
    printf("After mmap\n");
    getchar();
    /* Unmap mapped region. */
    ret = munmap(addr, (size_t)132*1024);
    if(ret == -1)
    errExit("munmap");
    printf("After munmap\n");
    getchar();
    return 0;
    }
  • 输出分析:

  • 在使用mmap之前:我们注意下面的一个内存地址段
  • 当我们通过mmap申请了132KB的空间的时候,看到如下图

    b7df0000-b7e12000 其中这段虚拟内存地址就包含了我们刚刚申请到132KB大小的地址空间
  • 其中b7df0000-b7e12000 是这个块的地址范围,对应的操作权限标志是rw-p即可读可写不可执行,私有的
  • 000000的文件偏移是因为没有映射任何的文件,00:00 是主要/次要的设备数-由于没有映射任何文件,所以这里也是0,最后的0是inode number,还是由于没有映射任何的文件,所以这里也还是0.
  • 当munmap之后,下面的输出我们可以看的到申请的内存被释放(变成原来的b7e11000-b7e12000),又交还给了操作系统。

    其它

  • 在64位上brk和32的结果是相同的,但是mmap的会有不同,原因暂时不明。

    reference

  • Syscalls used by malloc.