2

clone()syscallの後で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 ?? ()

私のOSはdebianlinux-2.6.32-5-amd64で、GNU Cライブラリ(Debian EGLIBC 2.11.3-4)の安定版リリースバージョン2.11.3を使用しています。私はeglibcがこのバグの犯罪者であると深く疑っています。clone()syscallで、realloc()を使用する前に十分ではありませんか?

4

3 に答える 3

3

clone自分で使用することはできません。使用するCLONE_VM場合は、少なくともclone、親または子のいずれかを呼び出した後、標準ライブラリから関数を呼び出さないように制限する必要があります。複数のスレッドまたはプロセスが同じメモリを共有するためには、共有リソース(ヒープなど)にアクセスする関数の実装は、次のことを行う必要があります。

  1. 複数の制御フローが潜在的にアクセスしているため、適切な同期を実行するように調整できることに注意してください。
  2. 通常は特別なマシンレジスタに格納されているスレッドポインタを介して、自分のIDに関する情報を取得できます。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 )であり、2 回行うことはできませんfree

于 2012-12-06T02:44:06.937 に答える
0

clone() syscall にフラグ 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 に答える