【发布时间】:2021-08-22 16:52:16
【问题描述】:
所以我们正在处理 SO 和 DLL 文件。在dlclose() / FreeLibrary() 之后,SO/DLL 仍然在内存中,这是一个问题,因为将来可能会更新 SO/DLL 文件,并且在第二个 dlopen() / LoadLibrary() 之后是旧版本缓存在进程的内存中。在 Windows 上,这是一个更大的问题,因为在进程运行时无法删除 .DLL 文件。
在 Linux 上,我们的问题通过 GCC 的 -fno-gnu-unique 选项解决,效果很好。我们的问题是,我们不想依赖 SO 是如何编译的,主要是因为我们不是为我们的产品编写 SO/DLL 文件的团队,我们只是使用这些库。是否可以以“强制方式”卸载 SO?我的意思是,我们不应该依赖 SO 是否使用-fno-gnu-unique 编译。我还研究了如何将 .SO 文件映射到进程内存映射 (/proc/pid/maps),不幸的是从进程中取消映射 .SO 不起作用。
在 Windows 上我无法解决这个问题。 FreeLibrary() 返回 OK,但 GetModuleHandle() 表示 DLL 仍然存在于内存中。我还确定没有对 DLL 的引用,当不再需要时,每个指针都设置为 nullptr。我绑定了UnmapViewOfFile() 句柄,我可以在进程完成之前删除.DLL!但是我的进程在程序退出或我再次尝试LoadLibrary() 时崩溃。
关于 Stackoverflow/etc 的许多问题都与此有关,但我无法理解,为什么我们不能强制从进程中清除 SO/DLL。 GCC 的-fno-gnu-unique 选项中告诉了基本答案:因为STB_GNU_UNIQUE 对象应该存在直到程序终止。在 Linux 上,我可以使用 -fno-gnu-unique 覆盖此行为。我很好奇,为什么这在 Windows 上不可能(使用 MS Visual Studio 编译器)?我还在其他地方阅读了一条评论“DLL 不应该被卸载”,但我想知道为什么。
谢谢。
示例
这是一个最小的例子:
DLL:
class Writer
{
public:
Writer(int i) { std::cout << "Writer ctor" << std::endl; this->i = i; }
~Writer() { std::cout << "Writer dtor" << std::endl; }
int get() { return i; }
void set(int i) { this->i = i; }
private:
int i;
};
std::thread* WorkerThread;
void proc()
{
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
std::cout << "DLL: I'm alive" << std::endl;
}
}
inline void __cdecl function()
{
static Writer w(1);
std::cout << "i=" << w.get() << std::endl;
w.set(w.get() + 1);
if (WorkerThread == nullptr)
{
WorkerThread = new std::thread(proc);
}
}
用户:
#include <iostream>
#include <windows.h>
#include <libloaderapi.h>
typedef void (__cdecl* function_type)();
int main()
{
while (1)
{
std::cout << "Enter to continue...";
getchar();
function_type function;
HINSTANCE dll_handler;
dll_handler = LoadLibraryA("power.dll");
if (dll_handler == NULL)
{
std::cout << "Error: Unable to load dll" << std::endl;
continue;
}
if ((function = (function_type)GetProcAddress(dll_handler, "function")) == NULL)
{
std::cout << "Error: Unable to find function 'initialize' entry point in dll" << std::endl;
}
else
{
function();
std::cout << "function() returned " << std::endl;
}
std::cout << "Enter to FreeLibrary()...";
getchar();
int res = FreeLibrary(dll_handler);
if (res == 0)
{
std::cout << "FreeLibrary() failed" << std::endl;
}
else
{
std::cout << "FreeLibrary() succeed" << std::endl;
}
}
return 0;
}
我使用 Visual Studio 16 在 Windows 10 上编译。
一个典型的输出:
Enter to continue...
Writer ctor
i=1
function() returned
Enter to FreeLibrary()...DLL: I'm alive
DLL: I'm alive
DLL: I'm alive
FreeLibrary() succeed
Enter to continue...DLL: I'm alive
DLL: I'm alive
DLL: I'm alive
DLL: I'm alive
DLL: I'm alive
DLL: I'm alive
DLL: I'm alive
DLL: I'm alive
DLL: I'm alive
DLL: I'm alive
DLL: I'm alive
DLL: I'm alive
DLL: I'm alive
DLL: I'm alive
i=2
function() returned
Enter to FreeLibrary()...DLL: I'm alive
DLL: I'm alive
DLL: I'm alive
DLL: I'm alive
DLL: I'm alive
DLL: I'm alive
DLL: I'm alive
DLL: I'm alive
FreeLibrary() succeed
Enter to continue...DLL: I'm alive
DLL: I'm alive
i=3
function() returned
Enter to FreeLibrary()...DLL: I'm alive
DLL: I'm alive
DLL: I'm alive
FreeLibrary() succeed
Enter to continue...DLL: I'm alive
i=4
function() returned
Enter to FreeLibrary()...DLL: I'm alive
DLL: I'm alive
FreeLibrary() succeed
Enter to continue...Writer dtor
^C
DLL 启动一个线程,并且该线程不会停止。 “FreeLibrary()成功”后,power.dll被power_main.exe使用,无法删除,但程序在下一个LoadLibrary()之前等待,所以理论上DLL应该是可删除的。
static Writer 对象和 i= 打印输出表明该对象在调用 FreeLibrary() 时没有被释放。
我想实现在调用FreeLibrary()时杀死DLL启动的线程并从程序内存中清除DLL。当然,优秀的程序员不会喜欢强行停止线程的想法,因为它可能持有资源和/或以不一致的方式离开其状态。
我们的问题是我们的程序无法删除FreeLibrary() 之后的 DLL 文件,因为 DLL 以“错误”的方式编程。前面说过,我们加载的DLL是其他开发团队开发的,说实话,我们不关心DLL的东西是否因为其他开发团队的编程错误而保持不一致,所以我们想强行清除DLL如上所述,来自我们的程序。你怎么看,有可能吗?
谢谢。
【问题讨论】:
-
请用更新后的代码编辑问题,而不是链接。
标签: linux windows dll shared-libraries