Linux内存管理

Posted by Run-dream Blog on June 9, 2021

内存管理

内存管理

  • 物理内存 只有内存管理模块可以操作

  • 虚拟地址 每个进程看到的是独立的、互不干扰的虚拟地址空间

    • 内核空间 在高地址 内核 同一个内核空间
    • 用户空间 在低地址 普通进程 独占整个空间
  • 用户态内存构成

    • Text Segment 存放二进制可执行代码
    • Data Segment 存放静态常量
    • BSS Segment 未初始化的静态变量
    • 堆(Heap)段 动态分配内存
    • Memory Mapping Segment 文件映射进内存
    • 栈(Stack)地址段 函数调用的函数栈
  • 可以查看进程内存空间的布局的命令

    cat /proc/<pid>/map
    pmap <pid>
    
  • 物理内存和虚拟地址映射

    • 分段机制 Linux中段只被用于访问控制和内存保护。

    分段机制

    段选择子和段内偏移量,段基地址加上段内偏移量得到物理内存地址。

    • 分页机制 Linux 主要使用分页的机制 多级页表

    分页机制

    • 段页机制

进程空间管理

  • 32位

    32位

  • 64位

64位

直接映射区: 这一块空间是连续的,和物理内存是非常简单的映射关系

vmalloc: 内核动态映射空间 类似用户空间的malloc

持久内核映射: 对应物理内存的高端内存

固定映射区域: 满足特殊需求

查看虚拟内存和物理内存的映射关系

cat /proc/pid/pagemap
cat /proc/kpagecount
cat /proc/kpageflags
cat /proc/kpagecgroup

物理内存

组织方式

  • 平坦内存模型(Flat Memory Model)从 0 开始对物理页编号
  • 对称多处理器 (Symmetric multiprocessing) 所有的 CPU 访问内存都要过总线 总线会成为瓶颈
  • 非一致内存访问 (Non-uniform memory access) NUMA

NUMA 详情

物理内存

当前的主流场景。

内存不是一整块。每个 CPU 都有自己的本地内存,CPU 访问本地内存不用过总线,在本地内存不足的情况下,每个 CPU 都可以去另外的 NUMA 节点申请内存。

  • 节点

  • 区域

    • DMA 直接内存存取

      CPU 只需向 DMA 控制器下达指令,让 DMA 控制器来处理数据的传送,数据传送完毕再把信息反馈给 CPU,这样就可以解放 CPU

    • 直接映射区

    • 高端内存区

    • 可移动区域 避免内存碎片

  • 页 物理内存的基本单位

    分配方式:

    • 整页分配
    • 将页划分成多个小块的存储池
      • slab 利用队列
      • slub 使用队列
      • slob 嵌入式

    伙伴系统(buddy)

    Linux 中的内存管理的“页”大小为 4KB。把所有的空闲页分组为 11 个页块链表,每个块链表分别包含很多个大小的页块,有 1、2、4、8、16、32、64、128、256、512 和 1024 个连续页的页块。

    每次分配内存的时候,优先从对应大小的页块链表去找空间的页块,如果找不到,则去更大的页块中去找页块,并将其拆分到可以分配,将未分配的加入到新的链表中去。

  • kswapd 负责物理页面的换入换出 内核线程,在系统初始化的时候就被创建

  • Slub Allocator 将从伙伴系统申请的大内存块切成小块,分配给其他系统。

    • 缓存
      • fast path 对应 struct kmem_cache 中的 kmem_cache_cpu
      • slow path 对应 struct kmem_cache 中的 kmem_cache_node

内存分配

用户态内存映射

  1. 用户态内存映射函数 mmap,包括用它来做匿名映射和文件映射。
  2. 用户态的页表结构,存储位置在 mm_struct 中。
  3. 在用户态访问没有映射的内存会引发缺页异常,分配物理页表、补齐页表。如果是匿名映射则分配物理内存;如果是 swap,则将 swap 文件读入;如果是文件映射,则将文件读入。

内核态内存映射

  1. 内核态内存映射函数 vmalloc、kmap_atomic

    kmap_atomic 没有页表的时候,就直接创建页表进行映射

    vmalloc 它只分配了内核的虚拟地址 访问它的时候,会产生缺页异常

  2. 内核态页表是放在 init_top_pgt,定义在 __INITDATA里面

  3. 内核态缺页异常

    do_page_fault -> vmalloc_fault