让我们看下虚拟内存:第一层理解
  1. 每个进程都有自己独立的4G内存空间,各个进程的内存空间具有类似的结构
  2. 一个新进程建立的时候,将会建立起自己的内存空间,此进程的数据,代码等从磁盘拷贝到自己的进程空间,哪些数据在哪里,都由进程控制表中的task_struct记录,task_struct中记录中一条链表,记录中内存空间的分配情况,哪些地址有数据,哪些地址无数据,哪些可读,哪些可写,都可以通过这个链表记录
  3. 每个进程已经分配的内存空间,都与对应的磁盘空间映射

linux虚拟内存与物理内存映射(Linux虚拟内存和物理内存的理解)(1)

  1. 计算机明明没有那么多内存(n个进程的话就需要n*4G)内存
  2. 建立一个进程,就要把磁盘上的程序文件拷贝到进程对应的内存中去,对于一个程序对应的多个进程这种情况,浪费内存!
第二层理解
  1. 每个进程的4G内存空间只是虚拟内存空间,每次访问内存空间的某个地址,都需要把地址翻译为实际物理内存地址
  2. 所有进程共享同一物理内存,每个进程只把自己目前需要的虚拟内存空间映射并存储到物理内存上。
  3. 进程要知道哪些内存地址上的数据在物理内存上,哪些不在,还有在物理内存上的哪里,需要用页表来记录
  4. 页表的每一个表项分两部分,第一部分记录此页是否在物理内存上,第二部分记录物理内存页的地址(如果在的话)
  5. 当进程访问某个虚拟地址,去看页表,如果发现对应的数据不在物理内存中,则缺页异常
  6. 缺页异常的处理过程,就是把进程需要的数据从磁盘上拷贝到物理内存中,如果内存已经满了,没有空地方了,那就找一个页覆盖,当然如果被覆盖的页曾经被修改过,需要将此页写回磁盘

linux虚拟内存与物理内存映射(Linux虚拟内存和物理内存的理解)(2)

总结:
  1. 既然每个进程的内存空间都是一致而且固定的,所以链接器在链接可执行文件时,可以设定内存地址,而不用去管这些数据最终实际的内存地址,这是有独立内存空间的好处
  2. 当不同的进程使用同样的代码时,比如库文件中的代码,物理内存中可以只存储一份这样的代码,不同的进程只需要把自己的虚拟内存映射过去就可以了,节省内存
  3. 在程序需要分配连续的内存空间的时候,只需要在虚拟内存空间分配连续空间,而不需要实际物理内存的连续空间,可以利用碎片。
补充理解:

linux虚拟内存与物理内存映射(Linux虚拟内存和物理内存的理解)(3)

接下来讨论下物理内存:物理内存的内核映射
  1. 高端内存不能全部映射到内核空间,也就是说这些物理内存没有对应的线性地址。不过,内核为每个物理页框都分配了对应的页框描述符,所有的页框描述符都保存在mem_map数组中,因此每个页框描述符的线性地址都是固定存在的。内核此时可以使用alloc_pages()和alloc_page()来分配高端内存,因为这些函数返回页框描述符的线性地址。
  2. 内核地址空间的后128MB专门用于映射高端内存,否则,没有线性地址的高端内存不能被内核所访问。这些高端内存的内核映射显然是暂时映射的,否则也只能映射128MB的高端内存。当内核需要访问高端内存时就临时在这个区域进行地址映射,使用完毕之后再用来进行其他高端内存的映射。

linux虚拟内存与物理内存映射(Linux虚拟内存和物理内存的理解)(4)

物理内存管理机制

伙伴算法

per-CPU页框高速缓存

slab缓存

vmalloc机制

物理内存的分配分区页框分配器

linux虚拟内存与物理内存映射(Linux虚拟内存和物理内存的理解)(5)

linux虚拟内存与物理内存映射(Linux虚拟内存和物理内存的理解)(6)

slab分配器

linux虚拟内存与物理内存映射(Linux虚拟内存和物理内存的理解)(7)

非连续内存区内存的分配
  1. 寻找一个新的连续线性地址空间;
  2. 依次分配一组非连续的页框;
  3. 为线性地址空间和非连续页框建立映射关系,即修改内核页表;

linux虚拟内存与物理内存映射(Linux虚拟内存和物理内存的理解)(8)

,