|
一般にデーモンプロセスとは,一般的にはインタラクティブに処理を行わず,
時間やある一定のトリガーによって動作する常駐プロセスのことを指す. そもそもデーモン(daemon)注1とは,ギリシャ神話に登場する「神々と人間の間に介在する 二次的な神」や「守護神」のことで,コンピューターの中で,人知れず動作する 「縁の下の力持ち」の常駐プロセスには,ふさわしい愛称と言えるだろう. さて,デーモンプロセスは,一般的なコマンドとして動作するアプリケーションと比較 してもそれほど複雑ではない.しかし,作り方によっては守護神にも悪魔に もなってしまう可能性を秘めている. デーモンプロセスを「守護神」にするか「悪魔」にしてしまうかは, プログラムの作者が正しい知識を持っているかどうかにかかっているといっても 過言ではない. 注1 デーモンといえば demon も日本語読みでは同じ発音になるが, demonのほうは,神々と人間の中間にある考えられる悪魔で,キリスト教ではdevilを 意味する. |
| 分 | 0〜59 |
| 時 | 0〜23 |
| 日 | 1〜31 |
| 月 | 1〜12 |
| 曜日 | 0〜6 (0 が日曜日) |
0 0 * * * /home/user/my_cmd |
services の書式
サービス名 ポート番号/プロトコル
---------
:
:
:
ftp-data 20/tcp
ftp 21/tcp
fsp 21/udp fspd
ssh 22/tcp # SSH Remote Login Protocol
ssh 22/udp # SSH Remote Login Protocol
telnet 23/tcp
# 24 - private
smtp 25/tcp mail
# 26 - unassigned
:
:
:
#
webster 765/tcp # Network dictionary
webster 765/udp
swat 901/tcp # Samba Web Administration Tool
#
:
:
:
|
inet.conf の書式
サービス名 ソケットタイプ プロトコル タイムアウト待ち時間 実行ユーザID コマンド コマンドの引数
---------
:
:
:
#
# These are standard services.
#
ftp stream tcp nowait root /usr/sbin/tcpd in.ftpd -l -a
telnet stream tcp nowait root /usr/sbin/tcpd in.telnetd
#
# Shell, login, exec, comsat and talk are BSD protocols.
#
shell stream tcp nowait root /usr/sbin/tcpd in.rshd
login stream tcp nowait root /usr/sbin/tcpd in.rlogind
#exec stream tcp nowait root /usr/sbin/tcpd in.rexecd
#comsat dgram udp wait root /usr/sbin/tcpd in.comsat
talk dgram udp wait nobody.tty /usr/sbin/tcpd in.talkd
ntalk dgram udp wait nobody.tty /usr/sbin/tcpd in.ntalkd
#dtalk stream tcp wait nobody.tty /usr/sbin/tcpd in.dtalkd
:
:
:
#
# End of inetd.conf
linuxconf stream tcp wait root /bin/linuxconf linuxconf --http
swat stream tcp nowait.400 root /usr/sbin/swat swat
|
| 利点 |
|---|
| ・プログラマーは最小の機能を作成することで,必要な機能を実現できる |
| ・機能が細分化されているため,コマンドの一部を差し替えることにより他の機能を実現することができる |
| ・処理を行うときに必要なコマンドを起動し,処理が終わると終了するため, 不必要なプロセスがシステム内に常駐しない |
| ・コマンドの処理にメモリリークなどがあっても,処理が終了するとプロセスが管理するリソースはすべて解放されるため,システムに影響を与えない.(ただし,デーモンプロセスにメモリリークがある場合を除く) |
| 欠点 |
| ・中核になるコマンド(ここではcrond, inetd)にトラブルが発生すると,それに依存するすべての処理が停止してしまう可能性がある. |
| ・コマンドが細分化しているため同じ処理を大量に実行すると,処理の実行個数分のプロセスを起動してシステムを圧迫,最悪の場合システムクラッシュの原因になる. |
| ・利用しているコマンドが,なんらかの理由で差し替えられると正しく動作しなくなる可能性がある |
| ・プロセスの起動に費やされるCPUは,スレッドよりはるかに大きいため処理速度が要求される処理には向かない |
○初期化 | ・不必要なシステムリソースを解放する | ・不要なシグナルの発生を無視する設定を行う ↓ ・起動した親プロセスから切り離す +→○待ち | ↓ ・処理を実行するためのトリガーが発生するまで待つ | ○処理実行 | | ・トリガーが発生すると処理を行う | | ・処理を終了すると再びトリガーが発生するまで待つ +−+ ・システムが終了するまで,待ちと処理の実行を繰り返す |
| void signal(int sig, void (*func)(int)) |
|
プロセスが受信するシグナルの設定を行う #include <signal.h> 引数 int sig ... 設定するシグナル void (*func)(int) ... 指定したシグナルが発生した場合の コールバック関数へのポインタシグナルを無視する場合は SIG_IGNを指定 戻り値 なし POSIX.1 に定義されているシグナル シグナル 値 内容 -------------------------------------------- SIGINT 2 キーボード割り込み SIGQUIT 3 キーボードによる中止 SIGILL 4 不正な命令 SIGABRT 6 abort からの中断シグナル SIGFPE 8 浮動小数点例外 SIGKILL 9 Kill シグナル SIGSEGV 11 不正なメモリ参照 SIGPIPE 13 パイプ破壊: 読み手の無いパイプへの書き出し SIGALRM 14 alarm からのタイマーシグナル SIGTERM 15 終了シグナル SIGUSR1 30,10,16 ユーザ定義シグナル1 SIGUSR2 31,12,17 ユーザ定義シグナル2 SIGCHLD 20,17,18 子プロセスの一旦停止または終了 SIGCONT 19,18,25 一旦停止からの再開 SIGSTOP 17,19,23 プロセスの一旦停止 SIGTSTP 18,20,24 端末より入力された一旦停止 SIGTTIN 21,21,26 バックグランドプロセスのtty入力 SIGTTOU 22,22,27 バックグランドプロセスのtty出力 |
| pid_t setsid(void) |
|
セッションを作成し,プロセス・グループIDを設定します 呼び出したプロセスは新しいセッションのリーダー, 新しいプロセスグループのプロセスグループリーダーとなり, tty の制御を持ちません. #include <unistd.h> 引数 なし 戻り値 正常終了 呼び出したプロセスのセッションID 異常終了 -1 |
| pid_t fork(void) |
|
子プロセスを生成します #include <unistd.h> 引数 なし 戻り値 正常終了 親の実行スレッドには子プロセスのPIDを返却する 子の実行スレッドには0を返却する 異常終了 -1 |
| mode_t umask(mode_t mask) |
|
ファイル作成マスクを設定します umask 値はopenで新しくファイルを作成する時に, アクセス件を設定するために使用されます. 具体的には umask 値に設定されている許可がopenのmode引き数から 取り消されます.(例えば,umask値=022とした場合modeに0666 が与えられると,新たに作成されたファイルは 0666~022 = 0644 が設定されます. #include <sys/types.h> 引数 mode_t mask 戻り値 以前のumask値 |
/*------------------------------------------------------
"testd.c"
Daemon Sample
24 Jul. 2000
Redhat-Linux 6.2J/GNU-C Programmed by S.Tsuneoka
------------------------------------------------------*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#define WAIT_TIME 10 /* [sec] */
#define LOGFILENAME "testd.log"
/*------------------------------------------------------
初期化
------------------------------------------------------*/
int init_daemon(int debug_mode)
{
if(debug_mode){
fprintf(stdout, "-- Debug mode --\n");
return 0;
}
switch(fork()){
case -1:
perror("fork");
return errno;
case 0:
setsid(); /* 子プロセスをターミナルから切り離す */
break;
default:
exit(0); /* 親プロセスを終了させる */
}
/* 標準(エラー)入出力を閉じて,/dev/nullで埋めておく */
close(0);
close(1);
close(2);
open("/dev/null", O_RDWR);
dup2(0, 1);
dup2(0, 2);
/* 不要なシグナルを禁止する */
signal(SIGALRM, SIG_IGN);
signal(SIGCHLD, SIG_IGN); /* ゾンビ止め */
signal(SIGHUP , SIG_IGN);
signal(SIGPIPE, SIG_IGN);
signal(SIGTERM, SIG_IGN);
/* 親のMASKを引き継がない */
umask(0);
return 0;
}
|
|
extern char **environ; int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg , ..., char * const envp[]); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); |
|
現在のプロセスイメージを新しいプロセスイメージで置き換えます. ※Win32APICreateProcessのように新しいプロセスを生成しませんので, あらかじめforkで子プロセスを生成し,子プロセスで本関数を実行 する必要があります. なお,system関数は内部でこれらの処理を行って,シェルを起動して います. #include <unistd.h> 引数 各関数で異なる 戻り値 正常終了 -1以外 異常終了 -1 (エラーコードはerrnoに設定される) |
/*------------------------------------------------------
イベント待ち
------------------------------------------------------*/
int wait_event(void)
{
FILE *fp = NULL;
char buf[256];
int ret_code = 0;
while(1){
/* 待ち */
sleep(WAIT_TIME);
/* 処理:ファイルに1行ログを出力する */
if((fp = fopen(LOGFILENAME, "a")) == NULL){
ret_code = errno;
perror("fopen");
break;
}
sprintf(buf, "%d - processed\n", time(NULL));
fputs(buf, fp);
if(fclose(fp) == EOF){
ret_code = errno;
perror("fclose");
break;
}
printf(buf); /* デバッグ用 */
}
return ret_code;
}
|
| 利点 |
|---|
| ・デーモンプロセスの常駐量が最小になる |
| ・処理のバグ(メモリリークなど)でシステムに影響を与えない |
| ・デーモン部分と処理部分を別モジュールにすることで,個別にデバッグが行える |
| 注意点 |
| ・同時期に大量の処理を行うとシステムや他のプロセスに悪影響を与える恐れがある |
| ・バグによってプロセス生成処理が無限ループすると,システムがデッドロック状態になる |
/*------------------------------------------------------
メイン
------------------------------------------------------*/
int main(int argc, char *argv[])
{
int debug_mode = 0;
int ret_code = 0;
if(argc >= 2){
if(strcmp(argv[1], "-d") == 0){
debug_mode = 1;
}
else{
puts("usage: testd [-d]");
puts(" -d ... debug mode");
return 1;
}
}
if((ret_code = init_daemon(debug_mode)) != 0){
fprintf(stderr, "init daemon failed code = %d\n", ret_code);
return 2;
}
if((ret_code = wait_event()) != 0){
fprintf(stderr, "wait event failed code = %d\n", ret_code);
return 3;
}
return 0;
}
|
(a)コンパイル [shinji@mystiy daemon]$ gcc -o testd testd.c [shinji@mystiy daemon]$ (b)デーモンとして実行 [shinji@mystiy daemon]$ ./testd -debug -- Debug mode -- 962022442 - processed 962022452 - processed 962022462 - processed 962022472 - processed 962022564 - processed 962022574 - processed ← [ctrl]+[c] で停止 [shinji@mystiy daemon]$ (c)デーモンとして実行 [shinji@mystiy daemon]$ ./testd ←実行後,すぐにコマンドプロンプトに戻る [shinji@mystiy daemon]$ ps -eaf | grep testd shinji 963 1 0 21:29 ? 00:00:00 ./testd [shinji@mystiy daemon]$ kill -9 963 ←数十秒待ってから実行する [shinji@mystiy daemon]$ ps -eaf | grep testd shinji 972 894 0 21:30 pts/0 00:00:00 grep testd [shinji@mystiy daemon]$ cat testd.log 962022584 - processed 962022594 - processed 962022604 - processed 962022614 - processed 962022624 - processed [shinji@mystiy daemon]$ |
日本のLinux情報 - JM Project;http://www.linux.or.jp/JM/ マニュアル検索 ftpd, telnetd, inetd, tcpd, crond, signal, setsid, fork, exec, umask RedhatLinux 6.2J;/etc/services, /etc/inet.conf