为什么克隆系统调用后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使用cloneCLONE_VM – 或者如果您这样做,您必须至少确保在父或子中调用clone后限制自己从标准库中调用任何函数。 为了使多个线程或进程共享相同的内存,访问共享资源(如堆)的任何函数的实现都需要

  1. 要意识到多个控制流可能正在访问它,以便他们可以安排执行适当的同步,以及
  2. 能够通过线程指针获取有关自己身份的信息,通常存储在特殊的机器寄存器中。 这是完全实现内部的,因此您无法安排通过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);