Linux内存

内存映射

只有内核可以访问物理内存
内核给每个进程提供独立虚拟地址空间,虚拟地址空间连续,进程通过访问虚拟地址空间访问内存
虚拟地址空间分为内核空间和用户空间
Linux内存分布
进程在用户态只能访问用户空间内存,只有进入内核态后,才能访问内核空间内存.
每个进程的地址空间都包含内核空间,但各个进程的内核空间其实关联的是相同的物理内存,当进程切换到内核态后,可以很方便地访问内核空间内存.
虚拟内存通过内存映射来管理,只有实际使用的虚拟内存才会分配物理内存.
内存映射将虚拟内存地址映射到物理内存地址,内核为每个进程维护一张页表,记录虚拟内存与物理地址的映射关系
linux虚拟内存和物理内存的映射
页面实际存储在CPU的内存管理单元MMU
当进程直接访问的虚拟内存在页表中查不到时,系统产生缺页异常,进入内核空间分配物理内存,更新进程页表,最后返回用户空间,恢复进程运行.
TLB是MMU中页表的高速缓存,由于进程的虚拟内存地址独立,TLB的访问速度比MMU快,通过减少进程的上下文切换,减少TLB的刷新次数,提供TLB缓存的使用率,提高CPU访问内存的性能.
MMU以页(通常为4K)为内存映射的最小单位,每次内存映射都需要关联4KB或4KB整数倍的内存空间.
页的大小只有4K,导致页表非常大,Linux使用多级页表和大页解决这个问题.
多级页表:把内存分为区块来管理,将原来的映射关系改为区块索引和区块的偏移.
Linux使用四级页表管理内存页.虚拟内存分为5个部分
linux多级页表
大页:比普通页更大的内存块,常见大小2MB和1GB.大页使用在大量内存的进程上,比如Oracle,DPDK等.

虚拟内存空间分布

虚拟内存空间用户空间内存,从低到高分别是五种不同的内存段

  1. 只读段,包括代码和常量等
  2. 数据段,包括全局变量等
  3. 堆,包括动态分配的内存,从低地址开始向上增长
  4. 文件映射段,包括动态库,共享内存等,从高地址向低地址向下增长
  5. 栈,包括局部变量和函数调用上下文等.栈的大小是固定的,一般为8MB,从高地址向低地址向下增长

堆和文件映射段的内存是动态分配的.

内存分配与回收

malloc()是C标准库提供的内存分配函数,对应系统调用brk()和mmap().
小块内存(小于128K)用brk()分配,通过移动堆顶的位置来分配内存,内存释放后不立即归还系统,先缓存起来,重复利用.
大块内存(大于128K)用mmap()分配,直接用内存映射mmap()来分配,在文件映射段找一块空闲分配出来.
brk()方式缓存可以减少缺页异常的发生,提高内存访问效率.由于内存未归还系统,在内存工作繁忙时,频繁的内存分配和释放会造成内存碎片.
mmap()方式释放直接归还系统,但mmap()都会发生缺页异常,内存工作繁忙时,频繁的内存分配会导致大量的缺页异常,使内核的管理负担增大.
当brk()和mmap()被调用时,其实并没有分配内存,只有首次访问这些内存时才分配,即通过缺页异常进入内核,再有内核来分配内存.
Linux使用伙伴系统管理内存.伙伴系统以页为单位管理内存,并通过相邻页的合并减少内存碎片.
Linux内核通过slab分配器管理小内存,主要作用是分配和释放内核中的小对象.
内存只分配不释放会造成内存泄漏,甚至耗尽系统内存,在应用程序使用完内存后,需要调用free()或unmap()释放不用的内存.

系统在发现内存紧张时会通过一系列机制回收内存:

  1. 回收缓存
  2. 回收不常使用内存,把不常使用的内存通过交换分区(Swap)写到磁盘.Swap是把一块磁盘当作内存使用,把进程暂时不用的数据写入磁盘(换出),当进程访问这些内存时,再从磁盘读取数据到内存(换入).Swap使系统可用内存变大了,只有内存不足时才会发生Swap交换,由于磁盘读写速度远慢于内存,Swap会导致严重内存性能问题.
  3. 杀死进程,内存紧张时系统通过OOM(Out of Memory)直接杀死占用大量内存的进程.OOM是一种内核保护机制,监控进程内存使用情况,使用oom_soure为每个进程内存使用情况评分. 1. 进程消耗内存越大,oom_soure越大 2. 进程运行占用CPU越多,oom_soure就越小. oom_soure越大表示占用内存越多,越容易被OOM杀死.进程的oom_soure可以在/proc/PID/oom_soure进行查看,可以手动修改/proc/PId/oom_adj进行调整,oom_adj范围为[-17,15],-17表示禁用OOM.
    1
    2
    3
    4
    5
    6
    7
    8
    $ ll /proc/15571/oom_*
    -rw-r--r-- 1 root root 0 Feb 26 13:20 /proc/15571/oom_adj
    -r--r--r-- 1 root root 0 Feb 26 13:20 /proc/15571/oom_score
    -rw-r--r-- 1 root root 0 Feb 26 13:20 /proc/15571/oom_score_adj
    $ cat /proc/15571/oom_*
    0
    16
    0

Linux内存管理
Linux内存管理2
缺页处理程序流程图
缺页异常处理程序的总体方案

参考资料