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


요즘 사이트에 있어서 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
Posted by stekilove
,