Linux 性能实战 | 附录:动态链接库的内存是共享的吗?深入理解 .so 如何影响进程内存占用
·
动态链接库的内存是共享的吗?深入理解 .so 如何影响进程内存占用
在分析 Linux 进程内存时,你可能经常会看到这样的现象:
$ pmap -x 1234 | grep libc
00007f... r-xp 1500K ... libc.so
00007f... rw-p 32K ... libc.so
或者:
$ smem -p 1234
PID RSS PSS USS Command
1234 20M 8M 5M app
一个进程明明加载了十几个 .so 文件,RSS 看起来很大,但 PSS 却小很多。
这时一个关键问题就出现了:
动态链接库的内存到底是共享的,还是每个进程独占的?
答案是:
动态库的代码段是共享的,数据段通常是每个进程独占的。
本文将从内存结构、虚拟内存机制和 Copy-on-Write 原理三个层面,彻底解释这个问题。
一、动态库加载后在内存中的真实结构
当一个进程加载 .so 文件时,Linux 会将其映射到进程虚拟地址空间。
结构如下:
可以看到:
| 段 | 是否共享 | 原因 |
|---|---|---|
| .text | 共享 | 只读,不会修改 |
| .rodata | 共享 | 只读 |
| .data | 私有 | 可写 |
| .bss | 私有 | 可写 |
核心决定因素只有一个:
是否可写
二、为什么代码段可以共享?
代码段包含机器指令,例如:
printf("hello");
编译后变成:
call printf
这些指令:
- 不会修改
- 所有进程执行逻辑相同
因此 Linux 可以安全共享:
优势:
- 节省物理内存
- 提高 cache 命中率
这也是动态链接库节省内存的核心原因。
三、为什么全局变量必须私有?
看一个例子:
动态库:
// libexample.so
int counter = 0;
void inc() {
counter++;
}
如果多个进程共享这块内存:
会发生:
进程A: counter++
进程B: counter++
结果:
- 进程之间互相影响
- 状态混乱
- 程序错误
这违反了 Linux 的基本原则:
进程地址空间必须隔离
所以必须私有。
四、Linux 如何实现“初始共享,写时私有”
Linux 使用的是:
Copy-on-Write (COW)
流程如下:
第一步:初始共享
此时:
物理页共享
节省内存。
第二步:进程写入时复制
当进程A修改变量:
counter++
Linux 执行:
复制物理页
变成:
进程A得到私有副本。
进程B仍共享原页。
这就是:
写时复制(Copy-on-Write)
五、从 mmap 权限理解本质
Linux 使用 mmap 加载动态库:
代码段:
mmap(..., PROT_READ | PROT_EXEC, MAP_SHARED)
只读共享。
数据段:
mmap(..., PROT_READ | PROT_WRITE, MAP_PRIVATE)
私有映射。
关键区别:
| 映射类型 | 是否共享 |
|---|---|
| MAP_SHARED | 共享 |
| MAP_PRIVATE | 私有(COW) |
这就是根本原因。
六、通过 smaps 可以直接验证
查看:
cat /proc/<pid>/smaps
示例:
7f... libc.so
r-xp Shared_Clean: 1500 kB
rw-p Private_Dirty: 32 kB
说明:
代码段:
Shared
数据段:
Private
七、完整生命周期图
八、这也是 RSS 和 PSS 差异的根本原因
RSS 包含:
共享代码 + 私有数据
但共享代码实际只占用一份物理内存。
PSS 会按比例分摊:
真实占用 ≈ 私有内存 + 共享内存 / 进程数
例如:
libc.so text = 2MB
10 个进程使用
RSS 每个进程 = 2MB
PSS 每个进程 = 200KB
九、真实系统示例
pmap -x <pid>
示例:
Address Kbytes RSS Dirty Mode Mapping
libc.so
r-xp 1500 1500 0 shared
rw-p 32 32 32 private
可以看到:
绝大多数共享,
少量私有。
十、为什么动态链接库可以节省大量内存
假设:
libc.so = 2MB
100 个进程
如果不共享:
200MB
实际:
≈ 2MB + 少量私有数据
节省 99% 内存。
十一、最终总结(工程视角)
动态库内存结构:
结论:
共享:
- 代码段
- 常量段
私有:
- 全局变量
- 静态变量
实现机制:
只读段 → 共享映射
可写段 → MAP_PRIVATE + Copy-on-Write
十二、对性能分析的实际意义
这解释了为什么:
不要用 RSS 判断真实内存占用,
而应该用:
smem
或:
/proc/pid/smaps
重点关注:
PSS
USS
Private_Dirty
而不是简单的 RSS。

更多推荐

所有评论(0)