Visual C++:在 DLL 加载期间全局变量初始化顺序是否确定?
Visual C++: Is global variables initialization order deterministic during DLL loading?
假设我构建简单的 DLL,由两个翻译单元组成:
第一
// foo.cpp
struct Foo
{
//...
} g_foo;
// ... other stuff
第二个
// bar.cpp
struct Bar
{
//...
} g_bar;
// ... other stuff
我知道 C++ 标准没有指定全局变量初始化的顺序。问题是:一旦我构建了 Windows DLL,在 LoadLibrary
调用期间执行的全局变量初始化的顺序是否确定(每个 LoadLibrary
调用将启动变量初始化 g_foo
和 g_bar
顺序相同)或者它可能取决于某些 loader/system 设置?
I'm aware of the fact that C++ standard doesn't specify the order of
global variables initialization.
To be precise, it does when the global variables are within a single translation unit:
有序动态初始化,适用于所有其他非本地
变量:在一个翻译单元中,初始化这些
变量总是按照它们的定义的精确顺序排列
出现在源代码中。
上面的 DLL 代码,其中在两个不同的翻译单元中有两个不同的全局变量,将在 link 之前产生 两个不同的 .OBJ 文件。然后,当 .OBJ 文件link一起形成一个 .DLL 时,C++“pre-main”运行时间代码将附加到 .DLL。当 .DLL 通过进程启动或 LoadLibrary
绑定到进程的地址 space 时,此存根代码将可以访问 DLL 中的 table,在调用之前在 DllMain 中,它将遍历,调用 table 中 link 时间合成的静态非成员函数,每个非成员函数的工作是 运行 宁一个 class中的成员构造函数table为对应的全局对象。当然,在从您的进程地址 space 中删除 DLL 期间,通过进程退出或 FreeLibrary
,非成员函数将被类似地调用,但顺序相反(后进先出)。
鉴于 table 被 LINK.EXE“烘焙”到 .DLL 中,DLL 中全局变量的构造顺序,无论它们来自相同的翻译单元还是不同的,将被预先确定。它不会是 predictable 之前 link-time,正如你所指出的,但无论它变成 after link-time,这就是它在 .DLL 的生命周期中保留的内容,因为只有 LINK.EXE 有能力构造那个全局变量构造函数 table,并且一旦它被构造,它就被构造.
如果有人想知道哪个在先:全局变量的构造,还是程序员提供的 DllMain,那是前者。 C++ 运行 时间代码是调用程序员提供的 DllMain 的代码,如 link provided by @Algirdas Preidžius.
中所示
假设我构建简单的 DLL,由两个翻译单元组成:
第一
// foo.cpp
struct Foo
{
//...
} g_foo;
// ... other stuff
第二个
// bar.cpp
struct Bar
{
//...
} g_bar;
// ... other stuff
我知道 C++ 标准没有指定全局变量初始化的顺序。问题是:一旦我构建了 Windows DLL,在 LoadLibrary
调用期间执行的全局变量初始化的顺序是否确定(每个 LoadLibrary
调用将启动变量初始化 g_foo
和 g_bar
顺序相同)或者它可能取决于某些 loader/system 设置?
I'm aware of the fact that C++ standard doesn't specify the order of global variables initialization. To be precise, it does when the global variables are within a single translation unit:
有序动态初始化,适用于所有其他非本地 变量:在一个翻译单元中,初始化这些 变量总是按照它们的定义的精确顺序排列 出现在源代码中。
上面的 DLL 代码,其中在两个不同的翻译单元中有两个不同的全局变量,将在 link 之前产生 两个不同的 .OBJ 文件。然后,当 .OBJ 文件link一起形成一个 .DLL 时,C++“pre-main”运行时间代码将附加到 .DLL。当 .DLL 通过进程启动或 LoadLibrary
绑定到进程的地址 space 时,此存根代码将可以访问 DLL 中的 table,在调用之前在 DllMain 中,它将遍历,调用 table 中 link 时间合成的静态非成员函数,每个非成员函数的工作是 运行 宁一个 class中的成员构造函数table为对应的全局对象。当然,在从您的进程地址 space 中删除 DLL 期间,通过进程退出或 FreeLibrary
,非成员函数将被类似地调用,但顺序相反(后进先出)。
鉴于 table 被 LINK.EXE“烘焙”到 .DLL 中,DLL 中全局变量的构造顺序,无论它们来自相同的翻译单元还是不同的,将被预先确定。它不会是 predictable 之前 link-time,正如你所指出的,但无论它变成 after link-time,这就是它在 .DLL 的生命周期中保留的内容,因为只有 LINK.EXE 有能力构造那个全局变量构造函数 table,并且一旦它被构造,它就被构造.
如果有人想知道哪个在先:全局变量的构造,还是程序员提供的 DllMain,那是前者。 C++ 运行 时间代码是调用程序员提供的 DllMain 的代码,如 link provided by @Algirdas Preidžius.
中所示