출처 : http://www.joinc.co.kr/modules/moniwiki/wiki.php/article/gdbm


프로그램 분석중 gdbm_exists 라는 함수를 발견 했습니다. 

뭔가 했더니 이런게 있군요... ㄷㄷㄷ



요즘 사이트에 있어서 mysql, oracle, postgresql 등 RDBMS 가 쓰이지 않는 곳이 거의 없을정도로 많은 인기를 끌고 있다. 
그 반면 NIS, BIND, sendmal, LDAP 등 많은 프로그램들이 각각의 자료를 관리하기 위해서 굳이 RDBMS 를 쓰지 않고 ndbm, dbm, gdbm 을 사용하고 있다. 왜 막강한 RDBMS를 쓰지 않고 이러한 간단한 dbm 을 쓰는걸까 ? 
그 이유는 간단한 일을 하기 위해서 RDBMS 는 너무 크고 너무 거추장 스럽다는 것이다. 소규모(1000 에서 10000 건) 정도의 전화번호부를 관리한다거나, sendmail 에서 수백건 미만의 hosts(relay 허용등) db를 관리하는데에는 실지로 RDBMS의 기능의 10%도 필요하지 않다. 이러한 간단한 DB를 유지하려고, 서버에 RDBMS 를 설치하고, 운용하고, 프로그래밍을 하고, 복잡한 SQL을 사용하는건 너무 소모적인 일이다. 그래서 이러한 간단한 소규모의 데이타를 관리하기 위해서 dbm 이 존재한다.

dbm 은 관계형 데이타 구조 모델을 가지지 않고, HASH 데이타 구조 모델을 가진다. HASH 는 Key(키), Value(값) 의 한쌍으로 이루어지는 데이타의 집합으로써 키를 이용해서 데이타를 저장하고, 검색하고, 삭제하는 작업을 한다.
어찌 보면 C 에서의 pointer 개념과 비슷하다고 할수 있는데, Value 를 Key 가 가르킨다고 보면 무난할듯 하다.
RDBMS 와는 달리 하나하나의 데이타가 다른 데이타와는 별개로 존재하므로 RDBMS 처럼 데이타간의 관계에 의한 질의 언어가 필요없이 간단하게 Key만을 호출하면, 그 키에 연결된 값을 가져올수 있게된다. 기능의 한계가 명확하므로, 배워서 구현하기가 매우 쉬우며, 작고 또한 빠르다라는 장점을 가진다. 

GDBM 은 GNU database mansger 로써 전통적으로 Unix 쪽에서 쓰이던 dbm 의 확장형이다.
아래의 예제는 간단한 주소록이다. 비록 간단하지만 세련된 코드는 아니지만 데이타의 입력, 검색, 삭제 등 dbm 으로써 가져야할 기본적인 기능을 살펴보는데 어려움은 없을것으로 생각된다. 

예제 : address.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <gdbm/gdbm.h>

const int FALSE = 0;
const int TRUE  = 1;


int main(int argc, char **argv)
{
    datum key_data;
    datum value_data;
    datum return_data;

    char cmd_char;
    int done = FALSE;

    GDBM_FILE dbf;


    int block_size = 0;

    int temp;

    char key_line[80];
    char value_line[255];

    key_data.dptr = NULL;
    value_data.dptr = value_line;

    if (access("/tmp/my_dic", F_OK) != 0)
    {
        printf( "사전 DB 파일이 존재 하지 않습니다"
                "영어 사전  DB 파일을 새로 만들겠습니까 (y/n)? ");
        cmd_char = getchar();

        while(1)
        {
            if (cmd_char != '')
            {
                char temp;
                do
                    temp=getchar();
                while (temp != '' && temp != EOF);
            }

            if (cmd_char == EOF) cmd_char = 'n';
            switch(cmd_char)
            {
                case 'y':
                    dbf = gdbm_open ("/tmp/my_dic", block_size, GDBM_WRCREAT|GDBM_FAST, 00664, NULL);
                    if (dbf == NULL)
                    {
                        perror("db file open error : ");
                        exit(0); 
                    }
                    break;
                case 'n':
                    return 1;
                    break;
            }

            if (cmd_char == 'y')
                break;
        }

    }
    else
    {
        dbf = gdbm_open ("/tmp/my_dic", block_size, GDBM_READER|GDBM_WRITER, 00664, NULL);
        if (dbf == NULL)
        {
            perror("db file open error : ");
            exit(0);
        }
    }


    printf("주소 관리 프로그램입니다. "
           "사용법이 궁금하시면 ? 를 입력하세요");
    // 사용자의 키보드 입력문자열을 받아서 필요한 
    // 행동을 취한다.
    while(!done)
    {
        printf("com -> ");
        cmd_char = getchar();

        if (cmd_char != '')
        {
            char temp;
            do
                temp = getchar();
            while (temp != '' && temp != EOF);
        }
        if (cmd_char == EOF) cmd_char = 'q';

        switch (cmd_char)
        {
            case 'q':
                done = TRUE;
                break;
            case '?':
                printf("i -- insert data"
                       "f -- fetch data"
                       "c -- data num"
                       "d -- data delete"
                       "q -- quit");
                break;

            case '':
                printf("");
                break;
            case 'i':
                if (key_data.dptr != NULL) free(key_data.dptr);
                printf("Key -> ");
                fgets (key_line, 80, stdin);
                key_line[strlen(key_line) - 1] = '0';

                key_data.dptr = key_line;
                key_data.dsize = strlen(key_line) + 1;

                printf("Value -> ");
                fgets(value_line, 255, stdin); 

                value_data.dsize = strlen(value_line) + 1;
                if (gdbm_store (dbf, key_data, value_data, GDBM_REPLACE) != 0)
                    printf("Item no inserted ");
                printf("");
                key_data.dptr = NULL;
                break;

            case 'f':
                if (key_data.dptr != NULL) free(key_data.dptr);
                printf("Key -> ");
                fgets (key_line, 80, stdin);
                key_line[strlen(key_line) -1] = 0;
                key_data.dptr = key_line;
                key_data.dsize = strlen(key_line) + 1;

                return_data = gdbm_fetch(dbf, key_data);
                if (return_data.dptr != NULL)
                {
                    printf("data -> %s", return_data.dptr);
                    free(return_data.dptr);
                }

                else
                    printf("No Such item found. ");
                key_data.dptr = NULL;
                break;
            case 'c':
                temp = 0;

                if (key_data.dptr != NULL) free (key_data.dptr);
                return_data =  gdbm_firstkey(dbf);
                while(return_data.dptr != NULL)
                {
                    temp ++;
                    key_data = return_data;
                    return_data = gdbm_nextkey(dbf, key_data);
                    free(key_data.dptr);
                }
                printf("%d 개의 자료가 있습니다", temp);
                key_data.dptr = NULL;
                break;

            case 'a':
                key_data = gdbm_firstkey(dbf);
                if (key_data.dptr == NULL)
                {
                    printf("No one tiem found");
                    break;
                }
                else
                {
                    printf("%s => ", key_data.dptr);
                    return_data = gdbm_fetch(dbf, key_data);
                    printf("%s", return_data.dptr);
                    free(return_data.dptr);
                }
                while(1)
                {
                    return_data = gdbm_nextkey(dbf, key_data);
                    if (return_data.dptr != NULL)
                    {
                        free(key_data.dptr);
                        key_data = return_data;
                        printf("%s => ", key_data.dptr);
                        return_data = gdbm_fetch(dbf, key_data);
                        printf("%s", return_data.dptr);
                        free(return_data.dptr);
                    }
                    else
                    {
                        printf("No such item found.");
                        break;
                    }
                }
                key_data.dptr = NULL;
                break;

            case 'd':
                if (key_data.dptr != NULL) free (key_data.dptr);

                printf("Key -> ");
                fgets (key_line, 80, stdin);
                key_line[strlen(key_line) -1] = 0;
                key_data.dptr = key_line;
                key_data.dsize = strlen(key_line) + 1;
                if(gdbm_delete (dbf, key_data) != 0)
                    printf("Item not found or deleted");
                key_data.dptr = NULL;
                break;
        }
    }

    printf("bye bye");
    return 0;
}

datum 의 dptr 멤버는 malloc 를 이용해서 메모리를 할당하고, 자동으로 해제시켜주지 않는다. 그러므로 반드시 필요없다고 생각되는 곳에서 free 를 해줘야 메모리 누수 및 오류를 방지할수 있다.
위 코드는 매우 명확해 보이므로 별도로 설명을하진 않을 생각이다. 아리송하더라도 한번 정도 컴파일 해서 사용해보면서 코드를 보면 쉽게 이해가 될것이다.
gdbm 에 대한 자세한 내용은 man 페이지를 참고하라
컴파일 방법은 아래와 같다.
[yundream@localhost test]# gcc -o address address.c -lgdbm



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

OpenMP를 이용한 병렬 프로그래밍  (0) 2014.06.18
리눅스 프로그래밍 팁  (0) 2014.06.10
atomic swap ptr 처리  (0) 2014.01.17
리눅스 swap 메모리  (0) 2013.10.31
rsync 연결 거부 에러 났을때 처리  (0) 2013.10.27

+ Recent posts