Unix Daemon Server Programming[출처]http://www.enderunix.org/d0cuments/eng/daemon.phpIntroductionUnix processes works either in foreground or background. A process running in foreground interacts with the user in front of the terminal (makes I/O), whereas a background process runs by itself. The user can check its status but he doesn't (need to) know what it is doing. The term 'daemon' is used for processes that performs service in background. A server is a process that begins execution at startup (not neccessarily), runs forever, usually do not die or get restarted, operates in background, waits for requests to arrive and respond to them and frequently spawn other processes to handle these requests. 유닉스 프로세스는 포그라운드와 백그라운드에서 동작한다. 프로세스는 터미널의 앞면에서 (입출력을 수행하면서) 사용자와 상호작용하기도 하고, 반면에 백그라운드 프로세스는 자체로 동작을 구동한다. 사용자는 이 상태를 체크하지만 백그라운드 프로세스가 하고 있는 일을 체크할 필요는 없다. '데몬'이라는 용어는 백그라운드에서 서비스를 수행하는 프로세스에 사용된다. 서버는 시작과 동시에 영원토록 실행하는 프로세세이다. 재시작을 하기 전까지는 중단되는 것은 일반적이지 않다. 백그라운드에서 동작하며, 요청이 도착하면 그것에 대해 대응하는 처리를 하며, 이 요청에 대한 다른 프로세스를 산출하기도 한다. Readers are suppossed to know Unix fundamentals and C language. For further description on any topic use "man" command (I write useful keywords in brackets), it has always been very useful, trust me :)) Keep in mind that this d0cument does not contain everything, it is just a guide. 이 글의 독자들은 유닉스의 기본과 C 언어를 알고 있다고 예상된다. 그래서 나아가 'man' 명령어의 사용에 대하여도 알고 있다고 생각된다. (바구니에서 유용한 키워드를 적는다). 이것은 언제나 매우 유용하다. 1) Daemonizing (programming to operate in background) [fork]데몬화 하기 : 백그라운드에서 동작하도록 프로그래밍하기 : fork First the fork() system call will be used to create a copy of our process(child), then let parent exit. Orphaned child will become a child of init process (this is the initial system process, in other words the parent of all processes). As a result our process will be completely detached from its parent and start operating in background. 첫째로 fork() 시스템 콜은 자식 프로세스의 복사본을 생성하기 위해서 사용되며, 그와 동시에 부모는 종료한다. 이 고아가 되는 자식 프로세스는 초기화 프로세스의 자식이 된다, 다른 뜻으로는 모든 프로세스의 부모가 된다. 따라서 그의 결과로 부모에서 완전히 분리되고 백그라운드에서의 동작이 시작된다. i=fork(); if (i<0) exit(1); /* fork error */ if (i>0) exit(0); /* parent exits */ /* child (daemon) continues */ 2) Process Independency [setsid]프로세스 동립성 [setsid] A process receives signals from the terminal that it is connected to, and each process inherits its parent's controlling tty. A server should not receive signals from the process that started it, so it must detach itself from its controlling tty. 프로세스는 터미널로부터 시그널을 받으며 그것은 그렇게 연결되고 각 프로세스는 부모 프로세스 tty에서 제어를 받는다. 서버는 시작된 프로세스로 부터 시그널을 받을 수 없고, 그래서 tty에서 제어되는 것으로 부터 분리되어야 한다. In Unix systems, processes operates within a process group, so that all processes within a group is treated as a single entity. Process group or session is also inherited. A server should operate independently from other processes. 유닉스 시스템에서 프로세스들은 프로세스 그룹 중에서 동작하고, 그래서 모든 프로세스 그룹은 단일 엔티티로 취급된다. 프로세스 그룹 또는 세션은 은 역시 상속된다. 서버는 다른 프로세스와는 독립적으로 동작해야 한다. setsid() /* obtain a new process group */ This call will place the server in a new process group and session and detach its controlling terminal. (setpgrp() is an alternative for this) 이 함수 콜은 서버를 터미널로부터 제어되는 것을 분리하고 새로운 프로세스 그룹이나 세션으로 시작하도록 한다. 3) Inherited Descriptors and Standart I/0 Descriptors [gettablesize,fork,open,close,dup,stdio.h]상속된 디스크립터와 표준 입출력 디스크립터 [gettablesize, fork, open, close, dup, stdio.h] Open descriptors are inherited to child process, this may cause the use of resources unneccessarily. Unneccesarry descriptors should be closed before fork() system call (so that they are not inherited) or close all open descriptors as soon as the child process starts running. 열린 디스크립터는 자식 프로세스로 상속된다. 이것은 리소스의 불확실한 리소스의 사용을 일으킬 수 있다. 불명확한 디스크립터는 fork() 시스템 콜을 수행하기 전에 닫아야 하며 (상속되지 않도록) 또는 자식 프로세스의 실행이 시작되 전에 곧바로 열려진 디스크립터를 닫아야 한다. for (i=getdtablesize();i>=0;--i) close(i); /* close all descriptors */ There are three standart I/O descriptors: standart input 'stdin' (0), standart output 'stdout' (1), standart error 'stderr' (2). A standard library routine may read or write to standard I/O and it may occur to a terminal or file. For safety, these descriptors should be opened and connected them to a harmless I/O device (such as /dev/null). 여기 세가지 입출력 디스크립터를 들면 : 표준 입력 'stdin' (0), 표준 출력 'stdout' (1), 표준 에러 'stderr' (2).있다. 표준 라이브러리 루틴은 읽거나 출력 할 수 있는 표준 입출력과 터미널이나 파일에 적용된다. 안전을 위해서 이 세 디스크립터는 열거나 연결하기에 해가 없는 입출력 디바디스로 되어야 한다. (/dev/null 같은) i=open("/dev/null",O_RDWR); /* open stdin */ dup(i); /* stdout */ dup(i); /* stderr */ As Unix assigns descriptors sequentially, fopen call will open stdin and dup calls will provide a copy for stdout and stderr. 유닉스는 디스크립터를 순차적으로 배정하며, fopen 콜은 stdout과 stderr를 복사히기 위해 dup콜이나 stdin을 제공하기 위해서 열어야 한다. 4) File Creation Mask [umask]파일 생성 마스크 [umask] Most servers runs as super-user, for security reasons they should protect files that they create. Setting user mask will pre vent unsecure file priviliges that may occur on file creation. 대부분 서버는 슈퍼유저 권한으로 실행한다. 서버가 생성한 파일의 보안에 관한 이유이다. 사용자 마스크를 설정하는 것은 파일 생성을 일으키는 권한에 관한 동작에 보안을 가지기 위함이다. umask(027); This will restrict file creation mode to 750 (complement of 027). 이것은 파일 생성모드를 750으로 제한한다. 5) Running Directory [chdir]실행 디렉토리 [chdir] A server should run in a known directory. There are many advantages, in fact the opposite has many disadvantages: suppose that our server is started in a user's home directory, it will not be able to find some input and output files. 서버는 알려진 디렉토리에서 실행된다. 이것은 많은 잇점을 가지고 있는데, 사실 반대는 많은 문제점을 가지고 있다. 사용자의 홈 디렉토리에서 서버가 시작된다고 가정하면 그것의 입력과 출력에 대한 파일을 찾을 수 없을 것이다. chdir("/servers/"); The root "/" directory may not be appropriate for every server, it should be choosen carefully depending on the type of the server. 모든 서버에서 루트 디렉토리인 '/'에서는 사용되지 않는다, 이것은 서버타입에 따라 조심스럽게 적용되어야 한다. 6) Mutual Exclusion and Running a Single Copy [open,lockf,getpid]상호배재와 단일 프로세스로 실행하기 [open, lockf, getpid] Most services require running only one copy of a server at a time. File locking method is a good solution for mutual exclusion. The first instance of the server locks the file so that other instances understand that an instance is already running. If server terminates lock will be automatically released so that a new instance can run. Recording the pid of the running instance is a good idea. It will surely be efficient to make 'cat mydaamon.lock' instead of 'ps -ef|grep mydaemon' 대부분 서비스는 한 서버에서 단일 사본으로 수행되는 것이 요구된다. 파일 잠금 기법은 상호배제에 좋은 해법이다. 첫번째 인스턴스로 파일을 잠그는 것은 다른 인스턴스가 이미 인스턴스가 실행되어 있음을 알기 위함이다. 만약 서버가 파일잠음을 자동으로 푸는 것은 새로운 인스턴스가 수행할 수 있게된다. 실행하는 인스턴스의 pid를 기록하는 것은 좋은 아이디어이다. 이것은 'ps -ef|grep mydaemon'대신에 'cat mydaemon.lock'을 만들게 되어 확실하게 효율적인 된다. lfp=open("exampled.lock",O_RDWR|O_CREAT,0640); if (lfp<0) exit(1); /* can not open */ if (lockf(lfp,F_TLOCK,0)<0) exit(0); /* can not lock */ /* only first instance continues */ sprintf(str,"%d\n",getpid()); write(lfp,str,strlen(str)); /* record pid to lockfile */ 7) Catching Signals [signal,sys/signal.h]시그널 생성 [signal, sys/signal.h] A process may receive signal from a user or a process, its best to catch those signals and behave accordingly. Child processes send SIGCHLD signal when they terminate, server process must either ignore or handle these signals. Some servers also use hang-up signal to restart the server and it is a good idea to rehash with a signal. Note that 'kill' command sends SIGTERM (15) by default and SIGKILL (9) signal can not be caught. 프로세스는 사용자 프로세스로부터 시그널을 받을 수 있다. 이것은 시그널과 그에 대한 행동을 잡기 위한 최선이다. 자식 프로세스는 종료될 때 SIGCHLD 시그널을 보내고, 서버는 이 시그널을 핸들링하거나 무시할 수 있다. 어떤 서버는 hang-up 시그널로 재시작과 rehash를 하는 좋은 아이디어로 사용한다. kill 명령은 SIGTERM(15)를 디폴트로 하고 SIGKILL(9) 시그널은 잡히지 않은 점에 유의하라. signal(SIG_IGN,SIGCHLD); /* child terminate signal */ The above code ignores the child terminate signal (on BSD systems parents should wait for their child, so this signal should be caught to avoid zombie processes), and the one below demonstrates how to catch the signals. 위의 코드는 종료 시그널을 무시하는 것이다. (현재 BSD 시스템에서는 자식 프로세스를 기다리게 된다. 그래서 이 시그널은 좀비 프로세스들을 피하는 것을 수행하게 된다.) 그리고 한가지 아래는 시그널을 잡기위한 것을 보여준다. void Signal_Handler(sig) /* signal handler function */ int sig; { switch(sig){ case SIGHUP: /* rehash the server */ break; case SIGTERM: /* finalize the server */ exit(0) break; } } signal(SIGHUP,Signal_Handler); /* hangup signal */ signal(SIGTERM,Signal_Handler); /* software termination signal from kill */ First we construct a signal handling function and then tie up signals to that function. 먼저 시그널 핸들링 함수를 구성하고 그 함수를 시그널에 연결한다. 8) Logging [syslogd,syslog.conf,openlog,syslog,closelog]로깅 [syslogd, syslog.conf, openlog, syslog, closelog] A running server creates messages, naturally some are important and should be logged. A programmer wants to see debug messages or a system operator wants to see error messages. There are several ways to handle those messages. 실행중인 서버는 메시지를 생성하고, 자연히 그 중 어떤 것은 로그에 적어야 하는 것이 중요하다. 프로그래머는 메시지로 디버깅하거나 시스템 운영자는 그 중 에러 메시지를 보기를 원한다. 여기에 이 메세지를 핸들링 하기위한 몇가지 방법이 있다. Redirecting all output to standard I/O :This is what ancient servers do, they use stdout and stderr so that messages are written to console, terminal, file or printed on paper. I/O is redirected when starting the server. (to change destination, server must be restarted) In fact this kind of a server is a program running in foreground (not a daemon). 표준 입출력에 대한 출력의 재지정 : 이것은 서버가 하는 것의 고전적인 일이다. 서버는 stdout과 stderr를 사용하여 메시지를 콘솔이나 터미널, 파일 또는 프린터에 의해 종이에 출력한다. 입출력은 서버가 시작하면서 재지정된다.(출력방향이 변경되면 서버는 재시작해야 한다). 시실 이런 종류의 서버는 데몬이 아닌 포그라운드로 된다. # mydaemon 2> error.log This example is a program that prints output (stdout) messages to console and error (stderr) messages to a file named "error.log". Note that this is not a daemon but a normal program. 이 예제는 stdout (2)과 stderr로 콘솔에 출력하는 메시지를 'error.log'로 명명된 파일로 지정하는 것이다. 이것은 데몬이 아닌 일반적인 프로그램임에 유의하라. Log file method :All messages are logged to files (to different files as needed). There is a sample logging function below. 로그 파일 기법 : 모든 메시지들은 파일로 기록된다. (요구된다면 다른 파일에도). 여기 로깅함수의 예제가 아래에 있다. void log_message(filename,message) char *filename; char *message; { FILE *logfile; logfile=fopen(filename,"a"); if(!logfile) return; fprintf(logfile,"%s\n",message); fclose(logfile); } log_message("conn.log","connection accepted"); log_message("error.log","can not open file"); Log server method :A more flexible logging technique is using log servers. Unix distributions have system log daemon named "syslogd". This daemon groups messages into classes (known as facility) and these classes can be redirected to different places. Syslog uses a configuration file (/etc/syslog.conf) that those redirection rules reside in. 로그 서버 기법 : 더 유연한 로깅 기법은 로그 서버를 이용하는 것이다. 유닉스 배포본은 "syslogd"라는 시스템 로그 데몬을 가지고 있다. 이 데몬 그룹은 메시지를 facility로 알려진 클래스로 이 다른 위치로 재지정가능한 메지지로 한다. syslog는 '/etc/syslog.conf'에 구성을 저장한 파일을 사용하고 재지정 규칙은 그 안에 존재한다. openlog("mydaemon",LOG_PID,LOG_DAEMON) syslog(LOG_INFO, "Connection from host %d", callinghostname); syslog(LOG_ALERT, "Database Error !"); closelog(); In openlog call "mydaemon" is a string that identifies our daemon, LOG_PID makes syslogd log the process id with each message and LOG_DAEMON is the message class. When calling syslog call first parameter is the priority and the rest works like printf/sprintf. There are several message classes (or facility names), log options and priority levels. Here are some examples : openlog 콜에서 'mydaemon'은 우리의 데몬을 확인 시작하고, LOG_PID 는 프로세스 ID와 각 개별 메시지를 syslogd 로그를 만든다. 그리고 LOG_DAEMON은 메시지 클래스 이다. 첫번째 파라메터로 syslog 콜이 호출될 때는 우선순위와 printf/sprint같은 나머지에 따른다. 여기 몇가지 메시지 클래스와 우선순위에 따른 로그 옵션이 있다.
AboutThis text is written by Levent Karakas<levent at mektup dot at >. Several books, sources and manual pages are used. This text includes a sample daemon program (compiles on Linux 2.4.2, OpenBSD 2.7, SunOS 5.8, SCO-Unix 3.2 and probably on your flavor of Unix). You can also download plain source file :exampled.c. Hope you find this d0cument useful. We do love Unix. /*UNIX Daemon Server Programming Sample ProgramLevent Karakas <levent at mektup dot at> May 2001To compile: cc -o exampled examped.cTo run: ./exampledTo test daemon: ps -ef|grep exampled (or ps -aux on BSD systems)To test log: tail -f /tmp/exampled.logTo test signal: kill -HUP `cat /tmp/exampled.lock`To terminate: kill `cat /tmp/exampled.lock`*/#include <stdio.h>#include <fcntl.h>#include <signal.h>#include <unistd.h>#define RUNNING_DIR "/tmp"#define LOCK_FILE "exampled.lock"#define LOG_FILE "exampled.log"void log_message(filename,message)char *filename;char *message;{FILE *logfile; logfile=fopen(filename,"a"); if(!logfile) return; fprintf(logfile,"%s\n",message); fclose(logfile);}void signal_handler(sig)int sig;{ switch(sig) { case SIGHUP: log_message(LOG_FILE,"hangup signal catched"); break; case SIGTERM: log_message(LOG_FILE,"terminate signal catched"); exit(0); break; }}void daemonize(){int i,lfp;char str[10]; if(getppid()==1) return; /* already a daemon */ i=fork(); if (i<0) exit(1); /* fork error */ if (i>0) exit(0); /* parent exits */ /* child (daemon) continues */ setsid(); /* obtain a new process group */ for (i=getdtablesize();i>=0;--i) close(i); /* close all descriptors */ i=open("/dev/null",O_RDWR); dup(i); dup(i); /* handle standart I/O */ umask(027); /* set newly created file permissions */ chdir(RUNNING_DIR); /* change running directory */ lfp=open(LOCK_FILE,O_RDWR|O_CREAT,0640); if (lfp<0) exit(1); /* can not open */ if (lockf(lfp,F_TLOCK,0)<0) exit(0); /* can not lock */ /* first instance continues */ sprintf(str,"%d\n",getpid()); write(lfp,str,strlen(str)); /* record pid to lockfile */ signal(SIGCHLD,SIG_IGN); /* ignore child */ signal(SIGTSTP,SIG_IGN); /* ignore tty signals */ signal(SIGTTOU,SIG_IGN); signal(SIGTTIN,SIG_IGN); signal(SIGHUP,signal_handler); /* catch hangup signal */ signal(SIGTERM,signal_handler); /* catch kill signal */}main(){ daemonize(); while(1) sleep(1); /* run */}/* EOF */ Last Update : 16.05.2001 |
수지 맥도널드 24에서 씀... sjkim comphy 2010.09.17
'Computer Science' 카테고리의 다른 글
소스인사이트 단축키 (0) | 2010.10.08 |
---|---|
유닉스(UNIX)개발관련 지침서 (0) | 2010.09.18 |
mp3 파일 헤더 (0) | 2010.08.01 |
Mp3 파일 구조 (0) | 2010.08.01 |
3차원 이미지를 느낄 수 있는 새로운 형태의 헤드-업 가상현실 (HUVR) 장치 개발 (0) | 2010.07.31 |