2017-02-13 3 views
0

In der Funktion last_letter() Die Zeile c = NULL bewirkt, dass dieses Programm segfault bei while (* c) wird es auskommentieren nicht. Was ist der Grund? Char-Zeiger auf NULL setzen und etwas neu zuweisen? Ich dachte, Zeiger auf NULL zu setzen und ihnen etwas anderes zuzuweisen war akzeptabel?Setzen von char * auf NULL segfault

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <pthread.h> 

char s1[] = "abcdefg"; 
char s2[] = "xyz"; 
char* c; 

void last_letter(char* a, int i) { 
    printf("last_letter (a is %s and i is %d)\n", a, i); 
    sleep(i); 
    c = NULL; // comment out, what is different? 
    sleep(i); 
    c = a; 
    sleep(i); 
    while (*c) { 
    c++; 
    } 
    printf("%c\n", *(c-1)); 
    return; 
} 


// This function will run concurrently. 

void* aa(void *ptr) { 
    last_letter(s2, 2); 
    return NULL; 
} 

int main() { 
    pthread_t t1; 
    int iret1 = pthread_create(&t1, NULL, aa, NULL); 
    if (iret1) { 
    fprintf(stderr,"Cannot create thread, rc=%d\n", iret1); 
    } 
    last_letter(s1, 5); 
    sleep(10); 
    printf("Ended nicely this time\n"); 
    return 0; //never reached when c = NULL is not commented out. 
} 
+3

Wenn Sie 'c' auf NULL setzen, dann könnte der * andere Thread * versuchen,' * c' zu verwenden, während 'c' NULL ist. – immibis

Antwort

1

Ihre Variable c ist global und wird von allen Threads geteilt. Es wird in last_letter verwendet, das die Möglichkeit hat, sowohl im Haupt- als auch im neuen Thread gleichzeitig aufgerufen zu werden. Da die Reihenfolge, in der jeder Thread last_letter ausführt, nicht bekannt sein kann, können beide Interleaving-Änderungen an c sein. Ein Thread könnte also NULL setzen, wenn der andere erwartet, dass es ein gültiger Wert ist.

Eine sehr einfache Art und Weise sehr wahrscheinlich den Absturz zu stoppen ist die Ordnung des Schlafes und der Aufruf an last_letter im Hauptthread

sleep(10); 
last_letter(s1, 5); 

Dieser kauft t1 10 Sekunden zu tauschen, zu beenden, was hoffentlich genug ist . Ein besserer Weg, es zu tun, ist t1 vor dem Aufruf last_letter im Haupt-Thread beizutreten. Noch ein besserer Weg ist es, c innerhalb last_letter zu verschieben, daher hat jeder Thread eine einzigartige Version von c und tritt nicht auf die Zehen der anderen Gewinde. Wenn c geteilt werden müsste, müssten Sie es mit einem Mutex schützen.

Auf diese Weise müssen Sie nicht Hauptausführung last_letter halten, während t1 es auch ausführt. Da main und t1 mit unterschiedlichen Daten arbeiten und diese Daten auch nicht mutieren, ist es sicher.

Sie möchten trotzdem join unter t1 anrufen, sonst besteht die Möglichkeit, dass der Hauptvorgang abgeschlossen und beendet wird, bevor t1 abgeschlossen wird.

#include <stdio.h> 
#include <unistd.h> 
#include <pthread.h> 

char s1[] = "abcdefg"; 
char s2[] = "xyz"; 

void last_letter(char* a, int i) { 

    char* c; 
    printf("last_letter (a is %s and i is %d)\n", a, i); 
    sleep(i); 
    c = NULL; // pointless but if you must 
    sleep(i); 
    c = a; 
    sleep(i); 
    while (*c) { 
    c++; 
    } 
    printf("%c\n", *(c-1)); 
} 

// This function will run concurrently. 
void* aa(void *ptr) { 
    last_letter(s2, 2); 
    return NULL; 
} 

int main() { 
    pthread_t t1; 

    int iret1 = pthread_create(&t1, NULL, aa, NULL); 
    if (iret1) { 
    fprintf(stderr,"Cannot create thread, rc=%d\n", iret1); 
    return 0; 
    } 

    last_letter(s1, 5); 
    pthread_join(t1, NULL); // should check return value in production code 

    printf("Ended nicely this time\n"); 
    return 0; //never reached when c = NULL is not commented out. 
} 
4

Erstens ist dies eine klassische Race-Bedingung. Dieses Programm hängt von der Reihenfolge ab, in der aa() aufgerufen wird. Das letzte Argument zu pthread_create() ist das Argument. Aus der Manpage:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, 
        void *(*start_routine) (void *), void *arg); 

Also, wenn aa() über den Pthread Mechanismus genannt wird, die ein Argument ist NULL, da pthread_create() mit einem NULL arg aufgerufen wird.

Dann dereferenziert diese Schleife einen NULL-Zeiger, was zu einem Absturz:

c = a; 
sleep(i); 
while (*c) { 
    c++; 
} 

so pthread_create() ein Nicht-Null-Argument geben und du bist auf einem besseren Weg.