2014-02-26 12 views
8

Hier ist das Stück Code, in dem Segmentierungsfehler auftritt (die perror wird nicht aufgerufen werden):Malloc Segmentierungsfehler

job = malloc(sizeof(task_t)); 
if(job == NULL) 
    perror("malloc"); 

genauer zu sein, sagt GDB, dass die segfault in einem __int_malloc Anruf geschieht, die ist ein Unterprogrammaufruf von malloc.

Da die malloc-Funktion parallel zu anderen Threads aufgerufen wird, dachte ich zunächst, dass es das Problem sein könnte. Ich habe Version 2.19 von glibc verwendet.

Die Datenstrukturen:

typedef struct rv_thread thread_wrapper_t; 

typedef struct future 
{ 
    pthread_cond_t wait; 
    pthread_mutex_t mutex; 
    long completed; 
} future_t; 

typedef struct task 
{ 
    future_t * f; 
    void * data; 
    void * 
    (*fun)(thread_wrapper_t *, void *); 
} task_t; 

typedef struct 
{ 
    queue_t * queue; 
} pool_worker_t; 

typedef struct 
{ 
    task_t * t; 
} sfuture_t; 

struct rv_thread 
{ 
    pool_worker_t * pool; 
}; 

Nun ist die künftige Umsetzung:

future_t * 
create_future() 
{ 
    future_t * new_f = malloc(sizeof(future_t)); 
    if(new_f == NULL) 
    perror("malloc"); 
    new_f->completed = 0; 
    pthread_mutex_init(&(new_f->mutex), NULL); 
    pthread_cond_init(&(new_f->wait), NULL); 
    return new_f; 
} 

int 
wait_future(future_t * f) 
{ 
    pthread_mutex_lock(&(f->mutex)); 
    while (!f->completed) 
    { 
     pthread_cond_wait(&(f->wait),&(f->mutex)); 
    } 
    pthread_mutex_unlock(&(f->mutex)); 
    return 0; 
} 

void 
complete(future_t * f) 
{ 
    pthread_mutex_lock(&(f->mutex)); 
    f->completed = 1; 
    pthread_mutex_unlock(&(f->mutex)); 
    pthread_cond_broadcast(&(f->wait)); 
} 

Der Thread-Pool selbst:

pool_worker_t * 
create_work_pool(int threads) 
{ 
    pool_worker_t * new_p = malloc(sizeof(pool_worker_t)); 
    if(new_p == NULL) 
    perror("malloc"); 
    threads = 1; 
    new_p->queue = create_queue(); 
    int i; 
    for (i = 0; i < threads; i++){ 
    thread_wrapper_t * w = malloc(sizeof(thread_wrapper_t)); 
    if(w == NULL) 
     perror("malloc"); 
    w->pool = new_p; 
    pthread_t n; 
    pthread_create(&n, NULL, work, w); 
    } 
    return new_p; 
} 

task_t * 
try_get_new_task(thread_wrapper_t * thr) 
{ 
    task_t * t = NULL; 
    try_dequeue(thr->pool->queue, t); 
    return t; 
} 

void 
submit_job(pool_worker_t * p, task_t * t) 
{ 
    enqueue(p->queue, t); 
} 

void * 
work(void * data) 
{ 
    thread_wrapper_t * thr = (thread_wrapper_t *) data; 
    while (1){ 
    task_t * t = NULL; 
    while ((t = (task_t *) try_get_new_task(thr)) == NULL); 
    future_t * f = t->f; 
    (*(t->fun))(thr,t->data); 
    complete(f); 
    } 
    pthread_exit(NULL); 
} 

Und schließlich die task.c:

pool_worker_t * 
create_tpool() 
{ 
    return (create_work_pool(8)); 
} 

sfuture_t * 
async(pool_worker_t * p, thread_wrapper_t * thr, void * 
(*fun)(thread_wrapper_t *, void *), void * data) 
{ 
    task_t * job = NULL; 
    job = malloc(sizeof(task_t)); 
    if(job == NULL) 
    perror("malloc"); 
    job->data = data; 
    job->fun = fun; 
    job->f = create_future(); 
    submit_job(p, job); 
    sfuture_t * new_t = malloc(sizeof(sfuture_t)); 
    if(new_t == NULL) 
    perror("malloc"); 
    new_t->t = job; 
    return (new_t); 
} 

void 
mywait(thread_wrapper_t * thr, sfuture_t * sf) 
{ 
    if (sf == NULL) 
    return; 
    if (thr != NULL) 
    { 
     while (!sf->t->f->completed) 
     { 
      task_t * t_n = try_get_new_task(thr); 
      if (t_n != NULL) 
      { 
      future_t * f = t_n->f; 
      (*(t_n->fun))(thr,t_n->data); 
      complete(f); 
      } 
     } 
     return; 
    } 
    wait_future(sf->t->f); 
    return ; 
} 

Die Warteschlange ist die lfds-Sperr-freie Warteschlange.

#define enqueue(q,t) {         \ 
    if(!lfds611_queue_enqueue(q->lq, t))    \ 
     {            \ 
     lfds611_queue_guaranteed_enqueue(q->lq, t); \ 
     }            \ 
    } 

#define try_dequeue(q,t) {       \ 
    lfds611_queue_dequeue(q->lq, &t);    \ 
    } 

Das Problem tritt auf, wenn die Anzahl der Aufrufe von async sehr hoch ist.

Valgrind Ausgang:

Process terminating with default action of signal 11 (SIGSEGV) 
==12022== Bad permissions for mapped region at address 0x5AF9FF8 
==12022== at 0x4C28737: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
+1

Ist es möglich, dass etwas anderes die Buchhaltung für 'malloc' durcheinanderbringt? – cnicutar

+1

Es hört sich an, als würde der Speicher irgendwo anders beschädigt. – imreal

+0

Es ist die einzige Erklärung, ich poste den ganzen Code. (Es ist wirklich ein minimales Modell, mit Speicherlecks, etc.). – guilhermemtr

Antwort

9

Ich habe herausgefunden, was das Problem ist: ein Stapelüberlauf.

Lassen Sie mich zuerst erklären, warum der Stapelüberlauf innerhalb von malloc auftritt (was wahrscheinlich der Grund dafür ist, dass Sie dies lesen). Als mein Programm ausgeführt wurde, nahm die Stackgröße jedes Mal zu, wenn es (rekursiv) mit der Ausführung einer anderen Task begann (aufgrund der Art, wie ich es programmiert hatte). Aber für jede solche Zeit musste ich eine neue Aufgabe mit malloc zuweisen. Malloc führt jedoch andere Unterroutinenaufrufe durch, wodurch der Stapel seine Größe sogar noch mehr erhöht als ein einfacher Aufruf zum Ausführen einer anderen Aufgabe. Also, was passiert ist, dass, selbst wenn es kein malloc gäbe, ich einen Stapelüberlauf bekommen würde.Da ich jedoch malloc hatte, war der Stapel, in dem der Stapel übergelaufen war, in malloc, bevor er durch einen weiteren rekursiven Aufruf übergelaufen ist. Die Abbildung unten zeigt, was geschieht:

ursprüngliche Stapel:

------------------------- 
| recursive call n - 3 | 
------------------------- 
| recursive call n - 2 | 
------------------------- 
| recursive call n - 1 | 
------------------------- 
|  garbage  | 
------------------------- 
|  garbage  | <- If the stack passes this point, the stack overflows. 
------------------------- 

Stapel während malloc Aufrufs:

------------------------- 
| recursive call n - 3 | 
------------------------- 
| recursive call n - 2 | 
------------------------- 
| recursive call n - 1 | 
------------------------- 
|  malloc   | 
------------------------- 
|  __int_malloc  | <- If the stack passes this point, the stack overflows. 
------------------------- 

Dann wird der Stapel wieder geschrumpft, und mein Code in einen neuen rekursiven Aufruf :

------------------------- 
| recursive call n - 3 | 
------------------------- 
| recursive call n - 2 | 
------------------------- 
| recursive call n - 1 | 
------------------------- 
| recursive call n  | 
------------------------- 
|  garbage  | <- If the stack passes this point, the stack overflows. 
------------------------- 

Dann rief malloc erneut innerhalb von t sein neuer rekursiver Aufruf. diesmal ist es überflutete jedoch: zum Beispiel

------------------------- 
| recursive call n - 3 | 
------------------------- 
| recursive call n - 2 | 
------------------------- 
| recursive call n - 1 | 
------------------------- 
| recursive call n  | 
------------------------- 
|  malloc   | <- If the stack passes this point, the stack overflows. 
------------------------- 
|  __int_malloc  | <- This is when the stack overflow occurs. 
------------------------- 

[. Der Rest der Antwort fokussierter ist um, warum ich insbesondere dieses Problem in meinem Code hatte]

Normalerweise, wenn Fibonacci Berechnung rekursiv, ab einer bestimmten Anzahl n wächst die Stapelgröße linear mit dieser Zahl. In diesem Fall erstelle ich jedoch Aufgaben, benutze eine Warteschlange, um sie zu speichern, und entziehe eine (fib) Aufgabe zur Ausführung. Wenn Sie dies auf Papier zeichnen, werden Sie sehen, dass die Anzahl der Aufgaben exponentiell mit dem n wächst, anstatt linear (auch wenn ich einen Stapel verwendet habe, um die Aufgaben so zu speichern, wie sie erstellt wurden, die Anzahl der zugewiesenen Aufgaben als genau wie die Stackgröße würde nur linear mit n wachsen. Was also passiert ist, dass der Stack exponentiell mit n wächst, was zu einem Stacküberlauf führt ... Nun kommt der Teil, warum dieser Überlauf innerhalb des Calls zu malloc auftritt Ich erklärte oben, dass der Stapelüberlauf innerhalb des malloc-Aufrufs stattfand, da der Stapel dort am größten war.Was geschah, war, dass der Stapel fast explodierte und da malloc Funktionen darin aufruft, wächst der Stapel mehr als nur der Aufruf von mywait und fib.

Vielen Dank! Wenn es nicht Ihre Hilfe war, wäre ich nicht in der Lage, es herauszufinden!

+0

sollte ich meine eigene Antwort als richtig markieren? – guilhermemtr

+1

Das war, was ich vermutete, da ich kein Problem finden konnte. Aber um sicherzustellen, dass dies das Problem ist, können Sie die "oberste" Ausgabe für eine Datei ausgeben und prüfen, wie die Speichernutzung ansteigt. +1 für die Antwort und die Frage. – Jekyll

+0

Wenn ich alle Threads entfernt habe, sagte Valgrind, dass dies ein Stack-Überlauf sein kann, obwohl es unwahrscheinlich ist. Ich habe das ulimit größer gemacht und ich könnte dann größere Fibeln laufen lassen. Wenn ich die Stapelgröße dupliziere, kann ich der vorherigen Zahl nur 1 hinzufügen. Aber ich werde tun, was du gesagt hast, nur um es zu bestätigen – guilhermemtr

9

A SIGSEGV (Segmentation Fault) in malloc Brennen in der Regel durch Heapbeschädigung verursacht wird. Heap Corruption verursacht keinen Segmentierungsfehler, also würden Sie das nur sehen, wenn malloc versucht, darauf zuzugreifen. Das Problem ist, dass der Code, der die Heap-Korruption erstellt, in jedem Punkt auch weit entfernt sein kann, wo der malloc aufgerufen wird. Es ist normalerweise der Next-Block-Zeiger innerhalb des Mallocs, der durch Ihre Heap-Beschädigung in eine ungültige Adresse geändert wird, so dass beim Aufruf von Malloc ein ungültiger Zeiger dereferenziert wird und Sie einen Segmentierungsfehler erhalten.

Ich denke, Sie können Teile Ihres Codes aus dem Rest des Programms isoliert versuchen, um die Sichtbarkeit des Fehlers zu reduzieren.

Darüber hinaus sehe ich, dass Sie nie den Speicher hier freigeben und es kann ein mögliches Speicherleck sein.

Um einen Speicher zu überprüfen Leck Sie den Befehl top top -b -n 1 ausführen und überprüfen:

RPRVT - resident private address space size 
RSHRD - resident shared address space size 
RSIZE - resident memory size 
VPRVT - private address space size 
VSIZE - total memory size 
+1

Das Problem ist, dass der Segmentierungsfehler nur nach vielen Aufrufen auftritt. – guilhermemtr

+0

hast du gesehen, ob es einen Speicherverlust gibt? Ich habe hier keine freie Stelle gesehen ... Kannst du irgendwann mal Speicher frei haben? – Jekyll

+0

Ich werde auf ein Problem stoßen, wenn ich Speicher nicht früher oder später freilasse ... wie dieses Programm nur hier zuweist ... – Jekyll