为什么克隆系统调用后会重新出现死锁?

时间:2012-12-06 02:38:01

标签: c clone deadlock realloc

我遇到一个问题,即在clone()系统调用之后某个时候realloc()死锁。

我的代码是:

#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <linux/types.h>
#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()之前还不够吗?

3 个答案:

答案 0 :(得分:3)

您不能自己clone使用CLONE_VM - 或者如果您这样做,您必须至少确保在调用clone后限制自己调用标准库中的任何函数无论是父母还是孩子。为了让多个线程或进程共享相同的内存,访问共享资源(如堆)的任何函数的实现都需要

  1. 要知道多个控制流可能会访问它,以便他们可以安排执行适当的同步,
  2. 能够通过线程指针获取有关其自身身份的信息,通常存储在特殊的机器寄存器中。这是完全实现内部的,因此您无法安排通过clone自己创建的新“线程”以获得正确设置的线程指针。
  3. 正确的解决方案是使用pthread_create,而不是clone

答案 1 :(得分:0)

你不能这样做:

for (i=0; i<200000; i++)
        ptr = realloc(ptr, sizeof(int)*i);
free(ptr);

第一次循环,i为零。 realloc( ptr, 0 )相当于free( ptr ),您不能free两次。

答案 2 :(得分:0)

我在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);