سؤال

هل هناك طريقة للتأكد من أن جميع خلق subprocess مات في زمن الخروج من الثعبان البرنامج ؟ قبل subprocess أعني تلك التي تم إنشاؤها مع subprocess.Popen().

إذا لا يجب أن تكرار عبر كل إصدار يقتل ثم يقتل -9?أي شيء النظيف ؟

هل كانت مفيدة؟

المحلول

يمكنك استخدام atexit هذا تسجيل أي تنظيف المهام ليتم تشغيلها عند إنهاء البرنامج.

atexit.سجل(func[, *وسائط[, **kargs]])

في عملية تنظيف ، يمكنك أيضا تنفيذ الخاص بك الانتظار ، وقتل عندما المطلوب مهلة يحدث.

>>> import atexit
>>> import sys
>>> import time
>>> 
>>> 
>>>
>>> def cleanup():
...     timeout_sec = 5
...     for p in all_processes: # list of your processes
...         p_sec = 0
...         for second in range(timeout_sec):
...             if p.poll() == None:
...                 time.sleep(1)
...                 p_sec += 1
...         if p_sec >= timeout_sec:
...             p.kill() # supported from python 2.6
...     print 'cleaned up!'
...
>>>
>>> atexit.register(cleanup)
>>>
>>> sys.exit()
cleaned up!

ملاحظة -- تسجيل وظائف لن يكون تشغيل إذا كانت هذه العملية (عملية الأصل) هو قتل.

التالية windows الأسلوب لم يعد هناك حاجة بيثون >= 2.6

وإليك طريقة لقتل عملية في نظام التشغيل windows.الخاص بك Popen كائن لديه pid السمة ، لذلك يمكنك فقط استدعاء من قبل النجاح = win_kill(p.pid) (يحتاج pywin32 تثبيت):

    def win_kill(pid):
        '''kill a process by specified PID in windows'''
        import win32api
        import win32con

        hProc = None
        try:
            hProc = win32api.OpenProcess(win32con.PROCESS_TERMINATE, 0, pid)
            win32api.TerminateProcess(hProc, 0)
        except Exception:
            return False
        finally:
            if hProc != None:
                hProc.Close()

        return True

نصائح أخرى

في * لشىء، ربما باستخدام مجموعات العملية يمكن أن تساعدك على الخروج - يمكنك التقاط فرعية ولدت من قبل فرعية الخاص بك، فضلا

if __name__ == "__main__":
  os.setpgrp() # create new process group, become its leader
  try:
    # some code
  finally:
    os.killpg(0, signal.SIGKILL) # kill all processes in my group

وهناك اعتبار آخر هو تصعيد إشارات: من SIGTERM (إشارة الافتراضي لkill) لSIGKILL (a.k.a kill -9). انتظر لفترة قصيرة بين الإشارات لإعطاء عملية فرصة للخروج نظيفة قبل أن kill -9 ذلك.

ووsubprocess.Popen.wait() هو السبيل الوحيد لضمان أن تكون كنت ميتا. في الواقع، POSIX OS وتتطلب منك الانتظار على أطفالك. فإن العديد من * لشىء خلق عملية "غيبوبة": طفل ميت التي الوالد لم تنتظر

إذا هو مكتوب بشكل جيد إلى حد معقول للطفل، فإنه ينتهي. في كثير من الأحيان، وقراءة الأطفال من الأنابيب ل. إغلاق المدخل هو تلميح كبير على الطفل أنه يجب إغلاق المحل والخروج.

إذا كان الطفل لديه البق ولا تنتهي، قد تضطر إلى قتله. يجب إصلاح هذا الخطأ.

وإذا كان الطفل هو "خدمة إلى الأبد" حلقة، ولا يهدف إلى إنهاء، يجب عليك إما قتل أو توفير بعض المدخلات أو الرسالة التي سوف إجبارها على إنهاء.


وتحرير.

في لOS القياسية، لديك os.kill( PID, 9 ). قتل -9 غير قاسية، راجع للشغل. إذا كان يمكنك قتلهم مع SIGABRT (6؟) أو SIGTERM (15) وهذا أكثر مهذبا.

في نظام التشغيل ويندوز، لم يكن لديك os.kill يعمل. انظروا الى هذا ActiveState صفة لإنهاء عملية في ويندوز.

لدينا العمليات التابعة التي هي خدمة WSGI. على إنهائها علينا القيام GET على URL الخاص؛ هذا يسبب للطفل لتنظيف والخروج.

تحذير:لينكس فقط!يمكنك جعل طفلك يتلقى إشارة عند الأم يموت.

أولا تثبيت بيثون-prctl==1.5.0 ثم تغيير إعدادات الأم رمز لإطلاق طفلك العمليات على النحو التالي

subprocess.Popen(["sleep", "100"], preexec_fn=lambda: prctl.set_pdeathsig(signal.SIGKILL))

ما يقول هذا هو:

  • إطلاق subprocess:النوم 100
  • بعد التفرع و قبل exec من subprocess الطفل سجلات "أرسل لي SIGKILL عندما بلدي الأم إنهاء".
<اقتباس فقرة>   

والاستطلاع ()

     

ومعرفة ما اذا كان تم إنهاء عملية طفل.   عودة رمز النتيجة السمة.

وأنا في حاجة إلى الاختلاف صغير من هذه المشكلة (تنظيف يصل فرعية، ولكن من دون الخروج من البرنامج بيثون نفسه)، ونظرا لأنه لم يرد ذكرها هنا بين إجابات أخرى:

p=subprocess.Popen(your_command, preexec_fn=os.setsid)
os.killpg(os.getpgid(p.pid), 15)

وsetsid سيتم تشغيل البرنامج في دورة جديدة، وبالتالي تعيين مجموعة عملية جديدة لذلك وأبنائها. ويدعو os.killpg عليه وبالتالي لا اسقاط العملية الثعبان الخاصة بك أيضا.

والجواب orip هي مفيدة ولكن لديه الجانب السلبي أنه يقتل العملية الخاصة بك وإرجاع رمز خطأ والديك. أنا تجنب أن مثل هذا:

class CleanChildProcesses:
  def __enter__(self):
    os.setpgrp() # create new process group, become its leader
  def __exit__(self, type, value, traceback):
    try:
      os.killpg(0, signal.SIGINT) # kill all processes in my group
    except KeyboardInterrupt:
      # SIGINT is delievered to this process as well as the child processes.
      # Ignore it so that the existing exception, if any, is returned. This
      # leaves us with a clean exit code if there was no exception.
      pass

وبعد ذلك:

  with CleanChildProcesses():
    # Do your work here

وبالطبع يمكنك أن تفعل هذا مع محاولة / باستثناء / أخيرا ولكن لديك للتعامل مع الحالات الاستثنائية وغير الاستثنائية بشكل منفصل.

<اقتباس فقرة>   

هل هناك طريقة للتأكد من كل شيء خلقنا فرعي أو جانبي لقوا حتفهم في وقت الخروج من برنامج بايثون؟ بواسطة فرعي أو جانبي أعني تلك التي تم إنشاؤها مع subprocess.Popen ().

هل يمكن أن تنتهك التغليف واختبار <م> أن جميع العمليات Popen قد أنهيت عن طريق القيام

subprocess._cleanup()
print subprocess._active == []
<اقتباس فقرة>   

إذا لم يكن كذلك، يجب أن أعاد على كل من يقتل إصدار وثم يقتل -9؟ أي شيء أنظف؟

وأنت لا يمكن التأكد من أن جميع فرعية لقوا حتفهم دون الخروج وقتل كل من بقي حيا. ولكن إذا كان لديك هذه المشكلة، وربما هو لأن لديك مشكلة تصميم أعمق.

وهناك حل للنوافذ قد يكون لاستخدام وظيفة API Win32 وعلى سبيل المثال كيف تلقائيا تدمير العمليات التابعة في ويندوز؟

إليك التنفيذ الثعبان القائمة

https://gist.github.com/ubershmekel/119697afba2eaecc6330

وأنا في حاجة فعلا للقيام بذلك، لكنها تشارك تشغيل الأوامر عن بعد. أردنا أن تكون قادرة على وقف العمليات عن طريق إغلاق الاتصال بالخادم. أيضا، إذا، على سبيل المثال، تقوم بتشغيل في REPL الثعبان، يمكنك اختيار لتشغيل المقدمة إذا كنت تريد أن تكون قادرة على استعمال Ctrl + C للخروج.

import os, signal, time

class CleanChildProcesses:
    """
    with CleanChildProcesses():
        Do work here
    """
    def __init__(self, time_to_die=5, foreground=False):
        self.time_to_die = time_to_die  # how long to give children to die before SIGKILL
        self.foreground = foreground  # If user wants to receive Ctrl-C
        self.is_foreground = False
        self.SIGNALS = (signal.SIGHUP, signal.SIGTERM, signal.SIGABRT, signal.SIGALRM, signal.SIGPIPE)
        self.is_stopped = True  # only call stop once (catch signal xor exiting 'with')

    def _run_as_foreground(self):
        if not self.foreground:
            return False
        try:
            fd = os.open(os.ctermid(), os.O_RDWR)
        except OSError:
            # Happens if process not run from terminal (tty, pty)
            return False

        os.close(fd)
        return True

    def _signal_hdlr(self, sig, framte):
        self.__exit__(None, None, None)

    def start(self):
        self.is_stopped = False
        """
        When running out of remote shell, SIGHUP is only sent to the session
        leader normally, the remote shell, so we need to make sure we are sent 
        SIGHUP. This also allows us not to kill ourselves with SIGKILL.
        - A process group is called orphaned when the parent of every member is 
            either in the process group or outside the session. In particular, 
            the process group of the session leader is always orphaned.
        - If termination of a process causes a process group to become orphaned, 
            and some member is stopped, then all are sent first SIGHUP and then 
            SIGCONT.
        consider: prctl.set_pdeathsig(signal.SIGTERM)
        """
        self.childpid = os.fork()  # return 0 in the child branch, and the childpid in the parent branch
        if self.childpid == 0:
            try:
                os.setpgrp()  # create new process group, become its leader
                os.kill(os.getpid(), signal.SIGSTOP)  # child fork stops itself
            finally:
                os._exit(0)  # shut down without going to __exit__

        os.waitpid(self.childpid, os.WUNTRACED)  # wait until child stopped after it created the process group
        os.setpgid(0, self.childpid)  # join child's group

        if self._run_as_foreground():
            hdlr = signal.signal(signal.SIGTTOU, signal.SIG_IGN)  # ignore since would cause this process to stop
            self.controlling_terminal = os.open(os.ctermid(), os.O_RDWR)
            self.orig_fore_pg = os.tcgetpgrp(self.controlling_terminal)  # sends SIGTTOU to this process
            os.tcsetpgrp(self.controlling_terminal, self.childpid)
            signal.signal(signal.SIGTTOU, hdlr)
            self.is_foreground = True

        self.exit_signals = dict((s, signal.signal(s, self._signal_hdlr))
                                 for s in self.SIGNALS)                                     

    def stop(self):
        try:
            for s in self.SIGNALS:
                #don't get interrupted while cleaning everything up
                signal.signal(s, signal.SIG_IGN)

            self.is_stopped = True

            if self.is_foreground:
                os.tcsetpgrp(self.controlling_terminal, self.orig_fore_pg)
                os.close(self.controlling_terminal)
                self.is_foreground = False

            try:
                os.kill(self.childpid, signal.SIGCONT)
            except OSError:
                """
                can occur if process finished and one of:
                - was reaped by another process
                - if parent explicitly ignored SIGCHLD
                    signal.signal(signal.SIGCHLD, signal.SIG_IGN)
                - parent has the SA_NOCLDWAIT flag set 
                """
                pass

            os.setpgrp()  # leave the child's process group so I won't get signals
            try:
                os.killpg(self.childpid, signal.SIGINT)
                time.sleep(self.time_to_die)  # let processes end gracefully
                os.killpg(self.childpid, signal.SIGKILL)  # In case process gets stuck while dying
                os.waitpid(self.childpid, 0)  # reap Zombie child process
            except OSError as e:
                pass
        finally:
            for s, hdlr in self.exit_signals.iteritems():
                signal.signal(s, hdlr)  # reset default handlers

    def __enter__(self):
        if self.is_stopped:
            self.start()

    def __exit__(self, exit_type, value, traceback):
        if not self.is_stopped:
            self.stop()

وبفضل مالكولم هاندلي عن التصميم الأولي. فعلت مع python2.7 على لينكس.

والبحث عن حل لينكس (بدون تثبيت prctl):

def _set_pdeathsig(sig=signal.SIGTERM):
    """help function to ensure once parent process exits, its childrent processes will automatically die
    """
    def callable():
        libc = ctypes.CDLL("libc.so.6")
        return libc.prctl(1, sig)
    return callable


subprocess.Popen(your_command, preexec_fn=_set_pdeathsig(signal.SIGTERM)) 

وهذا هو ما فعلته لبلدي التطبيق POSIX:

عند وجود التطبيق استدعاء الأسلوب قتل () من هذه الفئة: http://www.pixelbeat.org/libs/subProcess.py

وعلى سبيل المثال تستخدم هنا: http://code.google.com/p/ fslint / المصدر / استعراض / جذع / واجهة المستخدم الرسومية fslint-# 608

ويمكنك محاولة subalive، حزمة كتبت لمشكلة مماثلة. ويستخدم بينغ على قيد الحياة الدوري عبر RPC، وعملية الرقيق تنتهي تلقائيا عندما يتوقف سيد الأصوات على قيد الحياة لسبب ما.

https://github.com/waszil/subalive

مثال لسيده:

from subalive import SubAliveMaster

# start subprocess with alive keeping
SubAliveMaster(<path to your slave script>)

# do your stuff
# ...

مثال لفرعي أو جانبي الرقيق:

from subalive import SubAliveSlave

# start alive checking
SubAliveSlave()

# do your stuff
# ...
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top