循环system()和cout的线程会破坏堆栈
Threads looping system() and cout corrupt the stack
运行以下代码的进程因分段错误而崩溃:
#include <stdlib.h>
#include <iostream>
#include <pthread.h>
void* f( void* )
{
while( true )
{
// It crashes inside this call (with cerr, too).
std::cout << 0;
}
return NULL;
}
int main()
{
pthread_t t;
pthread_create( &t, NULL, &f, NULL );
while( true )
{
// It crashes with any script/app; true is just simple.
system( "true" );
}
return 0;
}
大约每隔几秒钟执行一次就会崩溃(输出有数千到数百万个'0')。上面的代码会使cout << 0
调用中的几个函数崩溃。根据f()
中调用的额外函数或放在堆栈上的数据,它会在不同的地方崩溃。在gdb中,有时堆栈对于函数调用的顺序没有意义。由此我推断堆栈已损坏。
我发现多线程应用程序调用fork()有一些问题(参见两个提到堆栈损坏的注释)。如果文件描述符没有设置为FD_CLOEXEC
,那么分叉/克隆进程会复制它们。但是,没有显式创建的文件描述符。(我试着在fileno( stdout )
和fileno( stderr )
上设置FD_CLOEXEC
,没有任何积极的变化。)
即使没有显式的文件描述符,我可以不混合线程和fork()
吗?我是否只需要用等效的功能替换system()
调用?或者内核中有导致崩溃的错误,并且在2.6.30之后已经修复了?
我在一个ARM AT91处理器(armv5tejl)上运行它,Linux 2.6.30(为我的特定外设设置了一些覆盖和补丁)用GCC 4.3.2编译。
Linux 2.6.30 #1 Thu May 29 15:43:04 CDT 2014 armv5tejl GNU/Linux
我一直在用-g
和-O0
[交叉]编译它,但没有这些,它仍然崩溃:
arm-atmel-linux-gnueabi-g++ -o system_thread system_thread.cpp -lpthread
我也尝试过-fstack-protector-all
标志:有时它在__stack_chk_fail()
中崩溃,但有时其他函数指针或数据损坏,它更早崩溃。
它加载的库(从strace):
libpthread.so.0
libstdc++.so.6
libm.so.6
libgcc_s.so.1
libc.so.6
注意:因为它有时不会崩溃,并不是真正响应^C
,我通常在后台运行它:
$ killall -9 system_thread; rm -f log; system_thread >log &
我已经为几个不同的体系结构和Linux内核版本编译了这个程序,但是我没有看到它在其他地方崩溃:
Linux 3.10.29 #1 Wed Feb 12 17:12:39 CST 2014 armv5tejl GNU/Linux
Linux 3.6.0-dirty #3 Wed May 28 13:53:56 CDT 2014 microblaze GNU/Linux
Linux 3.13.0-27-generic #50-Ubuntu SMP Thu May 15 18:06:16 UTC 2014 x86_64 x86_64 GNU/Linux
Linux 3.8.0-35-generic #50~precise1-Ubuntu SMP Wed Dec 4 17:25:51 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
编辑:请注意,在相同的体系结构(armv5tejl)上,它不会在Linux 3.10.29上崩溃。此外,在我的"设备"的早期版本(较旧的服务器和客户端应用程序)上运行时,它不会崩溃,具有相同的Linux版本- 2.6.30。所以操作系统的环境是有一定影响的。
BusyBox v1.20.1提供system()
调用的sh
这在使用您提到的2.6.30内核的ARM处理器上是可重现的,但在master中不能。我们可以使用git bisect
来找到这个bug被修复的地方(大约需要16次迭代)。请注意,由于git bisect
是为了找到回归,但在这种情况下,master是"好",而过去的版本是"坏",我们需要颠倒"好"answers"坏"的含义。
被分割发现的罪魁祸首是这个提交,修复涉及fork()的"用户空间数据损坏实例"。此症状与您描述的症状非常相似,并且也可能损坏堆栈外的内存。在将此提交和所需的父组件移植到2.6.30内核后,您发布的代码不再崩溃。
- C++线程的可用堆栈大小
- 应该将线程分配给堆栈还是堆?
- 其他线程堆栈上的可用内存无效
- 访问其他线程堆栈变量如何在C++中工作?
- 为什么RCOUT和RPRINTF在多线程时会导致堆栈限制错误
- 如何获取 systemc 线程的堆栈大小
- 线程安全堆栈实现
- C 线程堆栈地址范围
- 如何使用pthreads在Solaris上增加C 线程堆栈的大小
- 线程的单独堆栈空间的重要性
- 如何使用 Win32 API 获取任何线程的堆栈大小和堆栈限制
- 将信号从Anthoter非过程中发送到线程,并没有发生记录堆栈
- 多线程时,您该怎么做才能停止堆栈空间
- 分叉式堆栈复制线程C++
- 繁忙时线程安全堆栈互斥被破坏
- 线程的调用堆栈中充满了相同的函数调用--curl_inet_ntop()
- 非阻塞线程安全堆栈
- 在窗口中移动或指定线程堆栈空间
- 线程堆栈错误
- C++多线程堆栈溢出