Linux内存管理中的page cache的两种类型:MAP_PRIVATE和MAP_SHARED:原理和使用场景 MAP_PRIVATE和MAP_SHARED是mmap对文件映射时的两个核心标志它们的根本区别在于修改是否回写到后备文件以及修改是否对其他进程可见。这导致了截然不同的内存管理行为和适用场景。1. 核心语义对比特性MAP_PRIVATE文件映射MAP_SHARED文件映射对映射内存的写操作不回写到原文件回写到原文件修改对其他进程的可见性不可见每个进程拥有独立的私有点可见所有映射同一文件的进程共享同一物理页底层物理页类型读时使用文件的页缓存页写时触发COW分配新的匿名页始终使用文件的页缓存页页为脏时直接标记VMA 标志VM_MAYSHARE通常不设置VM_SHARED为 0VM_SHARED为 1VM_MAYSHARE为 1msync行为对MAP_PRIVATE的msync通常为no-op因为修改本就不打算写回可以主动刷出脏页到磁盘fork后子进程父子进程共享 COW 页面写入时各自分裂父子进程始终共享同一物理页写入互相可见2. 写时复制COW在MAP_PRIVATE中的运作这是两者最关键的实现差异。初始状态未写入两个进程MAP_PRIVATE同一个文件读同一页时它们的页表都指向同一个物理页该物理页属于文件的page cache。此时它们共享内存page-mapping指向文件的address_space。发生写操作当某个进程首次写该页时硬件触发写保护缺页。内核识别出这是一个MAP_PRIVATE的 COW 页面分配一个新的物理页匿名页。将原文件页的内容复制到新页。将进程页表项修改为指向新页并授予写权限。新页不再属于 page cache而是变成该进程私有的匿名页page-mapping被设置为指向anon_vma原文件页仍保持干净。此后该进程的修改局限于这个私有页不会影响文件和其他进程。内存开销每个执行了写操作的私有页都会额外消耗一份物理内存相比于共享时因此在大规模写时可能引起内存膨胀这点与MAP_ANONYMOUS类似。3.MAP_SHARED的写回与可见性共享页始终是 page cache 页对于MAP_SHARED无论读写都直接操作文件的 page cache 页。写入后该物理页被标记为脏PG_dirty由内核的回写线程flusher或通过msync/fsync最终写回磁盘。修改立即对其他映射者可见因为所有映射同一文件的进程使用相同的物理页任何进程的写入都会直接反映到其他进程的读取中。这是实现进程间共享内存IPC的标准方式之一。msync的作用msync对于MAP_SHARED非常关键它强制将指定范围内的脏页立即写回后备文件常用于数据库等需要保证数据落盘的场景。对MAP_PRIVATE调用msync是无意义的因为内核不会将私有修改写回文件。4. 典型使用场景MAP_PRIVATE文件映射动态链接器的共享库加载ld.so通过mmap以MAP_PRIVATE加载.so文件。代码段只读在所有进程间共享物理页数据段可写在第一次写入时通过 COW 创建进程私有副本。这样既节省了内存又保证了进程间的隔离。可执行文件的加载与共享库类似可执行程序的代码段也是MAP_PRIVATE数据段则在需要时私有化。MAP_SHARED文件映射进程间大块数据共享如共享内存数据库、多媒体处理。内存映射文件 I/O将文件直接映射到内存修改会直接写回避免显式的read/write系统调用常用于高性能存储系统。零拷贝场景多个进程需要协同修改同一文件时。5. 重要陷阱与注意点MAP_PRIVATE写入的额外内存如果对一个巨大的文件执行MAP_PRIVATE并进行大量写入每个被写的页都会产生一个匿名页副本可能导致系统内存急剧消耗。应当小心不要将私有映射用于修改量大的大数据集。文件截断的影响如果文件在MAP_PRIVATE映射后被ftruncate或写回私有页持有的是截断前的历史内容可能会造成不一致。类似地MAP_SHARED下文件大小的改变也会影响映射窗口可能触发SIGBUS。fork时的一致性MAP_SHARED映射在fork后父子进程仍然共享同一物理页修改相互可见这通常用于父子进程通信。MAP_PRIVATE映射在fork后父子进程的私有页在写入前也是共享的COW写入后分裂互不影响。MAP_PRIVATE与文件锁私有映射对文件的修改不会反映到磁盘因此文件锁flock/fcntl不能用于保护这些修改对其他进程的可见性。总结MAP_SHARED修改写回文件且所有映射者可见底层使用 page cache适合共享和持久化。MAP_PRIVATE修改不写回文件且对其他进程不可见使用 COW 创建匿名页副本适合节省内存且需要隔离的场景如动态库。两者的选择决定了数据流的路径页缓存 vs. 匿名页和内核的内存回收行为MAP_SHARED脏页可回收但需写回MAP_PRIVATE的私有页只能换出到 swap。