我正在尝试调试用GCC编译的C ++程序,该程序在启动时冻结。 GCC互斥锁保护函数的静态局部变量,似乎等待获取这样的锁是它冻结的原因。如何发生这种情况相当令人困惑。第一个模块A的静态初始化发生(有__static_init函数GCC调用在回溯中可见),它调用一个函数Foo(),它有一个静态局部变量。静态局部变量是构造函数通过多层函数调用的对象,然后突然回溯有几个??,然后它是在第二个模块B的静态初始化中(__static函数再次发生)然后调用Foo(),但由于Foo()从未在第一次返回本地静态变量上的互斥锁,并且它已锁定。
一个静态init如何触发另一个?我的第一个理论是共享库 - 模块A将调用模块B中的一些函数,这将导致模块B加载,从而触发B的静态初始化,但事实并非如此。模块A根本不使用模块B.所以我有第二个(也是可怕的)猜测。说:
模块A使用一些模板化函数或模板化类中的函数,例如: foo<int>::bar()
模块B也使用foo<int>::bar()
模块A完全不依赖于模块B
在链接时,链接器有两个foo<int>::bar()
实例,但这没关系,因为模板函数被标记为弱符号......
在运行时,模块A调用foo<int>::bar
,并且模块B的静态init被触发,即使模块B不依赖于模块A!为什么?因为链接器决定在链接时使用模块B的foo :: bar实例而不是模块A的实例。
此特定情况是否有效?或者,一个模块的静态init是否永远不会在另一个模块中触发静态init?
澄清: GCC会自动创建互斥锁以保护任何函数静态变量。我自己也没有用互斥量做任何事情。这是GCC使函数静态变量线程安全的方法。
更新:我知道在翻译单元之间没有定义静态初始化,我不应该依赖于订单。但我很好奇这是否是正常行为,作为调试问题的线索。编译器生成执行此操作的代码是否正常,或者它是否可能表明GCC中存在错误?
答案 0 :(得分:4)
欢迎来到the "static initialization order fiasco"。您应该只阅读整篇文章,因为它将(详细地)描述您如何遇到此问题&amp;如何解决它。
答案 1 :(得分:2)
Bill推出了有效的C ++第4项:
在不同的翻译单元中定义的非本地静态对象的初始化顺序是未定义的
简单地说,允许编译器做任何想做的事。