操作系统提供了相关的系统调用来完成相关工作。
对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 | #include <unistd.h> |
需要说明的是,但 sbrk()
的参数 increment
为 0 时,sbrk()
返回的是进程的当前 brk 值,increment 为正数时扩展 brk 值,当increment 为负值时收缩 brk 值。
二、mmap 映射区域操作相关函数
mmap() 函数将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。munmap 执行相反的操作,删除特定地址区域的对象映射。
1 | #include <sys/mman.h> |