2

我有一个问题,即在 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 中的调用栈是:

[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 库 (Debian EGLIBC 2.11.3-4) 稳定版本 2.11.3。我深深怀疑 eglibc 是这个 bug 的罪魁祸首。在 clone() 系统调用上,在使用 realloc() 之前还不够吗?

4

3 回答 3

3

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

  1. 请注意,多个控制流可能会访问它,以便他们可以安排执行适当的同步,并且
  2. 能够通过线程指针获取有关自己身份的信息,通常存储在一个特殊的机器寄存器中。这完全是内部实现的,因此您无法安排您自己创建的新“线程”clone以正确设置线程指针。

正确的解决方案是使用pthread_create,而不是clone

于 2012-12-06T03:18:38.923 回答
0

你不可以做这个:

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

第一次通过循环,i为零。 realloc( ptr, 0 )等于free( ptr ),你不能free两次。

于 2012-12-06T02:44:06.937 回答
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);
于 2012-12-07T03:30:05.233 回答