为什么克隆系统调用后realloc死锁?
我有一个问题,在clone()syscall之后的某个时候realloc()死锁。
我的代码是:
#include #include #include #include #include #include #include #define CHILD_STACK_SIZE 4096*4 #define gettid() syscall(SYS_gettid) #define log(str) fprintf(stderr, "[pid:%d tid:%d] "str, getpid(),gettid()) int clone_func(void *arg){ int *ptr=(int*)malloc(10); int i; for (i=1; i<200000; i++) ptr = realloc(ptr, sizeof(int)*i); free(ptr); return 0; } int main(){ int flags = 0; flags = CLONE_VM; log("Program started.\n"); int *ptr=NULL; ptr = malloc(16); void *child_stack_start = malloc(CHILD_STACK_SIZE); int ret = clone(clone_func, child_stack_start +CHILD_STACK_SIZE, flags, NULL, NULL, NULL, NULL); int i; for (i=1; i<200000; i++) ptr = realloc(ptr, sizeof(int)*i); free(ptr); return 0; }
gdb中的callstack是:
[pid:13268 tid:13268] Program started. ^Z[New LWP 13269] Program received signal SIGTSTP, Stopped (user). 0x000000000040ba0e in __lll_lock_wait_private () (gdb) bt #0 0x000000000040ba0e in __lll_lock_wait_private () #1 0x0000000000408630 in _L_lock_11249 () #2 0x000000000040797f in realloc () #3 0x0000000000400515 in main () at test-realloc.c:36 (gdb) i thr 2 LWP 13269 0x000000000040ba0e in __lll_lock_wait_private () * 1 LWP 13268 0x000000000040ba0e in __lll_lock_wait_private () (gdb) thr 2 [Switching to thread 2 (LWP 13269)]#0 0x000000000040ba0e in __lll_lock_wait_private () (gdb) bt #0 0x000000000040ba0e in __lll_lock_wait_private () #1 0x0000000000408630 in _L_lock_11249 () #2 0x000000000040797f in realloc () #3 0x0000000000400413 in clone_func (arg=0x7fffffffe53c) at test-realloc.c:20 #4 0x000000000040b889 in clone () #5 0x0000000000000000 in ?? ()
我的操作系统是debian linux-2.6.32-5-amd64,GNU C Library(Debian EGLIBC 2.11.3-4)稳定版本2.11.3。 我深深怀疑eglibc是这个bug的罪魁祸首。 在clone()系统调用上,使用realloc()之前还不够吗?
您CLONE_VM
使用clone
与CLONE_VM
– 或者如果您这样做,您必须至少确保在父或子中调用clone
后限制自己从标准库中调用任何函数。 为了使多个线程或进程共享相同的内存,访问共享资源(如堆)的任何函数的实现都需要
- 要意识到多个控制流可能正在访问它,以便他们可以安排执行适当的同步,以及
- 能够通过线程指针获取有关自己身份的信息,通常存储在特殊的机器寄存器中。 这是完全实现内部的,因此您无法安排通过
clone
自己创建的新“线程”以获得正确设置的线程指针。
正确的解决方案是使用pthread_create
,而不是clone
。
你不能做这个:
for (i=0; i<200000; i++) ptr = realloc(ptr, sizeof(int)*i); free(ptr);
第一次通过循环, i
是零。 realloc( ptr, 0 )
相当于free( ptr )
,你不能free
两次。
我在clone()系统调用中添加了一个标志CLONE_SETTLS。 然后僵局消失了。 所以我认为eglibc的realloc()使用了一些TLS数据。 当新线程在没有新TLS的情况下创建时,一些锁(在TLS中)在这个线程和他父亲之间共享,而realloc()使用那些锁定。 因此,如果有人想直接使用clone(),最好的方法是为新线程分配一个新的TLS。
代码片段喜欢这个:
flags = CLONE_VM | CLONE_SETTLS; struct user_desc* p_tls_desc = malloc(sizeof(struct user_desc)); clone(clone_func, child_stack_start +CHILD_STACK_SIZE, flags, NULL, NULL, p_tls_desc, NULL);