内存申请相关函数

操作系统提供了相关的系统调用来完成相关工作。

对heap的操作,操作系统提供了brk()函数,C运行时库提供了sbrk()函数。

对mmap映射区域的操作,操作系统提供了mmap()和munmap()函数。

这里要提到一个很重要的概念,内存的延迟分配,只有在真正访问一个地址的时候才建立这个地址的物理映射,这是 Linux 内存管理的基本思想之一。Linux 内核在用户申请内存的时候,只是给它分配了一个线性区(也就是虚拟内存),并没有分配实际物理内存;只有当用户使用这块内存的时候,内核才会分配具体的物理页面给用户,这时候才占用宝贵的物理内存。内核释放物理页面是通过释放线性区,找到其所对应的物理页面,将其全部释放的过程。

一、heap 操作相关函数

Heap 操作函数主要有两个,brk() 为系统调用,sbrk() 为 C 库函数。系统调用通常提供一种最小功能,而库函数通常提供比较复杂的功能。Glibc 的 malloc 函数族(realloc,calloc等)就调用 sbrk() 函数将数据段的下界移动,sbrk() 函数在内核的管理下将虚拟地址空间映射到内存,供 malloc() 函数使用。

内核数据结构 mm_struct 中的成员变量 start_code 和 end_code 是进程代码段的起始和终止地址,start_data 和 end_data是进程数据段的起始和终止地址,start_stack是进程堆栈段起始地址,start_brk是进程动态内存分配起始地址(堆的起始地址),还有一个 brk(堆的当前最后地址),就是动态内存分配当前的终止地址。C语言的动态内存分配基本函数是 malloc(),在 Linux 上的实现是通过内核的 brk 系统调用。brk() 是一个非常简单的系统调用,只是简单地改变 mm_struct 结构的成员变量brk的值。

1
2
3
#include <unistd.h>
int brk(void *addr);
void *sbrk(intptr_t increment);

需要说明的是,但 sbrk() 的参数 increment 为 0 时,sbrk() 返回的是进程的当前 brk 值,increment 为正数时扩展 brk 值,当increment 为负值时收缩 brk 值。

二、mmap 映射区域操作相关函数

mmap() 函数将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。munmap 执行相反的操作,删除特定地址区域的对象映射。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);

start:映射区的开始地址。
length:映射区的长度。

prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起。
Ptmalloc 中主要使用了如下的几个标志:
PROT_EXEC //页内容可以被执行,ptmalloc中没有使用
PROT_READ //页内容可以被读取,ptmalloc直接用mmap分配内存并立即返回给用户时设置该标志
PROT_WRITE //页可以被写入,ptmalloc直接用mmap分配内存并立即返回给用户时设置该标志
PROT_NONE //页不可访问,ptmalloc用mmap向系统“批发”一块内存进行管理时设置该标志

flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体
MAP_FIXED //使用指定的映射起始地址,如果由start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。Ptmalloc在回收从系统中“批发”的内存时设置该标志。
MAP_PRIVATE //建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能使用其中一个。Ptmalloc每次调用mmap都设置该标志。
MAP_NORESERVE //不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。当交换空间不被保留,同时内存不足,对映射区的修改会引起段违例信号。Ptmalloc向系统“批发”内存块时设置该标志。
MAP_ANONYMOUS //匿名映射,映射区不与任何文件关联。Ptmalloc每次调用mmap都设置该标志。

fd:有效的文件描述词。如果MAP_ANONYMOUS被设定,为了兼容问题,其值应为-1。
offset:被映射对象内容的起点。