2015-06-29 3 views
0

Ниже код просто создает два потока и пытается получить возвращаемые значения из них.Неверное значение выхода из pthread_exit

Я скомпилировал и запустил его на 32-разрядной системе glibc-2.15, и все прошло правильно (выход: r1: 1, r2: 2). Однако, когда я делал то же самое в 64-битной системе glibc-2.17, вывод был неправильным (вывод: r1: 0, r2: 2). Почему один и тот же код ведет себя по-разному в разных системах?

Примечание: Если типы r1 и r2 изменены на void* или int*, как указано ниже, код работает в обеих системах.

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

void* worker(void* arg) { 
    int i = (int) arg; 
    pthread_exit((void*)i); 
} 

int main(int argc, char** argv) { 

    pthread_t tid[2]; 
    int err = 0; 
    err = pthread_create(&tid[0], NULL, worker, (void*) 1); 
    if(err != 0) printf("error: %s\n", strerror(err)); 
    err = pthread_create(&tid[1], NULL, worker, (void*) 2); 
    if(err != 0) printf("error: %s\n", strerror(err)); 

    ///* 
    int r1 = 0, r2 = 0; // <-- WRONG: r1: 0, r2: 2 
    //void *r1, *r2; // <-- OK: r1: 1, r2: 2 
    pthread_join(tid[0], (void**) &r1); 
    pthread_join(tid[1], (void**) &r2); 
    printf("r1: %d, r2: %d\n", (int) r1, (int) r2); 
    //*/ 

    // make comment above snippet and uncomment below snippet: // <-- OK: r1: 1, r2: 2 
    /* 
    int *r1 = (int*) malloc(sizeof(int)); 
    int *r2 = (int*) malloc(sizeof(int)); 
    pthread_join(tid[0], (void**) r1); 
    pthread_join(tid[1], (void**) r2); 
    printf("r1: %d, r2: %d\n", (int)(*r1), (int)(*r2)); 
    */ 
    return 0; 
} 

ответ

1

Короткий ответ: на 64-битной системе, sizeof(void*) != sizeof(int) и пропусканием &int в pthread_join вы ссылающегося неопределенное поведение (и разлагающее стек, работает тот вариант программы под Address Sanitizer должен обнаружить ошибку).

В случае, когда вы проходите &int, но int была кучей распределённой, вы развращаете кучу вместо этого, но вы не замечаете, что пока (ваша программа, скорее всего, сбой позже последующий malloc или free). Запуск этого варианта программы под Valgrind или Address Sanitizer должен тривиально доказать вам кучу коррупции.

Более длинный ответ: pthread_join(tid, &x) выполняет, в основном это:

memcpy(&x, previosly_used_pthread_exit_value, sizeof(void*)); 

Теперь должно быть ясно, что передача в адрес любой переменной, для которой sizeof(x) < sizeof(void*) вызывает неопределенное поведение.

+0

Спасибо за такой ясный ответ. –