令我惊讶的是,当编译恰好共享一个静态库的两个单独的dylib时,该静态库中定义的全局变量似乎是共享的。 这篇SO文章似乎表明每个动态库都会将其全局变量分开,但在上述情况下,下面包含的测试代码证明这不是真的。我正在寻求确认这在 macOS(可能还有 Linux)上是可以预期的。
鉴于这种情况:
我预计当“AAA”打印“Bar”时它会是 111,而当“BBB”打印 bar 时它仍然是 123。相反,当“BBB”打印“Bar”时,它是 111,表明 - 从MyApp 的观点——只有一个共享的“Bar”实例。
我的怀疑是,由于“Bar”同时被“AAA”和“BBB”暴露,当你动态链接两个dylib时,两个“wins”之一,因为名称完全相同,链接器无法区分两个。
这个怀疑似乎可以通过在Xcode的“Other C++ Flags”中设置“-fvisibility=hidden”标志得到证实。如果我为动态库“AAA”和“BBB”执行此操作,那么这两个全局变量似乎是不同的。我预计这是因为'visibility=hidden'隐藏了“Bar”的两个副本,从而解决了上一段中描述的冲突。
有人可以证实我对此的理解吗?
--- 示例代码 ---
静态库CGlobalTest有一个C++类,如下所示。该类在函数内部声明了一个全局,在.cpp 文件中声明了一个类全局和静态全局。函数 GetGlobal() 根据 GlobalType 参数返回对其中之一的引用。
CGlobalTest.cpp:
class CGlobalTest
{
public:
CGlobalTest() { }
static int& GetFunctionGlobal()
{
static int sFunctionGlobal = 123;
return sFunctionGlobal;
}
static int& GetClassGlobal()
{
return sClassGlobal;
}
static int& GetFileGlobal();
static int& GetGlobal(
GlobalType inType)
{
switch (inType) {
case kFunctionGlobal:
return GetFunctionGlobal();
break;
case kClassGlobal:
return GetClassGlobal();
break;
case kFileGlobal:
return GetFileGlobal();
break;
}
}
static int sClassGlobal;
};
CGlobalTest.h
#include "static_lib.h"
int CGlobalTest::sClassGlobal = 456;
int sFileGlobal = 789;
int&
CGlobalTest::GetFileGlobal()
{
return sFileGlobal;
}
然后我有两个使用 CGlobalTest 静态库的动态库,称为 global_test_dynamic_1 和 global_test_dynamic_2。 1 和 2 的代码基本相同,所以我只包括第一个。
dynamic_lib_1.cpp:
#include "dynamic_lib_1.h"
#include "static_lib.h"
#include "stdio.h"
const char*
GlobalTypeToString(
GlobalType inType)
{
const char* type = "";
switch (inType) {
case kFunctionGlobal:
type = "Function Global";
break;
case kClassGlobal:
type = "Class Global";
break;
case kFileGlobal:
type = "File Global";
break;
}
return type;
}
void dynamic_lib_1_set_global(enum GlobalType inType, int value)
{
int& global = CGlobalTest::GetGlobal((GlobalType) inType);
global = value;
printf("Dynamic Lib 1: Set %s: %d (%p)\n", GlobalTypeToString(inType), global, &global);
}
void dynamic_lib_1_print_global(enum GlobalType inType)
{
const int& global = CGlobalTest::GetGlobal((GlobalType) inType);
printf("Dynamic Lib 1: %s = %d (%p)\n", GlobalTypeToString(inType), global, &global);
}
dynamic_lib_1.h
#ifdef __cplusplus
#define EXPORT extern "C" __attribute__((visibility("default")))
#else
#define EXPORT
#endif
#include "global_type.h"
EXPORT void dynamic_lib_1_set_global(enum GlobalType inType, int value);
EXPORT void dynamic_lib_1_print_global(enum GlobalType inType);
最后,有一个链接到两个dylib的应用程序。
#include "dynamic_lib_1.h"
#include "dynamic_lib_2.h"
#include "global_type.h"
#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>
#include <unistd.h>
typedef void (*print_func)(enum GlobalType inType);
typedef void (*set_func)(enum GlobalType inType, int value);
int main()
{
printf("App is starting up...\n");
// LOAD DYNAMIC LIBRARY 1
void* handle1 = dlopen("libglobal_test_dynamic_1.dylib", RTLD_NOW);
assert(handle1 != NULL);
print_func d1_print = (print_func) dlsym(handle1, "dynamic_lib_1_print_global");
assert(d1_print != NULL);
set_func d1_set = (set_func) dlsym(handle1, "dynamic_lib_1_set_global");
assert(d1_set != NULL);
// LOAD DYNAMIC LIBRARY 2
void* handle2 = dlopen("libglobal_test_dynamic_2.dylib", RTLD_NOW);
assert(handle1 != NULL);
print_func d2_print = (print_func) dlsym(handle2, "dynamic_lib_2_print_global");
assert(d2_print != NULL);
set_func d2_set = (set_func) dlsym(handle2, "dynamic_lib_2_set_global");
assert(d2_set != NULL);
enum GlobalType type;
printf("**************************************************\n");
printf("** FUNCTION GLOBAL\n");
printf("**************************************************\n");
type = kFunctionGlobal;
(d1_print)(type);
(d2_print)(type);
printf("** SET D1 TO 111 - THEN PRINT FROM D2\n");
d1_set(type, 111);
d1_print(type);
d2_print(type);
printf("** SET D2 TO 222 - THEN PRINT FROM D1\n");
d2_set(type, 222);
d2_print(type);
d1_print(type);
printf("**************************************************\n");
printf("** CLASS GLOBAL\n");
printf("**************************************************\n");
type = kClassGlobal;
(d1_print)(type);
(d2_print)(type);
printf("** SET D1 TO 111 - THEN PRINT FROM D2\n");
d1_set(type, 111);
d1_print(type);
d2_print(type);
printf("** SET D2 TO 222 - THEN PRINT FROM D1\n");
d2_set(type, 222);
d2_print(type);
d1_print(type);
printf("**************************************************\n");
printf("** FILE GLOBAL\n");
printf("**************************************************\n");
type = kFileGlobal;
(d1_print)(type);
(d2_print)(type);
printf("** SET D1 TO 111 - THEN PRINT FROM D2\n");
d1_set(type, 111);
d1_print(type);
d2_print(type);
printf("** SET D2 TO 222 - THEN PRINT FROM D1\n");
d2_set(type, 222);
d2_print(type);
d1_print(type);
return 0;
}