Freitag, Juni 5, 2020

Popen warten auf Kind-Prozess, auch wenn die sofortige Kind gekündigt hat

Arbeite ich mit Python 2.7 unter Windows 8/XP.

Ich habe ein Programm, dass läuft ein anderes Programm B mit dem folgenden code:

p = Popen(["B"], stdout=PIPE, stderr=PIPE)
stdout, stderr = p.communicate()
return

B läuft ein batch-Skript C. C ist ein langes script läuft und ich will B zu beenden, obwohl C noch nicht beendet ist. Ich habe es geschafft mit dem folgenden code (in B):

p = Popen(["C"])
return

Wenn ich B, funktioniert es wie erwartet. Wenn ich Ein aber ich erwartete es, um zu beenden, wenn B beendet. Aber wartet, bis C beendet, obwohl B schon exitted. Irgendwelche Ideen auf, was passiert ist und was mögliche Lösungen sein könnten?

Leider die offensichtliche Lösung wechseln zu schauen, wie B nicht eine option.

Hier ist eine funktionale Beispielcode, um dieses Problem zu veranschaulichen:
https://www.dropbox.com/s/cbplwjpmydogvu2/popen.zip?dl=1

Jede Eingabe wird sehr geschätzt.

  • Wenn ich dieses Recht, Sie haben das Programm Bin läuft das Programm B läuft das Programm C. Programm A läuft auch Programm C. Ist das richtig?
  • Nein, Ein Programm nicht ausführen C-Programm direkt. Es wäre toll, wenn Sie warf einen Blick auf angehängte Beispiel. Danke.
  • Also die zweite Popen ist von Programm B?
  • Ja. Einer läuft nur B und hat nichts zu tun mit C direkt.
  • Funktioniert die Versorgung close_fds=True zum zweiten Popen() Hilfe rufen? Ich vermute, dass C erbt die stdout/stderr-Rohre von A und somit Eine wartet, bis C schließt Sie.
  • Ich habe versucht, mit close_fds=True im zweiten Programm (B) und es scheint nicht, einen Unterschied zu machen. Könnten Sie bitte den beigefügten Beispiel-und sehen, ob etwas getan werden kann?

InformationsquelleAutor khattam | 2012-11-06

2 Kommentare

  1. 18

    Können Sie start_new_session analog für die C Teilprozess:

    #!/usr/bin/env python
    import os
    import sys
    import platform
    from subprocess import Popen, PIPE
    
    # set system/version dependent "start_new_session" analogs
    kwargs = {}
    if platform.system() == 'Windows':
        # from msdn [1]
        CREATE_NEW_PROCESS_GROUP = 0x00000200  # note: could get it from subprocess
        DETACHED_PROCESS = 0x00000008          # 0x8 | 0x200 == 0x208
        kwargs.update(creationflags=DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP)  
    elif sys.version_info < (3, 2):  # assume posix
        kwargs.update(preexec_fn=os.setsid)
    else:  # Python 3.2+ and Unix
        kwargs.update(start_new_session=True)
    
    p = Popen(["C"], stdin=PIPE, stdout=PIPE, stderr=PIPE, **kwargs)
    assert not p.poll()

    [1]: Prozess-Erstellung Flags für die Funktion CreateProcess()

    • Erfolgreich eingesetzt wird dieser code zum erstellen der Prozess-Gruppe (aber ohne die DETACHED_PROCESS flag) und psutil zu töten, die Prozess-Struktur (dh. Gruppe): stackoverflow.com/questions/1230669/…
    • Das funktioniert nur bei mir auf windows hätte ich close_fds=True dem Windows kwargs Linie: kwargs.update(creationflags=DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP, close_fds=True) (siehe die Antwort)
    • haben Sie bemerkt, dass es sagt, dass Sie nicht verwenden close_fds=True unter Windows, wenn Sie umleiten nicht von stdin,stdout, stderr?
    • Ja, ich interpretierte das docs bedeuten, dass, wenn Sie close_fds=True stdin, stdout und stderr wird nicht funktionieren (obwohl die Formulierung ist ein bisschen zweideutig, was passiert, wenn Sie tatsächlich tun Sie verwenden). Dein snippet funktioniert bei mir sehr gut nach Zugabe von close_fds, aber es wäre wahrscheinlich sicherer, weglassen stdin/stdout/stderr, wenn die Plattform Windows.
    • die Absicht der code ist std*=DEVNULL D. H., Sie können nicht auslassen, stdin/stdout/stderr hier (zumindest auf POSIX — close_fds=True nicht zu schließen, std* es). Sie brauchen nicht close_fds=True es sei denn, Sie haben andere (nicht-standard) Dateideskriptoren geöffnet wurde (entweder schließen oder übergeben close_fds=True — default auf Python 3 auf POSIX). Unter Windows close_fds=True ist genug (ohne Umleitung), so dass der Kind-Prozess wird nicht Erben, Elternteils Datei-Deskriptoren (habe ich noch nicht getestet, was passiert, wenn Sie nach stdout schreiben in diesem Fall oder pass std*=DEVNULL und close_fds=True zusammen auf Windows).
    • Die Kombination DETACHED_PROCESS und CREATE_NEW_PROCESS_GROUP macht keinen unmittelbaren Sinn. Jeder Prozess ist in einer Gruppe in Windows, auch wenn es nur die Gruppe für die winlogon.exe oder wininit.exe der Sitzung, sondern die nur API, die verwendet Gruppen ist GenerateConsoleCtrlEvent für das senden von Strg+Pause (oder Strg+C, wenn der Prozess manuell aktiviert werden, da eine neue Gruppe erstellen, zunächst ist es deaktiviert), um eine Gruppe von Prozessen, die an einer aktuellen Konsole. Die Injektion der Kontroll-thread wird koordiniert zwischen der Konsole Instanz (conhost.exe) und der Windows-Sitzung-server (csrss.exe).
    • Das entspricht einem Unix-Prozess-Struktur in Windows ist ein Job-Objekt. Der Prozess begonnen werden soll suspendiert (Schaffung Flagge CREATE_SUSPENDED) um sicherzustellen, dass es Hinzugefügt, um den job, bevor Sie laichen können andere Prozesse, die austreten den job. Dies funktioniert am besten in Windows 8+, da es unterstützt verschachtelte Aufträge, in der Erwägung, dass ältere Versionen erlauben nur einen einzigen Auftrag pro Prozess.

  2. 0

    Hier ist ein code-snippet angepasst von Sebastian ‚ s Antwort und diese Antwort:

    #!/usr/bin/env python
    import os
    import sys
    import platform
    from subprocess import Popen, PIPE
    
    # set system/version dependent "start_new_session" analogs
    kwargs = {}
    if platform.system() == 'Windows':
        # from msdn [1]
        CREATE_NEW_PROCESS_GROUP = 0x00000200  # note: could get it from subprocess
        DETACHED_PROCESS = 0x00000008          # 0x8 | 0x200 == 0x208
        kwargs.update(creationflags=DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP, close_fds=True)  
    elif sys.version_info < (3, 2):  # assume posix
        kwargs.update(preexec_fn=os.setsid)
    else:  # Python 3.2+ and Unix
        kwargs.update(start_new_session=True)
    
    p = Popen(["C"], stdin=PIPE, stdout=PIPE, stderr=PIPE, **kwargs)
    assert not p.poll()

    Habe ich nur getestet es persönlich auf Windows.

Kostenlose Online-Tests