linux에서 pthread 사용시 memory leak...

nTachyon의 이미지

안녕하세요~

제목에서처럼 pthread의 memory leak 문제에 대해 질문 올립니다.

소스는 아래에 적었습니다만, 아주 간단한 스레드 프로그램을 만들어 돌려보았는데...

dmalloc 메모리 디버거가 남긴 정보를 보면...
alloc calls: malloc 2, calloc 0, realloc 0, free 1
라는 분석이 나오더군요.

malloc 2개중의 하나는 소스내에서 strdup를 통해 malloc 된것이고, 이것은 바로 free 시켰습니다만...

나머지 한개의 malloc은 뭘까 궁금해서 gdb로 추적해봤더니 이렇게 밖에는 더이상 추적이 안되네요.
No line number information available for address 0x4002a839 <__pthread_initialize_manager+73>

더이상 추적은 안되고 단서는

pthread_initialize_manager

뭐 이정도인데...

제가 thread 함수내에서 들어가자마자 아무일도 안하고 바로 pthread_exit();로 탈출하도록 코드를 수정시키고 돌려보아도

여전히 malloc 갯수가 free 갯수보다 1개가 많네요.

혹 이 단서만으로 pthread의 initialize 부분에서의 메모리 문제에 대해 의견 있으신 분들은 조언 부탁드립니다.

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

int gRemainThread;
pthread_mutex_t threadmutex = PTHREAD_MUTEX_INITIALIZER;

void thread_func(void *arg)
{
    int j, loop_cnt;
    char *szPtr;

    loop_cnt = (int) arg;

    for(j=0; j<loop_cnt; j++)
    {
        szPtr = (char *) strdup("동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리 나라 만세 무궁화 삼천리 화려강산 대
한사람 대한으로 길이 보존하세");
        //printf("[%d] szPtr : [%s]\n", pthread_self(), szPtr);
        free(szPtr);
    }

    pthread_mutex_lock( &threadmutex );
    gRemainThread--;
    printf("[%d] 스레드 종료 - 남은 스레드 : %d\n", pthread_self(), gRemainThread);
    pthread_mutex_unlock( &threadmutex );

    pthread_exit((void*)0);
}

int main(int argc, char **argv)
{
    int i, thread_cnt, loop_cnt;
    pthread_t tid;

    if( argc != 3 )
    {
        printf("Usage : thread_test <thread_count> <loop_count_per_thread>\n");
        return(0);
    }

    gRemainThread = thread_cnt = atoi( argv[1] );
    loop_cnt = atoi( argv[2] );

    for(i=0; i<thread_cnt; i++)
    {
        pthread_create(&tid, NULL, (void*) &thread_func, (void*) loop_cnt);
        pthread_detach( tid );
    }

    printf("while 진입 전 - 남은 스레드 : %d\n", gRemainThread);
    while( gRemainThread > 0 )
    {
        printf("남은 스레드 : %d\n", gRemainThread);
        sleep(1);
    }

    pthread_mutex_destroy( &threadmutex );

    printf("메인 스레드 종료\n");

    return(0);
쎄피로의 이미지

실행은 어떻게 하셨는지 아규먼트를 알려주세요..

그리고 어디서 들은 봐로는 꼭 free한다고 해서 그 시점에

바로 메모리가 free되는 것이 아니라고 합니다.

저도 잘 모르겠네요 :(

세상은 넓고, 할 일은 많은데, 난 숨만 쉬고 있니?

nTachyon의 이미지

thread_test <생성할 스레드 갯수> <loop 횟수 per 스레드>
이렇게 실행하시면 되고요,

컴파일 할 때는 -lpthread 이 라이브러리 링크시켜 컴파일 하시면 됩니다.

dmalloc.com 에서 라이브러리 받아서 설치하시면, 위와 같은 report를 받아볼 수 있습니다.

아! dmalloc 사용하려면, dmalloc.h 파일 include 하시고, 스레드용의 libdmallocth.a 를 같이 링크해서 컴파일 해 주세요~~~

gcc -c main.c
gcc -o thread_test main.o libdmallocth.a -lpthread
ssehoony의 이미지

valgrind 에 의한 정보 입니다.
보니깐 뮤텍스 의 락킹이 잘 못 이용됐고, 뮤텍스 제거도 문제가 있네요.

Quote:
==5833== Memcheck, a memory error detector for x86-linux.
==5833== Copyright (C) 2002-2003, and GNU GPL'd, by Julian Seward.
==5833== Using valgrind-2.1.0, a program supervision framework for x86-linux.
==5833== Copyright (C) 2000-2003, and GNU GPL'd, by Julian Seward.
==5833== Command line
==5833== ./a.out
==5833== 1
==5833== 1
==5833== Startup, with flags:
==5833== --suppressions=/usr/local/valgrind-2.1.0/lib/valgrind/default.supp
==5833== -v
==5833== --leak-check=yes
==5833== Reading syms from /home/zinbtsysh/devilhero/a.out
==5833== Reading syms from /lib/ld-2.3.2.so
==5833== object doesn't have any debug info
==5833== Reading syms from /usr/local/valgrind-2.1.0/lib/valgrind/vgskin_memcheck.so
==5833== Reading syms from /usr/local/valgrind-2.1.0/lib/valgrind/valgrind.so
==5833== Reading syms from /usr/local/valgrind-2.1.0/lib/valgrind/libpthread.so
==5833== Reading syms from /lib/i686/libc-2.3.2.so
==5833== object doesn't have any debug info
==5833== Reading suppressions file: /usr/local/valgrind-2.1.0/lib/valgrind/default.supp
==5833== Estimated CPU clock rate is 598 MHz
==5833== REPLACING libc(__GI___errno_location) with libpthread(__errno_location)
==5833== REPLACING libc(__GI___h_errno_location) with libpthread(__h_errno_location)
==5833== REPLACING libc(__GI___res_state) with libpthread(__res_state)
==5833==
==5833== TRANSLATE: 0x40269D10 redirected to 0x4022402C
while 진입 전 - 남은 스레드 : 1
==5833== pthread_mutex_unlock: mutex is locked by a different thread
==5833== at 0x40222D41: __pthread_mutex_unlock (vg_libpthread.c:986)
==5833== by 0x40222D8A: __pthread_mutex_destroy (vg_libpthread.c:1007)
==5833== by 0x8048892: main (in /home/zinbtsysh/devilhero/a.out)
==5833==
==5833== pthread_mutex_destroy: mutex is still in use
==5833== at 0x40221C89: pthread_error (vg_libpthread.c:280)
==5833== by 0x40222D96: __pthread_mutex_destroy (vg_libpthread.c:1008)
==5833== by 0x8048892: main (in /home/zinbtsysh/devilhero/a.out)
[2] 스레드 종료 - 남은 스레드 : 0
메인 스레드 종료
==5833== TRANSLATE: 0x40346630 redirected to 0x40224134
==5833==
==5833== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
==5833==
==5833== 1 errors in context 1 of 2:
==5833== pthread_mutex_destroy: mutex is still in use
==5833== at 0x40221C89: pthread_error (vg_libpthread.c:280)
==5833== by 0x40222D96: __pthread_mutex_destroy (vg_libpthread.c:1008)
==5833== by 0x8048892: main (in /home/zinbtsysh/devilhero/a.out)
==5833==
==5833== 1 errors in context 2 of 2:
==5833== pthread_mutex_unlock: mutex is locked by a different thread
==5833== at 0x40222D41: __pthread_mutex_unlock (vg_libpthread.c:986)
==5833== by 0x40222D8A: __pthread_mutex_destroy (vg_libpthread.c:1007)
==5833== by 0x8048892: main (in /home/zinbtsysh/devilhero/a.out)
==5833== IN SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
==5833==
==5833== malloc/free: in use at exit: 200 bytes in 1 blocks.
==5833== malloc/free: 3 allocs, 2 frees, 332 bytes allocated.
==5833==
==5833== searching for pointers to 1 not-freed blocks.
==5833== checked 3696744 bytes.
==5833==
==5833== LEAK SUMMARY:
==5833== definitely lost: 0 bytes in 0 blocks.
==5833== possibly lost: 0 bytes in 0 blocks.
==5833== still reachable: 0 bytes in 0 blocks.
==5833== suppressed: 200 bytes in 1 blocks.
==5833== Reachable blocks (those to which a pointer was found) are not shown.
==5833== To see them, rerun with: --show-reachable=yes
--5833-- TT/TC: 0 tc sectors discarded.
--5833-- 627 chainings, 0 unchainings.
--5833-- translate: new 1145 (18445 -> 235182; ratio 127:10)
--5833-- discard 0 (0 -> 0; ratio 0:10).
--5833-- dispatch: 0 jumps (bb entries), of which 5726 (572600%) were unchained.
--5833-- 10/2061 major/minor sched events. 1146 tt_fast misses.
--5833-- reg-alloc: 132 t-req-spill, 44665+936 orig+spill uis, 5512 total-reg-r.
--5833-- sanity: 9 cheap, 1 expensive checks.
--5833-- ccalls: 4596 C calls, 56% saves+restores avoided (15360 bytes)
--5833-- 6074 args, avg 0.88 setup instrs each (1410 bytes)
--5833-- 0% clear the stack (13788 bytes)
--5833-- 1669 retvals, 35% of reg-reg movs avoided (1142 bytes)
ssehoony의 이미지

pthread_detach 를 pthread_join 으로 수정하고 테스트 해봤는데
뮤텍스 관련 에러는 사라지네요. 하지만 free 되지 않은 블럭은 그대로 있네요
뮤텍스에 의해 할당된 블럭은 아닌 것 같네요

제가 알기로 pthread 는 쓰레드 상태를 기록하기 위한 메모리를 할당하는 걸로 알고 있는데 그런 메모리와 관련이 있는게 아닌가 하네요.

nTachyon의 이미지

valgrind... 이름은 들어봤지만 써보진 않았는데 참 많은 정보를 주네요...
좋은 답변 남겨주셔서 감사합니다.

근데 뮤텍스의 락킹이 잘못 이용되었다는 부분은 에러가 아닌 것 같습니다만...

detach를 통해서 부모 쓰레드와 분리했을때, 부모/자식간의 연결 고리가 없어서 락을 만든 곳이 아닌 다른 쓰레드에서 락을 사용했다고 에러를 주는거 아닌가 싶습니다.
때문에 detach를 안시키고 부모스레드가 자식 종료때까지 기다리도록 pthread_join을 했을 때는 에러를 안내는 것 아닐까요?
걍 제 의견입니다. ^^;;;

저도 valgrind를 써서 좀 더 찾아보겠습니다.

일단은 올려주신 log도 몇 부분밖엔 모르겠는지라 ^^;;;

근데 눈에 띄는 부분은 메모리 alloc과 free 갯수가 역시나 차이가 나네요.

이거 정말... 에러가 아닌 에러... 뭐 그런거 아닐려나 ('' )a

에구구... ㅋㅋㅋ

답변 감사드립니다!!! :o

ssehoony의 이미지

음.. 락킹 에러에 대한 설명을 위의 join 에서 했는데 불 충분했나 보네요.
그러니깐 detach 를 이용하면 join 과는 다르게 쓰레드가 바로 죽는게 아니고 그냥 제어 연결을 끊는 건데요.

main 쓰레드에서 하부 쓰레드들을 detach 를 하고 밑에서 destroy 하는 부분에 가는 동안에 하부 쓰레드들은 lock 시킨 뮤텍스를 모두 unlock 를 해야 하는데 하부 쓰레드들이 lock 한 뮤텍스를 unlock 하기 전에 main 쓰레드에서 destory 로 먼저 가버린겁니다. 그래서 하부 쓰레드가 lock 을 갖고 있는 뮤텍스를 main 쓰레드에서 제거 할려고 하니깐 에러가 나는거져.

그래서 쓰레드가 완전히 종료되길 기다리는 join 함수를 이용하면 destory 에서 에러가 사라진다는 이야기지요.
즉, 원본 코드는 동기화에 문제가 있다는 이야기 입니다. ^^;

pynoos의 이미지

valgrind가 주는 정보는 참으로 좋은 것들이 많습니다.
그런데, 요놈이 주는 정보중에 libc 자체에서 생기는 메모리 leak이 있더군요.
그걸 leak으로 봐야할지 모르겠지만, startup code에서 할당한 메모리중에서 일부 exit과정에서 해제하지 않고 끝내는 경우가 있습니다.

사실 아무 문제없죠.

프로세스에 전담되어 있는 메모리가 해제되는 경우니까요...

위 내용도 그와 비슷한 상황이라 생각합니다. 일부 시스템 라이브러리에서 할당한 메모리들은 해제되지 않은 채 프로세스와 운명을 같이 합니다.

그럴지라도 염려없는 것은 그 메모리 양이 지속적으로 증가하는 것은 아니고, thread safety를 위한 것 등을 위해 사용되는 것이라 생각됩니다.

nTachyon의 이미지

에러 아닌 에러... 이게 에러가 아닌데 valgrind나 dmalloc 같은 것들이 워낙 타이트하게 memory 체크를 하다보니 에러라고 리포팅 주는 것 아닌가 생각됩니다.

valgrind를 써가며 돌려봐더 더이상 들어가질 못하겠네요.
결국 원론적인 문서같은데서나 힌트를 얻을까... 더 이상의 테스트로는 힘드네요.

사실 제가 이리도 이 문제에 집착(? ^^;)하는건 메모리 누수가 있다는 서버 프로그램(누군가의 얘기입니다 ^^)의 누수 원인 분석에 들어가기 전에...
dmalloc 이나 valgrind를 써서 기본적으로 나오는 시스템적인 malloc아니 realloc에 대한 내용을 먼저 확인하고 들어갈려고요. 그래서 dmalloc이나 valgrind의 리포트에서 제외(무시)할 수치가 어느정도인지 미리 확인해볼려고... 쩝

지금 이 문제만 해도 예제 코드를 돌릴 때 스레드 갯수에 따라 다음 변화가 있더라구요...

스레드 1개 돌릴 때 : ==11451== malloc/free: 4 allocs, 3 frees, 864 bytes allocated.
스레드 2개 돌릴 때 : ==11451== malloc/free: 7 allocs, 6 frees, 864 bytes allocated.

제가 코드상에서 명시적으로 malloc/free 한건 1/1 이니까, 스레드 1개당 3번의 malloc과 2번의 free는 라이브러리 차원에서 일어나는 일이다...라고 이해하고 들어갈려는 겁니다.

횡설수설이네요 ㅡㅡ;
사실 큰 소스속에서 대체 어디서 메모리 누수가 일어나나 찾을려니 막막하네요 ㅜㅡ

부하를 많이 줘야 그나마 누수가 늘어나는 걸 top으로 확인할 수 있으니 원...

아무튼 많은 답변 감사드립니다.

여러 님들 덕분에 좋은 정보 많이 얻었습니다.

그리고 혹여나... 메모리 누수 지점 찾는 노하우들 있으신가요? 제가 지금 처한 상황처럼 내가 만든 코드도 아닌데 메모리 누수는 있는 것 같더라... 라고 하니 이건 뜬 구름 잡아야 할 심정이네요 ㅎㅎㅎ

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.