J'ai écrit un code pour créer des threads et chaque fois qu'un des threads se termine, un nouveau thread est créé pour le remplacer. Comme je n'étais pas capable de créer un très grand nombre de threads (> 450) en utilisant pthreads, j'ai utilisé l'appel système clone à la place. (S'il vous plaît noter que je suis conscient de l'implication d'avoir un tel nombre de threads, mais ce programme est destiné à ne souligner que le système).
Comme clone() requiert que l'espace de pile pour le thread enfant soit spécifié en tant que paramètre, je place le bloc d'espace de pile requis pour chaque thread et le libère lorsque le thread se termine. Quand un thread finit, j'envoie un signal au parent pour l'avertir de la même chose.
Le code est donné ci-dessous:Débogage d'une erreur de segmentation dans un programme multi-thread (en utilisant 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;
}
J'ai utilisé deux tableaux pour garder la trace des processus enfants de pid et la zone de pile adresse de base (pour le libérer).
Lorsque j'exécute ce programme, il se termine après un certain temps. Exécuter avec gdb m'indique que l'un des thread obtient un SIGSEGV (défaut de segmentation). Mais cela ne me donne tout lieu, la sortie est similaire à ce qui suit:
Program received signal SIGSEGV, Segmentation fault.
[Switching to LWP 15864]
0x00000000 in ??()
Je l'ai essayée en cours d'exécution sous valgrind avec la ligne de commande suivante:
valgrind --tool=memcheck --leak-check=yes --show-reachable=yes -v --num-callers=20 --track-fds=yes ./a.out
Mais il continue à fonctionner sans aucun problème sous valgrind.
Je suis perplexe quant à la façon de déboguer ce programme. J'ai senti que cela pourrait être un débordement de pile ou quelque chose mais augmenter la taille de la pile (jusqu'à 74KB) n'a pas résolu le problème.
Ma seule requête est pourquoi et où est l'erreur de segmentation ou comment déboguer ce programme.
pour être honnête, je suis ignorant de la fonction clone, mais je l'ai vu dans OpenMP. Avez-vous essayé de changer la limite de taille de la pile, ulimit -s – Anycorn