是为每个进程重新加载 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 没有更改,它将共享此缓存副本。
旧版本还在内存中加载。它将一直保持到没有进程引用它为止。
有一些帖子和文章声称它被加载到物理内存中一次,而其他进程仅在加载时将它们的(运行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 没有更改,它将共享此缓存副本。
旧版本还在内存中加载。它将一直保持到没有进程引用它为止。