목요일, 12월 31, 2009

일기 (2009.12.24)

Today is Christmas eve.
So, i'm going to the Apgujeong Catholic Church with my mom,
two of us, for a long time. (actually, for a long time to me...)

we've arrived around on 10:30 pm.
Mass to starting on over 11:00 pm due to short concert for Christmas by JeongSik Kim(Baptismal name is Rogerio) who singer and song writer of hymn.
I really have touched; saw him and listen to his music in live before just known Rogerio.
i've feel peace of mind. ah, there is an event that to exchange present each other.
but i'm not ready for present. so i just taken from another.
i'm sorry for her... ^^;


hmm... too late,
Merry Christmas... peace...

-----
Cheers,
June

화요일, 12월 29, 2009

Linux 에서 메모리가 부족할 때 (When Linux Runs Out of Memory)

Linux 에서 메모리가 부족할 때 (When Linux Runs Out of Memory)

:: Translate to Korean: Linux 에서 메모리가 부족할 때
Source: http://network.hanb.co.kr/print.php?bi_id=1313

:: English source copy: When Linux Runs Out of Memory
Source: http://linuxdevcenter.com/pub/a/linux/2006/11/30/linux-out-of-memory.html


Published on Hanbit Network (http://network.hanb.co.kr)

리눅스에서 메모리가 부족할 때

제공: 한빛 네트워크
저자: Mulyadi Santosa, 이정목 역
원문: When Linux Runs Out of Memory

아마도 여러분은 좀처럼 이 상황에 직면하지 않을 것이지만, 만약 그렇게 된다면 여러분은 무엇이 문제(free 메모리의 부족이나 Out of Memory (OOM))인지 확실히 알게 될 것입니다. 그 결과는 전형적인데, 여러분은 더 이상 더 많은 메모리를 할당할 수 없고 커널은 태스크(일반적으로 현재 동작하고 있는 것)를 제거(kill)할 것입니다. 대량의 스와핑(swapping)은 일반적으로 이러한 상황을 동반하게 되며, 따라서 화면과 디스크의 움직임이 이를 반영합니다.

이 문제의 기저에는 다른 문제들이 놓여져 있는데, 얼마만큼의 메모리를 할당하기를 원하는가? 운영체제(OS; Operating System)가 얼마나 할당해 주고 있는가? OOM의 기본적인 원인은 간단합니다. 즉, 여러분은 사용 가능한 가상 메모리 공간보다 더 많은 것을 요구했을 것입니다. 필자가 "가상"이라고 말한 이유는 RAM이 free 메모리로 계산되는 유일한 공간이 아니며 어떠한 스왑 영역도 해당되기 때문입니다.

OOM 조사하기

OOM 조사를 시작하기 위해 먼저 대량의 메모리 블록을 할당하는 아래의 코드를 입력하고 실행시킵니다:
#include 
#include 

#define MEGABYTE 1024*1024

int main(int argc, char *argv[])
{
        void *myblock = NULL;
        int count = 0;

        while (1)
        {
                myblock = (void *) malloc(MEGABYTE);
                if (!myblock) break;
                printf("Currently allocating %d MB\n", ++count);
        }
        
        exit(0);
}
프로그램을 컴파일하고 실행한 다음 잠시동안 기다립니다. 조만간 OOM 상태가 될 것입니다. 이제 대량의 블록을 할당하고 그곳을 1로 채우는 다음의 프로그램을 컴파일하십시오.
#include 
#include 

#define MEGABYTE 1024*1024

int main(int argc, char *argv[])
{
        void *myblock = NULL;
        int count = 0;

        while(1)
        {
                myblock = (void *) malloc(MEGABYTE);
                if (!myblock) break;
                memset(myblock,1, MEGABYTE);
                printf("Currently allocating %d MB\n",++count);
        }
        exit(0);
        
}
차이점을 발견하셨습니까? A 프로그램이 B 프로그램보다 많은 메모리 블록을 할당합니다. 여러분이 B 프로그램을 실행한 후에 얼마 지나지 않아서 "죽었음(Killed)" 라는 단어를 볼 것이라는 것은 명백합니다. 두 프로그램 모두 동일한 이유로 종료됩니다. 더 이상 사용 가능한 공간이 없기 때문입니다. 보다 구체적으로 말하자면, A 프로그램은 malloc()이 실패하기 때문에 얌전하게 종료됩니다. B 프로그램은 리눅스 커널의 소위 말하는 OOM Killer 때문에 종료됩니다.

관찰할 첫 번째 사실은 할당된 블록의 양입니다. 여러분이256MB의 RAM과 888MB의 스왑(현재 필자의 리눅스 설정)을 가지고 있다고 가정해 봅시다. B 프로그램은 다음에서 종료되었습니다:
Currently allocating 1081 MB
한편, A 프로그램은 다음에서 종료되었습니다:
Currently allocating 3056 MB
A 프로그램은 나머지 1095MB를 어디에서 가져왔습니까? 제가 속인 건 절대 아닙니다. 여러분이 두 가지 리스트를 자세히 들여다보면 B 프로그램은 할당된 메모리 공간을 1로 채운다는 것을 발견하게 될 것입니다. 반면 A 프로그램은 단지 단순히 할당만 할 뿐입니다. 이런 현상은 리눅스가 지연된 페이지 할당(Deferred page allocation)을 사용하기 때문에 발생합니다. 다시 말해, 여러분이 실제로 그것을 사용하기 전까지 할당은 실질적으로 일어나지 않습니다. 예를 들면, 블록에 데이터를 쓰기 전까지입니다. 그래서 여러분이 블록을 건드리지 않으면 여러분은 좀 더 많은 공간을 요구하는 것이 가능합니다. 이것을 전문적인 용어로 낙관적 메모리 할당(optimistic memory allocation)이라고 합니다.

두 프로그램에서 /proc//status 을 확인해보면 진상이 드러날 것입니다. 아래에 A 프로그램이 나타나 있습니다:
$ cat /proc//status
VmPeak:  3141876 kB
VmSize:  3141876 kB
VmLck:         0 kB
VmHWM:     12556 kB
VmRSS:     12556 kB
VmData:  3140564 kB
VmStk:        88 kB
VmExe:         4 kB
VmLib:      1204 kB
VmPTE:      3072 kB
다음은 B 프로그램입니다. OOM Killer가 해체하기 바로 전 내용입니다.
$ cat /proc//status 
VmPeak:  1072512 kB
VmSize:  1072512 kB
VmLck:         0 kB
VmHWM:    234636 kB
VmRSS:    204692 kB
VmData:  1071200 kB
VmStk:        88 kB
VmExe:         4 kB
VmLib:      1204 kB
VmPTE:      1064 kB
VmRSS는 추가적인 설명이 필요합니다. RSS는 "Resident Set Size"를 의미합니다. 이것은 태스크에 소유되어 할당된 블록이 현재 RAM에 얼마만큼 존재하는지 알려줍니다. B 프로그램이 OOM에 도달하기 전에 swap 사용이 거의 100%(대부분이 888MB임)입니다. 반면에 A 프로그램은 swap을 전혀 사용하지 않습니다. malloc() 자체는 메모리 영역으로 예약된 영역 이상에 대해서는 아무것도 하지 않는다는 것이 확실합니다.

다른 의문이 또 생깁니다. 페이지를 사용하지도 않았는데, 왜 할당 제한이 3056MB일까요? 이것은 눈에 보이지 않는 제약을 드러냅니다. 32비트 시스템에서는 모든 응용 프로그램에 대해 4GB의 사용가능한 주소 공간이 있습니다. 리눅스 커널은 일반적으로 선형 주소를 분할하여 0에서 3GB는 사용자 공간으로, 3GB에서 4GB는 커널 공간으로 제공합니다. 사용자 공간은 태스크가 원하는 것을 할 수 있는 장소이고, 반면에 커널 공간은 오로지 커널만을 위한 장소입니다. 여러분이 이 3GB 경계를 넘어가려 한다면 세그먼테이션 폴트(segmentation fault)가 발생할 것입니다.
사이드 노트: 문맥교환(context-switching)을 희생시켜 전제 4GB를 사용자 공간으로 부여하는 커널 패치도 있다.
결론은 2가지의 기술적인 이유로 인해 OOM이 발생한다는 것입니다:
  1. VM에서 더 이상 사용가능한 페이지가 없을 때
  2. 사용자 주소 공간이 더 이상 존재하지 않을 때
  3. 1번과 2번 둘 다 일 때
그러므로 이러한 상황을 방지하기 위한 방법으로는:
  1. 사용자 주소 공간의 크기가 얼마나 하는지 알아야 합니다.
  2. 얼마나 많은 페이지가 사용가능한지 알아야 합니다.
여러분이 메모리 블록을 요구할 때, 여러분은 일반적으로 malloc()을 사용하여 미리 할당된 블록이 사용가능한지 런타임 C 라이브러리를 사용하여 알아볼 것입니다. 이 블록의 크기는 최소한 사용자가 요청하는 것과 같아야 합니다. 이미 사용 가능한 메모리 블록이 있다면, malloc()은 사용자에게 이 블록을 할당할 것이고 "사용됨(used)"로 표시할 것입니다. 만약 그렇지 않으면, malloc()은 힙(heap)을 확장함으로써 더 많은 메모리를 할당해야 합니다. 모든 요청된 블록은 힙(heap)이라 불리는 영역으로 들어갑니다. 지역변수와 함수의 리턴 주소를 저장하는 스택과 혼동하지 마시기 바랍니다. 이 두 가지 영역은 서로 다른 역할을 하고 있습니다.

주소 공간에서 힙은 어디에 위치해 있을까요? 프로세스의 주소 맵을 보면 정확히 어디인지 알 수 있습니다:
$ cat /proc/self/maps
0039d000-003b2000 r-xp 00000000 16:41 1080084    /lib/ld-2.3.3.so
003b2000-003b3000 r-xp 00014000 16:41 1080084    /lib/ld-2.3.3.so
003b3000-003b4000 rwxp 00015000 16:41 1080084    /lib/ld-2.3.3.so
003b6000-004cb000 r-xp 00000000 16:41 1080085    /lib/tls/libc-2.3.3.so
004cb000-004cd000 r-xp 00115000 16:41 1080085    /lib/tls/libc-2.3.3.so
004cd000-004cf000 rwxp 00117000 16:41 1080085    /lib/tls/libc-2.3.3.so
004cf000-004d1000 rwxp 004cf000 00:00 0
08048000-0804c000 r-xp 00000000 16:41 130592     /bin/cat
0804c000-0804d000 rwxp 00003000 16:41 130592     /bin/cat
0804d000-0806e000 rwxp 0804d000 00:00 0          [heap]
b7d95000-b7f95000 r-xp 00000000 16:41 2239455    /usr/lib/locale/locale-archive
b7f95000-b7f96000 rwxp b7f95000 00:00 0
b7fa9000-b7faa000 r-xp b7fa9000 00:00 0          [vdso]
bfe96000-bfeab000 rw-p bfe96000 00:00 0          [stack]
이것은 cat에 대한 실제 주소 공간 레이아웃입니다. 하지만 여러분은 아마도 다른 결과를 볼 수 있을지도 모릅니다. 이것은 리눅스 커널과 그것들을 정렬하는 런타임 C 라이브러리에 달려 있습니다. 현재 리눅스 커널 버전(2.6.x) 은 친절하게 메모리 영역에 이름을 붙여놓았지만 그것에 전적으로 의지하지 않기 바랍니다.

힙은 기본적으로 이미 프로그램 매핑과 스택에 주어지지 않은 빈 공간입니다. 그러므로, 그것은 사용 가능한 주소 공간을 좁혀갑니다. 힙은 완전한 3GB가 아닌 3GB에서 매핑이 된 것을 제외한 것입니다. 여러분의 프로그램 코드 세그먼트가 크면 클수록, 힙을 위한 공간은 적어집니다. 여러분의 프로그램에 동적 라이브러리를 더 많이 링크할수록 여러분이 얻을 수 있는 힙 공간은 더 적어집니다. 이것은 기억해야 할 만큼 중요합니다.

더 이상 메모리 블록을 할당할 수 없을 때 프로그램 A에 대한 맵은 어떻게 보이겠습니까? 프로그램이 종료되기 바로 직전에 프로그램을 잠깐 멈추도록(loop.c와 loop-calloc.c를 참고하십시오) 조금만 수정하면 최종 맵은 다음과 같습니다:
0009a000-0039d000 rwxp 0009a000 00:00 0 ---------> (allocated block)
0039d000-003b2000 r-xp 00000000 16:41 1080084    /lib/ld-2.3.3.so
003b2000-003b3000 r-xp 00014000 16:41 1080084    /lib/ld-2.3.3.so
003b3000-003b4000 rwxp 00015000 16:41 1080084    /lib/ld-2.3.3.so
003b6000-004cb000 r-xp 00000000 16:41 1080085    /lib/tls/libc-2.3.3.so
004cb000-004cd000 r-xp 00115000 16:41 1080085    /lib/tls/libc-2.3.3.so
004cd000-004cf000 rwxp 00117000 16:41 1080085    /lib/tls/libc-2.3.3.so
004cf000-004d1000 rwxp 004cf000 00:00 0
005ce000-08048000 rwxp 005ce000 00:00 0 ---------> (allocated block)
08048000-08049000 r-xp 00000000 16:06 1267       /test-program/loop
08049000-0804a000 rwxp 00000000 16:06 1267       /test-program/loop
0806d000-b7f62000 rwxp 0806d000 00:00 0 ---------> (allocated block)
b7f73000-b7f75000 rwxp b7f73000 00:00 0 ---------> (allocated block)
b7f75000-b7f76000 r-xp b7f75000 00:00 0          [vdso]
b7f76000-bf7ee000 rwxp b7f76000 00:00 0 ---------> (allocated block)
bf80d000-bf822000 rw-p bf80d000 00:00 0          [stack]
bf822000-bff29000 rwxp bf822000 00:00 0 ---------> (allocated block)
6개의 가상 메모리 영역(VMA ; Virtual Memory Area )이 메모리 요청을 반영합니다. VMA는 페이지와 동일한 접근권한과(혹은) 동일한 보조파일을 묶는 메모리 영역입니다. VMA는 사용자 공간이 사용가능 한한 사용자 공간 내 어디에도 존재할 수 있습니다.

지금 여러분은 "왜 6개인가? 모든 블록을 포함하는 하나의 큰 VMA로 하면 안 되는가?" 라고 생각할 지도 모릅니다. 거기에는 2가지 이유가 있습니다. 첫 번째는 블록들을 하나의 VMA로 합칠만한 그렇게 큰 "틈(hole)"을 찾는 것이 종종 불가능하기 때문입니다. 두 번째로 프로그램은 대략 3GB 블록을 한번에 할당하는 것을 요청하지 않고 부분적으로 요청합니다. 그러므로 glibc 할당자는 얼마나 많은 것을 원하든지 메모리를 정렬하는데 완전히 자유롭습니다.

왜 필자가 사용 가능한 페이지를 언급하겠습니까? 메모리 할당은 페이지 크기 단위로 발생합니다. 이것은 운영체제의 영역이 아니고 "기억 관리 장치(MMU ; Memory Management Unit)" 자체의 특징입니다. 페이지는 다양한 크기가 가능하지만 x86에서의 일반적인 설정은 4K입니다. 여러분은 getpagesize()나 sysconf()(_SC_PAGESIZE 매개변수 사용) libc 함수를 사용하여 직접 페이지 크기를 알아낼 수 있습니다. libc 할당자는 각각의 페이지를 관리하는데 그것들을 더 작은 블록으로 나누고, 프로세스에 할당하고, 할당 해제시키는 것 등의 작업을 수행합니다. 예를 들어, 실제로는 할당자가 어딘가 4105에서 4109 바이트 사이의 영역을 주더라도 여러분의 프로그램이 전체 4097 바이트를 사용한다면 여러분은 2개의 페이지를 필요로 합니다.

256MB의 RAM에 swap을 하지 않는 경우 여러분은 65536개의 페이지를 사용할 수 있습니다. 맞습니까? 실제로는 그렇지 않습니다. 여러분이 보지 않는 것은 어떤 메모리 영역은 커널 코드와 데이터에 의해 사용 중이라서 다른 요구에 대해서도 사용할 수 없을 것입니다. 또한 긴급상황이나 높은 우선순위에 필요한 예약된 메모리 영역이 있습니다. dmesg는 이러한 수치들을 보여줍니다:
$ dmesg | grep -n kernel
36:Memory: 255716k/262080k available (2083k kernel code, 5772k reserved,
    637k data, 172k init, 0k highmem)
171:Freeing unused kernel memory: 172k freed
init은 단지 초기화 단계에서 필요한 커널 코드와 데이터를 가리킵니다. 그러므로 커널은 그것이 더 이상 필요하지 않을 경우 할당해제 시킵니다. 2083 + 5772 + 637 = 8492KB가 남습니다. 실질적으로 말하면, 2123 페이지가 사용자의 관점에서 보면 없어졌습니다. 여러분이 더 많은 커널 기능을 사용하거나 더 많은 커널 모듈을 삽입한다면 독점적인 커널 사용을 위해 더 많은 페이지를 사용하게 되므로 좀더 신중해야 합니다.

또 다른 커널 내부 데이터 구조는 페이지 캐시(page cache)입니다. 페이지 캐시는 최근에 블록 장치로부터 읽어온 데이터를 채워놓습니다. 캐시를 하면 할 수록 여러분은 실질적으로 더 적은 양의 남은 페이지를 가지게 됩니다만 메모리가 꽉 찼을 때 커널이 그것들을 회수할 것이므로 실제로 점유되어 있는 것은 아닙니다.

커널과 하드웨어 관점에서 보면 아래 사항들을 기억하는 것이 중요합니다:
  1. 할당된 메모리 영역이 물리적으로 인접한다고 보장할 수는 없습니다. 그것은 오직 가상적으로 인접할 뿐입니다. 이러한 "환영(illusion)"은 주소 변환이 이루어지는 방법에 기인합니다. 보호모드 환경에서 하드웨어는 물리적 주소를 가지고 작동하는 데 반해 사용자는 가상 주소를 가지고 작업을 하게 됩니다. 페이지 디렉터리와 페이지 테이블이 이러한 두 주소 사이의 변환을 수행합니다. 예를 들어, 가상주소가 0과 4096으로 시작하는 두 블록은 물리적 주소인 1024와 8192에 맵핑될 수 있습니다.

    이것은 할당을 용이하게 하는데, 왜냐하면 실제로 항상 연속된 블록을 얻기란 매우 일어나기 힘든 일이며, 특히 큰 요청(메가바이트나 심지어 기가바이트의 경우)의 경우 더욱 그러합니다. 커널은 단순히 인접한 남은 블록이 아닌 모든 곳을 요청을 채울 빈 페이지로 인식합니다. 그러나 그것들이 가상적으로 인접하도록 보여지기 위해서는 페이지 테이블을 정렬하는 등의 추가적인 작업을 좀 더 수행할 것입니다. 이렇게 하는 데에는 비용이 수반됩니다. 왜냐하면 메모리 블록이 인접하지 않을 수 있기 때문인데, 가끔 L1과 L2 캐시가 제대로 이용되지 않기도 합니다. 가상적으로 인접한 메모리 블록은 다른 물리적 캐시 선상에 걸쳐 펼쳐질지도 모르는데 이는 (순차적) 메모리 접근 속도의 저하를 의미합니다.

  2. 메모리 할당은 두 단계를 거칩니다. 먼저 메모리 영역의 길이를 확장한 다음 필요할 경우 페이지를 할당합니다. 이것은 페이징을 요구합니다. VMA를 확장하는 동안 커널은 단순히 요청이 기존의 VMA와 겹치는지와 요청 범위가 아직 사용자 공간에 존재하는지를 검사합니다. 기본적으로 실제 할당이 일어날 수 있는가에 대한 검사는 생략합니다.

    따라서 비록 실제로 여러분이 가진 메모리가 16MB의 램과 64MB의 스왑 공간을 가짐에도 불구하고 여러분의 프로그램이 1GB 블록을 요구하여 메모리를 획득하는 것이 그리 이상한 일은 아닙니다. 이러한 "낙관적"인 방식은 모든 이들을 만족시킬 수는 없을 지도 모릅니다. 왜냐하면 여러분이 아직도 사용 가능한 빈 페이지가 남아 있다고 생각하는 그릇된 희망사항을 갖게 될지도 모르기 때문입니다. 리눅스 커널은 이러한 수용 범위를 초과하는(overcommit) 행위를 통제하는 조정가능한 파라미터들을 제공합니다.

  3. 페이지에는 익명 페이지와 파일기반 페이지의 두 가지 형태가 있습니다. 익명 페이지가 여러분이 malloc()을 수행할 때 얻게 되는 것과 같은 것인데 반해, 파일 기반 페이지는 디스크내의 파일을 mmap() 시키는 것으로부터 발생합니다. 익명 페이지는 어떤 파일과도 관계를 맺지 않습니다. RAM이 꽉 차게 되면 커널은 익명 페이지를 스왑 공간으로 스왑 아웃(swap out)시키고 파일기반 페이지를 내보내(flush) 현재 요청에 필요한 공간을 확보해 줍니다. 다시 말해 파일기반 페이지가 스왑 영역을 소비하지 않는 것과는 달리 익명 페이지는 스왑 영역을 소비합니다. 이것의 유일한 예외는 MAP_PRIVATE 플래그를 사용하는 mmap()된 파일들뿐입니다. 이 경우 파일 수정은 RAM내에서만 일어나게 됩니다.

    이것이 RAM을 확장하는 의미에서의 스왑을 이해하는 것의 시작입니다. 확실히 페이지에 접근하는 것은 그것을 RAM으로 되가져오는 것을 필요로 합니다.
할당자(allocator)의 내부 살펴보기

실제 작업들은 glibc 메모리 할당자 내부에서 실질적으로 일어납니다. 할당자는 블록을 응용 프로그램으로 보내주고 (가끔씩) 커널로부터 오는 힙으로부터 블록들을 잘라냅니다(carving).

커널이 일꾼(worker)이라면 할당자는 관리자(manager)입니다. 이 사실을 염두에 두면 최대의 효율성은 커널이 아닌 적절한 할당자에 의해 이루어진다는 사실을 이해하는 것이 쉬워집니다.

glibc는 ptmalloc이라 불리는 할당자를 사용합니다. Wolfram Gloger는 Doug Lea가 만들었던 원형 malloc 라이브러리의 수정 버전으로 glibc을 만들었습니다. 할당자는 할당된 블록들을 "덩어리(chunks)"로 보고 관리합니다. 덩어리는 여러분이 실제로 요청한 메모리 블록을 나타내며 요청의 크기를 나타내는 것은 아닙니다. 사용자 데이터 외에 이 덩어리 내부에는 별도의 헤더가 추가되어 있습니다.

할당자는 두 개의 함수를 사용하여 커널로부터 메모리 덩어리를 가져옵니다:
  • brk()는 프로세스의 데이터 세그먼트가 끝나는 지점을 지정합니다.
  • mmap()는 새로운 VMA를 생성하여 할당자로 전달합니다.
물론 malloc()은 현재 풀 안에 더 이상의 빈 덩어리가 없을 경우에 한해 이 함수들을 사용합니다.

brk()를 사용할 것인지 mmap()를 사용할 것인지에 대한 결정은 하나만 간단히 확인해 보면 됩니다. 요청의 크기가 M_MMAP_THRESHOLD보다 크거나 동일하다면 할당자는 mmap()를 사용합니다. 만약 M_MMAP_THRESHOLD보다 더 작으면 할당자는 brk()를 호출합니다. 기본값으로 M_MMAP_THRESHOLD의 크기는 128KB이나 여러분은 mallopt()를 사용하여 자유로이 그 값을 변경할 수도 있습니다.

OOM 상황에서 ptmalloc이 메모리 블록을 해제하는 방법을 살펴보는 것은 재미있는 일입니다. mmap()로 할당된 블록은 unmap() 호출로 할당 해제되며, 그리고 나서 완전히 해제됩니다. brk()으로 할당된 블록을 해제하는 것은 그것들이 해제되었다고 표시하는 것을 의미하지만 그 블록들은 여전히 할당자의 통제하에 남아있게 됩니다. 그 블록들은 요청의 크기가 빈 덩어리의 크기보다 작거나 같을 경우 다른 malloc() 호출을 채우기 위하여 빈 덩어리로 재할당될 수 있습니다. 할당자는 다수의 빈 덩어리들을 그것들이 인접한 만큼 병합시킬 수 있습니다. 심지어 빈 덩어리를 좀 더 작은 크기의 덩어리로 쪼개어 차후의 작은 크기의 요청으로 채울 수도 있습니다.

이것은 할당자 내의 차후 요청의 크기를 맞출 수 없을 경우 빈 덩어리가 방치될 수도 있음을 의미합니다. 빈 덩어리에 대한 병합 실패 역시 신속한 OOM의 발단이 될 수도 있습니다. 이는 일반적으로 적절한 메모리 단편화에서 부적절한 메모리 단편화로 진행해 나가는 징조입니다.

복구

한번 OOM 상황이 발생하면 이제 어떻게 됩니까? 커널은 한 프로세스를 확실히 종료시킬 것입니다. 왜 프로세스를 제거하는 것입니까? 이것은 그 이상의 메모리 요청을 중지시키는 유일한 방법이기 때문입니다. 커널은 프로세스 내부에 차후 요청을 자동적으로 중지하는 복잡한 메커니즘이 있다고 가정할 수 없기 때문에 그 프로세스를 제거하는 방법 외에는 선택의 여지가 없는 것입니다.

그렇다면 어떻게 커널은 정확히 어느 프로세스를 제거할지 알 수 있습니까? 그 답은 리눅스 소스코드인 mm/oom_kill.c 안에 놓여져 있습니다. 이 파일안의 C 코드는 소위 말해서 리눅스 커널의 OOM 킬러를 나타냅니다. badness() 함수는 각각의 존재하고 있는 프로세스들에 점수를 부여합니다. 가장 높은 점수를 기록한 프로세스가 희생하게 되는데, 그 평가 기준은 아래와 같습니다.
  1. VM 크기. 이 수치는 할당된 모든 페이지의 합산이 아닌 프로세스가 소유하는 모든 VMA 크기의 합입니다. VM의 크기가 클수록 점수는 높아지게 됩니다.
  2. #1과 관련하여 프로세스 자식의 VM의 크기도 중요합니다. VM의 크기는 한 프로세스가 하나 혹은 그 이상의 자식을 가질 경우 누적됩니다.
  3. 작업 우선순위가 0보다 낮은(niced 프로세스) 프로세스의 경우 더 많은 점수를 얻게 됩니다.
  4. 가정상 슈퍼유저 프로세스는 중요한 위치에 있으므로 점수를 감산합니다.
  5. 프로세스 실행시간. 더 오랫동안 실행될수록 점수는 낮습니다.
  6. 직접적인 하드웨어 접근을 수행하는 프로세스는 좀 더 면제됩니다.
  7. 잠재적인 희생 프로세스 리스트로부터 면제된 커널 쓰레드 뿐만 아니라 swapper (pid 0)와 init(pid 1) 프로세스들
가장 높은 점수를 획득한 프로세스가 후보에서 "당첨"되며 OOM 킬러가 그 프로세스를 곧 제거할 것입니다.

휴리스틱은 완벽하지는 않지만 일반적으로 대부분의 상황에서 꽤 효과가 있습니다. 기준 #1과 #2는 중요한 것은 VMA 크기이며 실제 프로세스가 가지는 페이지의 개수가 아님을 명확하게 보여줍니다. 여러분은 VMA 크기를 측정하는 것이 잘못된 경고를 유발할 것이라 생각할지도 모르지만 운좋게도 그렇지는 않습니다. badness() 호출은 남아있는 빈 페이지가 거의 없고 페이지 프레임 교정(page frame reclamation)이 실패할 경우 페이지 할당 함수(page allocation function) 내부에서 발생하게 되며 따라서 VMA의 크기는 프로세스에 의해 점유된 페이지의 개수와 근접하게 맞아떨어집니다.

그렇다면 왜 단순히 실제 페이지 개수를 세지는 않는 것입니까? 그렇게 했을 경우 좀 더 시간을 소모하며 잠금(lock)을 사용해야 할 필요가 있기 때문입니다. 따라서 프로시저의 비용을 너무 높게 만들어 신속한 결정을 할 수 없게 됩니다. OOM 킬러가 완벽하지 않다는 것을 알고 있으므로 여러분은 잘못된 프로세스 제거에 대비해야만 합니다.

커널은 SIGTERM 신호를 사용하여 대상 프로세스가 중지되어야 함을 알려줍니다.

OOM 위험을 줄이는 방법

OOM 위험을 회피하는 한 가지 간단한 규칙은 실제로도 간단한데, '머신의 현재 빈 공간 이상으로 할당하지 말라'는 것입니다. 하지만 실제로는 많은 인자들이 발생하기 때문에 이 방법에 대한 좀 더 구체적인 개선안이 있습니다.

1. 적절히 순서화된 할당(Properly Ordering Allocation)을 통해 단편화를 감소시켜라

복잡한 할당자를 사용할 필요는 없습니다. 여러분은 적절히 순서화된 메모리 할당과 해제로 단편화를 감소시킬 수 있습니다. 틈은 쉽사리 발생할 수 있기 때문에 여러분이 마지막으로 할당한 항목을 여러분이 해제하고자 하는 첫 번째 항목으로 만드는 LIFO 기법을 이용해 보십시오. 예를 들면, 이렇게 하는 대신:
void *a;
        void *b;
        void *c;
        ............
        a = malloc(1024);
        b = malloc(5678);
        c = malloc(4096);

        ......................

        free(b);
        b = malloc(12345);
이렇게 같이 하는 것이 더 낫습니다:
a = malloc(1024);
        c = malloc(4096);
        b = malloc(5678);
        ......................

        free(b);
        b = malloc(12345);
이 방법을 이용하면 a와 c 덩어리간에는 아무런 틈이 생기지 않을 것입니다. 또한 여러분은 존재하는 malloc()된 블록의 크기를 조정하기 위하여 realloc()을 고려해 볼 수도 있습니다.

두 예제 프로그램(fragmented1.c와 fragmented2.c)은 할당 재배치의 효과를 보여줍니다. 두 프로그램 끝의 보고서는 시스템(커널과 glibc 할당자)에 의해 할당된 바이트 수와 실제로 사용된 바이트 수를 보여줍니다. 예를 들어 커널 2.6.11.1과 glibc 2.3.3-27에 명시적으로 파라미터를 아무것도 주지 않고 실행시키면 fragmented2가 2089200 바이트를 (약 2MB)를 소비하는 반면 fragmented1은 319858832 바이트(약 305 MB)를 소비하였습니다. 이것은 152배나 작은 수치입니다!

여러분은 프로그램 파라미터로 다양한 숫자들을 전달하여 그 밖의 실험들을 수행해볼 수 있습니다. 이 파라미터는 malloc() 호출에 대한 요청 크기의 역할을 합니다.

2. 커널의 Overcommit 동작을 조정하라

여러분은 리눅스 커널의 소스코드 안에 Documentation/vm/overcommit-accounting로 문서화되어 있는 것과 같이 /proc 파일시스템을 통해 리눅스 커널의 동작을 변경할 수 있습니다. 커널의 overcommit을 조정할 경우 /proc/sys/vm/overcommit_memory에 숫자로 나타나 있는 3가지 선택사항들이 있습니다:
  • 0은 커널이 이러한 overcommit을 허용할지를 결정할 때 이미 정의되어 있는 휴리스틱 방식을 사용할 것임을 의미하며 이것은 기본값입니다.
  • 1은 항상 overcommit을 수행합니다. 아마 여러분은 이제 이 모드의 위험성을 실감하게 될 것입니다.
  • 2는 overcommit이 특정 워터마크를 초과하는 것을 방지합니다. 이 워터마크 또한 /proc/sys/vm/overcommit_ratio를 통해 조정 가능합니다. 이 모드에서는 전체 commit은 스왑 공간 크기 + overcommit_ratio 비율 * RAM 크기를 초과할 수 없습니다. overcommit 비율의 기본값은 50입니다.
기본 모드는 일반적으로 대부분의 상황에서 거의 작동하지만 모드 #2는 overcommit에 대하여 좀더 나은 보호를 제공해 줍니다. 반면 모드 #2는 여러분이 실행되는 모든 응용 프로그램이 필요로 하는 공간의 양이 얼마인지에 대하여 신중하게 예측하도록 요구합니다. 확실히 여러분은 제한이 너무 엄격하여 여러분의 응용 프로그램이 더 많은 메모리를 얻지 못하는 것을 보고 싶어하지는 않을 것입니다. 그러나 모드 #2는 프로그램이 갑자기 죽는 것을 회피할 수 있는 최선의 방법이기도 합니다.

여러분이 256MB의 램과 256MB의 스왑공간을 가지고 있고 overcommit 제한을 384MB로 설정하고 싶다고 가정해 봅시다. 이는 256 + 50 퍼센트 * 256MB를 의미하며 따라서 /proc/sys/vm/overcommit_ratio에는 50을 입력하면 됩니다.

3. 메모리 누수에 대하여 메모리 할당과 감사 후에 NULL 포인터를 검사하라

이것은 매우 간단한 규칙이나 때때로 생략됩니다. NULL을 검사함으로써 적어도 여러분은 할당자가 비록 차후에 필요로 하는 페이지를 할당할 것에 대한 확실한 보장이 없음에도 불구하고 할당자가 메모리 영역을 확장할 수 있음을 알게 됩니다. 일반적으로 여러분은 계획상 잠시 동안 할당을 회피하거나 지연시킬 필요가 있을 수 있습니다. overcommit을 조정하는 것과 함께 여러분은 OOM을 예상하는 쓸만한 방법을 가지는데malloc()이 나중에 빈 페이지를 획득할 수 없을 것이라 판단하게 되면 NULL을 리턴하는 것 때문입니다.

메모리 누수 또한 불필요한 메모리 소비의 원인입니다. 누수된 메모리 블록은 응용 프로그램이 더 이상 찾지 않는 것인데도 불구하고 커널이 회수하지 않는 것을 말합니다. 왜냐하면 커널의 입장에서 보면 태스크가 여전히 커널의 통제하에 있기 때문입니다. Valgrind는 여러분의 코드에서 재코드화할 필요 없이 이러한 메모리 누수 발생을 찾아내는 멋진 도구입니다.

4. 항상 메모리 할당 통계를 참고하라

리눅스 커널은 메모리 상태에 관한 완전한 정보를 알아보기 위한 방법으로 /proc/meminfo를 제공합니다. 또한 /proc 항목은 top, free, vmstat와 같은 유틸리티를 위한 정보의 원천이기도 합니다.

여러분이 확인해 보아야 할 것은 비어있는(free) 메모리와 회수가능한(reclaimable) 메모리입니다. "비어있는"이라는 말은 더 이상 설명할 필요가 없지만 "회수가능한"은 무엇을 의미하는 것입니까? 이것은 버퍼와 페이지 캐시(디스크 캐시)를 가리킵니다. 그것들은 회수가능한데 왜냐하면 메모리가 꽉 찼을 경우 리눅스 커널은 단순히 그것들을 디스크로 도로 내보낼(flush) 수 있기 때문입니다. 이러한 것들이 바로 파일기반 페이지입니다. 필자는 이러한 메모리 통계의 예를 살짝 편집해 보았습니다:
$ cat /proc/meminfo
   MemTotal:       255944 kB
   MemFree:          3668 kB
   Buffers:         13640 kB
   Cached:         171788 kB
   SwapCached:          0 kB
   HighTotal:           0 kB
   HighFree:            0 kB
   LowTotal:       255944 kB
   LowFree:          3668 kB
   SwapTotal:      909676 kB
   SwapFree:       909676 kB
위 출력물에 근거하면, 비어있는 가상 메모리는 MemFree + Buffers + Cached + SwapFree = 1098772kB가 됩니다.

필자는 빈 메모리 공간(회수가능한 것을 포함한)을 알아내는 정형화된 C(glibc) 함수를 찾지 못했습니다. 필자가 찾은 것 중 가장 근접한 것은 get_avphys_pages()이나 sysconf() (_SC_AVPHYS_PAGES 파라미터와 함께 사용)를 사용하여 알아낼 수 있는 것들입니다. 그것들은 단순히 빈 메모리의 양을 보여주며 빈 메모리 양과 회수가능한 메모리 양의 합을 보여주지는 않습니다.

이것은 정확한 정보를 얻기 위해서는 여러분이 직접 /proc/meminfo을 프로그램적으로 읽어들여 계산해야 한다는 것을 의미합니다. 만약 여러분이 게으르다면 그것을 어떻게 하는지에 대한 참고자료로 procps 소스 패키지를 사용하십시오. 이 패키지는 ps, top, free와 같은 도구들을 포함하고 있으며 GPL 라이선스하에서 사용 가능합니다.

5. 대체 메모리 할당자를 실험해 보라

서로 다른 할당자들은 메모리 덩어리들을 관리하고 가상 메모리 영역을 줄이고, 확장하고, 생성하는 데 있어 각기 다른 방법을 취합니다. Hoard가 한 예인데, University of Massachusetts의 Emery Berger는 이것을 고성능 메모리 할당자로서 제작하였습니다. Hoard는 멀티 쓰레드 응용 프로그램에서 가장 잘 작동할 것으로 보이며 per-CPU 힙의 개념을 도입하였습니다.

6. 64비트 플랫폼을 이용하라

대량의 주소공간을 필요로 하는 사용자는 64비트 플랫폼 사용을 고려해 볼 수도 있습니다. 리눅스 커널은 이러한 머신에서는 더 이상 3:1 VM 분할을 사용하지 않습니다. 다시 말해, 사용자 공간이 상당히 커진 것입니다. 이것은 4GB 이상의 램을 가진 머신과 잘 맞을 것입니다.

이것은 인텔 32비트 프로세서가 64GB크기의 램까지 주소를 부여할 수 있도록 하는 인텔 PAE(Physical Address Extension)와 같은 확장된 주소 스키마로의 연결이 없습니다. 이 주소체계는 물리 주소를 다루는 데, 가상 주소 상황에서도 사용자 공간 자체는 여전히 3GB(3:1 VM 분할을 가정하여)입니다. 이러한 여분 메모리는 도달가능하나 주소 공간으로 모두 맵핑가능한 것은 아닙니다. RAM의 맵핑불가능한 부분은 사용할 수 없습니다.

7. 구조체의 패킹된 타입을 고려하라

패킹된 속성은 구조체, 열거형, 유니온의 크기를 줄이는데 도움을 줄 수 있습니다. 이것은 구조체 배열의 경우 좀 더 바이트를 절약할 수 있는 방법입니다. 아래에 구조체 선언의 예가 나타나 있습니다:
struct test
   {
        char a;
        long b;
   } __attribute__ ((packed));
이렇게 하는 것을 반대하는 이유는 이렇게 하면 특정 필드를 정렬시키지 않게 되며 따라서 그 필드에 접근하고자 할 때 CPU 사이클을 더 사용하게 되기 때문입니다. 여기서 "정렬된(aligned)"이라는 것은 변수의 주소가 그 변수의 자료형 원래 크기의 배수라는 것을 의미합니다. 이 방법의 최종 결론은 데이터 접근 빈도(data access frequency)에 따라 실행시간은 상대적으로 느려질 수 있다는 것이나 페이지 정렬(page alignment)과 캐시 결합성(cache coherence)도 참작하십시오.

8. 사용자 프로세스에 ulimit()을 사용하라

ulimit -v으로 여러분은 프로세스가 mmap()로 할당할 수 있는 주소 공간을 제한할 수 있습니다. 한계에 도달하게 되면 모든 mmap(), 그리고 그러므로 malloc()의 호출은 0을 리턴할 것이며 커널의 OOM 킬러는 절대로 시작되지 않을 것입니다. 이는 여러분이 다른 모든 사용자를 신뢰할 수 없고 임의의 프로세스를 제거하는 것을 피하고자 하는 다중 사용자 환경에서 가장 유용합니다.

감사의 글

필자에게 도움을 준 몇몇 이들에게 공로를 돌립니다: Peter Ziljtra, Wolfram Gloger, Rene Hermant. Mr. Gloger도 마찬가지로 ulimit() 기법에 공헌을 해주셨습니다.

참고자료
  1. "Dynamic Storage Allocation: A Survey and Critical Review," by Paul R. Wilson, Mark S. Johnstone, Michael Neely, and David Boles. Proceeding 1995 International Workshop of Memory Management.
  2. Hoard: A Scalable Memory Allocator for Multithreaded Applications, by Emery D. Berger, Kathryn S. McKinley, Robert D. Blumofe, and Paul R. Wilson
  3. "Once upon a free()" by Anonymous, Phrack Volume 0x0b, Issue 0x39, Phile #0x09 of 0x12.
  4. "Vudo: An Object Superstitiously Believed to Embody Magical Powers," by Michel "MaXX" Kaempf. Phrack Volume 0x0b, Issue 0x39, Phile #0x08 of 0x12.
  5. "Policy-Based Memory Allocation," by Andrei Alexandrescu and Emery Berger. C/C++ Users Journal.
  6. "Security of memory allocators for C and C++," by Yves Younan, Wouter Joosen, Frank Piessens, and Hans Van den Eynden. Report CW419, July 2005
  7. Lecture notes (CS360) about malloc(), by Jim Plank, Dept. of Computer Science, University of Tennessee.
  8. "Inside Memory Management: The Choices, Tradeoffs, and Implementations of Dynamic Allocation," by Jonathan Bartlett
  9. "The Malloc Maleficarum," by Phantasmal Phantasmagoria
  10. Understanding The Linux Kernel, 3rd edition, by Daniel P. Bovet and Marco Cesati. O'Reilly Media, Inc.
Mulyadi Santosa 인도네시아에 살고 있는 자유기고가입니다.
Copyright © 2009 Hanbit Media, Inc.

-----
Cheers,
June

수요일, 12월 23, 2009

일기: Tuition Free University (2009.12.22)

i'm feel restless lately due to a bored working to go to travel as soon as.
meanwhile, i found some exciting thing to me.
so i introduce it here.

Tuition Free University

how think about this ?
i'm very exciting. that gives chance learn and research for study or cutting edge technology, and besides free! tuition is free you know,... over-more can be study in english.

I have a bachelor of Computer Engineering and a job being working around computer engineering with IT.
so why i need to it ?
Because, that is an adventure. simplify, more study and more research as a student of institute of technology, hmm, i could well be want to a student for a long time. Study and Research are give to me refresh and comfortably, yea i'm to tryin and more to tryin. So i want to study and research as long as possible.

here's a Tuition Free Swedish University KTH, ROYAL Institute of Technology.
Web site: http://www.kth.se (also see this: http://tuitionfreecolleges.mtnhome.org/)
currently free of charge, but will be charged to non-EU/EEA student. It may starting on autumn 2011 or later. It will be charged then they'll be published there web site.

-----
Cheers,
June

일요일, 12월 20, 2009

ㅋㄷㅋㄷ... 저금하고 싶어 죽겠지?

보기만 해도 저금을 하고 싶어 안달이 난다... ㅎㅎ
일단 한번 보시라...

Source:
http://www.youtube.com/watch?v=GrvJVt_-nAM



-----
Cheers,
June

월요일, 12월 14, 2009

registered twitter today!

Yea, i'm twitter family now...
i don't know what i do to do in twitter.
but i'll tryin to this with my blog.

if ya request and this is my twitter. thanks.
http://twitter.com/godmode2k

-----
Cheers,
June

토요일, 12월 12, 2009

Wiimote library

Wiimote

Source:
http://wiibrew.org/wiki/Wiimote/Library

damn good, Wiimote library...
the idea is much better created nowadays... (although past a few year...)

see below information
http://com.ssu.ac.kr/?document_srl=36844&mid=dept_contest&listStyle=&cpage=

it's a multi-touch interface via Wiimote.

-----
Cheers,
June

화요일, 12월 08, 2009

일기 (2009.12.07)

이렇게 늦은 밤,.. 아니 날을 새고 이른 새벽 이구나...

나 자꾸,,, 자꾸 가슴이 아프려고 한다.
전에 가슴이 너무 아파 며칠을 울고 또 울고,
가슴 앓이 하던 그때... 그때 처럼 가슴이 아프다.

가슴 아파서... 가슴이 너무 아파서 눈물을 흘리고 싶어도
흘리지 못하는 그런 기분 아니?

이런걸 마음에 흉터라고 해야할까?

이 흉터를 지우지 않고 그대로 남기고 싶은 이유는 뭘까?
아니, 내가 붙잡는 이유는 뭘까?

...
나 아직 못다 쓴 글이 있다.
아주 오래전에 여러번의 고민 끝에 3부작으로 쓰려던 조그만 대본...

가슴이 너무 벅차서,,,
결말을 이미 알고서 쓴다는게...
어쩌면 너무 가혹하다...

글 중간, 짧막하게 재미있는 상황을 글로 연출하며 킥킥 거렸던,,,
그때 그런 나에 모습...

최근에 그 비슷한 느낌이 있었지만,
잊고 있었다.

혹시 흉터가 아물었던 것일까?

어쩌면 날 기다리고 있었는지도 모르겠다.
왜 일까?

이미 알고 있던건 아닐까?
내가 다시 돌아갈 거라는 걸...

시간은 걸리겠지만, 흉터는 남겠지만,,,
나 괜찮으니까... 정말...

날 떠나지마...

-----
Cheers,
June

Delphi: Graphic Algorithms

Delphi Graphic Algorithm

Source:
http://home.hccnet.nl/david.dirkse/math/
http://home.hccnet.nl/david.dirkse/math/#delphi




Delphi programming

subject
type
format
Flicker free, smooth, painting
theory + source code
HTML / program
display
A Microseconds Counter
description + source code
HTML
display
Fast Drawing in Delphi
description + source code + program
HTML
display
a simple component
for color selection
description + source code
HTML
display
resizing Bitmaps
programming algorithm
HTML
display
programming the
sudoku helper/solver
Delphi programming
HTML
display
exponential curve fitting:
y = aebx + c
calculus application /
Delphi programming
HTML
display
solving Ax + By = C
mathematics/
Delphi programming
HTML
display
an ArrayButton Component
Delphi programming
HTML
display
your own Dialog Form
Delphi programming
HTML
display
FloodFill
programming algorithm/
Delphi programming
HTML
display


-----
Cheers,
June

토요일, 12월 05, 2009

일기 (2009.12.05)

고도원의 아침편지
(http://www.godowon.com/)


차가운 손

손이 차다는 말보다는
그 손을 끌어다 옆에 두는 편이 더 낫다.
보았다는 말보다는 느꼈다는 말이 더 낫다.
이상하다는 말보다는 특이하다는 말이 더 낫다.
"네 말을 이해 못하겠어"라고 말하기보다는
"다시 한번 말해줄래"라고
말하는 게 더 낫다.

- 김동영의《너도 떠나보면 나를 알게 될거야》중에서 -

* 차가운 손도 맞잡아 주면
신기하게도 두 사람의 손이 함께 따뜻해집니다.
서로 한 걸음만 더 나가면 거리가 두 배로 가까워집니다.
머리로는 이해하기 힘들다 해도 따뜻한 가슴으로
한번만 더 들어주면 마음의 빙하도 녹입니다.
작은 배려의 힘이 그토록 큽니다.


평균 2천 번

"제대로 해내지 못할 것 같아 두려워요."
"우리가 느끼는 두려움은 대부분 머릿속에서 만들어 낸
창작품입니다. 그걸 깨닫지 못하는 것뿐이죠.
걸음마를 배우는 아기를 보세요.
아기가 단번에 성공할 거라 믿나요?
다시 서 보고, 그러다 또 쿵하고 넘어지곤 하지요.
아기는 평균 2천 번을 넘어져야
비로소 걷는 법을 배웁니다."

- 로랑 구넬의《가고 싶은 길을 가라》중에서 -

* 넘어지면 낙심이 큽니다.
모든 것이 끝난 것 같기도 하고
다시는 못 일어설 것 같기도 합니다.
그러나 우리 모두는 평균 2천 번 넘어졌던
걸음마의 시절을 이미 넘기고 오늘에 이르렀습니다.
넘어졌다고 조금도 낙심할 것 없습니다.
'걷는 법'을 더 잘 배우면 됩니다.


-----

정말 인생에 있어서 잊지 않고  기억하고 싶은 글이다.
항상 나 보다는 다른 사람을 배려하고,
몇 번 넘어졌다고 해서 좌절하지 말고 계속 일어나며
도전하는 그런 마음가짐...
지금도 그렇고 앞으로도 꼭 필요할 것 같다.


Cheers,
June

SW Insight 2009년 10월 Issue(스마트폰 마켓플레이스, 도전과 기회)

SW Insight 2009년 10월 Issue(스마트폰 마켓플레이스, 도전과 기회)
 - 정보통신산업진흥원(http://www.nipa.kr) 산업정책연구팀, 정제호

Source:
http://www.software.or.kr/ICSFiles/afieldfile/2009/10/13/Issue.pdf


스마트폰 동향에 대한 내용들을 잘 분석 해 놓은 자료이다.
마케팅 부서나 기획 부서에서 참고 하기에 좋은 자료라고 생각 된다.


-----
Cheers,
June

목요일, 12월 03, 2009

Advanced Linux Programming

Advanced Linux Programming in Online

Source:
http://www.makelinux.net/alp/






• 

Advanced Linux Programming


Publisher
: New Riders Publishing
Pub Date
: June 11, 2001
ISBN
: 0-7357-1043-0
Pages
: 368


 Advanced Linux Programming is divided into two parts. The first covers generic UNIX system services, but with a particular eye towards Linux specific information. This portion of the book will be of use even to advanced programmers who have worked with other Linux systems since it will cover Linux specific details and differences. For programmers without UNIX experience, it will be even more valuable. The second section covers material that is entirely Linux specific. These are truly advanced topics, and are the techniques that the gurus use to build great applications. While this book will focus mostly on the Application Programming Interface (API) provided by the Linux kernel and the C library, a preliminary introduction to the development tools available will allow all who purchase the book to make immediate use of Linux.

-----

Cheers,
June

수요일, 12월 02, 2009

Delphi: TBitmap, BMP, DIB

Delphi: TBitmap, BMP, DIB
 - 자주 사용하는 것인데 아래 site 에서 정리 및 link 가 제법 잘 되어있기에 여기에 posting 한다.

Source:
http://www.efg2.com/Lab/Library/Delphi/Graphics/BMP.htm




TBitmap, BMP, DIB



general info
1. In D1/D2, a bitmap was a DIB (device independent bitmap) after loading, but became a DDB (device dependent bitmap) on the first screen draw -- the DIB was tossed and the DDB format was kept. The pixel format could (and usually did) change across load/modify/save sequences.

In D3, Danny Thorpe (Borland) reimplemented TBitmap to use a DIBSection to solve the load/modify/save problem present in D1/D2. The use of DIBs provided direct access to pixel memory (using the Scanline property) and the bitmap handle at the same time.

2. In D3/D4 if you load an RLE compressed bitmap, it is loaded as a DDB, but when it gets written out, it is written an an uncompressed (screen pixel format) DIB.

3. Getting a strange compile-time error with a TBitmap? There's a TBitmap defined in both the Windows and Graphics unit and they may be in the wrong order in your USES statement. Normally you want the one from the Graphics unit (put in last in your USES), unless you're directly using API functions.]4.  The standard windows glyphs are stored within comctl32.dll as bitmaps.
5.  TBitmap is defined in both windows.pas and graphics.pas units.  Since the compiler automatically recognizes the last one in the "Uses" statement, make sure "Graphics" follows "Windows" in the "Uses" statement.
Example of comparing two TBitmaps and counting number of pixels that match and differ.
Bitmaps and InterBase BLOB Fields (TI 797D)
Converting a BMP to a JPEG and Vice Versa (TI 4582D)
Creating a bitmap from a pixel array (FAQ 1205D)
Creating temporary canvas (FAQ 1277D)
Drawing transparent text onto a TBitmap (FAQ 2079D)
Extracting a Bitmap from A BLOB Field (TI 791D)
Extracting Icons (FAQ 1778D)
Handling bitmap display (FAQ 2418D) [size limitations]
See also Very Large Bitmap Lab Report
Delphi Pool TBitmap Tips
www.lmc-mediaagentur.de/dpool/graph04.htm
How can I create a bitmap from an icon? (FAQ 2752D)
How can I place a bitmap in a metafile? (FAQ 1842D)
How to paint a form with a bitmap (TI 551D)
Loading Bitmaps and Cursors from RES Files (TI1081D) [explains palette problem]
Loading bitmaps into dBASE and Paradox BLOB Fields (TI 779D)
Non VCL Bitmap buffering (FAQ 2774D)
Stretchdraw on an icon (FAQ 1818D)
Using the Win API GetObject to get a bitmap (FAQ 2518D)
  How to create a Bitmap from numeric data? (D5)
Patrick Martin's example TBitmap descendant using memory mapped files
Background Bitmaps on Forms by Robert Vivrette
www.undu.com/DN961001/00000006.htm
Big Bitmap Viewer by Grahame Marsh
www.undu.com/DN970101/00000013.htm
Load Bitmap Resource by Ray Lischner. Look for bmpres10.zip www.tempest-sw.com/freeware or via ftp at ftp://ftp.tempest-sw.com/pub/delphi/bmpres10.zip
Storing bitmaps as resources, pp. 525-531, Delphi Programming Problem Solver, Neil Rubenking
"Displaying your bitmaps quickly", Delphi Developer's Journal, July 1998, pp. 12-15. (Double buffering using Pixels instead of Scanline. This technique is not very quick compared to using Scanline!) File 98Jul/DDJ9873.ZIP at ftp.zdjournals.com/ddj
"Eliminate flicker when painting on a form or a TPaintBox control, " www.bcbdev.com/faqs/faq34.htm
Mike Shkolnik's UseNet Post:   Bitmap to TGraphicField
Joe Hecht's UseNet Post with BltTBitmapAsDib procedure
Rund um TBitmap
http://delphi.pjh2.de/articles/graphic/bitmap.php
efg's UseNet Post about how to create TList of TBitmaps
Assign
Borland example: ..\Delphi n\Demos\Doc\GraphEx.dpr
Transparent property to work for a CoolBar background bitmap (FAQ 1390D)
Extracting a Bitmap from A BLOB Field (TI 791D)
Converting a BMP to a JPEG and Vice Versa (TI 4582D)

efg's Note: There is a Very Subtle Bug/Feature in the Delphi 3/4 Assign when only using Scanline to access pixel data. Assign should always be a "deep copy" (all data copied) versus a "shallow copy" (only a pointer copied), but this isn't always true! The kludge fix is to copy a single pixel from one bitmap to the other using the Pixels property to force unique references. See Assign.Txt.  (Fixed in D5)
Create/Free
Creating temporary canvas (FAQ 1277D) Francesco Savastano's UseNet Post about initializing bitmap to specified color
Dormant
Problem with complex implementation that allows DDB and DIB Section to exist simultaneously in Single TBitmap as described in UseNet Post by Takuo Nakamura.  This note gives an example of when Dormant is needed. Nick Hodges' UseNet Post answering "How does TBitmap.Dormant work?"
Peter Haas' UseNet Post about using Bitmap.Dormant after setting PixelFormat to fix a problem in D1-D4 with the TBitmaInfoHeader
efg's example of converting TIcon to TBitmap, as well as extracting the icon's "AND" bitmap mask and the "OR" color bitmap.   [This is the first example that I needed "Dormant" but this solution only worked in Windows 2000.  An alternate approach was needed for Windows 98.  --efg]
See the Cursor Overlay Lab report for another Dormant example.
FreeImage
Finn Tolderlund's UseNet Post about Scanline and FreeImage
HandleType
(D3/D4)
THandleType = (bmDDB, bmDIB)

In D3/D4 if you create a TBitmap and DO NOT assign either the PixelFormat or the HandleType, a DDB is created.  If you set HandleType := bmDIB, or if you specify any PixelFormat (other than pfDevice), you get a DIB. 

A BMP loaded from a file is a DIB (HandleType = bmDIB). Problem with complex implementation that allows DDB and DIB Section to exist simultaneously in Single TBitmap as described in UseNet Post by Takuo Nakamura.  This note gives an example of when HandleType is needed.
LoadFromFile
Transparent property to work for a CoolBar background bitmap (FAQ 1390D)
How can I place a bitmap in a metafile? (FAQ 1842D)

See \Program Files\Borland\Delphi n\HELP\Examples\Bitmap\BMPFormU.PAS Using Bitmaps, p. 216, Delphi Component Design by Danny Thorpe:
"When you call TBitmap.LoadFromFile to load a BMP file into a bitmap object, the bitmap object keeps a copy of the original file data in a memory stream.  The bitmap does not create a bitmap handle unless you refer to the bitmap's Handle property in your own code."  [D2.  True in D3 and later?]]
Mask
Shannon Broskie's UseNet Post with DrawTitleBarImage function
Monochrome
Setting Monochrome to TRUE on a pf24bit bitmap changes the PixelFormat to pfDevice. My observation is that all non-white colors become black when Monochome is set to TRUE on a pf24bit bitmap.
 
Palette
efg's UseNet Post about how to create pf8bit TBitmap with 256 shades of gray. Also see Delphi Graphics Color Page
PixelFormat (D3/D4)
TPixelFormat = (pfDevice, pf1bit, pf4bit, pf8bit, pf15bit, pf16bit, pf24bit, pf32bit, pfCustom);

PixelFormat and Scanline are discussed at length in
Manipulating Pixels With Delphi's ScanLine Property  Peter Haas' UseNet Post about using Bitmap.Dormant after setting PixelFormat to fix a problem in D1-D4 with the TBitmapInfoHeader
Ian Martin's UseNet Post about PhotoShop's problem with pf16bit bitmaps
See also Monochrome.
Resource Files
See Resource Files on Delphi Graphics Algorithms page
SaveToClipboardFormat
The example in the D3 online help compiled (with a warning), but the online help example of SaveToClipboardFormat has never been quite right and does not compile (see efg's UseNet Post about this).  Here's a version of the online example that will compile in D3..D7:
USES clipbrd;  // I wish Borland showed what unit is needed in the help
procedure TForm1.Button1Click(Sender: TObject);
var
  MyFormat : Word;
  Bitmap   : TBitMap;
  AData    : THandle;
  APalette : hPalette;  // Wrong in D3-D7 online example
begin
  Bitmap := TBitmap.Create;
  try
    Bitmap.LoadFromFile('c:\Program Files\Common Files\Borland Shared\Images\Splash\256color\factory.bmp');
    Bitmap.SaveToClipBoardFormat(MyFormat,AData,APalette);
    ClipBoard.SetAsHandle(MyFormat,AData);
  finally
    Bitmap.Free;
  end;
end;
SaveToFile
Converting a BMP to a JPEG and Vice Versa (TI 4582D)
Scanline (D3)
The PixelFormat defines the layout of a Scanline. PixelFormat and Scanline are discussed at length in
Manipulating Pixels With Delphi's ScanLine Property
Scanline Enigma working with pf24bit Bitmaps (fixed in D4.02 and D5)
Finn Tolderlund's UseNet Post about Scanline and FreeImage
See also Monochrome.
Transparent
TransparentColor
TransparentMode
The Transparent property of a TBitmap works only if you use the Draw method to draw it on a canvas. If Transparent := TRUE, and TransparentMode := tmAuto, then the transparent color is the color of the bottom-leftmost pixel of the bitmap. If TransparentMode := tmFixed, then the transparent color is defined by the TransparentColor property.

Dynamically drawing a transparent image (FAQ 1794D)Also see "Transparency" in Section B of Delphi Graphics Algorithms.


 

BMP and DIB File Formats


File Format
Description/Comments
Archives
Bitmap Archives
Raize Button Glyphs
www.raize.com/DevTools/Tools/RzBmps.htm
Glyphs und Icons im Internet
http://community.borland.com/article/0,1410,25911,00.html
Images in BMP format -- 16800 images (8 MB)
www.eldos.org/files/bitmaps.zip 
9452 glyphs from Richey's Delphi-Box
http://inner-smile.com/dl_div.htm#glyph
More than 400 glyphs from MS Office Applications
ftp://ftp.scalabium.com/pub/officebtns.zip
Raize Button Glyphs
www.raize.com/RzBmps.htm 
1000 glyphs for free
www.rwakelin.freeserve.co.uk/sharedfiles/bmpiconz.zip

Links to various bitmap/icon collections
www.torry.net/docs_graphics.htm 
BMP
Bitmap and Metafile Functions, Tomes Graphical, pp. 273-356Windows Bitmap used with Microsoft Paint
Delphi 3 or 4: ..\Demos\Imagview
To create a RES file with Bitmaps: 
Edit a file name.RC and add a line like this for each bitmap:
NAME  BITMAP  "NAME.BMP"
all in upper case.  Replace "NAME" above with the appropriate bitmap name.   Once you have the name.RC file, use the resource compiler:  BRC32 name.RC
The file name.RES will be created.  Add {$R name.RES} to the unit needing the resource.
Loading Bitmaps and Cursors from RES files (TI 1081D)
Tried TI 1081D and you now see the message "Bitmap Image is not valid"?  Be sure to add the comment {$R name.RES} to the unit where you're using LoadResource.
Example of comparing two TBitmaps and counting number of pixels that match and differ.
efg's Resource Demo application shows how to use the following file types in resource files:  BMP, ICO, JPG, GIF (requires Anders Melander's TGIFImage), EMF, TXT, AVI, WAV and cursors
Peter Haas' UseNet Post about determining a bitmap's size without loading the complete TBitmap, and Joe Hecht's Reply Post with certain precautions 
Bitmap Orientation and biHeight
www.webartz.com/fourcc/fccbihgt.htm
biHeight and Video Formats in DirectShow (problem with inverted video images)
http://www.microsoft.com/hwdev/desinit/biheight.htm 
Scanline Tech Note explains accessing pixel data quickly for the various PixelFormats
24 bits/pixel to 8 bits/pixel bitmaps using the ReduceColors function.  See Anders Melander's UseNet Post about using TGIFImage to reduce colors.
RGB Formats
www.webartz.com/fourcc/fccrgb.htm
Encryption / Decryption
CryptImage.jpg (948 bytes)   efg's ImageCrypt Lab Report:  BMP Section
efg's Aspect Ratio Lab Report
Resource-Grabber will scan the directories and drives on your computer and extract all Bitmaps, Glyphs (button images), Icons, Cursors, JPG/JPEG images, WAVE and RMI sound files as well as AVI video clips it finds inside the programs and DLL files in any directory of your choice.  http://inner-smile.com/dl_res.htm
Glyph Viewer facilitates the search in large Glyph directories.  In a list all bit-maps of a directory as well as their file names and creation dates are displayed. The list can be sorted according to file names and creation dates. In a tree representation you can select the desired directory.
http://www.pics-software.de/swglyph.htm 
BMP Technical Tutorial: 
www.geocities.com/SiliconValley/Haven/7041/tp/showbmp.txt
Microsoft's "Error 481: Invalid Picture" Err Viewing BMP File
Danny Thorpe's UseNet Post about RLE BMPs
Rafe Aldridge's UseNet Post about Bug in D5 loading RLE BMPs
Bob Villiers' UseNet Post with VarArrayLoadFile, which loads a bitmap into a variant
efg's PelsPerMeter example with GetPelsPerMeter procedure to return biXPelsPerMeter and biYPelsPerMeter values, and an example based on a UseNet Post by Peter Klein that returns these same values.
Biff Kadidlehopper's UseNet Post to get/set the dpi in a bitmap file or bitmap image (the biXPelsPerMeter and biYPelsPerMeter values)
The biXPelsPerMeter is a 4-byte integer at an offset of 38 bytes from the start of the file; biYPelsPerMeter is a 4-byte integer at an offset of 42 bytes from the start of the file.
Example from Robert Poyntz with SpecialBMP routine to set the biXPelsPerMeter and biYPelsPerMeter values.  SpecialGIF procedure by Robert Poyntz with biXPelsPerMeter and biYPelsPerMeter example.
Supported by ImageLib www.imagelib.com
 
DIB
Windows Device-Independent Bitmap Delphi Programming & Assembly Language (DIBULTRA)
http://perso.magic.fr/sleon/prog/DIBUltra/DIBUltraE.htm
Joe Hecht's UseNet Post with BltTBitmapAsDib procedure
Chris Hill's UseNet Post explaining two ways the term DIB is used
efg's examples of working with DIBs while converting Lead Tool's BitmapHandle to TBitmap
Delphi Graphics Programming by Peter Dove and Don Peer in Delphi Informant:
Part IV, Apr 97, "Getting DIBs on Speed" (pp. 45-56)
DIBs. A class that provides easy access to Device Independent Bitmaps. Delphi Super Page: http://delphi.icm.edu.pl/ftp/d10free/dibs.zip
Wie ist eine DIB aufgebaut (How a DIB is structured)
http://delphi.pjh2.de/articles/graphic/bitmap.php#DIB
Supported by ImageLib www.imagelib.com
Non-Delphi:
DIBs and Their Use:
http://msdn.microsoft.com/library/techart/msdn_dibs2.htm 
Using Device-Independent Bitmaps and Palettes http://support.microsoft.com/support/kb/articles/q72/0/41.asp
"More Fun with MFC: DIBs, Palettes, Subclassing, and a Gamut of Reusable Goodies", Part I (Jan 97), www.microsoft.com/MSJ/0197/mfcp1/mfcp1.htm
www.microsoft.com/msj/code1993to1997/MSJJAN97.EXE
More Fun With MFC: DIBs, Palettes, Subclassing and a Gamut of Goodies, Part II (Mar 97)
www.microsoft.com/msj/0397/mfcp2/mfcp2.htm
www.microsoft.com/msj/code1993to1997/MSJMAR97.EXE
More Fun with MFC: DIBs, Palettes, Subclassing, and a Gamut of Goodies, Part III (Jun 97)
www.microsoft.com/msj/0697/mfc3/mfc3.htm 



Conversions


Conversion(s)
Description/Comments
411 to BMP
efg's 411 Lab Report to read .411 file and convert to a TBitmap
AVI to BMPs
Eddie Shipman's UseNet Post with Example of how to convert the following "common" AVIs into BMPs:   FindFolder, iFindFile, FindComputer, CopyFiles,iCopyFile, RecycleFile, EmptyRecycle,   DeleteFile
Toni Martir's notes and complete VFW example (see ShevinE's improvement below).   (Note:  to get Toni's complete example to compile in D3-D5, make sure you can compile with USES OLE2:

Tools | Environment Options | Library | Library Path
D3: C:\Program Files\Borland\Delphi 3\Lib\Delphi2
D4-5: $(DELPHI)\Lib\Delphi2)
Use Lizard.AVI or another AVI file with Toni's example.
ShevinE's improvements to Toni Martir's example (D3-D5 source).   Saves AVI to  BMP files as FrameXX.BMP.  Includes VFW.PAS file used in Toni's example.  (Requires USES OLE2 -- see above notes for details.)  [Use Lizard.AVI or another AVI file.]
efg's AVItoBMP example project (Delphi 3-5 source code). 
(Assumes "Jesus Lizard" AVI is present.)
The "Jesus Lizard" AVI file shows a lizard walking on water.
Download from here  
Bitmap to DIB Handle
Takuo Nakamura's UseNet Post with BitmapToDIBHandle routine
BitmapHandle to/from TBitmap
efg's example of converting from Lead's BitmapHandle with a BMP to several formats including CMP, EPS, JPG, PCT, PCX, PNG, RAS, TGA, TIF, WMF, or from TBitmap to BitmapHandle
BitmapToWMF
Procedure in ImageLib Suite DLL.Harm's UseNet Post about converting bitmap to EMF/WMF
BMPs to AVI
Tilo Arnold's UseNet PostSee Rob Anderson's AVIBUILD.
Current version does not handle pf24bit BMPs correctly.
Includes vfw.pas (Video for Windows) interface unit for avifil32.dll.
VB Info:  BMPs to AVI
www.shrinkwrapvb.com/avihelp/avihlp_5.htm
BMP to ICO
Tomes Graphical, pp. 359-361
Ignacio Alvarez's UseNet Post with BitmapToIcon function

How do I create an icon from a bitmap?  (FAQ 2748D)
BMP to GIF
TGIFImage from www.melander.dk/delphi/gifimage
BMP to JPG
Quick Conversion between Bitmaps and JPEG
www.undu.com/Articles/010119c.html
efg's TBitmap in TImage to JPG file
    
PROCEDURE ConvertBMPtoJPG (CONST BMPName: STRING; JPGName: STRING);
  VAR
    Bitmap: TBitmap;
    JPEG : TJPEGImage;
BEGIN
  Bitmap := TBitmap.Create;
  JPEG := TJPEGImage.Create;
  TRY
    Bitmap.LoadFromFile(BMPName);
    JPEG.CompressionQuality := 80;
    JPEG.Assign(Bitmap);
    JPEG.SaveToFile(JPGName);
  FINALLY
    Bitmap.Free;
    JPEG.Free
  END
END {ConvertBMPtoJPG};

Converting a BMP to a JPEG and Vice Versa (TI4528D)
efg's UseNet Post to convert TJPEGImage to TBitmap, change size of TBitmap, and convert back to TJPEGImage
efg's BMPJPG Lab Report. Interactively convert BMPs to JPGs or vice versa. Nearly all the JPEG compession options cn be controlled via the user interface, including image quality and progressive encoding and display.

efg's Command-line utility for batch conversion of BMPs to JPGs: BMPtoJPG.PAS
BMP to TIFF
Save BMP as TIFF 6.0  (D1-D5, updated Sept 2000)
http://delphi.icm.edu.pl/ftp/d10free/bmp2tiff.zip
http://community.borland.com/homepages/dsp/ftp/d10free/bmp2tiff.zip 
BMP to TXT
Example of how to convert BMP file to a TXT file of RGB values, and how to convert such a TXT file to RGB values in a TBitmap for display.
BMP to WMF
Non-Delphi:  AlgoLab Photo Vector turns raster images (.bmp, .jpg, and .png files) into vector images (.wmf files) with a minimum of fuss. www.zdnet.com/downloads/stories/info/0,,001EP8,.html 
Cursor to Bitmap
Thomas' UseNet Post about how to extract the cursor image from a cursor file for drawing on a bitmap
DIBtoBitmap
Procedure in ImageLib Suite DLL.  Also see hDIB to TBitmap.
hDIB to TBitmap
efg's Lead Tools-to-Delphi conversions show two ways to start with a hDIB and end up with a TBitmap.
- Method 1. hDIB to TBitmap resulting in bmDDB using StretchDIBits
- Method 2. hDIB to TBitmap resulting in bmDIB using MemoryStream 

ICO to TBitmap
Howard Moon's UseNet Post with ConvertIconToBitmap procedure
JPG to BMP
Converting a BMP to JPEG and Vice Versa (TI 4582D)
efg's BMPJPG Lab Report (see description above under BMP to JPG)
efg's
command-line utility for batch conversion of JPGs to BMPs: JPGtoBMP.PAS efg's UseNet Post to convert TJPEGImage to TBitmap, change size of TBitmap, and convert back to TJPEGImage
PGM to BMP or JPG
See efg's Interactive PGMP5 "viewer" to display PGM files (P5 format) or the command-line utility ConvertFaces to convert PGM files of the AT&T Laboratories Cambridge Database of Faces (400 images) at www.uk.research.att.com/facedatabase.html to BMP or JPG: PortableGrayMap.ZIP
RichEdit to TBitmap
Peter Haas' UseNet Post
TControl to TBitmap
efg's example of TControl to TBitmap including a TLabel, TPanel and TBitBtn.  
TIFF to BMP
TIFF to BMP
http://delphi.icm.edu.pl/ftp/d20free/tiff2bmp.zip
http://community.borland.com/homepages/dsp/ftp/d20free/tiff2bmp.zip 
TXT to BMP
Example of how to convert BMP file to a TXT file of RGB values, and how to convert such a TXT file to RGB values in a TBitmap for display.      How to create a Bitmap from numeric data? (D5)  Shows gray scale vs. spectrum.
WMF to BMP
Harm's UseNet Post with example







-----
Cheers,
June

화요일, 12월 01, 2009

2009년 11월 26일, 서울 압구정 퓨어 피부과 여직원 분들과의 갑작스런 미팅

음...
2009년 11월 26일 오늘, 예정에 없던 미팅을 하게되었다. ^^
원래 가려고 했던 분이 계셨는데 회의 때문에 불참하게 되었던 걸
내가 채우게 된것이다. 음...

이렇다...
회사의 특정 본부에 여친이 없는 남자를 대상으로 같은 건물 3층에 있는
피어피부과(Pure Esthetic) 여직원 (매니저들 및 간호사?) 들과 7 vs 7 미팅이 이루어졌다.

우선 퇴근 후 시간에 맞추어 3층 퓨어피부과로 갔다.
가볍게 인사를 한 뒤 뽑기(?)를 해서 우선 짝을 지었다.

단체로 몰려가면 재미 없으니 짝을 이루어 미션도 수행하면서
couple 마다 이동수단을 이용해서 도착하면 된다.

내 쪽지는 "장동건" 이렇게 적혀있었다. ^^v
내 짝은 "고소영" 이었다. ㅋㅋㅋ

우린 이렇게 제일 먼저 출발하게 되었다.

나이는 27 인데, 이름을 물어 보지 못한게 너무 미안했다.
기본적인 것인데...

피부과에서 어떤 일을 하는지와 이런저런 얘기를 하다보니 목적지에 거의 도착했다.
날이 조금 쌀쌀해서 내가 택시로 이동 하자고 했다.

우리의 미션은 "손 사진" 을 찍는 것이었다. 이해가 가질 않아서 그냥 손 잡는 모습을
휴대폰 카메라에 담았다. (미션은 그냥이었나 보다. 확인도 하질 않았으니...)

압구정역 2번 출구에 도착해서 목적지에 가려는데 다른 커플들도 보였다.
같이 이동을 하다 약도를 보는데 길이 아닌것 같아서 조금 헤매었지만
다행히 그애가 같이 일하는 언니에게 전화를 해서 쉽게 갈 수 있었다.

사실 도착은 우리가 제일 먼저인데 마지막으로 두 번째로 목적지에 도착을 하게 되었다. (미안 ^^;)

서로 자세히 얼굴도 보고 자기소개의 시간이 되었다. 아주 간단한 소개이다. 나는 이름, 나이, 그냥 한마디 했다. ㅠ.ㅠ

식사를 하면서 이런저런 얘기들이 오가고 술이 한 잔씩 들어가니 서로 말문이 트였나 보다.
^^ 장난도 많이 치고... 음... 나는 그냥 앉아있었다.
사람들이 허물없이 얘기하며 장난도 치는 모습이 어찌나 보기 좋으면서 즐거웠던지,
보고 듣는 내내 마음이 참 따뜻해졌다.

내가 최근에 이렇게 어울려 본적이 언제였던가 할 정도로 기억이 가물가물하다.
거의 없었던것 같다. 인생이란 간단하게 생각하면 이렇게 단순 하면서도 즐거울텐데
왜이리 알게 모르게 근심이 있는지 모르겠다.

누군가 "피할 수 없으면 즐겨라"고 했던가?... 그 말에 동의한다.
하지만, 근심은 조금 다른 것 같다. 즐기기엔 내가 너무 성숙 해 버린건 아닌지...
그 시절이 그립다. 부모님과 내 동생과 함께 지내며 밥먹고 하는 그런 시간...

지금도 그렇게 할 수는 있지만,.. 서울을 떠나 집으로 내려가서 가족과 살고 싶지만,
마음 먹은대로 쉽지만은 않다.
어쩌면 다른 속마음으론 미련이 남아있어서 그럴지도 모르겠다.

지금 내가 할수 있는건 하루에 한번 씩 부모님께 안부 전화 드리는 일,
휴가 내는 날은 꼭 집에 내려가기... 그리고 가족을 위해 기도하기 이다.
그저 우리 가족 모두 다치지 않고 아프지 않으며 항상 하는 일 모두 잘 될 수 있기를
바랄뿐이다. 이게 나에 전부 이다.

음...
다시 본론으로 돌아와서...
그렇게 보고 듣는 것 만으로도 나는 즐거웠다.
다른 사람들이 보기엔 성의가 없어 보였는지도 모르겠지만, 그렇게 보였다면
너무 죄송스럽다. 초면이며 내가 낯을 많이 가리다 보니 물어보기 전에는 말을 잘 못한다.

그러서 일까? 퓨어피부과 여자분들이 각자 선물을 준비 했는데, 그 중 나이가
제일 많으신 매니저분께서 나에게 선물을 주셨다. 응. 알고있다. 일부러 나게에 준 것을...
그분께 너무 감사하다. 말 없이 그냥 있던 날 위해 "Vibe" 콘서트 ticket 을 얻을 수 있는 기회지만, 그냥 나에게 주셨다.

찻잔이었는데 너무 예뻤다. 소중히 보관 할테야~  ㅎㅎ

오늘 너무 졸린다.
참,,, 퓨어피부과 여성분들의 캐릭터가 중복이 되지 않고 너무 unique 해서 그분들을 주제로 재주는 없지만 짧막한 글을 써보려고 한다. 그냥 sitcom 형태의 대본이다. 아직 시작은 하지 않았지만, 조만간 틈틈히 써볼 생각이다.

서울 강남 압구정 "퓨어 피부과 (Pure Esthetic)" 여직원분들... 실제로 만나서 이런저런 얘기도 듣고 보고 해보니, 내가 보기엔 모두들 참하고 순수하고, 항상 웃고(직업 상?) 착해 보인다. 물론 다를 예쁘시다.

이런 말 하면 좀 그렇지만, 나는 언제나 여자를 볼 땐 "그림에 떡" 이라는 생각을 갖는다. 나는 이런 쪽으론 자신이 없나 보다. 언젠가는 여친이 생기고 결혼도 하겠지만, 흑흑(ㅠ.ㅠ) 그때가 언제 일지는 모르지만, 아무튼... 항상 바르게, 상대방에게 부끄러운 짓은 하지 않으며 차분히 기다리려 한다.

나에게 우연, 인연이라는 것이 있다는 걸 꼭 알게 해주었으면 하는 바람이다.
風がある日に。。。

-----
Cheers,
June