一、来看整体的结构
1.mm_struct(内存描述符)
- 描述整个用户空间,定义在 mm_types.h。
- 每个进程只有一个,task_struct 中的
mm指针指向它。
2.vm_area_struct(VMA)
- 表示一个独立的虚拟内存区域,一个进程有多个 VMA,分别对应代码、数据、堆、栈、共享库等。
- 一个区域通常对应一个 VMA,但不是严格一一对应。例如每个共享库会有好几个 VMA,对某段区域调用 mprotect/munmap 也可能把一个 VMA 拆成多个。
堆(brk)是一个连续 VMA,malloc 大块内存时才会通过 mmap 新增 VMA。
操作 内核里发生什么 malloc(10) 多次 堆 VMA 可能扩大,VMA 数量不变 malloc(1MB) 通过 mmap 新增一个 VMA,位置在映射区,不在堆里
3.页表
记录虚拟内存和物理内存的映射关系。
二、fork()时发生了什么
fork()后,子进程会复制父进程的task_struct,mm_struct,页表。所以父子进程共用一份物理内存。
此时,父子进程共享的私有可写页被暂时标记为只读。当任一进程尝试写入时,就会触发写保护型缺页异常,进而启动写时复制(COW)机制。
于是内核会分配新的物理内存空间,将原有数据复制到新页面,并更新对应的页表映射,使父子进程分别指向不同的物理页,从而实现内存隔离。