Linuxの次の単純なデーモンプロセスの実装(Daemon)

5444 ワード

2012-03-17 wcdj
Linux/UNIXシステムが起動すると、デーモンプロセス(Daemonプロセスとも呼ばれる)と呼ばれる多くのサービスが開始されます.デーモンプロセスは、制御端末から離れ、バックグラウンドで周期的にタスクを実行したり、イベントの処理を待ったりするプロセスであり、端末から離れたプロセスは、実行中のプロセスの情報が任意の端末に表示され、プロセスが任意の端末によって生成された中断情報によって終了しないようにするためである.
 
デーモンプロセスを作成する一般的な手順は、次のとおりです.
 
(1)子プロセスを作成し、親プロセスを終了する
制御端末から離れるには親プロセスを終了する必要があり、その後の作業はサブプロセスによって完了します.Linuxでは、親プロセスがサブプロセスより先に終了すると、サブプロセスが孤児プロセスになり、システムが孤児プロセスを発見するたびに、自動的に1番プロセス(init)によって養子縁組され、元のサブプロセスがinitプロセスのサブプロセスになります.
ps–ef|grep ProcNameプロセスの親子関係をPID/PPIDで表示
 
(2)サブプロセスで新しいセッションを作成する
システム関数setsidを使用して完了します.
man 2 setsid setsid関数の説明を表示する
setsid – creates a session and sets theprocess group ID
#include
pid_t setsid(void);
setsid() creates a new session if thecalling process is not a process group leader. The calling process is theleader of the new session, the process group leader of the new process group,and has no controlling tty. The process group ID and session ID of the callingprocess are set to the PID of the calling process. The calling process will bethe only process in this new process group and in this new session.
≪プロセス・グループ|Process Group|ldap≫:1つ以上のプロセスの集合です.プロセスグループには、プロセスグループIDがあり、一意に識別されます.プロセス番号PIDに加えて、プロセスグループIDもプロセスの必須属性です.各プロセスグループには、チーム長プロセスがあり、チーム長プロセスのプロセス番号がプロセスグループIDに等しく、チーム長プロセスの終了によってプロセスグループIDが影響を受けません.
setsid関数の役割:新しいセッションを作成し、そのセッショングループのグループ長を担当するために使用されます.setsidの呼び出しには3つの役割があります
(a)プロセスを元のセッションの制御から解放する.
(b)プロセスを元のプロセスグループの制御から解放する.
(c)プロセスを元の制御端末の制御から解放する.
setsid関数を使用する目的:デーモンの作成の最初のステップがfork関数を呼び出してサブプロセスを作成し、親プロセスを終了します.fork関数を呼び出すと、サブプロセスは親プロセスのセッション期間、プロセスグループ、制御端末などをコピーするため、親プロセスは終了したが、セッション期間、プロセスグループ、制御端末などは変わっていないため、これは本当の意味での独立ではない.setsid関数を使用すると、プロセスを完全に独立させ、他のプロセスの制御から抜け出すことができます.
 
(3)現在のディレクトリをルートディレクトリに変更
forkを使用して作成されたサブプロセスは、親プロセスの現在の作業ディレクトリを継承します.プロセスの実行中に、現在のディレクトリがあるファイルシステムはアンインストールできないため、今後の使用に多くのトラブルが発生します.したがって、通常は、ルートディレクトリ/をデーモンプロセスの現在の作業ディレクトリとして使用します.これにより上記の問題を回避することができる.特別なニーズがあれば、現在の作業ディレクトリを別のパスに変更することもできます.作業ディレクトリを変更する方法はchdir関数を使用します.
 
(4)ファイル権限マスクのリセット
ファイル権限マスク:ファイル権限の対応するビットをブロックします.たとえば、ファイル権限マスクが050であると、ファイルグループ所有者の読み取り可能および実行可能権限(バイナリ、rwx、101に対応)がマスクされる.fork関数で作成されたサブプロセスは、親プロセスのファイル権限マスクを継承するため、サブプロセスがファイルを使用するのに多くの面倒をもたらします.したがって、ファイル権限マスクを0(つまり、権限をマスクしない)に設定すると、デーモンプロセスの柔軟性が向上します.ファイル権限マスクを設定する関数はumaskです.通常の使用方法はumask(0)です.
 
(5)ファイル記述子を閉じる
forkで作成されたサブプロセスも、親プロセスから開いているファイルを継承します.これらの開いているファイルは、デーモンプロセスによって読み書きされない場合がありますが、システムリソースが消費され、ファイルシステムがアンインストールできない可能性があります.setsid呼び出しを使用すると、デーモンプロセスはすでに所属する制御端末と連絡を失っているため、端末から入力された文字がデーモンプロセスに達することは不可能であり、デーモンプロセスで従来の方法(printfなど)で出力された文字が端末に表示されることも不可能である.したがって、ファイル記述子が0、1、2(すなわち、標準入力、標準出力、標準エラー出力)の3つのファイルは、存在する価値を失い、閉じるべきである.
 
(6)デーモン終了処理
ユーザーが外部でデーモンプロセスを停止する必要がある場合、通常killコマンドを使用してデーモンプロセスを停止します.従って、killからのsignal信号処理を実現するために、デーモンプロセスには符号化が必要であり、プロセスが正常に終了する.
 
次は簡単な実装です.
#include
#include
#include
#include// open
#include
#include
#include
#include
#include

#define MAXFILE 65535

volatile sig_atomic_t _running = 1;
int fd;

// signal handler
void sigterm_handler(int arg)
{
	_running = 0;
}

int main()
{
	pid_t pid;
	char *buf = "This is a Daemon, wcdj
"; /* * , * */ signal(SIGINT, SIG_IGN);// signal(SIGHUP, SIG_IGN);// signal(SIGQUIT, SIG_IGN);// signal(SIGPIPE, SIG_IGN);// signal(SIGTTOU, SIG_IGN);// signal(SIGTTIN, SIG_IGN);// signal(SIGTERM, SIG_IGN);// // test //sleep(20);// try cmd: ./test &; kill -s SIGTERM PID // [1] fork child process and exit father process pid = fork(); if(pid < 0) { perror("fork error!"); exit(1); } else if(pid > 0) { exit(0); } // [2] create a new session setsid(); // [3] set current path char szPath[1024]; if(getcwd(szPath, sizeof(szPath)) == NULL) { perror("getcwd"); exit(1); } else { chdir(szPath); printf("set current path succ [%s]
", szPath); } // [4] umask 0 umask(0); // [5] close useless fd int i; //for (i = 0; i < MAXFILE; ++i) for (i = 3; i < MAXFILE; ++i) { close(i); } // [6] set termianl signal signal(SIGTERM, sigterm_handler); // open file and set rw limit if((fd = open("outfile", O_CREAT|O_WRONLY|O_APPEND, 0600)) < 0) { perror("open"); exit(1); } printf("
Daemon begin to work..., and use kill -9 PID to terminate
"); // do sth in loop while(_running) { if (write(fd, buf, strlen(buf)) != strlen(buf)) { perror("write"); close(fd); exit(1); } usleep(1000*1000);// 1 s } close(fd); // print data if((fd = open("outfile", O_RDONLY)) < 0) { perror("open"); exit(1); } char szBuf[1024] = {0}; if(read(fd, szBuf, sizeof(szBuf)) == -1) { perror("read"); exit(1); } printf("read 1024 bytes:
%s
", szBuf); close(fd); return 0; } /* gcc -Wall -g -o test test.c ps ux | grep -v grep | grep test tail -f outfile kill -s SIGTERM PID */

参考:[1]デーモンプロセス
[2]Linux/Unixデーモンの作成
[3]Linux信号signal処理関数
[4] The usage of sig_atomic_t in linux signal mask function