首页 注册 登录
V2EX = way to explore V2EX 是一个关于分享和探索的地方
现在注册 已注册用户请 登录
V2EX C++

[求助] Linux 系统下动态库卸载后全局变量未重置的问题

Ainokiseki · 2025 年 9 月 5 日 · 2528 次点击
这是一个创建于 127 天前的主题,其中的信息可能已经有所发展或是发生改变。
遇到一个头疼的问题,兄弟部门需要调用我的代码,我把代码封装成了.so 动态库给他们使用。他们会写很多测试 case ,每个 case 依次运行,运行时首先使用 dlopen 装载我的库,运行完毕后使用 dlclose 卸载。下一个 case 重新使用 dlopen 装载。

目前遇到的问题是,在第二次 dlopen 之后,我的库里的全局变量的值仍然是上一次运行之后剩下的。而我期望在 dlclose 之后,全局变量们应该被析构掉,在 dlopen 之后重新构造以及初始化。

我询问 AI 得到的信息是,dlclose 之后如果引用计数为 0 ,那么会进行我期望的析构过程。但是没有直接手段能查看引用计数。可能导致引用计数不为 0 的原因包括:多次 dlopen 只有一次 dlclose ;引用的其他动态库依赖了我的库。其中第一条我认为不可能,我通过断点和日志能确信只有一次 dlopen 和一次 dlclose 。第二条也不可能,我查看了所有动态库的依赖库,没有任何一个依赖我的库。

我在 dlclose 和 dlopen 的时候均进行了检查,两次 dlopen 和一次 dlclose 都是成功的,没有错误信息。

求问有没有大佬遇到过类似的情况,要被搞疯了。。。
第 1 条附言 · 2025 年 9 月 8 日
试了两个方法成功解决了这个问题:编译的时候带上-fno-gnu-unique ,以及 dlopen 的时候用 RTLF_LOCAL
12 条回复 2025年09月22日 17:47:50 +08:00
yanqiyu
1
yanqiyu 2025 年 9 月 5 日
我觉得最简单的办法是在你的动态库里面加一个构造和析构会打 log 的对象看看这东西被析构了吗,如果不是的话去 gdb 给 dlopen 打断点看看是不是有别的没注意到的地方 dlopen 了

另外确定你们的环境不是 musl ?这东西的 dlclose 我记得是假的
yanqiyu
2
yanqiyu 2025 年 9 月 5 日
@yanqiyu 哦,断点已经用过了,那要不看看能不能把 glibc 记录 refcount 的结构抓出来看看?
ysc3839
3
ysc3839 2025 年 9 月 5 日 via Android
还是提供初始化和卸载函数吧
seers
4
seers 2025 年 9 月 5 日 via Android
dlclose 后检查下 soinfo 链,看看是不是确实关闭了
Noicdi
5
Noicdi 2025 年 9 月 5 日
> 可以定义一个或多个在共享库被加载和卸载时自动执行的函数,这样在使用共享库时就能够完成一些初始化和终止工作了。不管库是自动被加载还是使用 dlopen 接口(参见 42.1 节)显式加载的,初始化函数和终止函数都会被执行。
>
> 《 Linux/Unix 系统编程手册》 42.2 初始化和终止函数

那我的第一反应就是你提供全局变量的析构函数,注册到终止函数里,库被卸载的时候去析构。但是又考虑到库没被卸载。

> Shared objects may export functions using the __attribute__((constructor)) and __attribute__((destructor)) function attributes. Constructor functions are executed before dlopen() returns, and destructor functions are executed before dlclose() returns. A shared object may export multiple constructors and destructors, and priorities can be associated with each function to determine the order in which they are executed. See the gcc info pages (under "Function attributes") for further information.
>
> man dlopen

但是这里又说 `destructor functions are executed before dlclose() returns`,那试一试?
Noicdi
6
Noicdi 2025 年 9 月 5 日
Ainokiseki
7
Ainokiseki
OP
2025 年 9 月 5 日 via Android
@ysc3839 唉,我们代码有若干个全局 static 变量,一个个清太蛋疼了

@seers 看过了,确实没有关闭。。不知道为啥

@Noicdi 感谢,我周一试试 open 的时候带上 LOCAL 以及编译的时候加上那个 flag
passive
8
passive 2025 年 9 月 6 日 via Android
计数器只是一个原因
dlclose 并不会卸载 so ,前两天 hackernews 上刚有讨论
iceheart
9
iceheart 2025 年 9 月 6 日 via Android
dlclose 之后 cat /proc/[pid]/maps 确认有没有卸载。
Noicdi
10
Noicdi 2025 年 9 月 8 日
所以那个链接里提到的内容是有用的?那我也细看一下,对你提出的问题还挺感兴趣的。

话说是 RTLD_LOCAL 还是 RTLF_LOCAL ?
hwdq0012
11
hwdq0012 2025 年 9 月 8 日
我的动态库加载和卸载都是基于我自己定义的接口, 安装时 ioc 注册, 卸载时 ioc 手动清除, 上周也遇到个程序退出后静态资源没析构导致进程没退出的问题, 把那静态变量放 ioc 容器里就解决了
Ainokiseki
12
Ainokiseki
OP
2025 年 9 月 22 日 ❤️ 1
@Noicdi 通过实验,编译的时候带上-fno-gnu-unique 是真正起作用的做法。RTLD_LOCAL 没用
关于 · 帮助文档 · 自助推广系统 · 博客 · API · FAQ · Solana · 2541 人在线 最高记录 6679 · Select Language 创意工作者们的社区 World is powered by solitude VERSION: 3.9.8.5 · 28ms · UTC 05:10 · PVG 13:10 · LAX 21:10 · JFK 00:10
♥ Do have faith in what you're doing.

AltStyle によって変換されたページ (->オリジナル) /