2013-03-04 1 views
6

C에서 pthreads를 시작하려고합니다. 또한 "버그가 없다"라고 코드를 작성하는 광적인 사람입니다.c pthreads + valgrind = 메모리 누수 : 이유가 무엇입니까?

  1. 내가이 완료되면 가입 결합 가능한 스레드를 생성 (코드 1)
  2. 내가 조인 생성 : 추가 I 메모리가 누수하고 조심, Valgrind의 날을 말하고, 날씨에 관계없이려고 노력에도 불구하고

    내가 생성 한 후 분리 스레드 (코드 2)

  3. 내가 분리 된 스레드를 생성 (코드 3) 나는이 이미 논의 된 알

(012,338,248,919,876 참조, this 또한 this),하지만 난에 관해서는 여전히 궁금 :

나는 오류없이 결국 특정 실행에
  1. 이유는 무엇입니까?
  2. 분리 된 스레드를 처리 할 때 전체 mallocs()가 무작위로 나타나는 이유는 무엇입니까? < < 님이 제공 한 답변 nosed by main()에 추가 지연이있는 코드 스 니펫
  3. 분리 된 스레드를 처리 할 때도 "메모리 누수가 지속되는 이유는 무엇입니까? < < 같은 2

내가 이전에 응답하고 Valgrind의 추적에서 알고있는 것처럼로는 pthread_create()는, 따라서 몇 가지 누락을 필요 스레드가 사용하는 스택을 확장하고 시간에 그것을 재사용, 근본 원인이다 해방한다. 그러나 명확하지 않은 것은 실행 실행에 의존하는 이유와 분리 된 스레드를 만들 때 발생하는 이유입니다. 특정 답변, 주석 및 사람으로부터 보았을 때 분리 된 스레드의 자원은 스레드 완료시 해방됩니다. 나는이 문제를 해결하기 위해 다양한 조정을 시도했다. (메인 스레드가 끝나기 전에 각각의 스레드가 끝나기 전에 잠자기 시간을 추가하고, 스택 크기를 늘려서 더 많은 "작업"을 추가했다.)하지만 변경되지 않았다. 최종 결과. 또한 분리 된 쓰레드를 처리 할 때 전체 "mallocs()"가 무작위로 나타나는 이유는 무엇입니까? valgrind는 분리 된 쓰레드를 추적하지 못합니까? 이것은 또한 스택 크기에 의존하지 않는 것처럼 보입니다.

제공된 코드는 스레드 관리에 대한 조인 가능/조인 (join) 접근 방식이 더 적합한 것으로 보이는 관리자/근로자 모델의 모의 예입니다.

깨달음을 주셔서 감사합니다. 또한 이러한 (주석이 많은) 코드 스 니펫이 pthreads를 시작하고자하는 사람들에게 도움이되기를 바랍니다.

- swappy

PS sys 인 정보 : 데비안 64 비트 아치에 GCC는

코드 1 (결합 가능한 스레드가 결합) 니펫을 :

/* Running this multiple times with valgrind, I sometimes end with : 
    - no errors (proper malloc/free balance) 
    - 4 extra malloc vs free (most frequently) 
    The number of mallocs() is more conservative and depends on the number of threads. 
*/ 

#include <stdlib.h>    /* EXIT_FAILURE, EXIT_SUCCESS macros & the likes */ 
#include <stdio.h>    /* printf() & the likes */ 
#include <pthread.h>   /* test subject */ 

#define MAX_THREADS 100   /* Number of threads */ 
pthread_attr_t tattr;   /* Thread attribute */ 
pthread_t workers[MAX_THREADS]; /* All the threads spawned by the main() thread */ 

/* A mock container structure to pass arguments around */ 
struct args_for_job_t { 
    int tid; 
    int status; 
}; 

/* The job each worker will perform upon creation */ 
void *job(void *arg) 
{ 
    /* Cast arguments in a proper container */ 
    struct args_for_job_t *container; 
    container = (struct args_for_job_t *)arg; 

    /* A mock job */ 
    printf("[TID - %d]\n", container->tid); 

    /* Properly exit with status code tid */ 
    pthread_exit((void *)(&container->status)); 
} 

int main() 
{ 
    int return_code;       /* Will hold return codes */ 
    void *return_status;      /* Will hold return status */ 
    int tid;         /* Thread id */ 
    struct args_for_job_t args[MAX_THREADS]; /* For thread safeness */ 

    /* Initialize and set thread joinable attribute */ 
    pthread_attr_init(&tattr); 
    pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE); 

    /* Spawn detached threads */ 
    for (tid = 0; tid < MAX_THREADS; tid++) 
    { 
     args[tid].tid = tid; 
     args[tid].status = tid; 
     return_code = pthread_create(&workers[tid], &tattr, job, (void *)(&args[tid])); 
     if (return_code != 0) { printf("[ERROR] Thread creation failed\n"); return EXIT_FAILURE; } 
    } 

    /* Free thread attribute */ 
    pthread_attr_destroy(&tattr); 

    /* Properly join() all workers before completion */ 
    for(tid = 0; tid < MAX_THREADS; tid++) 
    { 
     return_code = pthread_join(workers[tid], &return_status); 
     if (return_code != 0) 
     { 
      printf("[ERROR] Return code from pthread_join() is %d\n", return_code); 
      return EXIT_FAILURE; 
     } 
     printf("Thread %d joined with return status %d\n", tid, *(int *)return_status); 
    } 

    return EXIT_SUCCESS; 
} 

코드 2 (분리 된 스레드를 니펫을 생성 후) :

/* Running this multiple times with valgrind, I sometimes end with : 
    - no errors (proper malloc/free balance) 
    - 1 extra malloc vs free (most frequently) 
    Most surprisingly, it seems there is a random amount of overall mallocs 
*/ 

#include <stdlib.h>    /* EXIT_FAILURE, EXIT_SUCCESS macros & the likes */ 
#include <stdio.h>    /* printf() & the likes */ 
#include <pthread.h>   /* test subject */ 
#include <unistd.h>   

#define MAX_THREADS 100   /* Number of threads */ 
pthread_attr_t tattr;   /* Thread attribute */ 
pthread_t workers[MAX_THREADS]; /* All the threads spawned by the main() thread */ 

/* A mock container structure to pass arguments around */ 
struct args_for_job_t { 
    int tid; 
}; 

/* The job each worker will perform upon creation */ 
void *job(void *arg) 
{ 
    /* Cast arguments in a proper container */ 
    struct args_for_job_t *container; 
    container = (struct args_for_job_t *)arg; 

    /* A mock job */ 
    printf("[TID - %d]\n", container->tid); 

    /* For the sake of returning something, not necessary */ 
    return NULL; 
} 

int main() 
{ 
    int return_code;       /* Will hold return codes */ 
    int tid;         /* Thread id */ 
    struct args_for_job_t args[MAX_THREADS]; /* For thread safeness */ 

    /* Initialize and set thread joinable attribute */ 
    pthread_attr_init(&tattr); 
    pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE); 

    /* Spawn detached threads */ 
    for (tid = 0; tid < MAX_THREADS; tid++) 
    { 
     args[tid].tid = tid; 
     return_code = pthread_create(&workers[tid], &tattr, job, (void *)(&args[tid])); 
     if (return_code != 0) { printf("[ERROR] Thread creation failed\n"); return EXIT_FAILURE; } 
     /* Detach worker after creation */ 
     pthread_detach(workers[tid]); 
    } 

    /* Free thread attribute */ 
    pthread_attr_destroy(&tattr); 

    /* Delay main() completion until all detached threads finish their jobs. */ 
    usleep(100000); 
    return EXIT_SUCCESS; 
} 

코드 3 (작성시 분리 된 스레드) 니펫 :

/* Running this multiple times with valgrind, I sometimes end with : 
    - no errors (proper malloc/free balance) 
    - 1 extra malloc vs free (most frequently) 
    Most surprisingly, it seems there is a random amount of overall mallocs 
*/ 

#include <stdlib.h>    /* EXIT_FAILURE, EXIT_SUCCESS macros & the likes */ 
#include <stdio.h>    /* printf() & the likes */ 
#include <pthread.h>   /* test subject */ 

#define MAX_THREADS 100   /* Number of threads */ 
pthread_attr_t tattr;   /* Thread attribute */ 
pthread_t workers[MAX_THREADS]; /* All the threads spawned by the main() thread */ 

/* A mock container structure to pass arguments around */ 
struct args_for_job_t { 
    int tid; 
}; 

/* The job each worker will perform upon creation */ 
void *job(void *arg) 
{ 
    /* Cast arguments in a proper container */ 
    struct args_for_job_t *container; 
    container = (struct args_for_job_t *)arg; 

    /* A mock job */ 
    printf("[TID - %d]\n", container->tid); 

    /* For the sake of returning something, not necessary */ 
    return NULL; 
} 

int main() 
{ 
    int return_code;       /* Will hold return codes */ 
    int tid;         /* Thread id */ 
    struct args_for_job_t args[MAX_THREADS]; /* For thread safeness */ 

    /* Initialize and set thread detached attribute */ 
    pthread_attr_init(&tattr); 
    pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); 

    /* Spawn detached threads */ 
    for (tid = 0; tid < MAX_THREADS; tid++) 
    { 
     args[tid].tid = tid; 
     return_code = pthread_create(&workers[tid], &tattr, job, (void *)(&args[tid])); 
     if (return_code != 0) { printf("[ERROR] Thread creation failed\n"); return EXIT_FAILURE; } 
    } 

    /* Free thread attribute */ 
    pthread_attr_destroy(&tattr); 

    /* Delay main() completion until all detached threads finish their jobs. */ 
    usleep(100000); 
    return EXIT_SUCCESS; 
} 
코드 1 (결합 스레드 & MEM 리크) 코드에 대한

==27802== 
==27802== HEAP SUMMARY: 
==27802==  in use at exit: 1,558 bytes in 4 blocks 
==27802== total heap usage: 105 allocs, 101 frees, 28,814 bytes allocated 
==27802== 
==27802== Searching for pointers to 4 not-freed blocks 
==27802== Checked 104,360 bytes 
==27802== 
==27802== 36 bytes in 1 blocks are still reachable in loss record 1 of 4 
==27802== at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==27802== by 0x400894D: _dl_map_object (dl-load.c:162) 
==27802== by 0x401384A: dl_open_worker (dl-open.c:225) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x4013319: _dl_open (dl-open.c:639) 
==27802== by 0x517F601: do_dlopen (dl-libc.c:89) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x517F6C3: __libc_dlopen_mode (dl-libc.c:48) 
==27802== by 0x4E423BB: pthread_cancel_init (unwind-forcedunwind.c:53) 
==27802== by 0x4E4257B: _Unwind_ForcedUnwind (unwind-forcedunwind.c:130) 
==27802== by 0x4E4069F: __pthread_unwind (unwind.c:130) 
==27802== by 0x4E3AFF4: pthread_exit (pthreadP.h:265) 
==27802== 
==27802== 36 bytes in 1 blocks are still reachable in loss record 2 of 4 
==27802== at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==27802== by 0x400B7EC: _dl_new_object (dl-object.c:161) 
==27802== by 0x4006805: _dl_map_object_from_fd (dl-load.c:1051) 
==27802== by 0x4008699: _dl_map_object (dl-load.c:2568) 
==27802== by 0x401384A: dl_open_worker (dl-open.c:225) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x4013319: _dl_open (dl-open.c:639) 
==27802== by 0x517F601: do_dlopen (dl-libc.c:89) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x517F6C3: __libc_dlopen_mode (dl-libc.c:48) 
==27802== by 0x4E423BB: pthread_cancel_init (unwind-forcedunwind.c:53) 
==27802== by 0x4E4257B: _Unwind_ForcedUnwind (unwind-forcedunwind.c:130) 
==27802== 
==27802== 312 bytes in 1 blocks are still reachable in loss record 3 of 4 
==27802== at 0x4C29DB4: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==27802== by 0x4010B59: _dl_check_map_versions (dl-version.c:300) 
==27802== by 0x4013E1F: dl_open_worker (dl-open.c:268) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x4013319: _dl_open (dl-open.c:639) 
==27802== by 0x517F601: do_dlopen (dl-libc.c:89) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x517F6C3: __libc_dlopen_mode (dl-libc.c:48) 
==27802== by 0x4E423BB: pthread_cancel_init (unwind-forcedunwind.c:53) 
==27802== by 0x4E4257B: _Unwind_ForcedUnwind (unwind-forcedunwind.c:130) 
==27802== by 0x4E4069F: __pthread_unwind (unwind.c:130) 
==27802== by 0x4E3AFF4: pthread_exit (pthreadP.h:265) 
==27802== 
==27802== 1,174 bytes in 1 blocks are still reachable in loss record 4 of 4 
==27802== at 0x4C29DB4: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==27802== by 0x400B57D: _dl_new_object (dl-object.c:77) 
==27802== by 0x4006805: _dl_map_object_from_fd (dl-load.c:1051) 
==27802== by 0x4008699: _dl_map_object (dl-load.c:2568) 
==27802== by 0x401384A: dl_open_worker (dl-open.c:225) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x4013319: _dl_open (dl-open.c:639) 
==27802== by 0x517F601: do_dlopen (dl-libc.c:89) 
==27802== by 0x400F175: _dl_catch_error (dl-error.c:178) 
==27802== by 0x517F6C3: __libc_dlopen_mode (dl-libc.c:48) 
==27802== by 0x4E423BB: pthread_cancel_init (unwind-forcedunwind.c:53) 
==27802== by 0x4E4257B: _Unwind_ForcedUnwind (unwind-forcedunwind.c:130) 
==27802== 
==27802== LEAK SUMMARY: 
==27802== definitely lost: 0 bytes in 0 blocks 
==27802== indirectly lost: 0 bytes in 0 blocks 
==27802==  possibly lost: 0 bytes in 0 blocks 
==27802== still reachable: 1,558 bytes in 4 blocks 
==27802==   suppressed: 0 bytes in 0 blocks 
==27802== 
==27802== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2) 
--27802-- 
--27802-- used_suppression:  2 dl-hack3-cond-1 
==27802== 
==27802== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2) 

Valgrind의 출력

Valgrind의 출력 1 (mem-leak 없음, 나중에 약간 실행)

--29170-- Discarding syms at 0x64168d0-0x6426198 in /lib/x86_64-linux-gnu/libgcc_s.so.1 due to munmap() 
==29170== 
==29170== HEAP SUMMARY: 
==29170==  in use at exit: 0 bytes in 0 blocks 
==29170== total heap usage: 105 allocs, 105 frees, 28,814 bytes allocated 
==29170== 
==29170== All heap blocks were freed -- no leaks are possible 
==29170== 
==29170== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2) 
--29170-- 
--29170-- used_suppression:  2 dl-hack3-cond-1 
==29170== 
==29170== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2) 
+0

valgrind 출력이란 무엇입니까? (Aside : 그 코드에서'pthread_exit'를 사용할 필요가 없습니다. 대신'return 0;'을 사용할 수 있습니다.) –

+0

반환 값 0을 적용하여 수정했습니다. main()에서 pthread_exit() 대신에. 또한 joined() 코드 스 니펫에 valgrind 출력을 추가하여 두 번째 및 세 번째 질문을 지우지 않은 main()의 완료 시간을 늘 렸습니다. – swappy

+0

그건 실제로 메모리 누수가 아니야, 모든 메모리는 여전히 접근 가능합니다. –

답변

5

스레드가 분리되었을 때 버그가 발생하여 정의되지 않은 동작이 발생합니다. 어떤 포인터는 손 당신의 작업자 스레드에

struct args_for_job_t args[MAX_THREADS]; 

:

주에서는 코드 줄이있다. 의 스택에있어 위의 args 배열에 액세스하는, 그리고 주요

는()

pthread_exit(NULL); 

그리고 주()가 존재 중단이 부분을 도달,하지만 당신은 여전히 ​​주위 작업자 스레드를 가질 수있다() 주 - 더 이상 존재하지 않습니다. 일부 실행에서는 main()이 종료되기 전에 작업자 스레드가 모두 완료 될 수 있지만 다른 실행에서는 완료되지 않을 수 있습니다.

+2

고마워요, 그런 행동을 의심했지만 다시 확인하고 싶었습니다. 문제는 pthread_exit (NULL) 전에 타이머 (usleep())를 추가하거나 (Jonathan이 제안한대로 0을 반환 함) 여전히 무작위로 동작했기 때문입니다. 필자는 return 대신 pthread_exit()를 사용하면 모든 worker가 완료 될 때까지 main() 스레드가 멈추도록 지시 할 것이라고 pthread_join()이 정확히 무엇을하는지 살고있었습니다. – swappy

+0

나는 고쳐 주었고 분리 된 쓰레드로 10000에서 100000으로 잠자기 시간을 맞 췄고 모든 메모리 누수가 사라진 것 같아서 main()이 종료되기 전에 완료 할 모든 시간을 남겨 둡니다. – swappy

+0

죄송합니다.'main'의'pthread_exit'가 기다리게하는 것이 옳습니다. 다른 스레드와 스 니펫 1에서'pthread_exit'에 대한 호출이 중복 됨 –