如何在Linux上热重新加载共享库
How to Hot Reload shared library on Linux
我正在尝试从Casey Muratori流行的手工英雄系列中复制一个很酷的技巧。在Win32上,Casey能够重新加载DLL,看到他的代码更改,只有几毫秒的延迟。
我正在尝试使用dlopen,dlsym,dlclose和stat在Linux上复制这种行为,但是我遇到了以下行为,并且我有一个直觉,我要么误解了关于小精灵的某些东西,例如链接器,或者也许是共享对象的概念。
我能够使他的代码在Win32上毫无困难,所以我觉得这是我缺少的Linux的。
我正在使用cmake构建,但我不特别相信cmake是罪魁祸首。
i制作共享库动态的副本,然后加载。每当原始共享对象更新的MTIME时,我都会关闭旧副本的句柄,制作新副本,然后尝试加载新副本。
我想指出的是,我打算在第一次更改后打破循环,因为我只是想弄清楚这一点。
#include <stdio.h>
#include <dlfcn.h>
#include <time.h>
#include <sys/stat.h>
#include <unistd.h>
void
CopyFile(const char* src, const char* dest)
{
FILE* fsrc;
FILE* fdest;
unsigned char buffer[512];
size_t bytes;
fprintf(stderr, "copy from: %s to %s!n", src, dest);
fsrc = fopen(src, "rb");
if ( fsrc == NULL )
┆ fprintf(stderr, "failed to open file: %s for readingn", src);
fdest = fopen(dest, "wb");
if ( fdest == NULL )
┆ fprintf(stderr, "failed to open file: %s for readingn", src);
while ( (bytes = fread(buffer, 1, sizeof(buffer), fsrc)) > 0 )
{
┆ fwrite(buffer, 1, bytes, fdest);
}
fclose(fsrc);
fclose(fdest);
fprintf(stderr, "copy complete!n");
}
int main(int argc, char** argv)
{
const char* libpath = "/home/bacon/dynamic.so";
const char* copypath = "/home/bacon/dynamic-copy.so";
CopyFile(libpath, copypath);
void* handle = dlopen(copypath, RTLD_NOW | RTLD_GLOBAL);
if ( handle == NULL )
fprintf(stderr, "failed to load %s, error = %sn", copypath, dlerror());
struct stat s;
stat(libpath, &s);
time_t oldtime = s.st_mtime;
while (true)
{
stat(libpath, &s);
if ( oldtime != s.st_mtime )
{
if ( handle != NULL )
{
if ( dlclose(handle) )
fprintf(stderr, "dlclose failed: %sn", dlerror());
else
handle = NULL;
}
CopyFile(libpath, copypath);
handle = dlopen(copypath, RTLD_NOW | RTLD_GLOBAL);
if ( handle == NULL )
fprintf(stderr, "failed to load %s, error = %sn", copypath, dlerror());
break;
}
}
}
至于动态库,任何事情都应该执行(示例标题(:
#ifndef DYNAMIC_HEADER
#define DYNAMIC_HEADER 1
#define DYNAMIC_API __attribute__ ((visibility("default")))
extern "C" DYNAMIC_API int
Add(int x, int y);
#endif /* DYNAMIC_HEADER */
和源文件:
#include "Dynamic.h"
int
Add(int x, int y)
{
return x + y;
}
共享库只提供了一些例程将几个数字添加在一起,我已经验证了我能够在没有热重加载技巧的情况下dlopen和dlsym。
我还验证了我的副本例程实际上复制了共享对象。
我希望最初的dlopen成功,而dlsym可以正确链接添加(它将(。然后,我会编辑Dynamic.cpp,也许返回X X Y或其他内容,保存文件并重新编译,期望While Loop拾取ST_MTIME的更改。
我注意到,当我运行代码并更新时,我收到了错误:
dlopen: file too short
果然,当我ls -la包含共享对象的目录时,副本为0。
以某种方式,STAT报告的ST_MTIME已更新,但是共享对象的实际内容为空?链接器是否锁定共享对象并防止读取?
如果我的代码并没有非常错误,我该如何规避这种行为?
我不愿睡觉和重试,因为这是一个相当瞬时的更新。
如果我的代码没有可怕的错误
这是非常错误的:您的代码正在使用(静态(链接器进行赛车(由make
或cmake
调用(。
当make
运行时,它(最终(调用:
gcc -shared -o /home/bacon/dynamic.so foo.o bar.o ...
然后,链接器将执行open("/home/bacon/dynamic.so", O_WRONLY|O_CREAT, ...)
(或同等(,一段时间后将write
,最后close
文件。
当m_time
更改时,您的程序将醒来,即open
之后的任何时间,并将尝试复制文件。如果您的副本在最终close
之前的任何时间发生,则最终可能会获得部分副本(包括包含0个字节的部分副本(。
最明显的解决方案是Zsigmond建议的解决方案:您必须修改Makefile
以链接您正在观看的不同的文件,并将mv
执行到最终目的地(原子((步骤。
另一种解决方案是具有依赖dynamic.so
的make
目标,例如
dynamic.so.done: dynamic.so
touch dynamic.so.done
在您的程序中,您会查看m_time
的dynamic.so.done
,并且只有在更新之前,请执行dynamic.so
的副本(保证该副本是close
d,by thit Point(。<<<<<<<<<<<<<。/p>
- 加载共享库时C++错误:libopencv_ximgproc.so.4.4
- 从 CMake 中的库目录加载共享库?
- 无法使用 python ctypes 加载C++共享库
- ./main:加载共享库时出错:libopencv_highgui.so.4.0:无法打开共享对象文件:没有这样的文件或
- 在 win32 上生成 R 包:无法加载共享对象 (.dll)
- 加载共享库时出错:libbsoncxx.so._noabi:无法打开共享对象文件:没有此类文件或目录
- 在 Linux 上,在 C++ 程序中,如何找到已加载共享库的路径?
- 如何更改路径以修复错误"./main:加载共享库 libmkl_core.so 时出错?
- 正在加载共享对象:文件中未定义版本Qt_5
- 将生成文件转换为CMakeLists;无法加载共享库
- 加载共享库时出现"错误:libSDL2_mixer-2.0.so.0:无法打开共享对象文件:没有这样的文件或目录
- 使用dlopen动态加载共享库
- 交叉编译 qt:加载共享库时出错
- 加载共享库时出现 Codelite 错误,但我可以编译它
- 加载共享库时出错:JVM.dll
- 如何在Linux上热重新加载共享库
- 在共享C++库中加载共享库时C++未定义的符号,该库本身由 Python 加载
- 在 Windows 中加载共享库时在特定路径中选取 dll
- 一种在没有 root 的情况下加载共享库的更简单方法
- C++ Linux 加载共享库时出错“未定义的符号:pthread_create”