5

Ho scritto un codice per creare alcuni thread e ogni volta che uno dei thread termina viene creato un nuovo thread per sostituirlo. Siccome non ero in grado di creare un numero molto elevato di thread (> 450) usando pthreads, ho usato invece la chiamata di sistema clone. (Si noti che sono consapevole dell'implicazione di avere un numero così elevato di thread, ma questo programma è pensato solo per sottolineare il sistema).
Come clone() richiede lo spazio di stack per il thread figlio da specificare come parametro, io malloc il blocco richiesto dello spazio di stack per ogni thread e lo liberano quando finisce il thread. Al termine di un thread, invio un segnale al genitore per avvisarlo dello stesso.
Il codice è il seguente:Errore di segmentazione del debug in un programma multi-thread (utilizzando clone)

#include <sched.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <signal.h> 
#include <unistd.h> 
#include <errno.h> 

#define NUM_THREADS 5 

unsigned long long total_count=0; 
int num_threads = NUM_THREADS; 
static int thread_pids[NUM_THREADS]; 
static void *thread_stacks[NUM_THREADS]; 
int ppid; 

int worker() { 
int i; 
union sigval s={0}; 
for(i=0;i!=99999999;i++); 
if(sigqueue(ppid, SIGUSR1, s)!=0) 
    fprintf(stderr, "ERROR sigqueue"); 
fprintf(stderr, "Child [%d] done\n", getpid()); 
return 0; 
} 

void sigint_handler(int signal) { 
char fname[35]=""; 
FILE *fp; 
int ch; 
if(signal == SIGINT) { 
    fprintf(stderr, "Caught SIGINT\n"); 
    sprintf(fname, "/proc/%d/status", getpid()); 
    fp = fopen(fname,"r"); 
    while((ch=fgetc(fp))!=EOF) 
    fprintf(stderr, "%c", (char)ch); 
    fclose(fp); 
    fprintf(stderr, "No. of threads created so far = %llu\n", total_count); 
    exit(0); 
} else 
    fprintf(stderr, "Unhandled signal (%d) received\n", signal); 
} 


int main(int argc, char *argv[]) { 
int rc, i; long t; 
void *chld_stack, *chld_stack2; 
siginfo_t siginfo; 
sigset_t sigset, oldsigset; 

if(argc>1) { 
    num_threads = atoi(argv[1]); 
    if(num_threads<1) { 
    fprintf(stderr, "Number of threads must be >0\n"); 
    return -1; 
    } 
} 
signal(SIGINT, sigint_handler); 

/* Block SIGUSR1 */ 
sigemptyset(&sigset); 
sigaddset(&sigset, SIGUSR1); 
if(sigprocmask(SIG_BLOCK, &sigset, &oldsigset)==-1) 
    fprintf(stderr, "ERROR: cannot block SIGUSR1 \"%s\"\n", strerror(errno)); 

printf("Number of threads = %d\n", num_threads); 
ppid = getpid(); 
for(t=0,i=0;t<num_threads;t++,i++) { 
    chld_stack = (void *) malloc(148*512); 
    chld_stack2 = ((char *)chld_stack + 148*512 - 1); 
    if(chld_stack == NULL) { 
    fprintf(stderr, "ERROR[%ld]: malloc for stack-space failed\n", t); 
    break; 
    } 
    rc = clone(worker, chld_stack2, CLONE_VM|CLONE_FS|CLONE_FILES, NULL); 
    if(rc == -1) { 
    fprintf(stderr, "ERROR[%ld]: return code from pthread_create() is %d\n", t, errno); 
    break; 
    } 
    thread_pids[i]=rc; 
    thread_stacks[i]=chld_stack; 
    fprintf(stderr, " [index:%d] = [pid:%d] ; [stack:0x%p]\n", i, thread_pids[i], thread_stacks[i]); 
    total_count++; 
} 
sigemptyset(&sigset); 
sigaddset(&sigset, SIGUSR1); 
while(1) { 
    fprintf(stderr, "Waiting for signal from childs\n"); 
    if(sigwaitinfo(&sigset, &siginfo) == -1) 
    fprintf(stderr, "- ERROR returned by sigwaitinfo : \"%s\"\n", strerror(errno)); 
    fprintf(stderr, "Got some signal from pid:%d\n", siginfo.si_pid); 

    /* A child finished, free the stack area allocated for it */ 
    for(i=0;i<NUM_THREADS;i++) { 
    fprintf(stderr, " [index:%d] = [pid:%d] ; [stack:%p]\n", i, thread_pids[i], thread_stacks[i]); 
    if(thread_pids[i]==siginfo.si_pid) { 
    free(thread_stacks[i]); 
    thread_stacks[i]=NULL; 
    break; 
    } 
    } 
    fprintf(stderr, "Search for child ended with i=%d\n",i); 
    if(i==NUM_THREADS) 
    continue; 
    /* Create a new thread in its place */ 
    chld_stack = (void *) malloc(148*512); 
    chld_stack2 = ((char *)chld_stack + 148*512 - 1); 
    if(chld_stack == NULL) { 
    fprintf(stderr, "ERROR[%ld]: malloc for stack-space failed\n", t); 
    break; 
    } 
    rc = clone(worker, chld_stack2, CLONE_VM|CLONE_FS|CLONE_FILES, NULL); 
    if(rc == -1) { 
    fprintf(stderr, "ERROR[%ld]: return code from clone() is %d\n", t, errno); 
    break; 
    } 
    thread_pids[i]=rc; 
    thread_stacks[i]=chld_stack; 
    total_count++; 
} 
fprintf(stderr, "Broke out of infinite loop. [total_count=%llu] [i=%d]\n",total_count, i); 
return 0; 
} 

ho usato paio di matrici per tenere traccia di indirizzo di base pid e la zona dello stack dei processi figli (per liberarlo).
Quando eseguo questo programma, termina dopo un po '. L'esecuzione con gdb mi dice che uno dei thread riceve un SIGSEGV (errore di segmentazione). Ma non mi dà alcun luogo, l'output è simile al seguente:

Program received signal SIGSEGV, Segmentation fault. 
[Switching to LWP 15864] 
0x00000000 in ??() 

Ho provato a correre sotto Valgrind con la seguente riga di comando:

valgrind --tool=memcheck --leak-check=yes --show-reachable=yes -v --num-callers=20 --track-fds=yes ./a.out 

ma continua a funzionare senza problemi sotto valgrind.
Sono perplesso su come eseguire il debug di questo programma. Ho ritenuto che questo potrebbe essere un eccesso di stack o qualcosa del genere, ma l'aumento delle dimensioni dello stack (fino a 74 KB) non ha risolto il problema.
La mia unica query è perché e dove si trova l'errore di segmentazione o come eseguire il debug di questo programma.

+0

ad essere onesti, io sono ignorante sulla funzione clone, ma ho visto questo in OpenMP. Hai provato a cambiare il limite di dimensioni dello stack, ulimit -s – Anycorn

risposta

1

Credo di aver trovato la risposta

Fase 1

Sostituire questo:

static int thread_pids[NUM_THREADS]; 
static void *thread_stacks[NUM_THREADS]; 

Con questo:

static int *thread_pids; 
static void **thread_stacks; 

Fase 2

Aggiungere questo nella funzione principale (dopo aver controllato argomenti):

thread_pids = malloc(sizeof(int) * num_threads); 
thread_stacks = malloc(sizeof(void *) * num_threads); 

Fase 3

Sostituire questo:

chld_stack2 = ((char *)chld_stack + 148*512 - 1); 

Con questo:

chld_stack2 = ((char *)chld_stack + 148*512); 

In entrambi i posti lo usi.

Non so se è davvero il tuo problema, ma dopo averlo testato non ho riscontrato alcun errore di segmentazione. Btw ho avuto solo errori di segmentazione quando si usano più di 5 thread.

Spero di aver aiutato!

edit: testato con 1000 fili e funziona perfettamente

EDIT2: spiegazione perché l'assegnazione statica di thread_pids e thread_stacks causa un errore.

Il modo migliore per farlo è con un esempio.

Assumere num_threads = 10;

Il problema si verifica nel codice seguente:

for(t=0,i=0;t<num_threads;t++,i++) { 
... 

thread_pids[i]=rc; 
thread_stacks[i]=chld_stack; 

... 
} 

Qui si tenta di accedere alla memoria che non appartiene a te (0 < = i < = 9, ma entrambi gli array hanno una dimensione di 5). Ciò può causare errori di segmentazione o danneggiamento dei dati. Il danneggiamento dei dati può verificarsi se entrambi gli array vengono allocati uno dopo l'altro, con conseguente scrittura sull'altro array. La segmentazione può accadere se scrivi in ​​memoria che non hai assegnato (staticamente o dinamicamente).

Potresti essere fortunato e non avere alcun errore, ma il codice non è sicuramente sicuro.

Informazioni sul puntatore non allineato: Penso di non dover spiegare più che nel mio commento.

+0

hi George. Ho provato il codice originale su amd64 e non ottiene la segmentazione. Sono curioso di sapere qual è il problema dei tuoi sistemi. Puoi spiegare brevemente? grazie – Anycorn

+0

Ciao George, la tua soluzione non ha funzionato per me :(BTW, puoi spiegare perché questi cambiamenti hanno funzionato per te? – Sukanto

+0

Beh, ho notato che non ho avuto alcun problema con <= 5 thread, ma con più di 5 thread ho sempre avuto un errore di segmentazione L'errore è che non hai abbastanza memoria allocata per ogni thread (lo si assegna staticamente) con il risultato di accedere a indirizzi non allocati più avanti nel codice. Sottraendo anche 1 da chld_stack + 148 * 512 si ottiene un indirizzo non valido (dovrebbe essere allineato alla parola, non è perché l'indirizzo è un numero dispari) .Questo può anche causare un errore di segmentazione o un danneggiamento dello stack Non so se sei sfortunato o io fortunato , ma come ho detto ha funzionato bene per me. – George

3

Trovato il problema reale.
Quando il thread worker segnala il processo padre utilizzando sigqueue(), il genitore a volte ottiene il controllo immediatamente e libera lo stack prima che il figlio esegua l'istruzione return. Quando lo stesso thread figlio utilizza l'istruzione return, causa un errore di segmentazione quando lo stack viene danneggiato.
Per risolvere questo ho sostituito

exit(0) 

invece di

return 0; 
Problemi correlati