我正在尝试调试使用 GCC 编译的 C++ 程序,该程序在启动时冻结。 GCC 互斥体保护函数的静态局部变量,看来等待获取这样的锁是它冻结的原因。这是如何发生的相当令人困惑。第一个模块 A 进行静态初始化(GCC 调用的 __static_init 函数在回溯中可见),它调用具有静态局部变量的函数 Foo()。静态局部变量是一个对象,其构造函数通过几层函数调用,然后回溯突然出现几个??,然后它位于第二个模块B的静态初始化中(__static函数再次出现) ,然后调用 Foo(),但由于 Foo() 从未返回第一次,局部静态变量上的互斥锁仍然被设置,并且它被锁定。
一个静态初始化如何触发另一个静态初始化?我的第一个理论是共享库——模块 A 会调用模块 B 中的某个函数,这会导致模块 B 加载,从而触发 B 的静态初始化,但情况似乎并非如此。模块A根本不使用模块B。所以我有第二个(也是可怕的)猜测。假设:
-
模块 A 使用某些模板函数或模板类中的函数,例如 foo::bar()
-
模块 B 也使用 foo::bar()
-
模块 A 根本不依赖于模块 B
-
foo::bar() 实例,但这没关系,因为模板函数被标记为弱符号...
-
在运行时,模块 A 调用 foo::bar
,并且模块 B 的静态初始化被触发,即使模块 B 不依赖于模块 A!为什么?因为链接器决定在链接时使用模块 B 的 foo::bar 实例而不是模块 A 的实例。
这个特定场景有效吗?或者一个模块的静态初始化永远不会触发另一个模块中的静态初始化?
说明:GCC 自动创建互斥体来保护任何函数静态变量。我自己没有对互斥体做任何事情。这是 GCC 使函数静态变量线程安全的方法。
更新:我知道翻译单元之间没有定义静态初始化,并且我不应该依赖于顺序。但我很好奇这是否是正常行为,可以作为调试问题的线索。编译器生成执行此操作的代码是否正常,或者是否可能表明 GCC 中存在错误?
I'm trying to debug a C++ program compiled with GCC that freezes at startup. GCC mutex protects function's static local variables, and it appears that waiting to acquire such a lock is why it freezes. How this happens is rather confusing. First module A's static initialization occurs (there are __static_init functions GCC invokes that are visible in the backtrace), which calls a function Foo(), that has a static local variable. The static local variable is an object who's constructor calls through several layers of functions, then suddenly the backtrace has a few ??'s, and then it's is in the static initialization of a second module B (the __static functions occur all over again), which then calls Foo(), but since Foo() never returned the first time the mutex on the local static variable is still set, and it locks.
How can one static init trigger another? My first theory was shared libraries -- that module A would be calling some function in module B that would cause module B to load, thus triggering B's static init, but that doesn't appear to be the case. Module A doesn't use module B at all. So I have a second (and horrifying) guess. Say that:
-
Module A uses some templated function or a function in a templated class, e.g. foo<int>::bar()
-
Module B also uses foo<int>::bar()
-
Module A doesn't depend on module B at all
-
At link time, the linker has two instances of foo<int>::bar()
, but this is OK because template functions are marked as weak symbols...
-
At runtime, module A calls foo<int>::bar
, and the static init of module B is triggered, even though module B doesn't depend on module A! Why? Because the linker decided to go with module B's instance of foo::bar instead of module A's instance at link time.
Is this particular scenario valid? Or should one module's static init never trigger static init in another module?
Clarification: GCC creates the mutexes automatically to guard any function static variable. I'm not doing anything with mutexes myself. It's GCC's way of making function static variables thread safe.
Update: I know that static initialization is not defined between translation units and that I shouldn't depend on an order. But I'm curious if this is normal behavior as a clue towards debugging the problem. Is it normal for a compiler to generate code that does this or is it potentially indicative of a bug in GCC?
发布评论
评论(2)
欢迎来到“静态初始化顺序惨败”。您可能应该阅读整篇文章,因为它将(详细)描述您可能如何遇到此问题和解决方案。如何修复它。
Welcome to the "static initialization order fiasco". You should probably just read that entire article, as it will describe (in detail) how you may be running into this issue & how to fix it.
Bill 拿出了《Effective C++ Item 4》:
简单地说,允许编译器为所欲为。
Bill pulls out Effective C++ Item 4 :
Simply put, the compiler is allowed to do whatever it wants.