来自垂死的多线程进程的核心转储

Posted

技术标签:

【中文标题】来自垂死的多线程进程的核心转储【英文标题】:core dump from dying multithreaded process 【发布时间】:2011-02-15 17:38:02 【问题描述】:

我的多线程应用程序(C++、SunOS)与共享库动态链接。程序中有几个线程,其中一些来自库。其中一个线程调用 exit() 并导致从共享库中的另一个线程生成核心转储:

(dbx) where
  [1] 0x0(0xbeee0b30, 0x0, 0x0, 0x1c00, 0x1, 0xbeee0b50), at 0x0
  [2] STLCollectionWrapper<std::vector<SM_Timer*,std::allocator<SM_Timer*> > >::empty(0xbeee0b30, 0x0, 0x0, 0x1c00, 0xbca12200, 0x0), at 0xbee04690
  [3] GenPtrSortVec<SM_Timer,std::less<SM_Timer>,std::allocator<SM_Timer> >::isEmpty(0xbeee0b30, 0x0, 0x0, 0x0, 0x4fb0e0, 0xbd436b90), at 0xbee04424
  [4] sm_tmr_process(0x341000, 0x8e400, 0xbeeba00f, 0x1c00, 0x1, 0xbeee0800), at 0xbee03968
  [5] sm_nm_process_timeouts(0xbc67bf94, 0xbc67bf98, 0xbd4c3800, 0x0, 0xbca12200, 0xbee830f0), at 0xbee813dc
  [6] TimerThreadObject::poll(0x0, 0xbc67c000, 0x0, 0x0, 0xbedf1530, 0x1), at 0xbedf15f4
(dbx) thread
current thread ($thread) is t@null
(dbx) lwps
  l@1 LWP suspended in __SLIP.FINAL__A()
  l@3 LWP suspended in find_composition_start()
o>l@6 signal SIGSEGV in 0x0()

堆栈帧 6-4 来自 libA,帧 3-2 来自 libB。第 1 帧必须是从 C++ 标准库 (/usr/lib/libCstd.so.1?) 调用的。如您所见,此调用失败。

在第 4 帧,代码调用了 GenPtrSortVec 类型的全局对象的 isEmpty() 方法。该对象位于定义方法 sm_tmr_process() 的同一模块的堆栈中。后来在第 2 帧,代码调用了 STL 向量对象的 empty() 方法。这个向量是 GenPtrSortVec 类的一个字段。

我有以下关于这个问题的问题:

    为什么第一帧的地址是 0x0?

    在取消程序中的所有线程之前,libCstd 是否可能已从垂死的进程中卸载?请注意,libCstd 会作为动态依赖项自动加载到进程中。

还有两个关于退出流程的问题:

    是否有可能在取消所有线程并销毁全局/静态对象之前自动卸载了自动加载的共享库?

    是否有可能在取消所有线程之前已销毁全局或静态对象?

【问题讨论】:

libA.cppGenPtrSortVec&lt; SM_Timer &gt; timeoutList; void sm_tmr_process() timeoutList.isEmpty(); libB.h:link 【参考方案1】:

1.1 - 可能是空指针调用(参见 Jörgen)

1.2 - 没有

2.1 - 没有

2.2 - 可能

1.2/2.1:共享库在程序加载到内存时加载。然后,动态链接器将扫描所有外部引用并修复它们。这就是动态链接的过程。这不会被撤消,即操作系统不会卸载以这种方式加载的库。一旦程序终止,整个进程映像就会被丢弃。

2.2 - 这取决于您的应用程序。全局/共享对象的初始化可能会出现问题 - 请参阅静态初始化惨败。这同样适用于销毁。两种情况下的顺序都是由实现定义的。

【讨论】:

【参考方案2】:

答案 1:您可能调用了 NULL 函数指针。可能不是直接的,而是间接的。你能用零覆盖一个对象的 vtable,然后调用它的虚方法吗?

【讨论】:

只是一个小小的说明:vtable 本身通常应该驻留在只读内存中(尽管我认为这是特定于实现的)。可能被覆盖的是指向 vtable 的指针。 如果指向 vtable 的指针被覆盖,那么它不应该在 NULL 处崩溃。取消引用 vtable 指针应该会在调用站点崩溃,对吧? @Jörgen 我会说这取决于。取消引用无效指针是未定义的行为,因此之后一切皆有可能;-) - 如果它被 0 覆盖,它实际上应该在调用站点崩溃。如果它被其他任何东西覆盖,您可能会发现 vtable 中的相关指针指向 NULL - 至少这是我今晚的理解...... 能否解释一下如何覆盖指向 vtable 的指针? 指向 vtable 的指针位于对象本身的“某处”。它实际驻留的位置完全取决于编译器。例如,如果你要去memcpy((void*)object_ptr, source_data, some_length),你很可能会覆盖它。我并不是暗示你会直接做这样的事情,但是如果涉及到强制转换,或者写入基于堆栈的数组(并且对象是在堆栈上创建的),那么很有可能在不被编译器捕获的情况下做到这一点。 ..

以上是关于来自垂死的多线程进程的核心转储的主要内容,如果未能解决你的问题,请参考以下文章

不同机器上的单个进程与单台机器上的多线程,核心等于单 CPU 机器的数量

Python的多线程和多进程模块对比测试

现在的多线程,就说两个线程吧(除主线程),是并行运行吗?

Python的多进程模块multiprocessing

回炉重造之重读Windows核心编程-006-线程

进程,线程,以及Python的多进程实例