为何卸载共享库后,其中全局变量的内存会被释放?
关于共享库卸载后全局变量失效的疑问解答
- 你对共享库数据段的理解有个关键偏差:数据段的“复制”是写时复制(COW)机制,不是加载时就完成复制
- 共享库加载时,进程地址空间会映射共享库的代码段和数据段,但数据段的映射属性是「只读+写时复制」。此时所有加载该库的进程共享同一份物理内存页。
- 只有当你的进程对共享库的全局变量执行写入操作时,操作系统才会把对应的数据页复制一份到进程的私有内存空间,之后这个变量的地址才指向进程自己的内存区域。
- 卸载共享库(比如调用
dlclose())时的核心行为:- 如果进程从未修改过共享库的全局变量,该变量的地址依然指向共享库的原始数据页,卸载后操作系统会回收这些页,此时访问这个地址就会触发无效内存访问错误。
- 如果进程已经修改过该全局变量,对应的数据页已经被复制到进程私有内存,卸载共享库不会影响这份私有内存,此时访问变量是完全正常的。
举个简单的验证例子:
// 共享库代码 libtest.c int global_var = 0;
编译成共享库:gcc -shared -fPIC libtest.c -o libtest.so
// 主程序 main.c #include <dlfcn.h> #include <stdio.h> int main() { void* handle = dlopen("./libtest.so", RTLD_NOW); int* global = dlsym(handle, "global_var"); // 情况1:只读取不修改 printf("Read global_var: %d\n", *global); dlclose(handle); // 这里访问会触发段错误,因为global指向的共享库页已被回收 // printf("After dlclose: %d\n", *global); // 情况2:先修改再卸载 handle = dlopen("./libtest.so", RTLD_NOW); global = dlsym(handle, "global_var"); *global = 100; // 触发写时复制 printf("Modified global_var: %d\n", *global); dlclose(handle); // 这里访问正常,因为变量已经在进程私有内存里 printf("After dlclose: %d\n", *global); return 0; }
编译主程序:gcc main.c -ldl -o main
运行后就能直观看到两种情况的差异。
内容的提问来源于stack exchange,提问作者SZYoo
相关产品推荐
相关产品推荐

