8

他の完全に独立したプロセスを起動する Python デーモンを構築しようとしています。

一般的な考え方は、特定のシェルコマンドに対して、数秒ごとにポーリングし、コマンドの正確にk 個のインスタンスが実行されていることを確認することです。pidfile のディレクトリを保持し、ポーリングすると、pid が実行されなくなったpidfileを削除して起動 (および pidfile を作成)します。

子プロセスも完全に独立している必要があるため、親プロセスが停止しても子プロセスは強制終了されません。私が読んだことから、subprocessモジュールでこれを行う方法はないようです。この目的のために、ここで言及されているスニペットを使用しました。

http://code.activestate.com/recipes/66012-fork-a-daemon-process-on-unix/

いくつかの必要な変更を加えました (添付のスニペットでコメントアウトされている行が表示されます)。

  1. ランチャー デーモンを無期限に存続させる必要があるため、元の親プロセスは終了できません。
  2. 子プロセスは、親と同じ cwd で開始する必要があります。

これが私のspawn fnとテストです:

import os
import sys
import subprocess
import time

def spawn(cmd, child_cwd):
    """
    do the UNIX double-fork magic, see Stevens' "Advanced 
    Programming in the UNIX Environment" for details (ISBN 0201563177)
    http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
    """
    try: 
        pid = os.fork() 
        if pid > 0:
            # exit first parent
            #sys.exit(0) # parent daemon needs to stay alive to launch more in the future
            return
    except OSError, e: 
        sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
        sys.exit(1)

    # decouple from parent environment
    #os.chdir("/") # we want the children processes to 
    os.setsid() 
    os.umask(0) 

    # do second fork
    try: 
        pid = os.fork() 
        if pid > 0:
            # exit from second parent
            sys.exit(0) 
    except OSError, e: 
        sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
        sys.exit(1) 

    # redirect standard file descriptors
    sys.stdout.flush()
    sys.stderr.flush()
    si = file('/dev/null', 'r')
    so = file('/dev/null', 'a+')
    se = file('/dev/null', 'a+', 0)
    os.dup2(si.fileno(), sys.stdin.fileno())
    os.dup2(so.fileno(), sys.stdout.fileno())
    os.dup2(se.fileno(), sys.stderr.fileno())

    pid = subprocess.Popen(cmd, cwd=child_cwd, shell=True).pid

    # write pidfile       
    with open('pids/%s.pid' % pid, 'w') as f: f.write(str(pid))
    sys.exit(1)

def mkdir_if_none(path):
    if not os.access(path, os.R_OK):
        os.mkdir(path)

if __name__ == '__main__':
    try:
        cmd = sys.argv[1]
        num = int(sys.argv[2])
    except:
        print 'Usage: %s <cmd> <num procs>' % __file__
        sys.exit(1)
    mkdir_if_none('pids')
    mkdir_if_none('test_cwd')

    for i in xrange(num):
        print 'spawning %d...'%i
        spawn(cmd, 'test_cwd')
        time.sleep(0.01) # give the system some breathing room

この状況では、問題なく動作しているように見え、親が強制終了されても子プロセスは存続します。ただし、元の親のスポーン制限にまだ遭遇しています。〜650回のスポーン後(同時にではなく、子プロセスが終了した後)、親プロセスは次のエラーでチョークします:

spawning 650...
fork #2 failed: 35 (Resource temporarily unavailable)

これらの独立した子プロセスを無期限に生成できるように、spawn 関数を書き直す方法はありますか? ありがとう!

4

2 に答える 2

5

プロセスのリストのおかげで、これは多くの基本的な制限の 1 つに達したためであると断言できます。

  • rlimitnproc特定のユーザーが実行できるプロセスの最大数 --ユーザーごとのプロセス制限の詳細についてはsetrlimit(2)bash(1) ulimit組み込みのを参照してください。/etc/security/limits.conf
  • rlimitnofile特定のプロセスが一度に開くことができるファイル記述子の最大数。(それぞれの新しいプロセスは、おそらく、子の、、および記述子のために、親で 3 つの新しいパイプを作成します。)stdinstdoutstderr
  • システム全体の最大プロセス数。を参照してください/proc/sys/kernel/pid_max
  • システム全体で開いているファイルの最大数。を参照してください/proc/sys/fs/file-max

亡くなった子供たちを刈り取っているわけではないため、これらのリソースの多くは必要以上に長く開いたままになっています。あなたの2 番目の子供は によって適切に処理されていますinit(8)-- 彼らの親は死んでいるので、彼らは に再親化されinit(8)、彼らが死ぬとinit(8)( wait(2)) 後片付けをします。

ただし、プログラムは、最初の子セットの後にクリーンアップする責任があります。C プログラムは通常、その呼び出しのsignal(7)ハンドラをインストールするか、子の終了ステータスを取得して、カーネルのメモリからそのエントリを削除します。SIGCHLDwait(2)waitpid(2)

しかし、スクリプトでのシグナル処理は少し面倒です。SIGCHLDシグナル処理を明示的に設定できる場合SIG_IGN、カーネルは、終了ステータスに関心がないことを認識し、子を取得します_.

追加してみてください:

import signal
signal.signal(signal.SIGCHLD, signal.SIG_IGN)

プログラムの上部近く。

これが に対して何をするかわからないことに注意してくださいSubprocess。それは喜ばないかもしれません。その場合は、シグナル ハンドラをインストールして呼び出す必要がありwait(2)ます。

于 2011-12-08T02:32:00.203 に答える
3

コードを少し修正したところ、問題なく 5000 プロセスを実行できました。だから私はあなたがいくつかの根本的な制限にぶつかったという@sarnoldに同意します。私の変更は次のとおりです。

proc = subprocess.Popen(cmd, cwd=child_cwd, shell=True, close_fds=True)    
pid = proc.pid

# write pidfile       
with open('pids/%s.pid' % pid, 'w') as f: f.write(str(pid))
proc.wait()
sys.exit(1)
于 2011-12-08T02:00:23.347 に答える