【C++ Primer】 命名空间

发布于:2023-01-04 ⋅ 阅读:(329) ⋅ 点赞:(0)

在这里插入图片描述

凑字数

用户态

在这里插入图片描述
内存映射 : 物理内存和虚拟内存之间、 文件中的内容映射到虚拟内存空间。

  • 申请小块内存用 brk; 申请大块内存或文件映射用 mmap

  • mmap:映射文件, 由 fd 得到 struct file

    • 调用 …->do_mmap
      • 调用 get_unmapped_area 找到一个可以进行映射的 vm_area_struct
      • 调用 mmap_region 进行映射
    • get_unmapped_area
      • 匿名映射: 找到前一个 vm_area_struct
      • 文件映射: 调用 file 中 file_operations 文件的相关操作, 最终也会调用到 get_unmapped_area
    • mmap_region
      • 通过 vm_area_struct 判断, 能否基于现有的块扩展(调用 vma_merge)
      • 若不能, 调用 kmem_cache_alloc 在 slub 中得到一个 vm_area_struct 并进行设置
      • 若是文件映射: 则调用 file_operations 的 mmap 将 vm_area_struct 的内存操作设置为文件系统对应操作(读写内存就是读写文件系统)
      • 通过 vma_link 将 vm_area_struct 插入红黑树
      • 若是文件映射, 调用 __vma_link_file 建立文件到内存的反映射
  • 内存管理不直接分配内存, 在使用时才分配

  • 用户态缺页异常, 触发缺页中断, 调用 do_page_default

  • __do_page_fault 判断中断是否发生在内核

    • 若发生在内核, 调用 vmalloc_fault, 使用内核页表进行映射
    • 若不是, 找到对应 vm_area_struct 调用 handle_mm_fault
    • 得到多级页表地址 pgd 等
    • pgd 存在 task_struct.mm_struct.pgd 中

四级页表
在这里插入图片描述

  • 全局页目录项 pgd 在创建进程 task_struct 时创建并初始化, 会调用 pgd_ctor 拷贝内核页表到进程的页表

  • 进程被调度运行时, 通过 switch_mm_irqs_off->load_new_mm_cr3 切换内存上下文

  • cr3 是 cpu 寄存器, 存储进程 pgd 的物理地址(load_new_mm_cr3 加载时通过直接内存映射进行转换)

  • cpu 访问进程虚拟内存时, 从 cr3 得到 pgd 页表, 最后得到进程访问的物理地址

  • 进程地址转换发生在用户态, 缺页时才进入内核态(调用__handle_mm_fault)

  • __handle_mm_fault 调用 pud_alloc, pmd_alloc, handle_pte_fault 分配页表项

    • 若不存在 PTE (页表项,新映射的页)
      • 匿名页: 调用 do_anonymous_page 分配物理页 ①
      • 文件映射: 调用 do_fault ②
    • 若存在 pte, 调用 do_swap_page 换入内存 ③
    • ① 为匿名页分配内存
      • 调用 pte_alloc 分配 pte 页表项
      • 调用 …->__alloc_pages_nodemask 分配物理页
      • mk_pte 页表项指向物理页; set_pte_at 插入页表项
    • ② 为文件映射分配内存 __do_fault
      • 以 ext4 为例, 调用 ext4_file_fault->filemap_fault
      • 文件映射一般有物理页作为缓存 find_get_page 找缓存页
      • 若有缓存页, 调用函数预读数据到内存
      • 若无缓存页, 调用 page_cache_read 分配一个, 加入 lru 队列, 调用 readpage 读数据: 调用 kmap_atomic 将物理内存映射到内核临时映射空间, 由内核读取文件, 再调用 kunmap_atomic 解映射
    • ③ do_swap_page 如果长时间不用 就要换出磁盘 , 也就是swap
      • 先检查对应 swap 有没有缓存页
      • 没有, 读入 swap 文件(也是调用 readpage)
      • 调用 mk_pte(创建页表项); set_pet_at(将页表项插入页表); swap_free(清理 swap)
        在这里插入图片描述
  • 避免每次都需要经过页表(存再内存中)访问内存

    • (TLB :快表 ,专门用来做地址映射的硬件设备,提高映射速度)
    • TLB就是页表的cache,存储了部分页表项,相对于是一个副本
    • 一般操作系统先查TLB,如果查不到 , 才会到内存中查询页表.
      在这里插入图片描述
      用户态的内存映射机制包含以下几个部分。

用户态内存映射函数 mmap,包括用它来做匿名映射和文件映射。

用户态的页表结构,存储位置在 mm_struct 中。

在用户态访问没有映射的内存会引发缺页异常,分配物理页表、补齐页表。如果是匿名映射则分配物理内存;如果是 swap,则将 swap 文件读入;如果是文件映射,则将文件读入。

内核态

涉及三块内容:

- 内存映射函数 vmalloc, kmap_atomic
- 内核态页表存放位置和工作流程
- 内核态缺页异常处理

内核态页表

在这里插入图片描述

  • 内核态页表, 系统初始化时就创建
    • swapper_pg_dir 指向内核顶级页目录 pgd
      • xxx_ident/kernel/fixmap_pgt 分别是直接映射/内核代码/固定映射的xxx级页表目录
    • 创建内核态页表
      - swapper_pg_dir 指向 init_top_pgt, 是 ELF 文件的全局变量, 因此再内存管理初始化之间就存在
      • init_top_pgt 先初始化了三项
        • 第一项指向 level3_ident_pgt (内核代码段的某个虚拟地址) 减去 __START_KERNEL_MAP (内核代码起始虚拟地址) 得到实际物理地址
        • 第二项也是指向 level3_ident_pgt
        • 第三项指向 level3_kernel_pgt 内核代码区
    • 初始化各页表项, 指向下一集目录
      • 页表覆盖范围较小, 内核代码 512MB, 直接映射区 1GB
      • 内核态也定义 mm_struct 指向 swapper_pg_dir
      • 初始化内核态页表, start_kernel setup_arch
        • load_cr3(swapper_pg_dir) 并刷新 TLB
        • 调用 init_mem_mappingkernel_physical_mapping_init, 用 __va 将物理地址映射到虚拟地址, 再创建映射页表项
        • CPU 在保护模式下访问虚拟地址都必须通过 cr3, 系统只能照做
        • load_cr3 之前, 通过 early_top_pgt 完成映射

vmalloc 和 kmap_atomic

  • 内核的虚拟地址空间 vmalloc 区域用于内存映射
    - kmap_atomic 临时映射
    • 32 位, 调用 set_pte 通过内核页表临时映射
    • 64 位, 调用 page_address→lowmem_page_address 进行映射

内核态缺页异常 (没有页表的时候)

- kmap_atomic 直接创建页表进行映射
- vmalloc 只分配内核虚拟地址, 访问时触发缺页中断, 调用 do_page_fault→vmalloc_fault 用于关联内核页表项
  • kmem_cache 和 kmalloc 用于保存内核数据结构, 不会被换出; 而内核 vmalloc 会被换出
    在这里插入图片描述