プロセス管理ツールの supervisor では、設定したプログラムをデーモン起動させ、意図せずにプロセスが終了した場合は、自動再起動する仕組みがある。

プログラムを確認すると、基本的には supervisord が各プログラムを fork & exec し、あとは異常終了により zombie になったプロセスがないか wait(non-blocking)  し、終了したプロセスを再び fork & exec しているだけ。supervisord デーモンを strace  すると select(2) と wait4(2) のシステムコールの嵐になっている。


supervisor のソースコードをもとに、fork & exec => wait & reap zombies => fork & exec の一連のフローを実装したプログラムが以下。fork したプログラム(ここでは 30秒の sleep)が終了すると、自動起動するようにしている。

import os
import signal
import sys
import time

class Supervisor(object):
    def __init__(self):
        self.pidhistory = {}

    def run(self):
        p = Process()
        pid = p.spawn()
        self.pidhistory[pid] = 'dummy'

    def runforever(self):
        while True:
            pid, sts = self.reap()
            if (pid, sts) <> (0, 0):
                sys.stderr.write(decode_wait_status(sts) + '\n')
                self.pidhistory.pop(pid, None)
                p = Process()
                self.pidhistory[p.spawn()] = 'dummy'

    def reap(self):
        # reap daemon process
            pid, sts = os.waitpid(-1, os.WNOHANG)
        except OSError, why:
            sys.stderr.write(str(why) + '\n')
            pid, sts = 0, 0
        return pid, sts

class Process(object):
    def __init__(self, path = '/bin/sleep', args = ['sleep', '30']):
        self.path = path
        self.args = args
        self.env = os.environ.copy()

    def _spawn_as_child(self):
        env = os.environ.copy()
        os.execve(self.path, self.args, self.env)

    def _spawn_as_parent(self, pid):
        sys.stderr.write('spawned: %r with pid %s\n' % ('sleep', pid))

    def spawn(self):
        pid = os.fork()
        if pid == 0:
        elif  pid > 0:
            return pid

def decode_wait_status(sts):
    if os.WIFEXITED(sts):
        es = os.WEXITSTATUS(sts) & 0xffff
        msg = "exit status %s" % es
    elif os.WIFSIGNALED(sts):
        sig = os.WTERMSIG(sts)
        msg = "terminated by signal %s" % sig
        msg = "unknown termination cause 0x%04x" % sts
    return msg

def main():
    s = Supervisor()

if __name__ == '__main__':


$ python
spawned: 'sleep' with pid 1371
.............................exit status 0
spawned: 'sleep' with pid 1533
..........terminated by signal 9
spawned: 'sleep' with pid 1534
..........terminated by signal 15
spawned: 'sleep' with pid 1535
.........terminated by signal 2
spawned: 'sleep' with pid 1536
..............................exit status 0


  • 今年の正月前後にやったシステム・プログラミングのよい復習になった。
  • supervisord のソースコードは各ファイルやクラスやメソッドが長く、入り組んでいて、自分には少し読みにくく感じた。
Tagged with: , , ,
Posted in linux, middleware

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: