Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
动态库,是得多动态?
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Zhihao Yuan
December 11, 2021
Programming
110
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
动态库,是得多动态?
运行时加载动态库的应用与挑战。
视频:
https://www.bilibili.com/video/BV1LU4y1K7om
Zhihao Yuan
December 11, 2021
More Decks by Zhihao Yuan
See All by Zhihao Yuan
DWARF 眼中的 C++
lichray
0
47
Callable Objects in Java, C#, Rust, and C++
lichray
0
73
非类型参数会梦见 OOP 吗?
lichray
0
180
Dynamically Loaded Libraries Outside the Standard
lichray
0
490
Thinking in Immediate: ImGUI
lichray
0
260
Lambda 和它们的环境
lichray
1
250
The Many Shades of reference_wrapper
lichray
0
260
Are We Macro-free Yet?
lichray
0
180
vvpkg: A cross-platform data deduplication library in C++14
lichray
0
200
Other Decks in Programming
See All in Programming
Apple Intelligence を用いた個人情報誤送信防止、及びユーザーリクエスト体験の改善について
yukiny
0
220
ゲームコントローラやキーボードのファームウェアをSwiftで書く
kishikawakatsumi
1
270
Starting & Sustaining Code-Based E2E Testing for Non-Coding QA Teams( #jasstniigata )
teyamagu
PRO
1
680
setup-vp GitLab対応の裏側
naokihaba
0
140
UPDATE をやめる — EF Core でマスタをバージョン管理する
panda728
PRO
0
980
Vue Fes Japan 2026 タイムテーブル徹底解説
448jp
1
560
Workers Cache を知る
syumai
0
310
モジュールの視点からSwiftを読み解く #iosdc
s_shimotori
0
220
iOS 27でニュースアプリはどう変わる!? 〜日経電子版の新機能対応と、開発事例から〜
lynnswap
7
13k
そのリトライ、死んだコネクションを使い回していませんか ── GoのHTTPクライアントとHTTP/2を実プロダクト障害から学び直す
myus4a
0
330
ソニーのクラウド共通基盤の変遷とAI時代の開発スタイルに合わせた進化 / The Journey of Sony’s Common Cloud Platform and Its Evolution for AI-Native Development
kenjiyoneyama
0
190
iOSDC Japan 2026 - Swiftで作って学ぼう!データベース自作入門
kaseken
2
400
Featured
See All Featured
Evolving SEO for Evolving Search Engines
ryanjones
0
300
Building a Scalable Design System with Sketch
lauravandoore
464
34k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.7k
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
3
4.3k
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
560
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.9k
The Limits of Empathy - UXLibs8
cassininazir
1
690
Designing Powerful Visuals for Engaging Learning
tmiket
1
580
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
6
1.2k
The Illustrated Children's Guide to Kubernetes
chrisshort
51
53k
The World Runs on Bad Software
bkeepers
PRO
72
12k
AI: The stuff that nobody shows you
jnunemaker
PRO
10
1.1k
Transcript
2021 Pure C++ PURECPP 动态库,是得多动态? 2021/12/11 Zhihao Yuan <
[email protected]
>
2021 Pure C++ PURECPP 背景知识
3 2021 Pure C++ | PURECPP 静态链接 app editor.o file.o
line.o search.o source.o
4 2021 Pure C++ | PURECPP 使用静态库 app editor.o file.o
line.o search.o source.o libed.a editor.o file.o line.o libre.a search.o
5 2021 Pure C++ | PURECPP 动态链接 app libed.so =>
libre.so => source.o libed.so editor.o file.o line.o libre.so search.o
6 2021 Pure C++ | PURECPP • 分解应用程序的载体 • 在构建时建立依赖性
• 也是 C++ 标准库的常见载体 依赖库
7 2021 Pure C++ | PURECPP #include "repromath_export.h" namespace repromath
{ REPROMATH_EXPORT auto ddot(int n, double const *x, double const *y) -> double; REPROMATH_EXPORT auto dsum(int n, double const *x) -> double; } 动态链接库的例子 CMake 自动生成的头文件 add_library(repromath SHARED) generate_export_header(repromath)
8 2021 Pure C++ | PURECPP #include <repromath.h> #include <stdio.h>
int main() { double x[] = {1., 2., 3.}; double y[] = {4., -5., 6.}; printf("result = %g\n", repromath::ddot(3, x, y)); } 用户侧的代码和静态链接相同
9 2021 Pure C++ | PURECPP • Level 0: 依赖库
「动态」的不同等级
10 2021 Pure C++ | PURECPP • Level 0: 依赖库
• Level 1: 延迟加载 「动态」的不同等级
11 2021 Pure C++ | PURECPP • 在程序运行过程中第一次用到依赖库导出的名字时再加载它 • 在「时机」上更加动态
• 目的一般是减少程序启动时间 延迟加载
12 2021 Pure C++ | PURECPP • Windows: 辅助库 delayimp.lib
+ linker 选项 /DELAYLOAD:mylib.dll • Solaris: linker 选项 -z lazyload -lmylib • Linux: DIY: Implib.so (类似 DLL 导入库的方案) 延迟加载跨平台吗?
13 2021 Pure C++ | PURECPP printf("address prior to use:
%p\n", repromath::ddot); printf("result = %g\n", repromath::ddot(3, x, y)); printf("address after using: %p\n", repromath::ddot); MSVC 的 /DELAYLOAD 选项
14 2021 Pure C++ | PURECPP • 函数的地址在程序运行过程中可以改变 – 因为槽的地址
≠ 实际函数的地址 • C++ 的对象在内存中不能变更位置 (relocate),但现在函数可以 延迟加载的迷惑行为
15 2021 Pure C++ | PURECPP 有没有可能更动态一点,在程序中直接控制 加载一个库的时机?
16 2021 Pure C++ | PURECPP #include "repromath_export.h" namespace repromath
{ REPROMATH_EXPORT auto ddot(int n, double const *x, double const *y) -> double; REPROMATH_EXPORT auto dsum(int n, double const *x) -> double; } 动态加载库的例子 CMake 自动生成的头文件 add_library(repromath MODULE) generate_export_header(repromath)
17 2021 Pure C++ | PURECPP • LoadLibraryEx("mylib.dll", nullptr, flags)
• LoadLibrary("mylib.dll") • GetProcAddress(handle, "function_or_variable_name") • FreeLibrary(handle) • GetLastError() APIs: Win32
18 2021 Pure C++ | PURECPP • dumpbin /exports repromath.dll
找到动态库导出的名字
19 2021 Pure C++ | PURECPP double x[] = {1.,
2., 3.}; double y[] = {4., -5., 6.}; auto lib = ::LoadLibraryW(L"repromath.dll"); /* ...处理错误 */ typedef auto ddot_t(int, double const *, double const *) -> double; auto ddot = (ddot_t *)::GetProcAddress(lib, "?ddot@repromath@@YANHPEBN0@Z"); printf("result = %g\n", ddot(3, x, y)); ::FreeLibrary(lib); Win32 下的例子
21 2021 Pure C++ | PURECPP • dlopen("mylib.so") • dlsym(handle,
"symbol_name") • dlclose(handle) • dlerror() APIs: POSIX
22 2021 Pure C++ | PURECPP • objdump -T librepromath.so
找到动态库导出的名字
23 2021 Pure C++ | PURECPP double x[] = {1.,
2., 3.}; double y[] = {4., -5., 6.}; auto lib = ::dlopen("./librepromath.so", RTLD_LOCAL | RTLD_NOW); /* ...处理错误 */ typedef auto ddot_t(int, double const *, double const *) -> double; auto ddot = (ddot_t *)::dlsym(lib, "_ZN9repromath4ddotEiPKdS1_"); printf("result = %g\n", ddot(3, x, y)); ::dlclose(lib); POSIX 下的例子
25 2021 Pure C++ | PURECPP • dlopen, LoadLibrary, 和
LoadLibraryEx 增加计数 • dlclose 和 FreeLibrary 减少计数 • GetModuleHandleEx 能增加已经加载了的库的引用计数 平台对载入的库使用了引用计数
26 2021 Pure C++ | PURECPP • Level 0: 依赖库
• Level 1: 延迟加载 • Level 2: 动态实体 「动态」的不同等级
27 2021 Pure C++ | PURECPP void *dlsym(void *handle, char
const *symbol); 近距离观察动态载入 API
28 2021 Pure C++ | PURECPP (ddot_t *)::dlsym(lib, "_ZN9repromath4ddotEiPKdS1_"); 近距离观察动态载入
API 类型为 void * 的指针可以被强制 转换为函数指针吗?
29 2021 Pure C++ | PURECPP • 函数指针和指向对象的指针之间的转换是 conditionally-supported •
不过 POSIX 要求 POSIX 实现支持这个转换 不一定
30 2021 Pure C++ | PURECPP FARPROC GetProcAddress(HMODULE hModule, LPCSTR
lpProcName); Win32 下动态载入 API 的情况 typedef INT_PTR (FAR WINAPI *FARPROC)();
31 2021 Pure C++ | PURECPP • 返回类型是指向函数的指针 • 指向哪个函数的指针?
GetProcAddress
32 2021 Pure C++ | PURECPP 在程序运行过程中,决议符号的结果会指向 • 程序之外 (foreign)
的函数,或者 • 对象模型之外 (foreign) 的对象 动态实体的奇怪之处
33 2021 Pure C++ | PURECPP • 在另一个编程语言中使用可加载模块 • 使用
Foreign Function Interface (FFI) 来访问 外语的「外」
34 2021 Pure C++ | PURECPP from ctypes import CDLL,
c_double x = (c_double * 3)(1., 2., 3.) y = (c_double * 3)(4., -5., 6.) repromath = CDLL('repromath.dll') ddot = repromath['?ddot@repromath@@YANHPEBN0@Z'] ddot.restype = c_double print("result = {}".format(ddot(3, x, y))); ctypes 的例子 遗漏这行,你会得到一堆垃圾
35 2021 Pure C++ | PURECPP • pybind11: 你写的 C++
代码会在 Python 运行时中组建模块 • JNA (Java Native Access): 你可以用 Java 语法声明 C 函数 • pydffi (DragonFFI): 你可以用 C++ 声明 C 或 C++ 函数… 强类型 FFI 是有必要的 import pydffi pydffi.dlopen("/path/to/libarchive.so") CU = pydffi.FFI().cdef("#include <archive.h>") a = funcs.archive_read_new()
36 2021 Pure C++ | PURECPP #include <repromath.h> int main()
{ double x[] = {1., 2., 3.}; double y[] = {4., -5., 6.}; auto lib = dlopen("repromath"); auto ddot = __magic<repromath::ddot>(lib); printf("result = %g\n", ddot(3, x, y)); } 假想的 C++ FFI
37 2021 Pure C++ | PURECPP • 声明决定了一个实体的符号 – 名字
– 类型 – extern "C" – ABI tag 等编译器扩展 用函数、对象声明决议动态实体
38 2021 Pure C++ | PURECPP • Level 0: 依赖库
• Level 1: 延迟加载 • Level 2: 动态实体 • Level 3: 插件系统 「动态」的不同等级
39 2021 Pure C++ | PURECPP • 如果把内存中的整个程序看作是一个「C++ 程序」,那么这是 ODR
violation – 症状各种各样 • 主流平台都有缓和这一风险的机制 – 不过总有「绕过」办法的问题… 载入两个定义了同一个实体的库会发生什么?
40 2021 Pure C++ | PURECPP • OpenSSL + LibreSSL
在同一个进程中能干什么好事? • 「ABI 兼容性」这个术语已经暗示了我们希望两个兼容的东西功能是可替换的 对于动态实体来说,符号冲突是意外 但如果是我们在利用 ABI 相同这一 点,用编程方式从多个载入库中获 得额外功能呢?
41 2021 Pure C++ | PURECPP • GIMP 的图形处理框架 •
拖拽文件就能添加、删除功能 案例:GEGL
42 2021 Pure C++ | PURECPP PLUGINAPP_API LPPLUGINSTRUCT plugin_app_create_plugin(void); PLUGINAPP_API
void plugin_app_destroy_plugin(LPPLUGINSTRUCT); PLUGINAPP_API const gchar* plugin_app_get_plugin_name(void); PLUGINAPP_API const gchar* plugin_app_get_plugin_provider(void); PLUGINAPP_API const gchar* plugin_app_get_menu_name(void); PLUGINAPP_API const gchar* plugin_app_get_menu_category(void); PLUGINAPP_API void plugin_app_run_proc(void); 一个典型的基于 C 的插件系统 该例子修改自 https://www.codeproject.com/Articles/38966 7/Simple-Plug-in-Architecture-in-Plain-C
43 2021 Pure C++ | PURECPP LPPLUGINSTRUCT plugin_app_create_plugin() { g_debug("PluginDialog1::plugin_app_create_plugin");
/* ... */ return PLS; } const gchar* plugin_app_get_plugin_name() { g_debug("PluginDialog1::plugin_app_get_plugin_name"); return "Dialog1 Plugin"; } 插件1 实现的东西
44 2021 Pure C++ | PURECPP LPPLUGINSTRUCT plugin_app_create_plugin() { g_debug("...");
/* ... */ return PLS; } const gchar* plugin_app_get_plugin_name() { g_debug("..."); return "Dialog2 Plugin"; } 插件2 实现的东西
45 2021 Pure C++ | PURECPP LPPLUGINSTRUCT plugin_app_create_plugin() { g_debug("Some
thing"); /* ... */ return PLS; } const gchar* plugin_app_get_plugin_name() { g_debug("..."); return "Dialog1 Plugin"; } 同一实体多个定义 LPPLUGINSTRUCT plugin_app_create_plugin() { g_debug("Some other thing"); /* ... */ return PLS; } const gchar* plugin_app_get_plugin_name() { g_debug("..."); return "Dialog2 Plugin"; }
46 2021 Pure C++ | PURECPP typedef LPPLUGINSTRUCT (*CREATEPROC) (void);
typedef void (*DESTROYPROC) (LPPLUGINSTRUCT); typedef const gchar* (*NAMEPROC) (void); typedef const gchar* (*PROVIDERPROC)(void); typedef const gchar* (*MENUPROC) (void); typedef const gchar* (*MENUCATPROC) (void); typedef void (*RUNPROC) (void); 为了之后使用 GetProcAddress 准备的指针类型
47 2021 Pure C++ | PURECPP void load_all_plugins(GtkWidget *widget, gpointer
user_data) { LPPLUGINSTRUCT pls = NULL; CREATEPROC create = NULL; MENUPROC menuproc = NULL; MENUCATPROC menucatproc = NULL; /* ... */ while (plugin_helper_get_plugin_list()) { create = (CREATEPROC) GetProcAddress(h, "plugin_app_create_plugin"); 用法 struct _PluginStruct { NAMEPROC nameProc; PROVIDERPROC providerProc; MENUPROC menuProc; MENUCATPROC menuCatProc; RUNPROC runProc; DESTROYPROC destProc; };
48 2021 Pure C++ | PURECPP • 给同一组函数提供不同实现 虚表 (Vtable)
49 2021 Pure C++ | PURECPP 接口 whereispython 基于 C++
的插件系统的例子 可执行文件 plugindemo 模块 fullinstaller microsoftstore
50 2021 Pure C++ | PURECPP class installation { public:
virtual auto executable() -> std::filesystem::path = 0; virtual auto windowed_executable() -> std::filesystem::path = 0; virtual ~installation() = default; }; class factory { public: virtual auto lookup(char const *ver) -> std::unique_ptr<installation> = 0; }; namespace whereispython 和同名头文件
51 2021 Pure C++ | PURECPP class fullinstaller : public
installation { std::unique_ptr<HKEY, hkey_deleter> hkey_; public: explicit fullinstaller(char const *version); auto executable() -> std::filesystem::path override { return string_value(L"ExecutablePath"); } auto windowed_executable() -> std::filesystem::path override { return string_value(L"WindowedExecutablePath"); } }; namespace fullinstaller 和同名 dll
52 2021 Pure C++ | PURECPP class fullinstaller_factory : public
factory { public: virtual auto lookup(char const *version) -> std::unique_ptr<installation> { try { return std::make_unique<fullinstaller>(version); } catch (std::exception &) { return nullptr; } } }; namespace fullinstaller 和同名 dll
53 2021 Pure C++ | PURECPP #pragma comment(linker, "/export:instance=?instance@@3Vfullinstaller_factory@whereispython@@A") whereispython::fullinstaller_factory
instance; fullinstaller.dll 的全局命名空间
54 2021 Pure C++ | PURECPP class microsoftstore : public
installation { std::filesystem::path install_location_; public: explicit microsoftstore(char const *version) { auto shell = PowerShell::Create() ->AddCommand("Get-AppxPackage") ... class microsoftstore_factory : public factory { ... namespace microsoftstore 和同名 dll
55 2021 Pure C++ | PURECPP #pragma comment(linker, "/export:instance=?instance@@3Vmicrosoftstore_factory@whereispython@@A") whereispython::microsoftstore_factory
instance; microsoftstore.dll 的全局命名空间 You can also use a DEF file
56 2021 Pure C++ | PURECPP template <class Factory> class
plugin { std::unique_ptr<HMODULE, library_deleter> lib_; Factory *obj_; public: explicit plugin(std::filesystem::path const &dll) : lib_([&] { /* ... */ }()), obj_([this] { if (auto pinst = (Factory *)::GetProcAddress(lib_.get(), "instance")) return pinst; /* ... */ }()) plugin.h 头文件里的 namespace plugindemo
57 2021 Pure C++ | PURECPP plugin<whereispython::factory> (Factory *)::GetProcAddress(lib_.get(), "instance")
whereispython::factory * whereispython::fullinstaller_factory * whereispython::fullinstaller_factory instance;
58 2021 Pure C++ | PURECPP • 载入一个目录下的所有插件 auto openplugins(std::filesystem::path
dir) -> std::vector<plugin<whereispython::factory>>; openplugin.h 头文件里的 namespace plugindemo
60 2021 Pure C++ | PURECPP for (auto &plugin :
plugindemo::openplugins(fs::current_path())) { if (auto python = plugin->lookup(argv[1])) { if (nonempty) std::cout << std::endl; std::cout << python->executable() << '\n'; std::cout << python->windowed_executable() << '\n'; nonempty = true; } } plugindemo 可执行文件的 main 函数
61 2021 Pure C++ | PURECPP • 删掉 fullinstaller.dll 会丢失第一组答案
• 删掉 microsoftstore.dll 会丢失第二组答案 • 拖入文件,插入 (plug-in) 功能 演示 查找版本 "3.7"
62 2021 Pure C++ | PURECPP • 原理上想违反,但不一定得是函数的 ODR •
允许控制以载入为目的导出的别名,这样的功能很有用 插件想要违反 One Definition Rule GCC & Clang int foo asm("myfoo") = 2;
63 2021 Pure C++ | PURECPP • Level 0: 依赖库
• Level 1: 延迟加载 • Level 2: 动态实体 • Level 3: 插件系统 • Level 4: Live update 「动态」的不同等级
64 2021 Pure C++ | PURECPP • 你不想在 GIMP 还在运行的时候就卸载这些
⤵️ 一个典型的插件系统
65 2021 Pure C++ | PURECPP • 调用已经卸载了的库中的函数会导致 access violation
• …函数现在有生命期了 但可能想要卸载旧代码
66 2021 Pure C++ | PURECPP • Apache 和 Nginx
的模块无法卸载 • Python 的模块系统无法卸载 C 和 C++ 扩展 – importlib.reload 也不会重新载入这些扩展 • musl libc 的 dlclose 是 no-op 应用与实现常常避免卸载动态库 重启进程解决一切问题 如果扩展创建的活着的对象不能保持扩展自身不 被回收,不卸载扩展避免了违反 Python 的语义 如果库可以被卸载,线程本地 存储 (TLS) 的实现会很复杂
67 2021 Pure C++ | PURECPP 库创建的对象能比库自身活得更长吗? 嵌套生命期 • 没有问题
✔ 对象可以逃逸 • 给每个对象加一个库的工厂对象的强引用? • 让库跟踪所有的对象? – 可以考虑用 GetModuleHandleEx 让库完全掌控 自身的卸载时机 • 如果库会创建生命期不受库管制的线程, 参考 FreeLibraryAndExitThread • 很难调试
68 2021 Pure C++ | PURECPP • 载入一个库时,初始化命名空间作用域中具静态存储期的对象 • 卸载一个库时,析构具静态存储期的对象
库与 C++ 对象生命期 别忘了,载入的库使用了引用计数
69 2021 Pure C++ | PURECPP • 当新线程开始运行时,初始化命名空间作用域中具线程存储期的对象 • 当一个线程退出时,析构具线程存储期的对象
库的生命期与 thread_local 相互作用 如果库在所有线程退出之 前被卸载了,会发生什么?
70 2021 Pure C++ | PURECPP 如果从进程地址空间中卸载了析构函数代码, 还怎么运行析构函数?
71 2021 Pure C++ | PURECPP class logger { public:
virtual ~logger() = default; }; class singleton { public: virtual auto get() -> logger & = 0; }; 测试一下
72 2021 Pure C++ | PURECPP class memory_logger : public
logger { std::unique_ptr<char[]> buf_ = std::make_unique<char[]>(1024); public: memory_logger() { /* 记录 thread id */ } ~memory_logger() { /* 记录 thread id */ } }; 该 logger 的对象实例将是线程特定的
74 2021 Pure C++ | PURECPP class memory_logger_singleton : public
singleton { public: virtual auto get() -> logger & override { thread_local memory_logger inst; return inst; } }; tslogger::memory_logger_singleton instance; 我们要返回这样一个实例
75 2021 Pure C++ | PURECPP auto load = []
{ return plugin<singleton>(fs::current_path() / libname); }; auto inst = load(); std::thread th[] = { /* 下一张幻灯片 */ }; for (auto &thr : th) thr.join(); 可执行文件的 main 函数
76 2021 Pure C++ | PURECPP 使用该库的线程 th[0] std::thread([&] {
inst->get(); /* th[1] 卸载了库之后 */ }) th[1] std::thread([&] { /* th[0] 初始化了 TLS 之后 */ inst->get(); inst.unload(); })
77 2021 Pure C++ | PURECPP class memory_logger : public
logger { ... memory_logger() { std::cout << " + thread (" << std::this_thread::get_id() << ") attached\n"; } ~memory_logger() { std::cout << " - thread (" << std::this_thread::get_id() << ") detached\n"; } 我们的线程特定 logger 会记录这些东西
78 2021 Pure C++ | PURECPP MSVC on Windows 10
79 2021 Pure C++ | PURECPP GCC + glibc on
Linux 表现好到不真实?
80 2021 Pure C++ | PURECPP class memory_logger_singleton : public
singleton { ... memory_logger_singleton() { std::cout << " + process attached\n"; } ~memory_logger_singleton() { std::cout << " + process detached\n"; } }; 把静态对象的活动也记录下来
81 2021 Pure C++ | PURECPP auto load = []
{ return plugin<singleton>(fs::current_path() / libname); }; auto inst = load(); std::thread th[] = { /* ... */ }; for (auto &thr : th) thr.join(); load(); 然后在所有线程退出之后再载入一次这个库
82 2021 Pure C++ | PURECPP • 虽然泄漏了库的句柄,但语义是对的 MSVC on
Windows 10
83 2021 Pure C++ | PURECPP • …静态对象没被重新初始化? GCC +
glibc on Linux
84 2021 Pure C++ | PURECPP • RTLD_NODELETE – 调用
dlclose() 时不卸载共享库 – 后果是,即便之后用 dlopen() 重新载入该库,其中的静态变量和全局变量也不会重新初始化 • DF_1_NODELETE (elf.h) – 自动给包含 thread_local 对象的共享库打上该标记,直到所有这些对象被销毁 – 此标记被清除之后,再次调用 dlclose() 才能卸载该库 glibc 在销毁 thread_local 对象的过程中调用 dlclose() 是无用操作
85 2021 Pure C++ | PURECPP • 函数可能有生命期了 • 实现需要防止具线程存储期的对象比它们的析构函数活得更长
随卸载而来的问题
2021 Pure C++ PURECPP 结语
87 2021 Pure C++ | PURECPP • 动态载入库「动态」的程度有不同级别,各有用处 结语
88 2021 Pure C++ | PURECPP 越动态,越多挑战 延迟加载 • 函数地址在程序运行过
程中可能会变 动态实体 (显式加载) • 存在程序和对象模型之 外的函数和对象 插件 (加载多个定义) • 违反 ODR Live update (卸载) • 函数可能有生命期 • 析构函数也有生命期给 实现 TLS 带来了挑战
89 2021 Pure C++ | PURECPP • 动态载入库「动态」的程度有不同级别,各有用处 • 在主流平台上用动态载入库创建抽象是可行的
• 一定程度的标准化能给这里的例子增加类型安全和可移植性 结语
2021 Pure C++ PURECPP Questions? @lichray zhihaoy/dl-examples