监控内存(一):Linux 内存怎么工作
内存映射
同 CPU 管理一样,内存管理也是操作系统最核心的功能之一。内存主要用来存储系统和应用程序的指令、数据、缓存等。
只有内核才可以直接访问物理内存。那么,进程要访问内存时,该怎么办呢?
Linux 内核给每个进程都提供了一个独立的虚拟地址空间,并且这个地址空间是连续的。这样,进程就可以很方便地访问内存,更确切地说是访问虚拟内存。
虚拟地址空间的内部又被分为内核空间和用户空间两部分,不同字长(也就是单个 CPU 指令可以处理数据的最大长度)的处理器,地址空间的范围也不同
image.png进程在用户态时,只能访问用户空间内存;只有进入内核态后,才可以访问内核空间内存。虽然每个进程的地址空间都包含了内核空间,但这些内核空间,其实关联的都是相同的物理内存。这样,进程切换到内核态后,就可以很方便地访问内核空间内存。
内存映射,其实就是将虚拟内存地址映射到物理内存地址。为了完成内存映射,内核为每个进程都维护了一张页表,记录虚拟地址与物理地址的映射关系。
image.pngMMU 和 TLB
一般来说,任何进程切换都会暗示着更换活动页表集,
Linux内核为每一个进程维护一个task_struct
结构体(即进程描述符PCB),task_struct->mm_struct
结构体成员用来保存该进程的页表。在进程切换的过程中,内核把新的页表的地址写入CR3
控制寄存器。
MMU是CPU的一部分,每个处理器core都有一个MMU,包含:
- TLB:是页表的高速缓存,存储着最近转化的一些目录项
- Table Walk Unit:负责从页表中读取虚拟地址对应的物理地址
对于每次转换,MMU首先在TLB中检查现有的缓存。如果没有命中,根据CR3寄存器,Table Walk Unit将从内存中的页表查询。
伙伴系统
内存分配与回收
malloc()
是 C 标准库提供的内存分配函数,对应到系统调用上,有两种实现方式,即 brk()
和 mmap()
。
- 对小块内存(小于 128K),C 标准库使用
brk()
来分配,也就是通过移动堆顶的位置来分配内存。这些内存释放后并不会立刻归还系统,而是被缓存起来,这样就可以重复使用。 - 而大块内存(大于 128K),则直接使用内存映射
mmap()
来分配,也就是在文件映射段找一块空闲内存分配出去。
当这两种调用发生后,其实并没有真正分配内存。这些内存,都只在首次访问时才分配,也就是通过缺页异常进入内核中,再由内核来分配内存。
参考资料
1、https://www.zhihu.com/question/63375062/answer/1403291487