C++强制卸载共享库
C++ force unloading shared library
我正在尝试创建一个可多次重新加载共享库的应用程序。但在某个时间点,dlmopen
失败并出现错误
/usr/lib/libc.so.6: cannot allocate memory in static TLS block
这是重现此问题的最少代码:
#include <dlfcn.h>
#include <cstdio>
#include <vector>
int main() {
for (int i = 0; i < 100; ++i) {
void *lib_so = dlmopen(LM_ID_NEWLM, "lib.so", RTLD_LAZY | RTLD_LOCAL);
if (lib_so == NULL) {
printf("Iteration %i loading failed: %s\n", i, dlerror());
return 1;
}
dlclose(lib_so);
}
return 0;
}
和空lib.cpp,用
编译
g++ -rdynamic -ldl -Wl,-R . -o test main.cpp
g++ -fPIC -shared lib.cpp -o lib.so
更新
好像连一个线程都崩溃了。问题是:如何强制卸载库或销毁使用 LM_ID_NEWLM
创建的未使用的命名空间?
旧的 glibc 版本可能有一些与此相关的错误:
https://bugzilla.redhat.com/show_bug.cgi?id=89692
https://sourceware.org/bugzilla/show_bug.cgi?id=14898
您使用的是什么版本?尝试使用较新的 glibc 版本,您的代码在我的计算机上运行良好 (glibc 2.23)。
进程可用的 link 映射名称空间的数量存在内置限制。这在评论中记录得很差:
The glibc implementation supports a maximum of 16 namespaces
在手册页中。
创建 link 地图命名空间后,将无法通过任何 API 支持 'erasing' 它。这就是它的设计方式,如果不编辑 glibc 源代码并添加一些挂钩,就没有真正的方法来解决这个问题。
使用命名空间重新加载库实际上并不是重新加载库 - 您只是加载库的新副本。这是命名空间的用例之一——如果您多次尝试 dlopen
同一个库,您将获得同一个库的相同句柄;但是,如果您在不同的命名空间中加载第二个实例,您将不会获得相同的句柄。如果要完成重新加载,您需要使用 dlclose
卸载库,这将在释放对库的最后一个剩余引用后卸载库。
如果你想尝试 'force unload' 一个库,那么你可以尝试发出多个 dlclose
调用直到它卸载;但是,如果您不知道库做了什么(例如生成的线程),那么在这种情况下可能无法防止崩溃。
我正在尝试创建一个可多次重新加载共享库的应用程序。但在某个时间点,dlmopen
失败并出现错误
/usr/lib/libc.so.6: cannot allocate memory in static TLS block
这是重现此问题的最少代码:
#include <dlfcn.h>
#include <cstdio>
#include <vector>
int main() {
for (int i = 0; i < 100; ++i) {
void *lib_so = dlmopen(LM_ID_NEWLM, "lib.so", RTLD_LAZY | RTLD_LOCAL);
if (lib_so == NULL) {
printf("Iteration %i loading failed: %s\n", i, dlerror());
return 1;
}
dlclose(lib_so);
}
return 0;
}
和空lib.cpp,用
编译g++ -rdynamic -ldl -Wl,-R . -o test main.cpp
g++ -fPIC -shared lib.cpp -o lib.so
更新
好像连一个线程都崩溃了。问题是:如何强制卸载库或销毁使用 LM_ID_NEWLM
创建的未使用的命名空间?
旧的 glibc 版本可能有一些与此相关的错误:
https://bugzilla.redhat.com/show_bug.cgi?id=89692 https://sourceware.org/bugzilla/show_bug.cgi?id=14898
您使用的是什么版本?尝试使用较新的 glibc 版本,您的代码在我的计算机上运行良好 (glibc 2.23)。
进程可用的 link 映射名称空间的数量存在内置限制。这在评论中记录得很差:
The glibc implementation supports a maximum of 16 namespaces
在手册页中。
创建 link 地图命名空间后,将无法通过任何 API 支持 'erasing' 它。这就是它的设计方式,如果不编辑 glibc 源代码并添加一些挂钩,就没有真正的方法来解决这个问题。
使用命名空间重新加载库实际上并不是重新加载库 - 您只是加载库的新副本。这是命名空间的用例之一——如果您多次尝试 dlopen
同一个库,您将获得同一个库的相同句柄;但是,如果您在不同的命名空间中加载第二个实例,您将不会获得相同的句柄。如果要完成重新加载,您需要使用 dlclose
卸载库,这将在释放对库的最后一个剩余引用后卸载库。
如果你想尝试 'force unload' 一个库,那么你可以尝试发出多个 dlclose
调用直到它卸载;但是,如果您不知道库做了什么(例如生成的线程),那么在这种情况下可能无法防止崩溃。