dlclose 后 dlopen(RTLD_NOLOAD) 仍然返回非空

Cri*_*ket 0 c++ shared-libraries dlopen

我正在编写一个函数来检查 .so 是否在 Linux 下加载,代码如下:

#include <iostream>
#include <dlfcn.h>
#include <unistd.h>
using namespace std;

bool isLibraryLoaded(const string& libPath)
{
   return (nullptr != dlopen(libPath.c_str(), RTLD_NOW | RTLD_NOLOAD));
}

int main(int argc, char** argv)
{
   const string libPath = "/path/to/library.so";

   cout << "loaded: " << isLibraryLoaded(libPath) << endl;

   sleep(8); // first execution of lsof

   // load lib
   void* handle = dlopen(libPath.c_str(), RTLD_NOW);
   cout << "handle: " << handle << endl;
   if (nullptr != handle)
   {
      cout << "loaded: " << isLibraryLoaded(libPath) << endl;
   }
   else
   {
      cout << "error: " << dlerror() << endl;
   }

   sleep(8); // second execution of lsof

   // unload lib
   if (0 == dlclose(handle))
   {
      cout << "loaded: " << isLibraryLoaded(libPath) << endl;
   }
   else
   {
      cout << "error: " << dlerror() << endl;
   }

   sleep(8); // third execution of lsof

   return 0;
}
Run Code Online (Sandbox Code Playgroud)

程序的输出:

loaded: 0
handle: 0x6420b0
loaded: 1
loaded: 1
Run Code Online (Sandbox Code Playgroud)

第一次执行 lsof:

# lsof /path/to/library.so
(return -1)
Run Code Online (Sandbox Code Playgroud)

第二次执行 lsof:

# lsof /path/to/library.so
COMMAND  PID   USER  FD   TYPE DEVICE  SIZE/OFF    NODE NAME
test    3240     me mem    REG   8,33 333493040 5242954 /path/to/library.so
Run Code Online (Sandbox Code Playgroud)

第三次执行 lsof:

# lsof /path/to/library.so
COMMAND  PID   USER  FD   TYPE DEVICE  SIZE/OFF    NODE NAME
test    3240     me mem    REG   8,33 333493040 5242954 /path/to/library.so
Run Code Online (Sandbox Code Playgroud)

程序很简单,就是要重新加载和卸载库。但事实并非如此。我很困惑并且有以下问题:

  1. 我的代码有问题吗?
  2. 为什么 dlclose() 没有真正卸载库?
  3. 潜在的原因可能是什么?
  4. C/C++ 有没有办法检测库是否加载/卸载?

我的环境:

g++ (Ubuntu 6.4.0-17ubuntu1~16.04) 6.4.0 20180424
Run Code Online (Sandbox Code Playgroud)

Emp*_*ian 5

这段代码:

bool isLibraryLoaded(const string& libPath)
{
   return (nullptr != dlopen(libPath.c_str(), RTLD_NOW | RTLD_NOLOAD));
}
Run Code Online (Sandbox Code Playgroud)

是错误的,因为 IFF 库已经加载(即如果函数返回true),则句柄引用计数将增加dlopen。您需要一个匹配dlclose来撤消该增量。

这是固定版本:

bool isLibraryLoaded(const string& libPath)
{
   void *h = dlopen(libPath.c_str(), RTLD_NOW | RTLD_NOLOAD);
   if (h != nullptr) {
     dlclose(h);
     return true;
   }
   return false;
}
Run Code Online (Sandbox Code Playgroud)

通过该修复:

$ ./a.out
loaded: 0
handle: 0x19382a0
loaded: 1
loaded: 0
Run Code Online (Sandbox Code Playgroud)