c++ - 动态加载类的静态成员变量

标签 c++ linux shared-libraries dynamic-loading

如果我在 C++ 中使用类似 dlopen 的东西加载一些符号,而该翻译单元中的其他类有 static 成员变量,这些静态的行为到底是什么成员变量。它们是否被初始化,因为库并没有真正加载你查找的符号(我认为后者不是真的,因为如果你查找的符号需要它们也需要加载)?

最佳答案

简而言之,无法保证无法在编译时初始化的静态变量会在引用同一翻译单元中的外部可见函数或变量之前被初始化。即使对于静态链接也是如此。至于试图让动态加载的库中的静态变量在加载时初始化,我的经验是通常你会很幸运,特别是对于小程序,但从根本上说这是未定义的行为,不应该依赖。由此产生的错误是不可预测的、难以重现的,并且是高度系统特定的。

首先,一些标准和解释为什么这是未定义的行为,然后是一些解决方法。

不幸的是,static 这个词在标准中重载了,所以请耐心等待。该标准引用了静态存储持续时间静态初始化。标准定义的存储持续时间类型有静态、线程、自动和动态。他们就像他们听起来的那样。静态存储持续时间意味着这样一个变量的生命周期是程序的整个持续时间。

静态初始化是一个不同的概念。虽然一个变量在每次程序执行时可能只存储一次,但在程序启动时可能并不知道它将初始化的值。在程序开始时,所有具有静态存储持续时间的变量都将被初始化为零,然后那些可以被初始化的变量将被初始化为常量。细节在 §3.6.2 中,但粗略地说,如果静态变量的初始化仅依赖于常量表达式,则静态变量将被常量初始化。零初始化和常量初始化一起称为静态初始化。对应的是动态初始化。这些是有趣的,但不幸的是,在动态链接的情况下,在 main() 第一次执行之前,或者在 dlopen() 之前,没有强制动态初始化发生的可移植方法> 返回,在动态加载的情况下。 C++ 根本不需要这样。

C++11 标准的关键部分在 §3.6.2 中:

It is implementation-defined whether the dynamic initialization of a non-local variable with static storage duration is done before the first statement of main. If the initialization is deferred to some point in time after the first statement of main, it shall occur before the first odr-use (3.2) of any function or variable defined in the same translation unit as the variable to be initialized.

不过,如果您进行过试验,您会注意到有时这确实有效。有时,您可以通过将任意代码填充到静态变量的构造函数中来让任意代码在库加载时运行。这是否发生完全取决于编译器(而不是链接器)。 dlopen 的联机帮助页解释。

If a dynamic library exports a routine named _init(), then that code is executed after the loading, before dlopen() returns

检查用标准 C++ 编写的小型共享对象的 asm 输出,我可以看到 clang 3.4 和 g++ 4.8 都添加了一个 _init 部分,但它们并不是必须这样做。

至于解决方法,已变得司空见惯的 gcc 扩展确实允许控制此行为。通过向函数添加构造函数属性,我们可以坚持它们在库初始化时运行。 dlopen 的链接联机帮助页建议使用此方法。

参见 GCC documentation关于函数属性和 this SO question其中有一个示例用法。这个扩展被 gcc、clang、IBM XL 支持,我猜 icc 也支持它。 MSVC 不支持这个,但我知道有类似的东西。

真正便携的解决方案是难以捉摸的。正如标准所说,如果您能以某种方式在与静态变量相同的翻译单元中引起 odr 使用,则必须初始化静态变量。调用一个函数,即使是为此目的的虚拟函数,也是可行的。

关于c++ - 动态加载类的静态成员变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24656088/

相关文章:

shared-libraries - yocto 项目 - 配方 sysroot 中缺少依赖项

c++ - 对齐堆栈上的数据 (C++)

c++ - const char指针模板特化

linux - nohup 不适用于 Chrome

java - 添加 Android 库 : Only a type can be imported. com.project.test.networktasklibrary 解析为一个包

linux - 共享库安装

c++ - 标准 C++ 库中 size_t 的有符号变体

c++ - 在 matlab mex 代码中抑制 mexPrintf

javascript - 我如何找到称为 node.js 脚本的进程?

linux - Linux Xlib 中的后备缓冲区(无 OpenGL)