プロセスの中でも、バックグラウンドで動作し続けるものをデーモンと言います。
今回はデーモンプロセスの作成について学んでいきます。
デーモンとは
サーバのようなサービスを提供するプロセスは、様々なユーザが利用できとても便利ですが、ある特定のユーザが起動した場合、そのユーザがログアウトするとサーバプロセスも終了してしまいます。
これでは不便なため、どの制御端末にも属さないプロセスとしてデーモン(Daemon)というものがあります。
今回はデーモンプロセスを作成するために以下の手順を行います。
- プログラムを起動し、forkをする
- 親プロセスを終了する
- 子プロセスをセッションリーダにする
- 2回目のforkをする
- 子プロセスを終了し、孫プロセスをデーモンとする
それでは、上記手順でスクリプトを作成します。
スクリプトの作成
以下のスクリプトは、Linuxネットワークプログラミングバイブルに記載されているデーモンのテストプログラムを、そのままPythonで書き直したものになります。
# daemon.py import os import signal import sys import syslog import time MAXFD = 64 def daemonize(nochdir, noclose): global MAXFD try: pid = os.fork() except OSError as e: print("fork():{}".format(e)) sys.exit(1) if pid: os._exit(0) os.setsid() signal.signal(signal.SIGHUP, signal.SIG_IGN) pid = os.fork() if pid: os._exit(0) if nochdir == 0: os.chdir("/") if noclose == 0: try: for i in range(MAXFD): os.close(i) except OSError as e: pass try: fd = os.open(os.devnull, os.O_RDWR, 0) os.dup2(fd, 0) os.dup2(fd, 1) os.dup2(fd, 2) if fd > 2: os.close(fd) except OSError as e: pass return if __name__ == '__main__': daemonize(0, 0) sys.stderr.write("stderr\n") syslog.syslog(syslog.LOG_USER | syslog.LOG_NOTICE, "daemon:cwd={}".format(os.getcwd()))
最終的にデーモンとなるプロセスは以下のようなものになります。
動作確認
それでは、上記で作成したスクリプトを実行してみます。
$ python3 daemon.py $
そのまま実行した際にプロンプトが返り、標準エラー出力のメッセージが表示されなかったことから、うまくdevnullにリダイレクトができたようです。
syslogのメッセージを確認するとカレントディレクトリがルートに変更されています。
$ sudo grep daemon /var/log/syslog Apr 23 00:00:00 ubuntu daemon.py: daemon:cwd=/
次にスクリプトの適当な箇所にスリープを入れて、psコマンドでその様子を確認します。
$ python3 daemon.py & [1] 2488 # 最初のfork前(親) $ ps -jA | grep [p]ython PID PGID SID TTY TIME CMD 2488 2488 2378 pts/0 00:00:00 python3 # 最初のfork後(子) $ ps -jA | grep [p]ython PID PGID SID TTY TIME CMD 2492 2488 2378 pts/0 00:00:00 python3 # 親プロセス終了 [1]+ Done python3 daemon.py # setsid実行後(子) $ ps -jA | grep [p]ython PID PGID SID TTY TIME CMD 2492 2492 2492 ? 00:00:00 python3 # 2回目のfork後(孫) $ ps -jA | grep [p]ython PID PGID SID TTY TIME CMD 2499 2492 2492 ? 00:00:00 python3
最終的に制御端末を持たず、かつセッションリーダーではないプロセス(デーモン)が作成されました。