本文主要是介绍疑难杂症之malloc死锁__lll_lock_wait_private,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
查看glibc源码可知, malloc内部是有锁的。那说明malloc是一个线程安全型函数,但是它不是一个可重入函数。重入的意思是,比如当前线程正在做malloc, 如果此时因为某种原因触发了信号,那么操作系统会保存好现场(正在执行的malloc),转而去执行信号处理函数,如果信号处理函数里面又有malloc的调用,那么此时就发生了malloc重入。当malloc重入时,可能导致线程死锁。
main.c 如下:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <pthread.h>
using namespace std;
void signal_handler(int signum);#define NUM 100000void* test( void* args )
{ while(1) { printf("try to malloc in test, thread id = %u\r\n", pthread_self()); void *p = malloc(NUM); } return NULL;
}int main()
{ signal(SIGTERM, signal_handler); pthread_t thread1; pthread_create(&thread1, NULL, test, NULL);for(;;){printf("malloc in main function, thread id = %u\r\n", pthread_self());void *p = malloc(NUM);}pthread_join(thread1, NULL);return 0;
} void signal_handler(int signum)
{ printf("receive SIGTERM, malloc again, thread id = %u\r\n", pthread_self());void *p = malloc(NUM);
}
主线程和子线程都一直在做malloc, 信号处理函数也在调用malloc. 此时运行一个脚本,让其不断触发SIGTERM,那么程序运行一段时间之后可能发生死锁。
编译:
g++ main.c -lpthread
运行:
./a.out
查看线程号:
ps -ef | grep a.out
root 6378 5558 0 11:21 pts/1 00:00:01 ./a.out
同时启动一个javascript, 让其不断触发SIGTERM,脚本如下:
# $language = "JScript"
# $interface = "1.0"function main()
{shellCmds();
}function shellCmds()
{for(;;){var cmd1 = "kill 6378";crt.Screen.Send(cmd1+"\r\n");}}
运行一段时间之后系统死锁了:
查看此时的调用栈:
#0 0x00007f2258ed597e in __lll_lock_wait_private () from /lib64/libc.so.6
#1 0x00007f2258e6fc93 in _L_lock_9971 () from /lib64/libc.so.6
#2 0x00007f2258e6e169 in malloc () from /lib64/libc.so.6 //第二次进入malloc
#3 0x0000000000400840 in signal_handler(int) ()
#4 <signal handler called>
#5 0x00007f2258ec541a in mmap64 () from /lib64/libc.so.6
#6 0x00007f2258e6cbc4 in _int_malloc () from /lib64/libc.so.6
#7 0x00007f2258e6e174 in malloc () from /lib64/libc.so.6 //第一次进入malloc
#8 0x000000000040080e in main ()
然而有时候,malloc的调用可能是间接的,比如往文件里面写log也会调用malloc, 以下是一段写文件的调用栈,最终block在malloc
#0 __lll_lock_wait_private (futex=0x74800018) at ./lowlevellock.c:32
#1 0x76120b08 in __GI___libc_malloc (bytes=bytes@entry=4096) at malloc.c:3063
#2 0x7610548c in __GI__IO_file_doallocate (fp=0x74a43428) at filedoalloc.c:101
#3 0x76118adc in __GI__IO_doallocbuf (fp=fp@entry=0x74a43428) at genops.c:365
#4 0x76115b60 in _IO_new_file_seekoff (fp=0x74a43428, offset=0, dir=1, mode=<optimized out>) at fileops.c:960
#5 0x76117360 in _IO_new_file_attach (fp=fp@entry=0x74a43428, fd=fd@entry=218) at fileops.c:379
#6 0x76111ecc in _IO_vdprintf (d=d@entry=218, format=format@entry=0x765aa1d8 "%u", arg=arg@entry=0x74a43520) at iovdprintf.c:46
#7 0x760f1874 in __GI___dprintf (d=d@entry=218, format=format@entry=0x765aa1d8 "%u") at dprintf.c:33
最后总结:
1 malloc是不可重入函数, 如果重入了可能会死锁。所以信号处理函数里面不能调用malloc.
2 malloc的调用有可能是间接地或者说是隐式的。所以信号处理函数应该尽可能简单, 比如只设置一个flag, 然后让其他线程根据这个flag来做其它事情。
这篇关于疑难杂症之malloc死锁__lll_lock_wait_private的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!