是为每个进程重新加载 libc 到内存还是共享一个副本?

Is libc reloaded to memory for every process or a single copy is shared?

有一些帖子和文章声称它被加载到物理内存中一次,而其他进程仅在加载时将它们的(运行domized)虚拟地址映射到它: How Libc shared library loaded in memory and shared amongst processes?

但是我用我的小共享库做了我自己的小实验,结果并不像预期的那样:

1- 我创建了一个名为 libadd.so 的共享库,代码如下:

int add(int a){ return ++a;}

2- 使用 libadd 制作的 main.c,代码如下:

while(1){
   printf("%d\n", add(1));
   sleep(1);
 }

运行 main 将按预期每秒循环打印 2

3- 虽然 main 是 运行,但我在单独的过程中将 libadd.so 修改为 return 0; 并重新 运行 main

结果: 第一个进程继续打印 2,而新进程正在打印 0。如果 libadd.so 真的只加载到物理内存一次,而其他进程只将它们的虚拟地址映射到它,那么两个进程都应该打印 0,不是吗?我的例子显示在可执行内存中有这个库的 2 个不同副本。

你的小实验没有考虑到可执行代码存在于一个完全不同的段中,名为 .text,它不同于 .data 段(pre-initialized 具有非零值全局变量),.bss 段(全局变量 default-initialized 归零),堆栈和动态分配的内存。

进程之间共享的是 .text 段(以及用于常量数据的 .rodata 段,本质上只是 .text 没有 PROT_EXEC 权限集)。其他一切都被映射为进程私有内存。

3- While main is running I modified libadd.so to return 0; and re-ran main in a separate process.

OS 注意到 libadd.so 自加载以来发生了变化,它加载了新版本并将其链接到新进程。如果您 运行 另一个使用 libadd.so 的进程,并且 libadd.so 没有更改,它将共享此缓存副本。

旧版本还在内存中加载。它将一直保持到没有进程引用它为止。