2011-12-08 33 views
8

में अनिश्चितकालीन डिमोनाइज्ड प्रक्रिया उत्पन्न हो रही है, मैं एक पाइथन डिमन बनाने की कोशिश कर रहा हूं जो अन्य पूरी तरह से स्वतंत्र प्रक्रियाओं को लॉन्च करता है।पाइथन

सामान्य विचार किसी दिए गए खोल कमांड के लिए है, हर कुछ सेकंड में मतदान करें और सुनिश्चित करें कि के कमांड के उदाहरण चल रहे हैं। हम पिडफाइल की निर्देशिका रखते हैं, और जब हम मतदान करते हैं तो हम उन पिडफाइल को हटाते हैं जिनके पिड अब नहीं चल रहे हैं और शुरू होते हैं (और इसके लिए पिडफाइल बनाते हैं) हालांकि कई प्रक्रियाओं को हमें के तक पहुंचने की आवश्यकता है।

बच्चे की प्रक्रियाओं को पूरी तरह से स्वतंत्र होने की भी आवश्यकता है, ताकि यदि माता-पिता की प्रक्रिया मर जाती है तो बच्चों को मार नहीं दिया जाएगा। मैंने जो पढ़ा है, उससे ऐसा लगता है कि subprocess मॉड्यूल के साथ ऐसा करने का कोई तरीका नहीं है। इस उद्देश्य से, मैं टुकड़ा यहाँ उल्लेख प्रयोग किया है:

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

मैं एक जोड़े को आवश्यक संशोधन किए गए हैं (आप लाइनों संलग्न स्निपेट में बाहर टिप्पणी की देखेंगे):

  1. मूल माता पिता प्रक्रिया बाहर नहीं निकल सकती है क्योंकि हमें अनिश्चित काल तक जारी रखने के लिए लॉन्चर डिमन की आवश्यकता है।
  2. बच्चे प्रक्रियाओं को माता-पिता के समान सीडब्ल्यूडी से शुरू करने की आवश्यकता है।

यहाँ मेरी अंडे 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 spawns (नहीं समवर्ती, बच्चों समाप्त कर दिया है) माता पिता प्रक्रिया त्रुटि के साथ chokes:

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

मेरे अंडे समारोह के पुनर्लेखन के लिए इतना है कि मैं इन स्वतंत्र बच्चे प्रक्रियाओं को अनिश्चित काल के अंडे कर सकते हैं कोई तरीका है? धन्यवाद!

+0

आपकी प्रक्रिया तालिका कैसी दिखती है? क्या 'ps aux' ज़ोंबी प्रक्रियाओं का एक विशाल ढेर दिखाता है जो काटने की प्रतीक्षा कर रहा है? (मुझे पहले-फोर्क वाले बच्चों पर 'प्रतीक्षा()' में कोई कोड नहीं दिख रहा है।) – sarnold

+0

मुझे ऐसा लगता है: http://pastebin.com/qDrFmHWk –

+0

इसके बजाय निर्देशिका में परिवर्तनों की निगरानी करने के लिए pyinotify पर विचार करें मतदान के 'पीआईडी ​​= subprocess.Popen (cmd, CWD = child_cwd, खोल = सच, close_fds = सच) लेकिन यह अभी भी विफल रहा है .pid': ' 647 को उत्पन्न करने ... कांटा # 2 में विफल रहा है – aitchnyu

उत्तर

5

धन्यवाद your list of processes लिए मैं कहना है कि इस वजह से आप मौलिक सीमाओं की एक संख्या में से एक हिट को तैयार हूँ:

  • rlimit प्रक्रियाओं उसे किसी उपयोगकर्ता की nproc अधिकतम संख्या निष्पादित करने की अनुमति है - setrlimit(2), bash(1)ulimit अंतर्निहित, और /etc/security/limits.conf प्रति उपयोगकर्ता प्रक्रिया सीमाओं के विवरण के लिए देखें।
  • rlimit nofile फ़ाइल डिस्क्रिप्टर की अधिकतम संख्या एक दी गई प्रक्रिया को एक बार में खोलने की अनुमति है। (प्रत्येक नई प्रक्रिया शायद माता पिता में तीन नए पाइप बनाता है, बच्चे के stdin, stdout, और stderr वर्णनकर्ता के लिए।)
  • प्रणाली चौड़ा प्रक्रियाओं की अधिकतम संख्या; /proc/sys/kernel/pid_max देखें।
  • सिस्टम-व्यापी खुली फ़ाइलों की अधिकतम संख्या; /proc/sys/fs/file-max देखें।

क्योंकि आप अपने मृत बच्चों का उपयोग नहीं कर रहे हैं, इनमें से कई संसाधनों को उनके मुकाबले लंबे समय तक खुला रहता है। दूसरा बच्चों को init(8) द्वारा ठीक तरह से संभाला जा रहा है - उनके माता-पिता मर चुके हैं, इसलिए उन्हें init(8) पर फिर से parented हैं, और init(8) उनके मरने पर (wait(2)) के बाद साफ हो जाएगा।

हालांकि, आपका प्रोग्राम पहले बच्चों के सेट के बाद सफाई के लिए ज़िम्मेदार है। सी प्रोग्राम आमतौर पर हैंडलर SIGCHLD के लिए हैंडलर wait(2) या waitpid(2) को बच्चों की निकास स्थिति काटने के लिए कहते हैं और इस प्रकार कर्नेल की स्मृति से इसकी प्रविष्टियों को हटा देते हैं।

लेकिन एक स्क्रिप्ट में सिग्नल हैंडलिंग थोड़ा परेशान है। यदि आप SIGCHLD सिग्नल स्वभाव को SIG_IGN पर स्पष्ट रूप से सेट कर सकते हैं, तो कर्नेल को पता चलेगा कि आपको बाहर निकलने की स्थिति में रूचि नहीं है और आप बच्चों के लिए बच्चों काट लेंगे।

जोड़ने का प्रयास करें:

import signal 
signal.signal(signal.SIGCHLD, signal.SIG_IGN) 
अपने कार्यक्रम के शीर्ष के निकट

ध्यान दें कि मुझे नहीं पता कि यह Subprocess के लिए क्या करता है। यह प्रसन्न नहीं हो सकता है। यदि ऐसा है, तो आपको wait(2) पर कॉल करने के लिए install a signal handler की आवश्यकता होगी।

+1

सबप्रोसेस SIGCHLD जादू को संभालने का मानना ​​है। Close_fds के साथ इसे पायथन के कुछ संस्करणों में बग को हल करना चाहिए (http://bugs.python.org/issue4216 देखें)। –

+0

सिग्नल सेटिंग और क्लोज_एफडीएस ने ओएसएक्स और उबंटू पर इसे मेरे लिए हल किया! 50k प्रक्रियाओं को आसानी से किया था। आप दोनों को शुक्रिया! –

+0

@ILYA: यदि _all_ प्रक्रियाओं को बनाने के लिए 'सबप्रोसेस 'का उपयोग किया जा रहा था तो शायद यह ठीक काम करेगा; लेकिन आधे प्रक्रियाएं इस मामले में हाथ से बनाई गई हैं। – sarnold

3

मैं आपके कोड को थोड़ा संशोधित कर रहा हूं और बिना किसी समस्या के 5000 प्रक्रियाओं को चलाने में सक्षम था। इसलिए मैं @ कर्नाल्ड से सहमत हूं कि आपने कुछ मौलिक सीमा को मारा है। मेरे संशोधनों हैं:

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

में स्विच : 35 (संसाधन अस्थायी रूप से अनुपलब्ध) स्पॉइंग 648 ... कांटा # 1 असफल: 35 (संसाधन अस्थायी रूप से अनुपलब्ध) ' –

+0

सिग्नल सेटिंग के साथ क्लोज_एफडीएस मेरे लिए पूरी तरह से काम करता है! –