読者です 読者をやめる 読者になる 読者になる

Pythonでprefork型のデーモンを書く

Pythonのお勉強を兼ねて、先日 Cで書いた C言語でprefork型のデーモンを書く(3): デーモン化 - Sleepless geek in SeattlePythonに移植してみた。
条件分岐、ループ、関数定義、ファイル操作、fork、wait、シグナルハンドラ、連想配列、などなど短いプログラムだけど、いろいろな要素が入っているのでなかなか良いサンプルになった。

#!/usr/bin/env python

import os
import sys
import signal
import time

MAX_CHILDREN=16
PID_FILE='/var/run/my_prefork_daemon.pid'

hash_children={}

def main():
    #デーモン化
    daemonize()

    #プロセスIDを書いておく
    write_pid()

    #シグナルハンドラの登録
    signal.signal(signal.SIGTERM, kill_all_children )

    # 親プロセスのループ 
    # 子プロセスを監視して、子が死んだらすぐに新しい子プロセスを作成する
    while True:
        while len( hash_children )>=MAX_CHILDREN:
            child_pid = os.wait()
            del( hash_children[child_pid] )

        pid = os.fork()
        if pid == 0:
            signal.signal( signal.SIGTERM, lambda sig,status:sys.exit(0) )
            break
        else:
            #ハッシュテーブルに子プロセスのプロセスIDを保存しておく
            hash_children[pid]=1
            time.sleep(0.1)

    # 子プロセスのループ 
    while True:
        #ここがメインルーチンになる。
        time.sleep(1)


def kill_all_children(sig, status):
    for pid in hash_children.keys():
        os.kill( pid, signal.SIGTERM )
    os.exit(0)


def write_pid():
    f = open(PID_FILE, mode='w');
    f.write("%d" % os.getpid())
    f.close()

def daemonize():
    os.chdir("/")
    
    try:
        pid = os.fork()
        if pid > 0:
            sys.exit(0)
            
    except OSError, e: 
        raise Exception, "%s [%d]" % (e.strerror, e.errno)

    os.setsid()
    os.umask(0)
    sys.stdin = open('/dev/null', 'r')
    sys.stdout = open('/dev/null', 'w')
    sys.stderr = open('/dev/null', 'w')

if __name__ == "__main__":
    main()

参考
C言語でprefork型のデーモンを書く(1): 非デーモン prefork サンプル - Sleepless geek in Seattle
C言語でprefork型のデーモンを書く(2): 非デーモン prefork シグナルハンドラ付き - Sleepless geek in Seattle
C言語でprefork型のデーモンを書く(3): デーモン化 - Sleepless geek in Seattle
C言語でprefork型のデーモンを書く(4): init スクリプト - Sleepless geek in Seattle