출처 : http://sunjinyang.wordpress.com/2009/06/18/debugging-memory-leaks-with-tcmalloc-google-perftools/


리눅스에서 메모리 침범이나 메모리 누수, 혹은 복잡한 메모리 접근 관련 오류를 디버깅할때는 대부분 Valgrind 도구를 이용합니다. 하지만 Valgrind는 많은 메모리를 사용하고 실행 속도가 현저하게 느려지기 때문에, 별도의 타겟 장비에서 제한된 조건으로 동작하는 어플리케이션에는 조금 무리가 있습니다. 게다가 Valgrind의 메모리 검사 도구는 프로그램이 종료된 시점에서 누수된 메모리만 찾기 때문에, 실행 중에는 엄청나게 메모리를 사용하다가 정상적인 객체 해제 루틴이 호출되면 모든 메모리가 정리되어 찾을 수 없는 경우는 발견하지 못합니다.

구글 성능 도구를 다시 검토하고 사용하게 된 이유도 바로 여기에 있습니다. 현재 디버깅 중인 프로그램의 문제가 대략 이렇습니다. 몇날 며칠이고 문제없이 가동해야 하는 프로그램이 특정 설정을 적용한 후에는, 자고 일어나면 10~20메가씩 메모리 점유율이 한꺼번에 올라갑니다. 하지만 Valgrind 도구를 사용해도 어느 부분이 문제인지 찾을 수가 없습니다. 지난 글에서 TCMalloc 메모리 할당자로 교체한 후 문제가 해결된 줄 알았던 바로 그 패턴이기도 합니다. 그래서 이번에는 프로파일 기능을 이용해 직접 메모리 누수를 디버깅 해보고, 나중을 위해 그 과정을 정리해 보았습니다.

디버깅 환경

디버깅 환경은 우분투 9.04 x86_32 플랫폼입니다. x86_64 플랫폼에서는 이상하게 호출 그래프가 그려지지 않아서 일단 무시했습니다. 사용한 버전은 공식 홈페이지에서 다운로드 받은 1.2 버전입니다. 1.3 버전은 이상하게 프로파일 기능이 동작하지 않아 역시 무시했습니다.

TCMalloc 도구 설치 및 연결

호출 그래프를 생성하기 위해 dot 프로그램이 필요한데 이 프로그램은 graphviz 패키지에 들어있으므로 설치해야 합니다.

$ sudo apt-get install graphviz

우선, 공식 홈페이지에서 google-perftools 압축 파일을 다운로드 한 뒤 다음과 같이 빌드하고 설치합니다. [2011.05.02 추가]INSTALL 문서에도 명시되어 있듯이, x86_64 환경에서는 libunwind 라이브러리를 미리 설치한 뒤 빌드해야 정상적으로 동작합니다.

$ cd google-perftools*
$ ./configure --prefix=/usr
$ make
$ sudo make install

TCMalloc 라이브러리를 연결하는 방법은 공식 문서에도 나와 있듯이 디버깅할 프로그램 링크 마지막에 ‘-ltcmalloc‘ 옵션을 추가하거나, 실행할때 프로그램 앞에 ‘LD_PRELOAD=/usr/lib/libtcmalloc.so execute-file‘ 처럼 라이브러리를 먼저 로드해주면 됩니다. 저는 첫번째 방법을 사용했습니다. [2011.05.02 갱신] 언제부터인지는 확실치 않지만 첫번째 방법은 동작하지 않고 두번째 방법으로 해야 메모리 프로파일이 정상적으로 동작합니다.

참고로, 디버깅할 프로그램을 컴파일할때는 디버깅 심볼 옵션(-g)이 있어야 호출 그래프에서 정확한 함수 이름이 표시됩니다. 또한 최적화 옵션(-O2 등)을 사용 안하면 더 정확한 함수 호출 그래프를 얻을 수 있습니다.

프로파일 데이터 얻기

TCMalloc 라이브러리를 연결해도 기본적으로 프로파일 기능은 동작하지 않습니다. HEAPPROFILE 환경변수에 프로파일 정보를 주기적으로 덤프할 파일 이름 접두사(prefix)를 지정해야만 동작합니다.

$ HEAPPROFILE=/tmp/profile execute-file

접두사는 파일 절대 경로로 디렉토리를 포함할 수 있습니다.

만일 덤프 파일이 너무 자주 생성되거나 반대로 너무 드물게 생성된다면 환경 변수를 통해 간격을 조절할 수 있습니다.

$ HEAPPROFILE=/tmp/profile 
  HEAP_PROFILE_ALLOCATION_INTERVAL=107374182400 
  execute-file

더 자세한 옵션은 공식 문서를 확인해 보시기 바랍니다.

위 예제에서 지정한 방식대로 프로그램을 실행하면 /tmp/profile.0001.heap/tmp/profile.0002.heap/tmp/profile.0003.heap 등과 같은 프로파일 덤프 파일이 실행 도중 계속 생성됩니다.

결과 그래프 얻기

포스트스크립트(PostScript) 파일 형식으로 메모리 프로파일 정보를 포함한 함수 호출 그래프를 얻으려면 다음과 같이 형식으로 pprof 프로그램을 실행하면 됩니다.

$ pprof --ps --lines 
    execute-file 
    /tmp/profile.0001.heap 
    > profile-0001.ps

프로파일 덤프 파일에 대하여 하나씩 그래프를 생성해 직접 눈과 손으로 비교하는 것도 나쁘지 않지만, 두 프로파일의 차이점만 그래프로 만들어주는 옵션이 있으므로 이를 사용하면 더 편리합니다. 즉, 시간대별 메모리 사용량의 달라진 부분이 정확하게 어느 함수 호출 때문인지 알 수 있게 해줍니다.

$ pprof --ps --lines 
    --base /tmp/profile.0001.heap 
    execute-file 
    /tmp/profile.0002.heap 
    > profile-0002-diff.ps

물론, PDF(--pdf), GIF(--gif) 등과 같은 다른 형식으로 그래프 파일을 얻을 수도 있습니다. 더 자세한 옵션은 ‘pprof --help‘를 참고하시면 됩니다.

결론

며칠간 디버깅에 적용해보니 문제가 발생한 패턴이 무엇이었는지도 찾아내고, 간과했던 작은 메모리 누수 버그들도 함께 발견할 수 있었습니다. 하지만, 언제나 그렇듯이, 디버깅 사태까지 오기 전에 더 튼튼하게 설계하고, 더 꼼꼼하게 프로그래밍하고, 더 철저하게 코드 리뷰와 테스트를 거치는 게 정도임을 새삼 깨닫습니다.

출처 : http://blog.daum.net/jchern/13756799


download : http://code.google.com/p/google-perftools/downloads/list

toturial : http://google-perftools.googlecode.com/svn/trunk/doc/cpuprofile.html

 

설치방법

tar -zxvf google-perftools-1.6.tar.gz

cd google-perftools-1.6

./configure

make

su

make install

 

 

사전 작업

1. 프로파일링을 워하는 프로그램 컴파일시에 -lprofiler를 같이 컴파일한다.

2. <google/profiler.h>를 include한다

 

bash >

export LD_LIBRARY_PATH=/usr/local/lib

export CPUPROFILE=output.txt( CPUPROFILE로 설정된 파일명으로 출력파일이 생성된다 )

 

특정 함수에 대해서만 프로파일링을 하기 위해서는

ProfilerStart()와 ProfilerStop()를 사용하면 된다.

ProfilerStart()에는 함수명을 파라미터로 준다.( ProfileStart("test_func"); )

여기까지 설정한후에 실행을 하면

CPUPROFILE에 설정한 파일명으로 파일이 하나 생성이 된다.

이파일은 바이너리 포맷이라서 그냥 볼수는 없고

 

/usr/local/bin/pprof 라는 파일로 볼수 있다.

/usr/local/bin/pprof [실행파일명] [output.txt]

(pprof)라고 프로프트가 뜨면 top를 입력

 

(pprof) top

 

상위에 노출되는 함수들이 실행시간이 오래걸리는 함수들이다.

 

 

 

특정 함수에 대한 프로파일링 :

pprof    --gv --focus=함수명   실행파일 출력파일

 

특정 함수를 제외한 프로파일링

pprof   --gv --ignore=함수명  실행파일 출력파일


 

좀더 자세한 내용은 요기서 ; http://google-perftools.googlecode.com/svn/trunk/doc/cpuprofile.html

 

Memory Allocator for Multithreaded Applications

출처 : http://www.mimul.com/pebble/default/2011/05/15/1305469792430.html 

 

응용 프로그램에서 메모리 사용은 필수 불가결한 거죠. 그런데도 메모리에 대한 사용상 효율, 성능을 잘 고려하지 않고 그냥 있는 malloc을 사용하고 말죠. 그런데 트래픽이 많은 서비스가 요즘 많아지다보니 메모리 사용에도 신경을 써야하는 경우가 많습니다.
그래서 메모리 할당에 관해 정보를 정리하고 공유합니다.


메모리를 할당받기 위해 사용하는 malloc()함수는 일반적으로 glibc에 포함된 메모리 할당자에서 구현이 되어 있습니다. 그리고 메모리 할당자는 ptmalloc입니다.
메모리 할당자의 역할은 brk/sbrk/mmap등을 사용해서 시스템으로부터 큰 메모리 영역을 할당 받아서, 이것을 적절하게 분할하여 어플리케이션이 요청하는 메모리 할당을 처리하게 됩니다.
하지만, 빈번하게 메모리를 할당/해제하고 수십 개의 쓰레드가 동작하는 프로그램에서는 어쩔 수 없이 메모리 단편화(Memory Fragmentation)가 발생하여 메모리 사용량이 늘어나게 되죠.
그래서 규모가 큰 어플리케이션들은 glibc의 기본 메모리 할당자인 ptmalloc이 메모리 단편화 문제가 심하고, multi thread, multi processor에 대한 고려가 적어서 performance bottleneck이 존재하여 다른 메모리 할당자를 사용하는 경우가 있습니다.


데이터 섹션(메모리 사용 구조)

 - 전역 메모리(Global Memory) : 지역 함수(Local Function), 전역 변수(Global Variable), 정적 변수(Static Variable)
 - 스택 메모리(Stack Memory) : 지역변수와 매개 변수가 저장된 곳. 컴파일 타임에 크기 결정. 메모리 생명 주기를 알기 때문에 단편화가 안일어남.
 - 힙 메모리(Heap Memory) : 동적 메모리 할당을 위한 곳. 프로그래머가 할당 및 해제해야 함. 런타임에 크기 결정. 메모리 생명 주기를 모르기 때문에 단편화가 일어남.

컴파일러 및 링커가 메모리 할당 기능을 수행할 때에는 메모리 단편화가 일어나지 않습니다. 왜냐하면 컴파일러가 데이터의 수명을 알고 있기 때문입니다.
메모리 할당자는 기본적으로 오버헤드, 내부 단편화, 외부 단편화 등 3가지 측면에서 메모리를 낭비합니다.

메모리 낭비 종류

 - 오버헤드 : 메모리 할당자는 할당 상태를 설명해 주는 일부 데이터를 저장해야 하는데. 즉, 모든 자유 블록의 위치, 크기, 소유정보, 그리고 내부 상태와 관련된 정보를 저장해야 합니다. 일반적으로 메모리 할당자가 이러한 오버헤드(부가) 정보를 저장하기에 가장 좋은 장소는, 할당자가 자체 관리하는 메모리.
 - 내부 단편화 : 모든 메모리 할당 작업은 프로세서 아키텍처에 따라 4, 8, 16으로 나뉠 수 있는 주소에서 시작되어야 함. 이렇게 미리 정의된 크기로만 클라이언트에게 블록을 할당하는 데는 또 다른 이유가 있을 수 있다. 만약 클라이언트가 41바이트 블록을 요청하면 42, 48 또는 그 이상의 바이트를 얻게 된다. 클라이언트가 요청한 크기를 반올림한 결과 남게 되는 여분의 공간. 이것이 내부 단편화.
 - 외부 단편화 : 애플리케이션이 연속해서 세 블록을 할당한 후, 가운데를 제거하면 외부 단편화가 발생.


Memory Allocator 종류

 - tcMalloc : Google에서 만들었고 오픈 소스며, 메모리 프로파일 기능도 제공함. Linux, 작은 사이즈의 allocation에 최적화 된 것, MacOsx, Windows 지원. 레퍼런스 가장 많음. 임베드 시스템에도 많이 활용됨, NUMA 아키텍처 지원
 - jemalloc : 오픈 소스며, Linux ,MacOsx에 최적화 되었고 Windows도 지원. FireFox, Facebook에서 쓰여 레퍼런스가 늘어가고 있음. NUMA 아키텍처 지원은 아직 레퍼런스 찾지 못함
 - nedMalloc : 오픈소스며 간단하고 Windows에 최적화. dlmalloc을 뿌리로 둠.

싱글 스레드 환경에서는 ptmalloc과 위의 메모리 관리자와의 성능과 효율이 크게 다르지 않을 수 있고 멀티 쓰레드 환경에서 위의 메모리 할당자를 사용하므로써 많은 성능 향상을 보았다고 함.


tcMalloc 소개

 1. 작동 방식(성능 향상 방식)
  - 중앙 메모리 관리자와 쓰레드별 메모리 관리자를 구분하고 작은 크기(32K 이하)의 메모리 할당/해제 요청은 쓰레드별 메모리 관리자가 처리하고, 부족할 경우 중앙 메모리 관리자에서 얻어오는 방식으로 처리함.
  - 큰 메모리(32K 이상)는 전역 관리자에서 페이지 크기(4K) 단위로 클래스를 나누어 mmap()을 이용하여 할당하는 함.
 2. 지원 환경
  - Linux (32 and 64 bit), Mac OSX, Windows (32 bit only), Solaris
  - NUMA-aware TCMalloc(NUMA아키텍처 지원됨)
 3. 사용 사례
  - Webkit, MySQL, HyperTable, Memcached, Redis, Nginx


jemalloc 소개

 1. 작동 방식(성능 향상 방식)
  - 쓰레드별 메모리 관리자 Arena와 작은 단위의 잦은 메모리 할당의 경우, arena를 참조 하지 않고, 바로 malloc을 할 수 있도록, 각 스레드에게 thread cache라는 영역을 가지고 있어 성능 향상을 줌.
 2. 지원 환경
  - Linux(32, 64 bit), Windows, MacOSX
 3. 사용 사례
  - FireFox, FaceBook


nedMalloc 소개

 1. 작동 방식(성능 향상 방식)
  - dlmalloc에서 출발했고, 오픈 소스며 내용은 간단하지만 성능도 좋다는 평으로 알려짐.
 2. 지원 환경
  - Windows(32) 최적화됨, MacOSX, Linux
 3. 사용 사례
  - 소소한 개인 사용자 위주. 윈도우 개발자들이 많이 채택함. 일부 Javascript 엔진에 사용


SLAB Allocator

 - 슬랩은 내부 단편화 문제를 해결할 뿐만이 아니라 커널 내에서 흔히 일어나는 dynamic memory 할당의 overhead를 줄이기 위하여 캐싱하는 역할을 하여 성능 개선에도 큰 도움을 주고 있음.
 - 캐시는 관리가 필요한 오브젝트 종류별로(예를 들면task_struct, file, buffer_head, inode 등) 작성되고 그 오브젝트의 슬랩을 관리하고 슬랩은 하나 이상의 연속된 물리 페이지로 구성되어 있으며, 일반적으로 하나의 페이지로 구성된다. 캐시는 이러한 슬랩들의 복수개로 구성됨.
 - 자주 사용되는 오브젝트들을 미리 할당하여 놓고 사용자 요구가 있을 때 마다 바로 반환하는 것이다. 이것은 물리 메모리를 확보하기 위하여 검색 및 회수, 반환과 같은 긴 여행을 떠날 필요가 없으므로 시스템의 성능을 향상시킨다. 또한 다 사용된 오브젝트들을 반납받아 커널의 메모리 할당자에게 반환하지 않고 보관했다가 재사용하기 때문에 시스템의 성능을 향상시킬 수 있게 됨.


응용 예(MySQL-tcMalloc)

 - MySQL 성능 향상 위한 tcMalloc 적용 예


// 64bit 머신일 경우에만 필요함. libunwind
wget http://download.savannah.gnu.org/releases/
  libunwind/libunwind-0.99-alpha.tar.gz
tar zxvf libunwind-0.99-alpha.tar.gz
cd libunwind-0.99-alpha/
CFLAGS=-fPIC ./configure
make CFLAGS=-fPIC
make CFLAGS=-fPIC install

wget http://google-perftools.googlecode.com/
   files/google-perftools-1.7.tar.gz
tar zxvf google-perftools-1.7.tar.gz
cd google-perftools-1.7/
./configure
make && make install

vi /etc/ld.so.conf
/usr/local/lib 라인 추가
/sbin/ldconfig

vi /usr/local/mysql/bin/mysqld_safe
export LD_PRELOAD=/usr/local/lib/libtcmalloc.so


[참고 사이트]

 

1. http://code.google.com/p/gperftools/wiki/GooglePerformanceTools 에 들어가서 최신 다운 경로를 가지고 옴

 (지금 2013년 2월 기준으로 http://gperftools.googlecode.com/files/gperftools-2.0.tar.gz 이거이니까..)

 

 

 

 

 

2. 압축푼곳에 들어가서 ./configure 실행

 

 

 

3. 잘되면 모르겠는데 안되면 해당 로그를 찾아 의존성 패키지를 설치하자.

내 경우에는 g++가 없다는 에러가...

 

 

4. 이후

#make

#make install

 

* 주의점

  INSTALL 파일에 의하면 64비트 환경에서 설치할땐 에러가 있다고 한다.

(https://groups.google.com/forum/?fromgroups#!topic/google-perftools/ZUvzAkYI3G0)

  libunwind를 살포시 설치해 주자.

 

 

=============================================================================================================

64비트 리눅스라면...

 

1. http://pkgs.org/centos-6-rhel-6/epel-x86_64/gperftools-libs-2.0-3.el6.2.x86_64.rpm.html

여기로 들어간다.

libunwind 설치 페이지가 있는데 rpm를 wget을 이용해서 다운로드 한다.

 

이후.. rpm 설치...

=============================================================================================================

 

요약...

 

이러면 centos 에서 설치가 된다..... ㅠㅠ

출처 : http://gamedevforever.com/31

참고 1 : http://sunjinyang.wordpress.com/2009/04/29/tcmalloc-google-perftools/

참고 2 : http://ditto954.tistory.com/entry/TcMalloc-lib-%EC%A0%81%EC%9A%A9-%EC%8B%9C%EB%8F%84

참고 3 : http://dev.kthcorp.com/2011/06/01/mysql5-5-with-tcmalloc-benchmark/


안녕하세요.  라오그람이란 필명을 사용하는 김효진 입니다저는 서버  개발을 하고있구요원래는 게임 서버 개발자이지만 현재는 게임쪽이 아닌 잠시 다른  서버 분야를 개발하고 있습니다.  없는 실력에 네임드 분들 사이에서 글을 쓴다는 것이 영광스럽기 그지 없네요ㅠㅠ 앞으로 더욱  열심히 공부해서 좋은 글을 쓰도록 하겠습니다.
 

 

 제가 이번에 다루어 보고자 하는 주제는 구글에서 만든 TCMalloc입니다



 보통 멀티 스레드 환경의 서버를 만들다 보면 메모리 풀을 사용하게 됩니다. 메모리 풀의 이점은 다음과 같습니다.


첫째, 빠른 메모리 할당.

둘째, 메모리 단편화 감소.

하나의 커다란 메모리 풀을 사용하면 단순하게 malloc을 호출해서 메모리를 할당하는 것 보다 속도가 빠릅니다. 메모리 풀에 따라 다르겠지만 일반적으로 볼 때 처음 프로그램이 실행될 때 필요한 메모리를 통째로 잡아놓고 메모리가 필요한 경우에는 잡아놓은 메모리 안에서 잘라서 주기 때문이죠. 하지만 멀티 스레드 환경에서는 메모리 풀도 속도가 저하됩니다. 왜냐하면 여러 스레드에서 하나의 메모리 풀에 접근을 하게 되면 lock에 대한 cost가 발생합니다. 동시에 접근하는 스레드가 많을 수록 lock을 기다리는 시간이 많아지고 그러다보면 메모리를 얻기 위하여 기다리는 스레드들은 일을 할 수 없게 됩니다. 

 lock cost로 인한 속도 저하는 또 다른 방법으로 해결 할 수 있습니다. 

thread별로 따로 메모리 풀을 두고 관리하고 
다른 thread에서 접근 해야 하는 메모리는 전역 메모리 풀을 두어 관리하고… 
뭐 이런 식으로 메모리 풀을 설계하면 되죠. 


하.지.만 

귀찮잖아요? 


그래서 친절하신 구글에서 우리들을 위하여 TCmalloc(Thread Caching malloc)이란 이름으로 메모리 할당하는 라이브러리를 내어 놓았습니다.

 

 이름도 거창하게 Thread Caching을 달고 나오신 TCmalloc은 메모리를 Thread Local Cache와 Central Heap으로 나누어서 관리합니다. Thread Local Cache는 32K이하의 작은 오브젝트 들을 담당하며 메모리가 부족할 시에는 Central Heap에서 메모리를 얻어와서 할당합니다. 그리고 32K가 넘어가는 큰 오브젝트들은 Central Heap에다 4K의 페이지 단위로 나누어서 메모리 맵을 이용하여 할당합니다. 사실 단순한 Central Heap은 일반적으로 사용하는 메모리 풀과 다를 바 없습니다. 하지만 Thread Local Cache가 있음으로 해서 메모리 할당시 불필요한 동기화가 줄어들게 되고 그에 따른 lock cost가 꽤 많이 감소되어 성능이 향상됩니다. 

 특히 리눅스 환경에서는 pthread_mutex_t가 거지같이(?) 느리기 때문에 개인적으로는 꽤 많은 향상을 얻을 수 있었습니다. 자체적으로 만든 memory pool과 비교해보고 나서 '나는 무엇을 하고 있었나' 라는 심한 좌절감을 느꼈었죠Orz...


 TCmalloc의 사용법은 정말 간단합니다. 다운로드를 받고 설치를 한 후 라이브러리를 링크 해주면 끝납니다. 헤더파일 Include이런 것 전혀 필요 없습니다. 단지 TCmalloc 라이브러리를 링크해주는 것 만으로 malloc이 TCmalloc으로 바뀌는 마법 같은 효과를 얻을 수 있습니다.

 

구글에서는 이렇게 말하고 있습니다. 

- TCmalloc을 사용하려면 Linker flag에 "-ltcmalloc"을 넣으면 된다. 

- 따로 컴파일 할 필요 없이 $ LD_PRELOAD="/usr/lib/libtcmalloc.so"를 해주는 것으로도 효과를 볼 수 있다. 하지만 이것은 편법이기 때문에 추천하지는 않는다.'


단순하게 -ltcmalloc을 넣는 것 만으로 메모리 할당에서 20%이상의 성능향상을 얻을 수 있다니 참 쉽죠?
(윈도우에서는 
Project Dependencies에 추가해 주시면 됩니다) 

더불어 TCmalloc을 사용하면 구글에서 제공하는 heap checker와 heap profiler까지 덤으로 사용할 수 있게 됩니다. 


< 스레드 수와 메모리 용량에 따른 TCMalloc과 PTMalloc이 CPU초당 처리할 수 있는 횟수 비교>
Mops : Millions operations per second 
 



출처 : Google TCmalloc Document에서 발췌
* 자세한 내용은 링크를 따라가시면 보실 수 있습니다.


위의 표를 보면 Single Thread에서는 크게 효과를 보지 못합니다. 하지만 Thread의 수가 늘어날 수록, 메모리 단위가 작을 수록 TCmalloc이 효율이 더 뛰어난 것을 보실 수 있습니다.

위의 내용이 길고 두서없어서 읽기 귀찮으신 분들을 위하여 간단하게 요약을 하자면...

1. Google Performance Tools Home에서 Google Performance Tools를 다운 받는다.
2. INSTALL파일을 읽어보고 자신의 환경에 맞게 설치한다. 
3. Library를 링크시키고 사용한다.
4. 성능 향상이 일어난다!!!!



 아직 게임 서버에는 적용을 해 볼 수 없었지만 현재 하고 있는 프로젝트에서는 memory allocator를 TCmalloc으로 대체하고 난 이후 평균적으로 20%정도의 성능 향상이 일어났고 메모리 할당에 대한 속도 저하 걱정을 많이 덜 수 있었습니다.  요즘은 유저들의 PC가 좋아짐에 따라 클라이언트들도 꽤 많이 멀티스레드를 지향하고 있다고 들었습니다. 

몇 분의 투자로 20%의 성능 향상. 
같이 누려보시는 것은 어떨까요? 


'리눅스 서버에 대해서 > 리눅스 팁들' 카테고리의 다른 글

vi 기본설정  (0) 2013.01.10
putty 설정  (0) 2013.01.10
쉘 프로그래밍 강좌  (0) 2013.01.10
GNU Make 강좌  (0) 2013.01.07
이클립스에서 C++ 환경 만들기  (0) 2012.12.10

+ Recent posts