为什么 Linux 要使用写入时复制(COW)?从动态链接库内存共享讲清楚设计原则
在分析 Linux 内存时,很多工程师都会看到类似现象:
- 一个进程加载了 20MB 的动态库
- 启动 10 个相同进程
- 系统内存却没有增加 200MB,而只增加了很少一部分
这是 Linux 内存管理中一个非常核心的机制:写入时复制(Copy-On-Write, COW)。
但很多人有几个疑问:
- 动态库不是每个进程都要用吗?为什么不分别加载?
- 为什么不是一开始就分开,而是'写的时候才复制'?
- 动态库哪些内存共享,哪些不共享?
- 这个机制背后的设计原则是什么?
本文从工程视角,一步一步讲清楚。
一、先理解:进程的虚拟内存结构
一个进程的虚拟内存典型结构如下:
虚拟内存空间
+------------------+
| code segment | 代码段(.text)
+------------------+
| rodata segment | 只读数据段
+------------------+
| data segment | 已初始化全局变量
+------------------+
| bss segment | 未初始化全局变量
+------------------+
| heap | malloc 分配
+------------------+
| mmap region | 动态库映射
+------------------+
| stack |
+------------------+
动态库(.so)通过 mmap 加载到 mmap region。
二、动态库加载后,哪些内存是共享的?
假设动态库 libexample.so 大小为 2MB,它的内部结构是:
libexample.so
.text 1.5MB ← 代码段(只读)
.rodata 0.3MB ← 常量段(只读)
.data 0.1MB ← 可写全局变量
.bss 0.1MB ← 可写全局变量
关键区别:
| 段 | 是否共享 | 原因 |
|---|---|---|
| .text | ✅ 共享 | 只读,不会修改 |
| .rodata | ✅ 共享 | 只读 |
| .data | ❌ 私有(COW) | 可能修改 |
| .bss | ❌ 私有(COW) | 可能修改 |
也就是说:
- 2MB 动态库中,大约 1.8MB 是共享的
- 只有 0.2MB 可能变成私有
这就是内存节省的来源。
三、关键机制:写入时复制(COW)
Linux 并不会一开始就复制 data/bss,而是:
先共享,写的时候再复制
流程如下:
- 进程 A 加载 libexample.so
- 进程 B 加载 libexample.so
- 共享物理页


