1. mmap和munmap
#include <sys/mman.h>
void *mmap(void *start,size_t length, int prot, int flags,int fd, off_t offset);
int munmap(void *start,size_t length);
  mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,后一个页不被使用的空间将会清零。mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。
  munmap用来取消参数start所指的映射内存起始地址,参数length则是欲取消的内存大小。当进程结束或利用exec相关函数来执行其他程序时,映射内存会自动解除,但关闭对应的文件描述词时不会解除映射。返回值如果解除映射成功则返回0,否则返回-1,错误原因存于errno中错误代码EINVAL
  2. malloc和free
  虽然可以使用低级的mmap和munmap函数来创建和删除虚拟存取器的区域,但是C程序员还是觉得用动态存储器分配器(dynamic memory allocator)更方便,也有更好的移植性。
  动态存取器分配器维护着一个进程的虚拟存储器区域,称为堆(heap)。分配器将堆视为一组不同大小的块(block)的集合来维护。每个块是一个连续的虚拟存储器片(chunk),要么是已经分配的,要么是空闲的。
  分配器有两种基本风格。两者风格都要求应用显示分配块,他们的不同之处在于由两个实体来负责释放已分配的块。显示分配器(explicit alloctor),要求应用显示地释放任何已分配的块。隐式分配器(implicit allocator),又称作垃圾收集器(garbage collector),分配器检测一个已分配块何时不再被程序所使用,那么释放这个块。
  malloc遇到问题(例如,程序请求的存储器块比可用的虚拟存储器还要大),那么他返回NULL,并设置errno。malloc不初始化它返回的存储器。如果想要已初始化的动态存储器的应用程序可用使用calloc,calloc是一个基于malloc的瘦包装函数,它将分配的存储器初始化为零。想要改变一个以前已分配的大小,可用使用realloc函数。
  动态存储器分配器,例如malloc,可用通过使用mmap和munmap函数,显示地分配和释放堆存储器,或者还可以使用sbrk函数(sbrk控制内核中brk指针实现heap的增缩),如果成功,sbrk返回brk的旧值,否则返回-1,并将errno设置为ENOMEM。
  Free函数释放已分配的堆块。在C程序的上下文中,应用调用malloc,但没调用Free,垃圾收集器识别垃圾块,并相应地调用free,将这些块放回到空闲链表中。
  3. 垃圾收集器的基本知识
  垃圾收集器将存储器视为一张有向可达图(reachability graph),图中节点被分成一组根节点和一组堆节点。每个堆节点对应于堆中的一个已分配块。有向边p->q意味着块p中的某个位置指向块q某个位置。根节点对应于这样一种不在堆中的位置,他们包含指向堆的指针。这些位置可以是寄存器、栈里的变量,或是虚拟存储器中读写数据区内的全局变量。垃圾收集器的角色是维护可达图的某种表示,并通过释放不可达节点,将他们返回给空闲链表,来定期地回收他们。
  像ML和Java这样的语言的垃圾收集器,对应用如何创建和使用指针有很严格的控制,能够维护可达图的一种精确的表示,因此也能够回收所有的垃圾。然而,诸如C和C++这样的语言的收集器通常不能维护可达图的精确表示。这样的收集器也叫做保守的垃圾收集器(conservative garbage collector)。从某种意义上来说他们是保守的,即每个可达快都被正确的标记为可达了,而一些不可达的块却可能被错误的标记为可达。
  无论何时需要堆空间,应用都会用通常的方式调用malloc。如果malloc找不到一个合适的空闲块,那么它调用垃圾收集器,希望能收到一些垃圾到空闲链表中。收集器识别垃圾块,并通过调用Free函数将他们返回给堆。