5

Ich schrieb einen Code, um einige Threads zu erstellen und wann immer einer der Threads einen neuen Thread erstellt wird, um es zu ersetzen. Da ich mit Pthreads keine sehr große Anzahl von Threads (> 450) erstellen konnte, habe ich stattdessen den Klon-Systemaufruf verwendet. (Bitte beachten Sie, dass ich die Auswirkungen einer so großen Anzahl von Threads weiß, aber dieses Programm soll nur das System belasten).
Da clone() die Angabe des Stack-Bereichs für den untergeordneten Thread als Parameter erfordert, mallociere ich den erforderlichen Stapelspeicherplatz für jeden Thread und befreie ihn, wenn der Thread fertig ist. Wenn ein Thread fertig ist, sende ich ein Signal an das Elternteil, um es davon zu benachrichtigen.Debugging Segmentierung Fehler in einem Multi-Thread (mit Klon) -Programm

#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; 
} 
Ich habe paar Arrays Spur des Kindprozesses pid und der Stapelbereich Basisadresse zu halten verwendet

(es für die Freigabe):
Der Code ist unten angegeben.
Wenn ich dieses Programm starte, wird es nach einiger Zeit beendet. Das Ausführen mit gdb sagt mir, dass einer der Threads einen SIGSEGV (segmentation fault) bekommt. Aber es ist gibt mir keine Stelle, ist die Ausgabe ähnlich der folgenden:

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

Ich habe versucht, es unter valgrind mit dem folgenden Befehl ausgeführt wird:

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

Aber es hält ohne Probleme läuft unter Valgrind.
Ich bin verwirrt, wie dieses Programm debuggen. Ich hatte das Gefühl, dass dies ein Stapelüberlauf oder etwas anderes sein könnte, aber das Erhöhen der Stapelgröße (bis zu 74 KB) hat das Problem nicht gelöst.
Meine einzige Frage ist warum und wo ist die Segmentierung Fehler oder wie dieses Programm zu debuggen.

+0

ehrlich zu sein, ich bin über Klonfunktion unwissend, aber ich habe dies in OpenMP gesehen. Haben Sie versucht, die Größe der Stackgröße zu ändern, ulimit -s – Anycorn

Antwort

1

Ich glaube, ich fand die Antwort dieses

Schritt 1

ersetzen:

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

Durch diese:

static int *thread_pids; 
static void **thread_stacks; 

Schritt 2

Fügen Sie diese in der Hauptfunktion (nach Argumenten Prüfung):

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

Schritt 3

Ersetzen Sie diese:

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

Durch diese:

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

In beiden Orten verwenden Sie es.

Ich weiß nicht, ob es wirklich Ihr Problem ist, aber nach dem Testen habe ich keine Segmentierung Fehler. Übrigens habe ich nur Segmentierungsfehler erhalten, wenn mehr als 5 Threads verwendet wurden.

Ich hoffe, ich habe geholfen!

edit: mit 1000 Fäden getestet und läuft perfekt

edit2: Erklärung, warum die statische Zuweisung von thread_pids und thread_stacks einen Fehler verursacht.

Der beste Weg, dies zu tun, ist mit einem Beispiel.

Angenommen num_threads = 10;

Das Problem tritt in dem folgenden Code:

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

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

... 
} 

Hier können Sie versuchen, auf Speicher zuzugreifen, die nicht Ihnen gehören (0 < = i < = 9, aber beide Arrays haben eine Größe von 5). Dies kann entweder zu Segmentierungsfehlern oder zu Datenbeschädigungen führen. Datenkorruption kann auftreten, wenn beide Arrays nacheinander zugewiesen werden, was zum Schreiben in das andere Array führt. Segmentierung kann passieren, wenn Sie in Speicher schreiben, den Sie nicht zugewiesen haben (statisch oder dynamisch).

Sie können Glück haben und haben keine Fehler, aber der Code ist sicherlich nicht sicher.

Über den nicht ausgerichteten Zeiger: Ich denke, ich muss nicht mehr als in meinem Kommentar erklären.

+0

hi George. Ich habe ursprünglichen Code auf amd64 versucht, und es erhält keine Segmentierung. Ich bin neugierig, was das Problem Ihrer Systeme ist. Kannst du es kurz erklären? danke – Anycorn

+0

Hallo George, deine Lösung hat nicht für mich funktioniert :(BTW, können Sie erklären, warum diese Änderungen für Sie gearbeitet haben? – Sukanto

+0

Nun habe ich festgestellt, dass ich kein Problem mit <= 5 Threads hatte, aber mit mehr als 5 Threads Ich habe immer einen Segmentierungsfehler Der Fehler ist, dass Sie nicht genügend Speicher für jeden Thread zugewiesen haben (Sie ordnen es statisch) mit dem Ergebnis des Zugriffs auf nicht zugewiesene Adressen später in Ihrem Code. Auch durch Subtraktion von 1 von chld_stack + 148 * 512 Sie erhalten eine ungültige Adresse (sollte Wort ausgerichtet sein, nicht weil die Adresse eine ungerade Zahl ist.) Das kann auch einen Segmentierungsfehler oder eine Stapelbeschädigung verursachen Ich weiß nicht, ob Sie Pech haben oder mich glücklich , aber wie ich sagte, es hat gut für mich gearbeitet. – George

3

Das tatsächliche Problem gefunden.
Wenn der Worker-Thread den übergeordneten Prozess mithilfe von sigqueue() signalisiert, erhält das übergeordnete Element manchmal das Steuerelement sofort und gibt den Stapel frei, bevor das untergeordnete Element die return-Anweisung ausführt. Wenn derselbe untergeordnete Thread eine return-Anweisung verwendet, verursacht er einen Segmentierungsfehler, da der Stapel beschädigt wurde.
dieses I ersetzt

zu lösen
exit(0) 

statt

return 0;