Engineering Note

プログラミングなどの技術的なメモ

Python プロセスのデーモン化

daemon

プロセスの中でも、バックグラウンドで動作し続けるものをデーモンと言います。

今回はデーモンプロセスの作成について学んでいきます。

 

 

デーモンとは

サーバのようなサービスを提供するプロセスは、様々なユーザが利用できとても便利ですが、ある特定のユーザが起動した場合、そのユーザがログアウトするとサーバプロセスも終了してしまいます。

これでは不便なため、どの制御端末にも属さないプロセスとしてデーモン(Daemonというものがあります。

 

今回はデーモンプロセスを作成するために以下の手順を行います。

 

  1. プログラムを起動し、forkをする
  2. 親プロセスを終了する
  3. 子プロセスをセッションリーダにする
  4. 2回目のforkをする
  5. 子プロセスを終了し、孫プロセスをデーモンとする

 

それでは、上記手順でスクリプトを作成します。

 

スクリプトの作成

以下のスクリプトは、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

 

最終的に制御端末を持たず、かつセッションリーダーではないプロセス(デーモン)が作成されました。

 

参考書籍

Linuxネットワークプログラミングバイブル

ふつうのLinuxプログラミング 第2版 Linuxの仕組みから学べるgccプログラミングの王道