我假设您有一个通过(例如)链接的程序:
cc -o mypgm mypgm.o -lshlib
在执行时,一旦 ELF 解释器加载了 libshlib.so 并执行了构造函数,库就不会再次加载。 旁注:要找到您的口译员,请执行以下操作:readelf -a mypgm | grep interpreter:
如果程序接收到一个信号(例如SIGUSR1),该信号要么被信号处理程序捕获(假设signal或sigaction已被调用来设置一个),要么采取默认操作(这是SIGUSR1的[IIRC]程序终止。这不会导致库被重新加载。
没有其他操作也可能导致库被重新加载。只有析构函数会在程序退出时被调用(例如main 返回或exit 被调用)。
即使手动调用析构函数也没有效果,因为构造函数和析构函数是独立的。 (例如,构造函数可以执行able = malloc(...),析构函数可以执行free(able)。但是,析构函数可以执行free(baker)。)。调用析构函数不会“重置”构造函数。
要获得“重新加载”效果,需要通过dlopen/dlsym/dlclose动态加载/卸载库。也就是说,链接命令是:
cc -o mypgm mypgm.o
然后,mypgm 将 [在某些时候] 调用 dlopen("libshlib.so")(并且将调用构造函数)。当 [and if] mypgm 调用 dlclose 时,libshlib.so 将被卸载(并调用析构函数)。
如果mypgm 然后调用dlopen("libshlib.so") 秒次,构造函数将被调用[再次]。
更新:
请注意,调用 dlclose 并不必然卸载库或调用析构函数。
我刚刚检查了代码 [in glibc]。该库有一个引用计数。如果在进入 dlclose 时引用计数为 1,则库将被卸载,对于上面的 libshlib.so 和 dlopen 应该是这种情况[因为没有其他人将其颠倒]。
换句话说,要强制执行“所需”行为,其他任何东西都不应通过-lshlib 引用libshlib。不是程序或任何其他.so。这奠定了基础。
请注意,如果libshlib.so 想要glibc,但程序也是如此,卸载libshlib 将降低glibc 引用计数,但glibc 将保留,因为它的引用计数[仍然] >0。
存在无法卸载库的情况(实际上,这些情况比可以卸载库的情况更常见)。
同样,这取决于引用计数和 [可能] 某些状态。当从“静态”链接加载库时(与 dlopen 相比),引用计数会增加一个额外的增量,因此不会被拉动。
代码还处理构造函数在其自己的库上调用dlopen的情况。
对于给定的libA,如果它需要libB,B 的引用计数会随着 A 的加载/卸载而增加/减少。
如果库没有卸载,那么析构函数是否会运行,以及后续的dlopen是否会再次运行构造函数都没有很好的定义
以这种方式对libshlib 使用dlopen 的全部意义在于保证在dlopen 处加载并在dlclose 处卸载[以及构造函数/析构函数操作]。如果没有对其的静态引用或循环依赖(这是起始条件),则此将为真。
更新 #2:
关于“因为没有其他人把它搞砸”的部分太简单了。
不要将散文与实质混淆。
如上所述:如果没有对它的静态引用或循环依赖,这个将为真。
这意味着只有为shlib 执行dlopen/dlclose 的可执行文件/对象引用shlib 中的符号。
而且,这仅限通过dlsym。否则,它是一个静态引用(即在对象的符号表中作为 UNDEF)]。
而且,noshlib 拖入的共享库是指shlib [循环依赖] 中定义的符号。
查看符号解析期间设置了 DF_1_NODELETE 的所有位置。
是的,我确实看过了。
DF_1_NODELETE 设置在仅以下位置。它们都不适用于这种情况[或大多数dlopen 场景]。
- 如果
dlopen 的flags 参数有RTLD_NODELETE,我们可以避免。
- 如果启用了分析,profile 映射 [not 与我们的
dlopen] 相关联得到 DF_1_NODELETE 集
- 如果符号具有类型 10 (
STB_GNU_UNIQUE) 的绑定类型(例如 LOCAL、GLOBAL、WEAK 等)
- 符号被非动态对象引用为 [在其符号表中] 无法删除的 UNDEF [因为所指对象设置了 DF_1_NODELETE]。由于上述先决条件,这不适用。
- 添加依赖项时出现
malloc 失败[来自_不同对象]。此代码甚至不会针对此处的案例执行。
而且,除了 OP 的使用,dlopen/dlclose 有正当理由按照我的设置/描述工作。
在这里查看我的答案:Is it possible to perform munmap based on information in /proc/self/maps?
在那里,OP 需要有一个可以运行数月/数年的不间断程序(例如,高可靠性、任务关键型应用程序)。如果 [通过包管理器等] 安装了其中一个共享库的更新版本,则程序必须动态、即时、重新执行,能够加载较新的版本。