2011-12-08 39 views
8

Tôi đang cố gắng xây dựng một trình tiện ích Python để khởi chạy các quy trình độc lập hoàn toàn khác.Không xác định quá trình tạo daemon được sinh ra trong Python

Ý tưởng chung là dành cho lệnh trình bao đã cho, thăm dò sau mỗi vài giây và đảm bảo rằng chính xác k phiên bản lệnh đang chạy. Chúng tôi giữ một thư mục pidfiles và khi chúng tôi thăm dò ý kiến, chúng tôi xóa các pidfile mà các pidfile không còn chạy nữa và khởi động (và tạo các pidfiles), tuy nhiên, chúng tôi cần có nhiều quy trình để truy cập k trong số chúng.

Quy trình con cũng cần phải hoàn toàn độc lập, để nếu quá trình cha mẹ chết thì trẻ sẽ không bị giết. Từ những gì tôi đã đọc, có vẻ như không có cách nào để làm điều này với mô-đun subprocess. Để kết thúc này, tôi đã sử dụng đoạn đề cập ở đây:

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

tôi đã thực hiện một vài điều chỉnh cần thiết (bạn sẽ nhìn thấy những dòng chú thích trong đoạn mã đính kèm):

  1. Phụ huynh gốc quá trình không thể thoát vì chúng tôi cần trình khởi chạy để tiếp tục vô thời hạn.
  2. Quy trình con cần bắt đầu bằng cùng một ngày với cha mẹ.

Dưới đây là fn spawn của tôi và một bài kiểm tra:

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 

Trong tình huống này, mọi thứ dường như làm việc tốt, và các quá trình con tồn tại ngay cả khi cha mẹ bị giết. Tuy nhiên, tôi vẫn đang chạy vào một giới hạn đẻ trứng trên cha mẹ ban đầu. Sau ~ 650 spawn (không được kiêm nhiệm, các em đã hoàn thành) quá trình cha mẹ nghẹn với lỗi:

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

Có cách nào để viết lại chức năng đẻ trứng của tôi để tôi có thể đẻ trứng các quá trình con độc lập vô thời hạn? Cảm ơn!

+0

Bảng xử lý của bạn trông như thế nào? Liệu ps ps' có cho thấy một đống các quá trình zombie khổng lồ đang chờ được gặt hái? (Tôi không thấy bất kỳ mã nào ở đây để 'wait()' trên các con đầu tiên được phân chia.) – sarnold

+0

Tôi nghĩ vậy: http://pastebin.com/qDrFmHWk –

+0

Hãy xem xét pyinotify để theo dõi những thay đổi trong thư mục thay thế bỏ phiếu. – aitchnyu

Trả lời

5

Nhờ your list of processes tôi sẵn sàng nói rằng điều này là bởi vì bạn đã nhấn một trong một số những hạn chế cơ bản:

  • rlimit nproc số lượng tối đa của các quá trình một người dùng nào đó được phép thực thi - xem setrlimit(2), bash(1)ulimit được tích hợp sẵn và /etc/security/limits.conf để biết chi tiết về giới hạn quy trình cho mỗi người dùng.
  • rlimit nofile số lượng tối đa các bộ mô tả tệp được quy trình cho phép được phép mở cùng một lúc. (Mỗi quy trình mới có thể tạo ba ống mới trong số cha mẹ, cho các số stdin, stdoutstderr mô tả.)
  • Số lượng quy trình tối đa trên toàn hệ thống; xem /proc/sys/kernel/pid_max.
  • Số lượng tệp mở tối đa trên toàn hệ thống; xem /proc/sys/fs/file-max.

Vì bạn không gặt hái những đứa con đã chết của mình, nhiều tài nguyên trong số các tài nguyên này được mở lâu hơn thời gian cần thiết. giây thứ hai trẻ em của bạn đang được xử lý đúng cách bởi init(8) - cha mẹ của chúng đã chết, vì vậy chúng được đổi lại thành init(8)init(8) sẽ dọn dẹp sau khi chúng (wait(2)) khi chúng chết.

Tuy nhiên, chương trình của bạn có trách nhiệm làm sạch sau khi tập hợp con đầu tiên. Các chương trình C thường cài đặt trình xử lý signal(7) cho SIGCHLD gọi wait(2) hoặc waitpid(2) để gặt hái trạng thái thoát của trẻ em và do đó xóa các mục nhập khỏi bộ nhớ của hạt nhân.

Nhưng xử lý tín hiệu trong tập lệnh hơi khó chịu. Nếu bạn có thể đặt bố cục tín hiệu SIGCHLD thành SIG_IGN một cách rõ ràng, hạt nhân sẽ biết rằng bạn không quan tâm đến trạng thái thoát và sẽ gặt hái các con cho bạn_.

Hãy thử thêm:

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

gần phía trên cùng của chương trình của bạn.

Lưu ý rằng tôi không biết điều này làm gì cho Subprocess. Nó có thể không hài lòng. Nếu đúng như vậy, bạn cần phải install a signal handler để gọi wait(2) cho bạn.

+1

Subprocess giả sử để xử lý phép thuật SIGCHLD. Kết hợp với close_fds nó sẽ giải quyết lỗi trong một số phiên bản của python (xem http://bugs.python.org/issue4216). –

+0

Cài đặt tín hiệu và close_fds đã giải quyết nó cho tôi trên OSX và Ubuntu! Đã 50k quá trình dễ dàng. Cảm ơn cả hai bạn! –

+0

@ILYA: Nếu 'Subprocess' được sử dụng để tạo các quy trình _all_, nó có thể đã hoạt động tốt; nhưng một nửa quy trình được tạo bằng tay trong trường hợp này. – sarnold

3

Tôi đã sửa đổi một chút mã của bạn và có thể chạy 5000 quy trình mà không gặp bất kỳ sự cố nào. Vì vậy, tôi đồng ý với @sarnold rằng bạn nhấn một số hạn chế cơ bản. sửa đổi của tôi là:

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) 
+0

chuyển sang: 'pid = subprocess.Popen (cmd, PT = child_cwd, vỏ = True, close_fds = True) .pid' nhưng nó vẫn thất bại: 'đẻ trứng 647 ... ngã ba # 2 thất bại : 35 (Tài nguyên tạm thời không khả dụng) sinh sản 648 ... ngã ba # 1 không thành công: 35 (Tài nguyên tạm thời không khả dụng) ' –

+0

close_fds cùng với cài đặt tín hiệu hoạt động hoàn hảo cho tôi! –

Các vấn đề liên quan