Порядок изменения в pthread_join вернет ошибку сегментации в Linux

Можете ли вы объяснить, почему в Linux (не в Mac) я получаю ошибку сегментации, когда делаю:

pthread_join(thread2, (void**)&status);
pthread_join(thread1, (void**)&status);

Но это нормально, когда я делаю:

pthread_join(thread1, (void**)&status);
pthread_join(thread2, (void**)&status);

Я попробовал на Mac, и все в порядке, но в Linux код работает правильно, только если я выполняю соединение потока 1, а затем соединение потока 2...

Это мой код:

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

void *print_msg( char *ptr );

main(){
    pthread_t thread1, thread2;
    char *message1 = "Ping";
    char *message2 = "Pong";
    int status;
    pthread_create(&thread1, NULL, print_msg, message1);
    printf("tid thread1= %d\n", (int)thread1);
    pthread_create(&thread2, NULL, print_msg, message2);
    printf("tid thread2= %d\n", (int)thread2);
    pthread_join(thread2, (void**)&status);
    pthread_join(thread1, (void**)&status);
    printf("Thread 1 end with: %d\n", (int)status);
    printf("Thread 2 end with: %d\n", (int)status);
    exit(0);
}

void *print_msg( char *ptr ){
    char *msg;
    void *val=0;
    msg = (char *) ptr;
    printf("%s \n", msg);
    pthread_exit(val);
}

person Giovanni    schedule 12.11.2016    source источник
comment
Вы можете сначала исправить все предупреждения компилятора, их несколько. Вы можете скомпилировать свой код с помощью -Wall и -Wextra, что говорит о большом количестве потенциальных ошибок. Вы также можете прочитать документ pthread, содержащий пример того, как использовать pthread_join()   -  person SSC    schedule 12.11.2016


Ответы (2)


Ваш состав (void**)&status - проблема.

status — это целое число с неинициализированным значением. Это тоже не указатель. sizeof(status) скорее всего 4. В то время как sizeof(void*) скорее всего 8. Таким образом, когда pthread_join вызывается, он собирается удалить 4 байта стека после местоположения стека status. Это вполне может быть обратным адресом основной функции. Но на данный момент мы находимся на территории неопределенного поведения.

Измените объявление status на void*. Как и в случае с любым значением указателя, инициализируйте его значением NULL. То есть:

void* status = NULL;

Затем упростите свои операторы pthread_join, чтобы вам не требовалось приведение типов.

pthread_join(thread2, &status);
pthread_join(thread1, &status);
person selbie    schedule 25.11.2016
comment
Было бы неплохо отметить, что это соответствует типу процедуры запуска для pthread_create: void *(*start_routine) (void *) - person Michael Foukarakis; 25.11.2016

Думаю, я понял это.

Проблема в том, что Linux не имеет достаточно времени, чтобы создать все, что ему нужно для управления потоком, потому что мы немедленно запрашиваем соединение. Если мы просто вставим между ними хотя бы одну дурацкую инструкцию, мы решим проблему:

...
pthread_create(&thread1, NULL, print_msg, message1);
printf("tid thread1= %d\n", (int)thread1);
pthread_create(&thread2, NULL, print_msg, message2);
int a=0;
pthread_join(thread2, (void**)&status);
pthread_join(thread1, (void**)&status);
...
person Giovanni    schedule 25.11.2016
comment
Кроме того, причина, по которой вышеизложенное, кажется, работает, вероятно, заключается в том, что вы вставили новую переменную стека, a. Поскольку он следует за status в порядке объявления, вероятно, это то, что поглощает переполнение вашего буфера. Я не удивлюсь, если значение a изменится после возврата из pthread_join. - person selbie; 25.11.2016