Was nach dem Tod des Elternprozesses übrig bleibt — Kindprozesse in einem Job Object halten

· Aktualisiert am: · · Windows, Windows-Entwicklung, C#, C++, Win32-API, Messtechnik und Steuerung

Änderungsverlauf (Erstfassung, veröffentlicht am 29. Aug 2026)
Erstveröffentlichung

Sie haben die UI im Task-Manager beendet, und trotzdem lässt sich die Kamera nicht erneut öffnen. Die Überwachungs-App ist weg, und doch hält der Helfer des SDK noch den COM-Port. Ein Neustart des Elternprozesses erzeugt eine zweite Instanz, und in Shared Memory und Named Pipes treten Probleme auf. Sobald Sie ein Geräte-SDK in einen eigenen Prozess auslagern, stoßen Sie auf diese Ausfälle „nach dem Tod des Elternprozesses“.

Der Ausgangspunkt, um die Ursache zu denken, ist: Unter Windows enden Kind- und Enkelprozesse nicht automatisch, wenn der Elternprozess endet. Die Eltern-Kind-Beziehung beim Start und die Lebensdauerverwaltung beim Ende sind zwei verschiedene Dinge. Das Werkzeug, das diese Lücke füllt, ist das Job Object.

Dieser Artikel bestätigt zuerst, warum die Beendigungsbehandlung des Elternprozesses allein nicht reicht, und ordnet dann, wie man Prozesse in einen Job nimmt, die Beendigungspolitik und die Überwachung. Am Ende verbindet er das mit den Ausfällen in der Geräteanbindung und mit dem Untersuchungsverfahren.

Zielgruppe sind WinForms-/WPF-/Dienstentwickler, die ein Geräte-SDK in einen eigenen Prozess auslagern. Die Voraussetzung ist Windows 10/11 (für die Stellen, die Nested Jobs und PROC_THREAD_ATTRIBUTE_JOB_LIST nutzen), und der Code wird in C++ (Win32-API) und C# (.NET 6 oder höher) gezeigt. Die Schwierigkeit ist Mittelstufe.

Dieser Artikel setzt die Artikel „Keine Rückmeldung“, Herunterfahren, Schlaf/Fortsetzen und Named Pipes fort und behandelt die Lebensdauer außerhalb des Prozesses.

1. Zuerst das Fazit

Der Ausgangspunkt des Entwurfs ist, „was nicht zurückbleiben darf und was Sie behalten wollen“ festzulegen, bevor Sie festlegen, „wie beendet wird“.

Ein Job Object ist ein Mechanismus, der einen Prozessbaum zu einer Einheit macht. Allerdings gehen das zwangsweise Beenden der Nachfahren im Moment, in dem der Elternprozess verschwindet, und das Behalten von Abbildern oder Endzustand dieser Nachfahren so, wie sie stehen, nicht zusammen. Entscheiden Sie, ob Sie das vom Prozess gehaltene Gerät zurückholen oder zuerst Diagnosematerial sichern.

  • Die Einheit, die die Lebensdauer verwaltet, ist der Job, nicht die Eltern-Kind-Linie. Er hängt einer Gruppe von Prozessen Beschränkungen, Benachrichtigungen und Sammelbeendigung an. Einmal zugeordnet, kann ein Prozess nicht mehr austreten, bis er endet, und ab Windows 8 können Jobs Nested Jobs sein.12
  • Legen Sie die Job-Zugehörigkeit fest, bevor das Kind läuft. Wenn Sie nach dem Start Assign aufrufen, verfehlen Sie die dazwischen geborenen Enkel. CREATE_SUSPENDED und JOB_LIST bei der Erzeugung schließen unterschiedliche Wettlauffenster (Kapitel 4).
  • Automatisches Einsammeln und das Bewahren von Diagnoseinformationen werden als Beendigungspolitik gewählt. Die Zündbedingung von KillOnJobClose ist „das letzte Job-Handle schließt“. Es ist stark gegen einen Elternabsturz, aber schwach für die Nachanalyse der Nachfahren, die mit ihm zwangsbeendet werden; brauchen Sie beides, sammelt die Überwachungsseite zuerst und beendet danach (Kapitel 5).3

Was eine Messanwendung will, ist nicht das Töten selbst. Es ist keinen Prozess zurückzulassen, der das Gerät hält, und anormale Enden beobachten zu können.

Was Sie wissen wollen Zu lesende Kapitel
Warum die Beendigungsbehandlung des Elternprozesses allein nicht reicht Kapitel 2–3: die Eltern-Kind-Beziehung und die Rolle des Jobs
Wie man Nachfahren verwaltet, ohne welche zu verfehlen Kapitel 4–6: Erzeugung, Beendigungspolitik, Überwachung
Was bei SDKs und Diensten zum Problem wird Kapitel 7–9: Ausfälle, Ressourcengrenzen, Nested Jobs und Breakaway
Was Sie vor Ort untersuchen und wie Sie wählen Kapitel 10–11: Untersuchungsverfahren und Entscheidungstabelle

Die Wissenskarte darunter dient dazu, die Beziehungen der Elemente erneut zu überblicken. Wenn Sie lieber beim Mechanismus beginnen, fahren Sie mit Kapitel 2 fort.

In der Abbildung kennzeichnet eine durchgezogene Linie eine stets geltende Beziehung und eine gestrichelte Linie eine bedingte (die Bedingungen stehen bei jeder Beziehung auf der Detailseite). Die vollständige Liste der Beziehungen (21 insgesamt, mit Beleg und Sicherheitsgrad) und die Definitionen der wichtigsten Konzepte sind auf der Detailseite der Wissenskarte (auf Japanisch) zusammengestellt. Daten: JSON-LD / Turtle

2. Warum WaitForExit nicht reicht

Auf ein Ende „warten“ und Lebensdauern aneinanderbinden sind verschiedene Dinge

Process.WaitForExit() ist eine API dafür, dass „der Elternprozess auf das Ende des Kindes wartet“. Was dieser Artikel betrachtet, ist die Gegenrichtung: was mit dem Kind zu tun ist, wenn der Elternprozess zuerst stirbt. Mit der Windows-Eltern-Kind-Beziehung allein wird das Ende des Elternprozesses nicht an das Kind weitergegeben.

Process.Kill() und CloseMainWindow() kümmern sich für sich genommen auch nicht um Enkelprozesse oder die Gerätehandles, die diese Enkel halten. Die Handles des Prozesses, der endete, werden freigegeben, aber das Problem sind die Handles, die die überlebenden Nachfahren halten.

.NETs Kill(entireProcessTree: true) läuft die Nachfahren ab und beendet sie, kann aber Prozesse verfehlen, die während der Aufzählung oder nachdem der Elternprozess zuerst starb erzeugt wurden, und es wird nie aufgerufen, sobald der Elternprozess selbst abgestürzt ist. Sie brauchen einen Mechanismus, der nicht allein vom Aufräumcode des Elternprozesses abhängt.

Für jeden der Hauptgründe, aus denen ein Elternprozess endet, hier, was vor Ort zurückbleibt.

Warum der Elternprozess stirbt Was dem Kind geschieht Was vor Ort bleibt
UI mit der Schaltfläche X geschlossen, Beendigungsbehandlung unvollständig Nichts (das Process-Objekt wird lediglich verworfen) Helferprozess, Kamerasperre
Nur der Elternprozess im Task-Manager beendet Das Kind lebt weiter COM-Port, USB, Shared Memory
Absturz durch eine unbehandelte Ausnahme Keine Garantie, dass das finally des Elternprozesses läuft Temporäre Dateien, exklusive Sperren
Timeout beim Dienststopp Der SCM kümmert sich nur um den Elternprozess Kinder, die in Sitzung 0 zurückbleiben
Die Lücke zwischen der Lebensdauer des Elternprozesses und der Belegung des GerätsWenn der Elternprozess endet, verschwinden sein Warten und sein Process-Objekt, aber Kind- und Enkelprozesse leben weiter, und ihr Halten von Gerätehandles, Named Pipes und Sperrdateien bleibt ebenfallsElternprozess endetWeg: Warten des Elternprozesses, Process-ObjektZurück: Kind- und EnkelprozesseHalten von GerätehandlesServerseite von Named PipesSperrdateien, Shared Memory

Abbildung 1: Die Lebensdauer des Elternprozesses und die Belegungslebensdauer des Geräts sind versetzt. Der Aufräumcode des Elternprozesses läuft am wenigsten gerade dann, wenn der Elternprozess einen anormalen Tod stirbt.

Das Ziel ist der Fall „das Betriebssystem läuft, und nur der Elternprozess endete“

Die Abläufe, in denen Herunterfahren oder Schlaf das ganze Betriebssystem anhält, gehören zum Artikel über das Herunterfahren und zum Artikel über Schlaf/Fortsetzen. Dieser Artikel behandelt den Fall, in dem das Betriebssystem gesund bleibt und nur der Elternprozess endet. In Messumgebungen ist das der häufigere Fall, und die zurückbleibenden Prozesse sind schwerer zu bemerken.

3. Was ein Job Object ist

Die Grundoperationen sind Erzeugen, Zuordnen, Setzen und Abfragen

Ein Job Object ist ein Kernelobjekt, das eine Gruppe von Prozessen als eine Einheit verwaltet. Teilt man die Grundoperationen nach Rolle, ergeben sich die folgenden vier.14

API Rolle
CreateJobObject Einen Job erzeugen, dem noch kein Prozess angehört
AssignProcessToJobObject Einen Prozess dem Job zuordnen
SetInformationJobObject Grenzen und andere Einstellungen setzen
QueryInformationJobObject Abrechnungsinformationen wie CPU-Zeit, Seitenfehler und Prozesszahl lesen

Die Zugehörigkeit ist unumkehrbar; ein Prozess kann nicht austreten, bis er endet. Außerdem enthalten die Abrechnungsinformationen Summen, die von bereits beendeten Prozessen aufgelaufen sind.

Ein Kind, das ein Mitgliedsprozess mit CreateProcess erzeugt, gehört standardmäßig zum selben Job. Mit anderen Worten: Der Kern des Werts eines Jobs ist, dass Enkel und Urenkel automatisch eintreten.1 Die Pfade, die der Zugehörigkeit entgehen, etwa Breakaway und Stellvertreterstarts über WMI, werden in Kapitel 9 getrennt untersucht.

Grundstruktur eines Job ObjectsEin Kind und ein Enkel gehören zu dem Job Object, das der Elternprozess erzeugt hat, und der Job erzwingt Grenzen, sendet Benachrichtigungen an einen Abschlussport und führt Sammelbeendigung je Prozessbaum ausElternprozessJob ObjectKind (Geräte-SDK-Host)Enkel (Herstellerhelfer)Grenzen (Speicher, CPU)Benachrichtigungen (Abschlussport)Sammelbeendigung

Abbildung 2: Ein Job ist ein Behälter, der drei Dinge je Prozessbaum bereitstellt: Grenzen, Benachrichtigungen und Sammelbeendigung.

Unterschiede der Betriebssystemgenerationen, und wie es sich von einer Sandbox unterscheidet

Unter Windows 7 und früher konnte ein Prozess nur zu einem Job gehören; ab Windows 8 wurden Nested Jobs (mehrfache Zugehörigkeit) möglich.5 Der Haupttext dieses Artikels setzt Windows 10/11 voraus; die Punkte, auf die unter Windows 7 und früher zu achten ist, stehen in Kapitel 9 und in den FAQ.

Einen Prozess in einen Job zu stecken macht daraus keinen Container und keine Sandbox. Netzwerkzugriff lässt sich nicht beschränken, und das Zugriffstoken (die Rechte) ist ein eigener Mechanismus. UI-Beschränkungen allein können auch keine Sicherheitsgrenze erzeugen. Die Rolle in diesem Artikel ist strikt Lebensdauer und Ressourcen eines Prozessbaums zu einer Einheit zu machen.

4. Der richtige Einstieg — Das Rennen zwischen Erzeugung und Zuordnung

Wenn Sie nach dem Start Assign aufrufen, werden dazwischen Enkel geboren

Ein Kindprozess kann in den ersten Millisekunden, nachdem er zu laufen beginnt, Enkel erzeugen. Ein SDK, das seinen Helfer startet, ist der typische Fall. Holen Sie die PID nach Process.Start() und rufen dann Assign auf, landet jeder Enkel, der vor dem Assign geboren wurde, außerhalb des Jobs.

Nach dem Loslaufen zuzuordnen verfehlt EnkelDas Kind läuft direkt nach Process.Start bereits, und jeder Enkel, den es im Wettlauffenster vor dem Aufruf von AssignProcessToJobObject erzeugt, landet außerhalb des JobsKind läuft bei Process.Start anWettlauffenster bis AssignIn diesem Fenster geborene EnkelLaufen außerhalb des Jobs weiterAssignProcessToJobObjectNur danach geborene Enkel kommen hinein

Abbildung 3: Das Wettlauffenster mag nur wenige Millisekunden dauern, aber der Start des SDK-Helfers geschieht genau dort.

Verfahren A: Angehalten erzeugen, zuordnen, dann laufen lassen

Die klassische Methode mit der breitesten Kompatibilität nutzt CREATE_SUSPENDED. Sie schließt das Fenster, in dem das Kind zuerst läuft und Enkel erzeugt, mit der folgenden Reihenfolge.67

  1. Den Job mit CreateJobObject erzeugen
  2. Die Grenzen zuerst mit SetInformationJobObject setzen
  3. CreateProcess mit CREATE_SUSPENDED aufrufen (der Anfangsthread läuft nicht)
  4. Mit AssignProcessToJobObject hineinnehmen
  5. Schlägt das fehl, kein Resume; sofort TerminateProcess (keine einzige Anweisung außerhalb des Jobs laufen lassen)
  6. Mit ResumeThread laufen lassen

Was Verfahren A schließt, ist das Wettlauffenster gegen die Enkelerzeugung. Das Fenster gegen einen Absturz des Elternprozesses selbst bleibt. Stürzt der Elternprozess zwischen Schritt 3 und 4 ab, bleibt ein angehaltener Kindprozess zurück, der noch nicht im Job ist. Er läuft nicht, verschwindet aber auch nicht von selbst.

Wenn Sie dieses Fenster einschließlich der Widerstandsfähigkeit gegen einen Elternabsturz schließen wollen, nutzen Sie Verfahren B darunter.

Verfahren, mit SUSPENDED zu starten und das Kind dann in den Job zu nehmenDen Job erzeugen und Grenzen setzen, das Kind mit CREATE_SUSPENDED starten, mit AssignProcessToJobObject zuordnen, bei Fehlschlag ohne Resume mit TerminateProcess stoppen, und bei Erfolg mit ResumeThread laufen lassenErfolgFehlerJob mit CreateJobObject erzeugenGrenzen mit SetInformationJobObjectKind mit CREATE_SUSPENDED startenAssignProcessToJobObjectMit ResumeThread laufen lassenSofort beenden, kein Resume

Abbildung 4: Das Gerüst von Verfahren A. Eine Umsetzung, die den Zweig „wenn es fehlschlägt, nicht laufen lassen“ weglässt, erzeugt nur dann einen verwaisten Prozess, wenn etwas schiefgeht.

// C++: der minimale Kern von Verfahren A (Fehlerbehandlung nur als Gerüst)
HANDLE job = CreateJobObjectW(nullptr, nullptr);   // Unbenannt ist in Ordnung. Nicht vererbbar machen
if (!job) return HRESULT_FROM_WIN32(GetLastError());

JOBOBJECT_EXTENDED_LIMIT_INFORMATION limits = {};
limits.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
if (!SetInformationJobObject(job, JobObjectExtendedLimitInformation,
                             &limits, sizeof(limits))) {
    DWORD err = GetLastError();         // Sichern, bevor CloseHandle es überschreibt
    CloseHandle(job);                   // Das Kind nicht in einem Job ohne Grenzen laufen lassen
    return HRESULT_FROM_WIN32(err);
}

STARTUPINFOW si = { sizeof(si) };
PROCESS_INFORMATION pi = {};
if (!CreateProcessW(exePath, cmdline, nullptr, nullptr, FALSE,
                    CREATE_SUSPENDED, nullptr, nullptr, &si, &pi)) {
    DWORD err = GetLastError();
    CloseHandle(job);                   // Das Job-Handle bei Startwiederholungen nicht leaken
    return HRESULT_FROM_WIN32(err);
}

if (!AssignProcessToJobObject(job, pi.hProcess)) {
    DWORD err = GetLastError();         // Sichern, bevor Terminate es überschreibt
    TerminateProcess(pi.hProcess, 1);   // Nicht außerhalb des Jobs laufen lassen
    // Handles schließen und err als Fehler weiterreichen
} else if (ResumeThread(pi.hThread) == (DWORD)-1) {
    DWORD err = GetLastError();
    TerminateProcess(pi.hProcess, 1);   // Nicht angehalten zurücklassen
    // Handles schließen und err als Fehler weiterreichen
}
CloseHandle(pi.hThread);
// Bei Erfolg geht der Besitz von job und pi.hProcess an das Lebensdauer-
// verwaltungsobjekt des Aufrufers (das Gegenstück zum C#-Wrapper in Kapitel 4).
// job zu schließen zündet KillOnJobClose, und pi.hProcess dient in Kapitel 6
// dazu, festzustellen, „wer starb“

Verfahren B: Unter Windows 10 und später den Prozess bereits im Job erzeugen

Legen Sie das Job-Handle mit PROC_THREAD_ATTRIBUTE_JOB_LIST auf die Attributliste von STARTUPINFOEX. Übergeben Sie sie dann an CreateProcess mit dem Flag EXTENDED_STARTUPINFO_PRESENT. Ohne das Flag wird die Struktur nicht als erweiterte interpretiert, und die Attribute werden ignoriert.89

Mit dieser Methode gehört der Prozess zum Job, bevor sein Anfangsthread läuft. Das Wettlauffenster „erzeugt, aber noch kein Mitglied“ existiert gar nicht mehr, sodass weder SUSPENDED noch der Zweig, der nach der Erzeugung Assign aufruft und den Fehlschlag behandelt, nötig ist.

Methode Wann die Zugehörigkeit festliegt Verbleibende Hinweise
Verfahren A: SUSPENDED → Assign → Resume Nach der Erzeugung des Kindes, bevor sein Anfangsthread läuft Stürzt der Elternprozess vor dem Assign ab, bleibt ein angehaltener Kindprozess zurück
Verfahren B: mit JOB_LIST erzeugen Bei der Prozesserzeugung Erfordert Windows 10 oder später. Attributliste und erweitertes Startflag angeben
Wie sich die Wettlauffenster von Verfahren A und Verfahren B unterscheidenVerfahren A erzeugt den Prozess angehalten und ruft dann Assign und Resume auf, braucht also einen Zweig, der bei Fehlschlag beendet; Verfahren B erzeugt den Prozess mit dem Job auf der Attributliste, ist also im Moment der Geburt bereits Mitglied und hat weder Wettlauffenster noch FehlerzweigVerfahren A: angehalten erzeugenMit Assign beitretenMit Resume startenFehlerzweig erforderlichVerfahren B: mit Attributliste erzeugenMitglied im Moment der GeburtKein Wettlauffenster, kein Fehlerzweig

Abbildung 5: Verfahren A ist „hineinnehmen, dann laufen lassen“; Verfahren B ist „bereits drinnen geboren“. Wenn Sie Windows 10 oder später voraussetzen können, ist der Grund zur Wahl genau das Vorhandensein oder Fehlen des Wettlauffensters und des Fehlerzweigs.

Drei Umsetzungen, die Sie in beiden Verfahren vermeiden

  • Die PID nach Process.Start() holen und dann hineinnehmen (die Enkel kommen zuerst raus)
  • Das Job-Handle vom Kind erben lassen (das Kind hält das Handle auch nach dem Tod des Elternprozesses weiter, KillOnJobClose zündet also nicht mehr — Kapitel 5)
  • Einen Assign-Fehlschlag schlucken und den Betrieb fortsetzen (ein Geräteprozess außerhalb des Jobs treibt den nächsten Ausfall)

In .NET den Besitzer des Job-Handles im Code ausdrücken

System.Diagnostics.Process hat keinen Begriff eines Jobs, und es gibt keinen offiziellen Wrapper. Schreiben Sie einen dünnen Wrapper mit P/Invoke oder CsWin32.

Der Punkt ist, das Job-Handle in ein SafeHandle zu packen und IDisposable zu machen. Das letzte Job-Handle in Dispose() zu schließen zündet KillOnJobClose. Die Entwurfsabsicht „die Lebensdauer des Wrappers ist die Lebensdauer des Kindbaums“ lässt sich als Besitz ausdrücken.

Darunter ein Gerüst, das den Handle-Besitz zeigt. Die Erzeugung geschieht auf der P/Invoke-Seite von Verfahren A/B, und ein echtes Projekt braucht außerdem CsWin32-Konfiguration, unsafe-Angaben und so weiter.

// C#: ein dünner Wrapper, der nur den Besitz des Job-Handles trägt (Erzeugung über P/Invoke von Verfahren A/B)
sealed class ChildProcessJob : IDisposable
{
    private readonly SafeFileHandle _job;    // Im Feld halten bleiben

    public ChildProcessJob()
    {
        _job = PInvoke.CreateJobObject(default, null);
        if (_job.IsInvalid) throw new Win32Exception();
        var limits = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION();
        limits.BasicLimitInformation.LimitFlags =
            JOB_OBJECT_LIMIT.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
        if (!PInvoke.SetInformationJobObject(_job,
            JOBOBJECTINFOCLASS.JobObjectExtendedLimitInformation,
            &limits, (uint)sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)))
        {
            int err = Marshal.GetLastWin32Error();  // Vor Dispose sichern
            _job.Dispose();              // Keinen Job ohne KillOnJobClose herausgeben
            throw new Win32Exception(err);
        }
    }

    public void Dispose() => _job.Dispose();  // Der Baum darunter endet hier
}

Umgekehrt beenden Sie den Kindbaum, wenn Sie das Handle schließen, ohne es schließen zu wollen. Halten Sie eine Referenz auf den Wrapper über die gesamte Lebensdauer des Elternprozesses. Tun Sie das nicht, wird der Kindbaum grundlos ausgelöscht, sobald der GC das SafeHandle einsammelt.

5. KillOnJobClose — „Halten“ und „zusammen sterben“

Der Auslöser ist „das letzte Handle schließt“, nicht „der Tod des Elternprozesses“

JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE ist ein Begrenzungsflag, das jeden Prozess unter dem Job beendet, wenn das letzte Job-Handle geschlossen wird.3

Ob der Elternprozess an einer Ausnahme endet, im Task-Manager beendet oder als Dienst zwangsgestoppt wird: Der Kernel schließt alle Handles dieses Prozesses. War das das letzte Job-Handle, enden Kinder und Enkel. Die Stärke dieses Mechanismus ist, dass er nicht voraussetzt, dass die Beendigungsbehandlung des Elternprozesses läuft.10

Allerdings bleibt das Handle nach dem Ende des Elternprozesses, wenn Sie das Job-Handle vom Kind erben lassen. Dann ist es nicht „das letzte Handle“, und der Kindbaum endet nicht.

Zeitlinie von KillOnJobCloseOb der Elternprozess normal endet, abstürzt oder zwangsbeendet wird, schließt der Kernel alle Handles des Elternprozesses, und war das das letzte Job-Handle, schließt der Job und der Prozessbaum darunter wird auf einmal beendetJaNein (geerbt)Elternprozess verschwindet (einschließlich Absturz)Kernel schließt alle HandlesLetztes Job-Handle?Den ganzen Baum darunter beendenKinder leben weiter

Abbildung 6: Der Auslöser ist „das letzte Handle wurde geschlossen“, nicht „der Elternprozess starb“. Deshalb darf das Handle nicht vom Kind geerbt werden.

Zwischen sofortigem Einsammeln und dem Bewahren von Diagnosematerial wählen

Was Sie bei Zwangsbeendigung verlieren, ist nicht nur das Halten des Geräts. Sie verlieren auch die Chance, ein Absturzabbild der Kinder und Enkel zu nehmen, die mit dem Elternprozess beendet werden, den letzten gültigen Frame und die Chance, eine halb geschriebene Messdatei zu leeren.

Das Absturzabbild des Elternprozesses selbst ist eine andere Sache. WER behandelt die unbehandelte Ausnahme, während der Elternprozess noch lebt, das Abbild des Elternprozesses kann also geschrieben werden, bevor die Handles geschlossen werden. Worüber es hier geht, ist das Nachanalysematerial der Kinder und Enkel auf der Seite, die zwangsbeendet wird.

Politik Geeignet für Was Sie verlieren
Mit KillOnJobClose Standorte, an denen ein doppeltes Öffnen des Geräts das schlimmste Ergebnis ist Nachanalysematerial, die letzten Samples
Ohne (nur Überwachung) Standorte, an denen Abbilder und Protokolle Vermögenswerte sind Verwaiste Prozesse und gehaltene Ports bei Vernachlässigung
Ohne, plus ein Überwachungsprozess, der TerminateJobObject aufruft Wenn ein eigener Steuerdienst existiert Die Umsetzung ist verdoppelt

Als Eingabe für die Entscheidung listen Sie zuerst „was nicht zurückbleiben darf“ und „was Sie behalten wollen“.

Was nicht zurückbleiben darf: eine offene Kamera oder ein Digitizer, exklusive Nutzung eines seriellen oder USB-Ports, die Serverseite einer Named Pipe, eine Lizenzdongle-Sitzung, Shared Memory und Sperrdateien.

Was Sie behalten wollen: Absturzabbilder, den letzten gültigen Frame oder Zähler, die Chance, einen Befehl zu senden, der das Gerät in einen sicheren Zustand zurücksetzt (wenn möglich, vor dem Töten senden).

Töten, oder nur überwachen?Ist ein doppeltes Öffnen des Geräts das schlimmste Ergebnis, KillOnJobClose setzen; sind Absturzabbilder und der letzte Frame Vermögenswerte, nicht setzen und stattdessen überwachen; existiert ein eigener Steuerdienst, TerminateJobObject von dort aufrufenDas Freigeben des Geräts kommt zuerstAbbilder und Endzustand sind VermögenswerteEin eigener Steuerdienst existiertWas schützen Sie im Moment des Sterbens?Mit KillOnJobCloseNur überwachen (nicht töten)Überwachungsseite ruft TerminateJobObject

Abbildung 7: Wählen Sie nach „was schützen Sie im Moment des Sterbens“, nicht nach „töten oder nicht“. Wollen Sie beides, landen Sie bei der Anordnung der dritten Zeile, in der die Überwachungsseite zuerst das Abbild nimmt und dann den Baum abbaut.

Das Job-Handle der Überwachungsseite übergeben, solange der Elternprozess lebt

Die dritte Zeile der Tabelle, die Anordnung, in der ein Überwachungsprozess den Baum mit TerminateJobObject beendet, braucht Vorbereitung. Die Überwachungsseite muss das Job-Handle holen, bevor der Elternprozess stirbt.

Der Job, den das Verfahren dieses Artikels erzeugt, ist unbenannt, es gibt also keinen Weg, ihn von außen zu erreichen, sobald der Elternprozess weg ist. Es gibt zwei Wege, ihn zu übergeben.

Methode Was zu tun ist, solange der Elternprozess lebt
Das Handle des unbenannten Jobs duplizieren Das Handle mit DuplicateHandle an den Überwachungsprozess übergeben
Einen benannten Job verwenden Von Anfang an benannt erzeugen; die Überwachungsseite öffnet ihn mit OpenJobObject und hält ihn

Weil Namen global kollidieren können, nehmen Sie eine eindeutige GUID oder ähnliches auf. Vergessen Sie diese Vorbereitung, hat die Überwachungsseite kein Mittel, den Baum zu beenden, selbst wenn sie die Anomalie des Elternprozesses erkennt.

Abbilder nehmen und das Gerät sichern, bevor zwangsbeendet wird

Ein durch KillOnJobClose beendetes Kind bekommt keine Vorwarnung, genau wie bei TerminateProcess. Es tritt keine unbehandelte Ausnahme auf, selbst wenn WER (LocalDumps) am Kind konfiguriert ist, bleibt also kein Abbild dieser Zwangsbeendigung. Was WER auffangen kann, ist der Fall, in dem das Kind wegen seines eigenen Absturzes endet.

Ist die Anforderung „sowohl automatisches Einsammeln als auch Abbilder“, verlagern Sie die Verantwortung für das Beenden auf die Überwachungsseite. Die Reihenfolge ist: das Abbild nehmen, solange das Ziel lebt, das Gerät bei Bedarf in einen sicheren Zustand zurücksetzen, und zuletzt mit TerminateJobObject beenden.

So abbauen, dass automatisches Einsammeln und Abbilder zusammenbestehenErkennt der Überwachungsprozess eine Anomalie, nimmt er zuerst ein Abbild, sendet bei Bedarf einen Befehl, der das Gerät in einen sicheren Zustand zurücksetzt, und baut den Baum zuletzt mit TerminateJobObject ab, sodass automatisches Einsammeln und Nachanalyse zusammenbestehenÜberwachungsseite erkennt eine AnomalieZuerst das Abbild nehmenDas Gerät in einen sicheren Zustand zurücksetzenMit TerminateJobObject abbauen

Abbildung 8: Die einzige Antwort auf „sowohl automatisches Einsammeln als auch Abbilder“. Drehen Sie die Reihenfolge um, und das Ziel zum Abbilden existiert nicht mehr.

In einer Anordnung, in der KillOnJobClose das Kind im Moment, in dem der Elternprozess verschwindet, zwangsbeendet, hat das Kind auch keine Chance, einen Befehl „Gerät in einen sicheren Zustand zurücksetzen“ zu senden. Für Geräte, die das brauchen, wählen Sie die Anordnung der dritten Zeile, und lassen Sie die Überwachungsseite den Sicherungsbefehl zuerst senden und dann TerminateJobObject aufrufen.11

6. Mit einem Abschlussport auf „leer“ warten

Allein auf das Job-Handle zu warten bestätigt nicht, dass der Baum geendet hat

Das Job-Handle wird nicht signalisiert, wenn alle Prozesse darunter geendet haben. Es wird nur signalisiert, wenn alle Prozesse beendet wurden, weil die Jobzeitgrenze überschritten wurde.12

Um „weiterzugehen, sobald der Kindbaum leer ist“, ordnen Sie dem Job einen I/O-Abschlussport (IOCP) zu.1314 Nehmen Sie die Zuordnung vor, während der Job leer ist, bevor Sie irgendeinen Prozess hineinnehmen. Ordnen Sie mittendrin zu, können Sie die Benachrichtigungen von Prozessen verfehlen, deren Zustand sich während der Zuordnung änderte.15

Erzeugung, Ende, Anomalie und Null mit vier Nachrichten beobachten

Nachricht Was sie Ihnen sagt
JOB_OBJECT_MSG_NEW_PROCESS Ein Prozess ist dem Job beigetreten. Erkennt auch die Erzeugung von Enkeln
JOB_OBJECT_MSG_EXIT_PROCESS Ein Prozess ist geendet
JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS Ein Prozess ist mit einem anormalen Exitcode wie einer Zugriffsverletzung geendet. Besonders wichtig in Messanwendungen13
JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO Die Zahl aktiver Prozesse hat 0 erreicht

Ein NEW_PROCESS-Paket trägt nur die neue PID. Es sagt Ihnen nicht die Eltern-Kind-Beziehung, also wer den Prozess erzeugt hat. Brauchen Sie die Linie, fügen Sie ein anderes Mittel wie ETW hinzu.

Ablauf der Abschlussport-BenachrichtigungenDer Job stellt Erzeugung, Ende, anormales Ende und Null-Nachrichten an den Abschlussport, ein eigener Überwachungsthread empfängt sie mit GetQueuedCompletionStatus, und nur die Ergebnisse gehen an den UI-ThreadUI-ThreadÜberwachungsthreadAbschlussportJob ObjectUI-ThreadÜberwachungsthreadAbschlussportJob ObjectNEW / EXIT_PROCESSABNORMAL_EXIT / ZEROAuf Abschlusspaket wartenNachricht und PIDNur die Ergebnisbenachrichtigung übergeben

Abbildung 9: Führen Sie GetQueuedCompletionStatus auf einem eigenen Thread aus. Warten Sie auf dem UI-Thread, und die UI geht bei jedem Fehlverhalten eines Kindes auf „Keine Rückmeldung“.

Auf einem eigenen Thread und einem eigenen Port warten, und bei Timeout die Abrechnung abfragen

Führen Sie diese Überwachungsschleife auf einem Abschlussport, der allein für die Benachrichtigungen des Jobs erzeugt wurde. Hängen Sie sich auf denselben Port wie bestehende E/A, ist das Paket bereits entnommen, wenn GetQueuedCompletionStatus zurückkehrt. Werfen Sie es mit continue weg, weil der Schlüssel anders ist, wartet der Besitzer dieser E/A für immer auf seinen Abschluss. Dasselbe gilt für den Zweig fehlgeschlagener Pakete.

Wenn Sie teilen, brauchen Sie einen eigenen Mechanismus, der je Schlüssel an jeden Besitzer zustellt. Dieser Artikel trennt die Ports und übergibt nur Ergebnisse an den UI-Thread.

Er setzt außerdem voraus, dass ein Job eine Startgeneration ist, bei jedem Start neu erzeugt. Wiederverwenden Sie ihn über Wiederholungen, enthält TotalProcesses die vorherige Generation, und die Wache, die einen leeren Job vor dem Start unterscheidet, funktioniert nicht mehr.

// C++: Gerüst des Überwachungsthreads (Timeout + Abrechnung als Versicherung gegen verfehlte Benachrichtigungen)
DWORD msg; ULONG_PTR key; LPOVERLAPPED info;
bool treeEmpty = false;
while (!treeEmpty) {
    if (!GetQueuedCompletionStatus(iocp, &msg, &key, &info, 5000)) {
        if (info != nullptr) continue;                // Abschlusspaket einer fehlgeschlagenen E/A. Weiter überwachen
        if (GetLastError() != WAIT_TIMEOUT) break;    // Port zerstört und Ähnliches: anhalten
        JOBOBJECT_BASIC_ACCOUNTING_INFORMATION acct = {};
        if (QueryInformationJobObject(job, JobObjectBasicAccountingInformation,
                                      &acct, sizeof(acct), nullptr))
            treeEmpty = (acct.TotalProcesses > 0 &&   // Einen leeren Job vor dem Start nicht für fertig halten
                         acct.ActiveProcesses == 0);  // Versicherung für eine verlorene ZERO-Benachrichtigung
            // Annahme: Der Job wird bei jedem Start neu erzeugt (1 Job = 1 Startgeneration).
            // Wird der Job über Wiederholungen wiederverwendet, zählt TotalProcesses noch
            // die vorherige Generation, und diese Wache kann die Generationen nicht trennen
        continue;                        // Bei Abfragefehler nicht schließen, dass er leer ist
    }
    if ((HANDLE)key != job) continue;    // Gegen den bei der Zuordnung verwendeten CompletionKey prüfen.
                                         // Dieser Port wird als allein für die Job-Überwachung erzeugt angenommen
                                         // (siehe den Text darunter. So zu verwerfen auf einem geteilten Port
                                         //  lässt die Besitzer anderer E/A für immer warten)
    DWORD pid = (DWORD)(UINT_PTR)info;   // Manche Nachrichten tragen eine PID
    switch (msg) {
    case JOB_OBJECT_MSG_NEW_PROCESS:          /* Enkelerzeugung protokollieren */ break;
    case JOB_OBJECT_MSG_EXIT_PROCESS:         /* Ende protokollieren */ break;
    case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS:/* anormales Ende: Abbild prüfen */ break;
    case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:  treeEmpty = true; break;
    }                                    // Das break des switch allein beendet das Warten nicht
}

Benachrichtigungen, Abrechnung und Handles stellen jeweils andere Informationen fest

Benachrichtigungen tragen grundsätzlich keine Zustellgarantie. Die einzigen garantierten sind die Benachrichtigungen der mit JobObjectNotificationLimitInformation gesetzten Grenzen. Sie können nicht schließen „keine Benachrichtigung = es ist nicht geschehen“.15

Aggregatzustand wie „ist er leer geworden“ wird zusätzlich durch Abfragen der Abrechnungsinformationen bestätigt. Aber Abrechnung ist ein Satz von Aggregatzählern, sie kann die PID, den Exitcode oder ob das Ende anormal war für ein verlorenes EXIT/ABNORMAL_EXIT nicht rekonstruieren. Das ist das Gebiet des Haltens von Prozesshandles und von ETW.

ACTIVE_PROCESS_ZERO ist auch kein Beleg für ein normales Ende. Die Zahl kann durch Zwangsbeendigung auf null gegangen sein, und die Nachricht selbst unterscheidet das nicht. Beurteilen Sie die Qualität des Endes anhand von EXIT/ABNORMAL_EXIT und dem Exitcode.

Entscheiden Sie nicht allein anhand der PID, dass es derselbe Prozess ist

Die PID in einem Abschlusspaket wird wiederverwendet. Solange Sie kein Prozesshandle halten, gibt es keine Garantie, dass die PID noch denselben Prozess bezeichnet.13

Was das in Kapitel 4 gehaltene pi.hProcess festnageln kann, ist nur die PID des Kindes, das Sie direkt gestartet haben. Für Enkel, die das SDK erzeugt, rufen Sie OpenProcess in dem Moment auf, in dem NEW_PROCESS ankommt, um ein Handle zu holen, und gleichen von da an gegen dieses Handle ab.

Trotzdem bleibt ein kurzes Fenster zwischen der Benachrichtigung und dem Open. Wird die PID in diesem Intervall wiederverwendet, greifen Sie einen anderen Prozess. Wenn eine strenge Identifikation des einzelnen Prozesses nötig ist, prüfen Sie mit Telemetrie, die eine Erzeugungszeit trägt, etwa ETW-Prozessstartereignisse, gegen.

Überwachung, die nicht allein auf Benachrichtigungen setztWeil Abschlussport-Nachrichten der Benachrichtigung dienen und keine Zustellgarantie tragen, kombinieren Sie sie mit dem Abfragen der Abrechnungsinformationen und dem Halten von Prozesshandles, um auf Verfehlen und PID-Wiederverwendung vorbereitet zu seinAbschlussport-BenachrichtigungenKeine Zustellgarantie (Benachrichtigungszweck)Abrechnungsinformationen ebenfalls abfragenPIDs werden wiederverwendetProzesshandles halten

Abbildung 10: Benachrichtigungen sind der Hauptweg; Abrechnung und Handles sind die Versicherung. Erst mit beidem können Sie sagen, dass Sie „beobachten“.

Übrigens kursierten einst Umsetzungen, die „den Thread, der darauf wartet, dass der Job leer wird, mit TerminateThread töten“, aber mit einem Abschlussport braucht man den Wartethread nicht zwangsweise zu beenden. Raymond Chen hat auch einen Artikel veröffentlicht, der dieses alte Muster auf den Abschlussport-Ansatz umschreibt.16

7. Ausfälle, die in Messung und Geräteanbindung tatsächlich geschehen

Hier werden die bisher behandelten Mechanismen auf sechs Ausfälle angewendet, die in Geräteumgebungen geschehen. Für jeden Fall bestätigen wir neben Ursache und Abhilfe „was zurückblieb“.

Ausfall 1: Nur der Elternprozess stirbt, und die Kamera bleibt offen

In einem Aufbau, in dem das Hersteller-SDK einen Helferprozess für die Frameübertragung hat, lässt das Beenden der Eltern-UI im Task-Manager nur den Helfer zurück. Der neu gestartete Elternprozess erhält device busy während der SDK-Initialisierung. An manchen Standorten erholt sich nichts, bis das Gerät stromlos geschaltet wird.

Was blieb: der Helferprozess und das exklusive Öffnen der Kamera.

Nur der Elternprozess stirbt und die Kamera bleibt offenDas erzwungene Beenden der UI entfernt den Elternprozess, aber der SDK-Helferprozess bleibt und hält das Kamerahandle, sodass der neu gestartete Elternprozess das erneute Öffnen mit device busy verfehltDie UI im Task-Manager beendenElternprozess verschwindetSDK-Helfer bleibtHält die Kamera nochdevice busy nach dem Neustart

Abbildung 11: Das wahre Gesicht von „der Prozess ist weg, und doch lässt sich das Gerät nicht öffnen“. Der Schuldige ist oft nicht der Prozess, dessen Name im Task-Manager stand.

Ausfall 2: Ein Enkel landet außerhalb des Jobs

Es gibt zwei Pfade, und die Abhilfen müssen getrennt werden.

(a) Das SDK verwendet einen eigenen Job. Das ist der Fall, in dem die Gegenseite bereits in einem anderen Job ist. Unter Windows 7 ist es ein Job je Prozess, Ihr Assign schlägt also fehl. Unter Windows 8 und später können Nested Jobs ihn aufnehmen, aber wenn Ihr Job UI-Beschränkungen hat, sind Nested Jobs selbst unmöglich (Kapitel 9).

(b) Das SDK erzeugt den Enkel mit CREATE_BREAKAWAY_FROM_JOB. Das funktioniert nur, wenn Ihr Job BREAKAWAY_OK erlaubt, und der Enkel wird von Anfang an außerhalb des Baums geboren. Nested Jobs können ihn auch nicht aufnehmen. Erlauben Sie es nicht, schlägt die Erzeugung des SDK fehl; hat die Überwachung Vorrang, ist der Grundansatz, es nicht zu erlauben und als Fehlschlag zu erkennen.

Was blieb: ein Enkel, der außerhalb der Überwachung läuft, und ein Assign, dessen Fehlschlag geschluckt wurde.

Zwei Pfade, auf denen ein Enkel außerhalb des Jobs landetAuf dem Pfad, auf dem das SDK einen eigenen Job verwendet, können Nested Jobs ihn unter Windows 8 und später aufnehmen, scheitern aber bei UI-Beschränkungen; auf dem Pfad, auf dem das SDK den Enkel mit Breakaway erzeugt, funktioniert es nur, wenn Sie es erlaubt haben, und der Enkel ist von Anfang an außerhalb des BaumsEnkel landet außerhalb des Jobs(a) SDK verwendet eigenen Job(b) Mit Breakaway erzeugtWin8+ nimmt ihn per Nested Job aufScheitert bei UI-BeschränkungenFunktioniert nur, wenn erlaubtEnkel von Anfang an außerhalb des Baums

Abbildung 12: Dasselbe „Rauskommen“, aber (a) lässt Raum, ihn per Nested Job aufzunehmen, während (b) in dem Moment, in dem Sie es erlauben, als nicht wiederherstellbar feststeht. Die Abhilfe beginnt mit dem Identifizieren des Pfads.

Ausfall 3: Ein Geräteprozess, der von einem Dienst gestartet wurde

Wenn ein Dienst stoppt, wartet der SCM nur auf den Elternprozess; die in Sitzung 0 erzeugten Kinder und Enkel liegen außerhalb des Umfangs der Stoppbehandlung. Job + KillOnJobClose deckt auch diesen außerhalb liegenden Bereich ab. Der Entwurf einer Anordnung, die einen Dienst und die interaktive Sitzung überspannt, bleibt dem Artikel über die Benutzergrenze überlassen.

Was blieb: zurückbleibende Prozesse in Sitzung 0, und ein Fehlalarm in der Doppelinstanzprüfung beim nächsten Start.

Ausfall 4: Ein Kind, das einen Monat später stirbt

Wenn Sie die Abrechnung des Jobs (PeakJobMemoryUsed, E/A-Zähler, Gesamtprozesszahl) nicht regelmäßig aufzeichnen, können Sie hinterher nicht nachverfolgen, „welche Generation des Helfers wann anfing zu schwellen“.17 Die Struktur, einen Monat später an einem Handle-Leak zu sterben, ist wie im Artikel über den Langzeitausfall einer Industriekamera seziert, und die Abrechnung des Jobs ist der Einstieg in diese Untersuchung.

Was blieb: Protokolle, die nicht reichen, um die Ursache festzustellen.

Ausfall 5: Die Beendigungsbehandlung des Elternprozesses wartet auf das Ende des Kindes

Wenn Sie WaitForExit aufrufen oder auf dem UI-Thread auf das Ende des Baums warten, geht der Elternprozess an dem Tag, an dem das Kind einfriert, auf „Keine Rückmeldung“. Überlassen Sie das Warten auf das Ende dem IOCP-Thread, und legen Sie auf die UI nur Fortschritt und eine Abbruchschaltfläche. Der Mechanismus ist wie im Artikel „Keine Rückmeldung“ beschrieben.

Was blieb: ein Elternprozess, der mit dem Kind hängt.

Nicht auf dem UI-Thread des Elternprozesses auf das Ende wartenAuf dem UI-Thread auf das Ende des Kindes zu warten pflanzt einen Hänger des Kindes in die Keine-Rückmeldung des Elternprozesses fort, überlassen Sie das Warten auf das Ende also dem IOCP-Überwachungsthread und legen Sie auf den UI-Thread nur eine Fortschrittsanzeige und eine AbbruchschaltflächeAuf dem UI-Thread auf das Kindesende wartenDer Tag, an dem das Kind hängtElternprozess ebenfalls Keine Rückmeldung (mitgerissen)Auf dem IOCP-Thread wartenUI hat nur Fortschritt und Abbruch

Abbildung 13: Die Seite, die die Anomalie des Kindes beobachtet, darf nicht wegen der Anomalie des Kindes einfrieren. Allein das Trennen, wo Sie warten, entfernt den mitgerissenen Hänger.

Ausfall 6: Scheitert nur unter einem Debugger

Entwicklungswerkzeuge und Starter können Ihren Elternprozess bereits in irgendeinen Job gesteckt haben. Unter Windows 8 und später retten Nested Jobs Sie meist, aber auf Geräte-PCs unter 7 oder früher wird das Assign zu ERROR_ACCESS_DENIED und erzeugt Unterschiede wie „es funktioniert nur auf der Entwicklungsmaschine nicht“ oder „nur in der Produktion“. Der übliche erste Schritt ist, die eigene Zugehörigkeit mit IsProcessInJob zu prüfen.18

Was blieb: Prüfzeit, die damit verbracht wurde, die Ursache des Umgebungsunterschieds nicht festnageln zu können.

8. Welche Grenzen anhängen

Grenzen haben einen Zweck und Nebenwirkungen

Beschränkt man die Liste auf die Grenzen, die in einer Messanwendung zählen, hier die Gründe, jede zu nutzen, und die Nebenwirkungen.319

Grenze Warum Sie sie nutzen Wenn Sie übertreiben
KILL_ON_JOB_CLOSE Das Gerät freigeben, wenn der Elternprozess verschwindet Nachanalysematerial verschwindet
ACTIVE_PROCESS Ein durchgehendes Vermehren der Kinder des SDK stoppen Auch legitime Helfer werden bei der Erzeugung abgelehnt
JOB_MEMORY / PROCESS_MEMORY Eine Obergrenze für Lecks im Langbetrieb Die Zuweisung riesiger Bildpuffer beginnt zu scheitern
DIE_ON_UNHANDLED_EXCEPTION Keine Fehlerdialoge auf unbeaufsichtigten Maschinen Interaktives Debuggen wird schmerzhaft
CPU-Ratensteuerung Verhindern, dass ein Bildverarbeitungskind die UI aushungert Frame-Fristen werden verfehlt
BREAKAWAY_OK Einen Ausweg für ein SDK lassen, das einen eigenen Job braucht Prozesse verschwinden aus der Überwachung
UI-Beschränkungen Sandboxartiges Anziehen Nested Jobs brechen (Kapitel 9)

Benachrichtigungsgrenzen dienen der Beobachtung; erzwungene Grenzen dienen der Ablehnung und Beendigung

Trennen Sie „lockere Grenzen zur Benachrichtigung“ von „Grenzen, die Dinge anhalten, wenn sie überschritten werden“. JobObjectNotificationLimitInformation benachrichtigt Sie nur über den Überschuss; der Prozess läuft weiter.15

Die Grenzen des Extended Limit werden erzwungen, aber die Form der Erzwingung unterscheidet sich je Grenze.3

Grenze Was geschieht, wenn sie überschritten wird
Speichergrenze Die Commit-Operation, die sie überschreiten würde, scheitert. Der Prozess selbst bleibt am Leben
ACTIVE_PROCESS Die Erzeugung oder Zuordnung, die sie überschreiten würde, scheitert. Ein Prozess, der sie durch Zuordnung überschritt, wird beendet
Prozesszeit (PROCESS_TIME) Nur der Prozess, der sie überschritt, wird beendet
Jobzeit (JOB_TIME) Eine Grenze für den Aggregatwert; standardmäßig werden alle Prozesse unter dem Job beendet

Setzen Sie das, ohne den Unterschied zu kennen, und Sie werden „einen Helfer, der direkt nach der Erzeugung verschwindet“ als einen anderen Fehler fehldiagnostizieren. Für den Langbetrieb ist die sichere Reihenfolge, zuerst mit Benachrichtigungsgrenzen zu beobachten und die erzwungenen Grenzen festzulegen, sobald der Trend bekannt ist.

Grenzen zur Benachrichtigung und Grenzen zur ErzwingungEine Grenze von JobObjectNotificationLimitInformation benachrichtigt nur über den Überschuss und der Prozess läuft weiter, während Extended-Limit-Grenzen erzwungen werden: eine Speichergrenze lässt die Operation scheitern, ACTIVE_PROCESS lässt Erzeugung und Zuordnung scheitern, und Zeitgrenzen beenden den ProzessBeobachtenAnhaltenZweck der Grenze?Benachrichtigungsgrenze: läuft weiter, wenn überschrittenErzwungene Grenze: ablehnen oder beendenDie Generation aus Abrechnungsprotokollen identifizierenCommit-Fehler, Erzeugung abgelehnt, Beendigung

Abbildung 14: Dieselbe „Grenze“, aber Benachrichtigung und Erzwingung sind verschiedene Dinge, und die Erzwingung wirkt je Grenze anders. Stellen Sie eine erzwungene Grenze auf, ohne zuerst zu beobachten, und sie zündet fälschlich auf einem Gipfel normalen Betriebs.

Die Beziehung zwischen CPU-Ratensteuerung und periodischer Verarbeitung bleibt dem Artikel über Soft Real-Time überlassen; hier gehen wir nicht weiter als eine Gegenmaßnahme für „der Geräteprozess frisst die UI“.

9. Nested Jobs, Breakaway und ein Gegenüber, das bereits in einem Job ist

Nested Jobs als „Enthaltensein von Prozessmengen“ denken

Die Regeln von Nested Jobs unter Windows 8 und später lassen sich in vier Punkte ordnen.5

  • Der Elternjob ist die weitere Menge, und der Kindjob ist eine Teilmenge davon (Zuordnen in einer Reihenfolge, die dieses Enthaltensein bricht, scheitert)
  • Bei den wesentlichen Ressourcengrenzen gilt die einschränkendste entlang der Kette
  • Ein Job mit UI-Beschränkungen kann kein Nested Job sein
  • Benachrichtigungen werden auch an die Abschlussports jedes Elternjobs entlang der Kette zugestellt (der Kindjob braucht keinen Port)
Hierarchie von Nested Jobs und wirksame GrenzenDer Elternjob ist die weitere Menge und der Kindjob eine Teilmenge davon, und bei den wesentlichen Ressourcengrenzen gilt der einschränkendste Wert entlang der Kette. Ein Job mit UI-Beschränkungen kann kein Nested Job seinElternjob (weitere Menge)Kindjob (Teilmenge)MitgliedsprozesseWirksame Grenze = einschränkendster WertJob mit UI-BeschränkungenKein Nested Job möglich

Abbildung 15: Denken Sie Nested Jobs als „Enthaltensein von Mengen“. UI-Beschränkungen zerbrechen Nested Jobs, es ist also sicherer, sie einem Job für die Lebensdauerverwaltung nicht anzuhängen.

Breakaway ist der Pfad, einen Prozess von Anfang an außerhalb des Jobs zu erzeugen

Breakaway ist der legitime Pfad, auf dem mit CreateProcess erzeugte Nachfahren den Baum verlassen.3

Einstellung auf der Job-Seite Bedingung, dass das Kind außerhalb des Jobs geboren wird
JOB_OBJECT_LIMIT_BREAKAWAY_OK Mit angegebenem CREATE_BREAKAWAY_FROM_JOB erzeugen
SILENT_BREAKAWAY_OK Kein Flag nötig. Jedes Kind wird außerhalb geboren

Manchmal ist es nötig, weil das SDK einen eigenen Job verwendet. Allerdings ist ein auf diesem Pfad geborener Prozess sowohl von der Sammelbeendigung als auch von der Überwachung ausgeschlossen. Wenn Sie es erlauben, legen Sie auch fest, wer das Entkommene verwaltet.

Der Pfad aus dem Baum durch BreakawayWenn BREAKAWAY_OK am Job gesetzt ist, wird ein Enkel, der mit CREATE_BREAKAWAY_FROM_JOB erzeugt wird, außerhalb des Jobs geboren und verschwindet aus dem Umfang von Sammelbeendigung und ÜberwachungNormale ErzeugungErzeugung mit BREAKAWAYJob (mit BREAKAWAY_OK)KindprozessEnkel ebenfalls im JobEnkel geht aus dem JobAußerhalb von Überwachung und Sammelbeendigung

Abbildung 16: Breakaway hat zwei Gesichter: „ein Ausweg für ein SDK, das ihn braucht“ und „ein Loch in der Überwachung“. Hängen Sie es an, legen Sie fest, wer das Entkommene betreut.

Ein Stellvertreterstart über WMI lässt sich auch bei verbotenem Breakaway nicht verhindern

Pfade, auf denen ein Drittprozess in Ihrem Namen startet, etwa Win32_Process.Create von WMI, sind eine andere Sache. Der tatsächliche Elternprozess ist der WMI-Anbieter, der geborene Prozess ist also von Anfang an außerhalb des Jobs. Breakaway zu verbieten schließt dieses Loch nicht.1

Ob das SDK diesen Pfad verwendet, prüft man nicht am NEW_PROCESS-Protokoll, sondern an den Eltern-Kind-Beziehungen in Process Explorer.

Zuerst die Zugehörigkeit zu einem bestehenden Job und die Einschränkungen von Windows 7 und früher prüfen

Wenn das Gegenüber bereits in einem Job ist (Ausfall 6), lautet das Verfahren: mit IsProcessInJob prüfen → wenn Nested Jobs eingerichtet werden können, Assign wie es ist → wenn nicht (Windows 7, oder UI-Beschränkungen), den Entwurf ändern.18

Unter Windows 7 und früher können Sie einem Gegenüber, das bereits zu einem anderen Job gehört, kein zweites Mal Assign geben. BREAKAWAY_OK ist kein Flag, das einen bereits zugeordneten Prozess hinterher entfernt. Dieser Ausweg funktioniert nur, wenn die SDK-Seite Breakaway anfordert, wenn sie das Kind erzeugt. Ist das nicht zu erwarten, ändern Sie den Entwurf vor dem Start unter der Annahme „ein Prozess, ein Job“.12

Zuletzt entscheiden, ob der Elternprozess selbst in den Job kommt

Zum Schluss ein Wort zum Entwurf „den eigenen Prozess in den eigenen Job stecken“. Sitzt der Elternprozess selbst auch darunter, ist er beim Absturz des Elternprozesses ebenfalls in den Zielen von KillOnJobClose, und die Lebensdauer des ganzen Baums stimmt vollständig überein. Aber es ist ein zweischneidiges Schwert, das zu einer unbeabsichtigten Massenbeendigung wird, wenn Sie den Umgang mit dem Job-Handle falsch machen; sicherer ist, mit „Elternprozess außen, nur der Kindbaum innen“ zu beginnen.

10. Wie man untersucht

Untersuchen Sie in der Reihenfolge Zugehörigkeit → Abrechnung und Benachrichtigungsprotokolle → Gerätebelegung. Das ist das Bestätigungsverfahren, um nicht allein durch Anschauen von Prozessnamen zu schließen „nichts ist zurückgeblieben“.

  • Process Explorer: die Prozesseigenschaften haben eine Registerkarte Job, die den Job zeigt, dem der Prozess angehört, und seine Grenzen. Das ist der schnellste Weg, zu bestätigen „in welchem Job ist dieser Helfer“
  • IsProcessInJob: der Einstieg, die eigene Zugehörigkeit oder die des Gegenübers aus Code zu prüfen18
  • QueryInformationJobObject: Basic Accounting (Gesamtprozesszahl, CPU-Zeit) und Extended Limit (PeakJobMemoryUsed und so weiter) regelmäßig aufzeichnen417
  • Das Abschlussport-Protokoll in einer Datei behalten: die Zeitlinie NEW_PROCESS / EXIT / ABNORMAL_EXIT wird der einzige Beleg in einer Untersuchung einen Monat später
  • Rückstände nicht anhand der PID prüfen: worauf zu schauen ist, sind Gerätehandles, Pipenamen und Sperrdateien. „Kein Prozess im Task-Manager sichtbar“ bedeutet nicht „das Gerät wurde freigegeben“
Verfahren zur Untersuchung von RückständenZuerst die Zugehörigkeit mit IsProcessInJob und der Registerkarte Job von Process Explorer bestätigen, die Abrechnung mit QueryInformationJobObject lesen, und Rückstände zuletzt anhand von Gerätehandles, Pipenamen und Sperrdateien beurteilen, nicht anhand dessen, ob ein Prozess existiertZugehörigkeit mit IsProcessInJob bestätigenRegisterkarte Job von Process ExplorerAbrechnung mit QueryInformationJobObjectRückstände anhand von Gerätehandles und Pipenamen beurteilen

Abbildung 17: Untersuchen Sie in der Reihenfolge „Zugehörigkeit → Abrechnung → Belegung“. Schließen Sie nicht allein durch Anschauen von Prozessnamen „nichts ist zurückgeblieben“.

11. Ein grober Leitfaden zum Wählen (Entscheidungstabelle)

Hier werden die bisherigen Wahlen nach Situation zusammengefasst. Gehen Sie für die Beendigungspolitik zu Kapitel 5, für die Überwachungsvoraussetzungen zu Kapitel 6 und für die Beziehung zu bestehenden Jobs zu Kapitel 9 zurück.

Situation Empfehlung
Der UI-Körper und das Geräte-SDK wurden in eigene Prozesse getrennt In einen Job nehmen und mit einem Abschlussport überwachen
Der schlimmste Fall ist, dass das Gerät nach dem Verschwinden des Elternprozesses gehalten wird KillOnJobClose setzen
Der Frame oder das Abbild im Moment des Absturzes ist ein Vermögenswert KillOnJobClose nicht setzen; die Überwachungsseite sichert und ruft dann TerminateJobObject
Das Hersteller-SDK erzeugt Helfer Mit SUSPENDED oder JOB_LIST erzeugen und NEW_PROCESS protokollieren
Assign gibt ERROR_ACCESS_DENIED zurück Zuerst den bestehenden Job und ob Nested Jobs möglich sind prüfen. UI-Beschränkungen verdächtigen
Kinder in der interaktiven Sitzung von einem Dienst erzeugen Keine UI-Beschränkungen anhängen. Zum Entwurf im Artikel über die Benutzergrenze zurückkehren
Auf dem UI-Thread auf das Ende von Enkeln warten Aufhören. Auf den IOCP-Thread verlagern

12. Zusammenfassung

Die Lebensdauer eines Kindes ist nicht die Lebensdauer des Process-Objekts des Elternprozesses. Auch das Ende des Elternprozesses wird nicht automatisch an das Kind weitergegeben. Ein Job Object ist der Mechanismus, der diesen Prozessbaum zu einer Einheit macht und Grenzen, Benachrichtigungen und Sammelbeendigung behandelt.

Um bis zu den Enkeln zu verwalten, legen Sie die Zugehörigkeit fest, bevor das Kind läuft. Verfahren A ist SUSPENDED + Assign; Verfahren B, unter Windows 10 und später, ist JOB_LIST. KillOnJobClose wirkt durch das Schließen des letzten Job-Handles unabhängig davon, warum der Elternprozess endete, verliert aber auch das Nachanalysematerial der mitbeendeten Nachfahren.

Genau deshalb sollten Sie zuerst „was nach dem Absturz nicht zurückbleiben darf“ und „was Sie behalten wollen“ listen und zwischen sofortigem Einsammeln und Beendigung wählen, nachdem die Überwachungsseite gesammelt und gesichert hat. Das ist das Entwurfsverfahren, das dieser Artikel vermitteln will.

Als Nächstes zu schreiben wären die Einzelheiten des Startens von Prozessen über Sitzung 0 und die interaktive Sitzung, oder das Warten auf Geräte mit overlapped E/A. Sobald Sie die „äußere Lebensdauer“ eines Prozesses im Griff haben, wartet als Nächstes die Lebensdauer der E/A.

Weiterführende Artikel

Zugehörige Beratungsfelder

KomuraSoft LLC übernimmt den Prozessisolationsentwurf von Windows-Apps, die mit Kameras, Messgeräten und seriellen/USB-Geräten arbeiten, die Untersuchung von Gerätebelegungsstörungen wie zurückbleibenden SDK-Helfern und device busy sowie den Aufbau von Überwachungs- und automatischen Wiederherstellungsmechanismen für lang laufende Apps. Sprechen Sie uns auch von einem einzelnen Fall „Elternprozess neu starten und das Gerät lässt sich nicht öffnen“ an.

Quellen

  1. Microsoft Learn, Job Objects. Dazu, dass ein Job Object ein Kernelobjekt ist, das eine Gruppe von Prozessen als eine Einheit verwaltet, dass von einem Mitgliedsprozess erzeugte Kindprozesse standardmäßig demselben Job zugeordnet werden (außer über Win32_Process.Create), zu den zwei Begrenzungsflags für Breakaway, zur Sammelbeendigung mit TerminateJobObject und dazu, wie man einen Prozessbaum in Umgebungen ohne Nested Jobs verwaltet.  2 3 4 5

  2. Microsoft Learn, AssignProcessToJobObject function (jobapi2.h). Dazu, dass die Zuordnung zwischen einem Prozess und einem Job unumkehrbar ist, zu einem Job je Prozess unter Windows 7 und früher mit mehrfacher Zugehörigkeit (Nested Jobs) ab Windows 8, und zu den wirksamen Grenzen und der Weitergabe von Breakaway unter Nested Jobs.  2

  3. Microsoft Learn, JOBOBJECT_BASIC_LIMIT_INFORMATION structure (winnt.h). Zu den Begrenzungsflags JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE (alle Prozesse beenden, wenn das letzte Job-Handle schließt), ACTIVE_PROCESS (eine Obergrenze gleichzeitig aktiver Prozesse), JOB_MEMORY (eine Commit-Obergrenze für den ganzen Job), DIE_ON_UNHANDLED_EXCEPTION und BREAKAWAY_OK / SILENT_BREAKAWAY_OK.  2 3 4 5

  4. Microsoft Learn, JOBOBJECT_BASIC_ACCOUNTING_INFORMATION structure (winnt.h). Dazu, dass der Job Abrechnungsinformationen wie Gesamtprozesszahl, CPU-Zeit und Seitenfehlerzahl hält, einschließlich der von beendeten Prozessen aufgelaufenen Summen, und dass man sie mit QueryInformationJobObject holt.  2

  5. Microsoft Learn, Nested Jobs. Dazu, dass Nested Jobs eine Eltern-Kind-Hierarchie bilden (der Kindjob ist eine Teilmenge der Prozesse des Elternjobs), dass ein Job mit gesetzten UI-Beschränkungen kein Nested Job sein kann, dass die wirksame Grenze der einschränkendste Wert entlang der Kette ist, dass Benachrichtigungen an alle Abschlussports der Elternjobkette gesendet werden, und dass die Hierarchie von der untersten Ebene beendet wird.  2

  6. Microsoft Learn, Process Creation Flags. Zu CREATE_SUSPENDED (den Anfangsthread angehalten erzeugen und bis ResumeThread nicht laufen lassen) und CREATE_BREAKAWAY_FROM_JOB (der Job des Aufrufers muss JOB_OBJECT_LIMIT_BREAKAWAY_OK haben). 

  7. Raymond Chen, Closing the race window between creating a suspended process and putting it in a job (The Old New Thing). Zum klassischen Verfahren, mit CREATE_SUSPENDED zu erzeugen und den Prozess dann in einen Job zu nehmen, und dazu, wie man sein Wettlauffenster schließt. 

  8. Microsoft Learn, UpdateProcThreadAttribute function (processthreadsapi.h). Dazu, dass PROC_THREAD_ATTRIBUTE_JOB_LIST Job-Handles dem erzeugten Kindprozess in der angegebenen Reihenfolge zuordnet, und dass es unter Windows 10 / Windows Server 2016 und später unterstützt wird. 

  9. Raymond Chen, A more direct and mistake-free way of creating a process in a job object (The Old New Thing). Zur Nutzung von PROC_THREAD_ATTRIBUTE_JOB_LIST, um einen Prozess vom Moment der Erzeugung an einem Job angehören zu lassen. 

  10. Raymond Chen, Destroying all child processes (and grandchildren) when the parent exits (The Old New Thing). Zur Anordnung, die Nachfahren zusammen beendet, wenn der Elternprozess verschwindet, mit einem Job mit KILL_ON_JOB_CLOSE, und zur Bedeutung, das Job-Handle nicht erben zu lassen. 

  11. Microsoft Learn, TerminateJobObject function (jobapi2.h). Dazu, alle dem Job zugeordneten Prozesse zwangsweise zu beenden, als wäre TerminateProcess auf jedem einzeln aufgerufen worden. 

  12. Microsoft Learn, Job Objects - Managing Job Objects. Dazu, dass das Job-Objekt signalisiert wird, wenn alle Prozesse wegen Überschreitens der Jobzeitgrenze beendet werden, dass der Job zerstört wird, wenn das letzte Handle schließt, und dass das Schließen die Beendigung aller Mitgliedsprozesse auslöst, wenn KILL_ON_JOB_CLOSE angegeben ist. 

  13. Microsoft Learn, JOBOBJECT_ASSOCIATE_COMPLETION_PORT structure (winnt.h). Zur Liste der an den Abschlussport gesendeten Nachrichten wie JOB_OBJECT_MSG_NEW_PROCESS / EXIT_PROCESS / ABNORMAL_EXIT_PROCESS / ACTIVE_PROCESS_ZERO, zu den Exitcodes, die als anormale Enden beurteilt werden, dazu, dass PID-Wiederverwendung für Nachrichten, die eine PID zurückgeben, nicht auszuschließen ist, solange kein Prozesshandle gehalten wird, und dazu, dass die Zustellung von Benachrichtigungen nicht garantiert ist.  2 3

  14. Raymond Chen, How do I wait until all processes in a job have exited? (The Old New Thing). Dazu, dass Warten auf das Job-Handle „wurde leer“ nicht erkennen kann, und zur Notwendigkeit, auf JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO am Abschlussport zu warten. 

  15. Microsoft Learn, Job Objects - Job Limits and Notifications. Dazu, dass das Zuordnen des Abschlussports, während der Job inaktiv ist, vorzuziehen ist (Verringerung der Möglichkeit, Benachrichtigungen für Prozesse zu verfehlen, deren Zustand sich während der Zuordnung ändert), dass die Nachrichtenzustellung außer für die mit JobObjectNotificationLimitInformation gesetzten Grenzen nicht garantiert ist, und dass Prozesse nach dem Überschreiten einer Benachrichtigungsgrenze weiterlaufen.  2 3

  16. Raymond Chen, Removing the TerminateThread from code that waits for a job object to empty (The Old New Thing). Zum Umschreiben des alten Musters, den Wartethread mit TerminateThread zu töten, in ein Warten auf Basis des Abschlussports. 

  17. Microsoft Learn, JOBOBJECT_EXTENDED_LIMIT_INFORMATION structure (winnt.h). Zum Setzen von Speichergrenzen je Prozess und je Job und zum Ermitteln des Spitzenspeichers mit PeakProcessMemoryUsed / PeakJobMemoryUsed.  2

  18. Microsoft Learn, IsProcessInJob function (jobapi.h). Dazu, festzustellen, ob ein Prozess im angegebenen Job (oder in irgendeinem Job) läuft.  2 3

  19. Microsoft Learn, JOBOBJECT_CPU_RATE_CONTROL_INFORMATION structure (winnt.h). Dazu, die CPU-Rate (Anteil der Zyklen oder Gewicht) je Job zu steuern. 

Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.

Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.

Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.

Häufige Fragen

Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.

Ist ein Job Object ein Container oder eine Sandbox?
Nein. Ein Job Object ist ein Kernelobjekt, das einer Gruppe von Prozessen Beschränkungen, Benachrichtigungen und Sammelbeendigung anhängt. Es kann Obergrenzen für Speicher, CPU und Prozesszahl setzen, kann aber den Netzwerkzugriff nicht beschränken, und das Zugriffstoken (die Rechte) ändert sich nicht. Es hat UI-Beschränkungen, aber die allein machen daraus keine Sicherheitsgrenze. Wenn Isolation oder Sicherheit das Ziel ist, müssen Sie es mit einem anderen Mechanismus wie AppContainer oder Containern kombinieren. Dieser Artikel behandelt die Verwendung, Lebensdauer und Ressourcen eines Prozessbaums als eine Einheit zu behandeln.
Reicht Process.Kill() nicht aus?
Process.Kill() beendet nur diesen einen Prozess; Enkelprozesse erreicht es nicht. Kill(entireProcessTree: true) in .NET Core 3.0 und später läuft die Nachfahren ab und beendet sie, aber weil es den Prozessbaum aus den Eltern-Kind-Beziehungen in diesem Moment aufzählt, kann es Prozesse verfehlen, die während der Aufzählung geboren werden, und Prozesse, deren Linie gerissen ist, weil der Elternprozess zuerst starb. Außerdem wird keine der beiden Methoden aufgerufen, wenn der Elternprozess selbst abstürzt. Wenn Sie Nachfahren unabhängig davon einsammeln wollen, ob der Elternprozess lebt oder stirbt, ist ein Job Object plus KillOnJobClose, das die Lebensdauer dem Kernel anvertraut, die zuverlässige Wahl.
Tritt das Kind automatisch in den Job ein, wenn der Elternprozess in einem Job ist?
Standardmäßig ja. Ein Kindprozess, den ein zu einem Job gehörender Prozess mit CreateProcess erzeugt, gehört automatisch zum selben Job. Die Ausnahme ist Breakaway. Wenn JOB_OBJECT_LIMIT_BREAKAWAY_OK am Job gesetzt ist und das Kind mit dem Flag CREATE_BREAKAWAY_FROM_JOB erzeugt wird, oder wenn JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK gesetzt ist, wird das Kind außerhalb des Jobs geboren. Beachten Sie außerdem, dass Prozesse, die über Win32_Process.Create von WMI erzeugt werden, dem Job nicht zugeordnet werden.
Kann man einen Prozess wieder aus einem Job nehmen, sobald er drin ist?
Nein. Die Zuordnung durch AssignProcessToJobObject ist unumkehrbar, und die Zugehörigkeit dauert bis zum Ende des Prozesses. Die Entwurfsoptionen sind daher drei: nicht hineinnehmen, von Anfang an mit Breakaway außerhalb erzeugen, oder einen eigenen Job als Nested Job verwenden. Ein "später wieder herausnehmen" gibt es nicht. Diese Unumkehrbarkeit ist auch der Grund, den Job vor der Erzeugung vorzubereiten.
Wirkt KillOnJobClose auch, wenn der Elternprozess abstürzt?
Ja. JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE ist ein Mechanismus, der die Prozesse darunter beendet, wenn das letzte Job-Handle geschlossen wird, und ob der Elternprozess normal endet, an einer unbehandelten Ausnahme stirbt oder im Task-Manager beendet wird: Der Kernel schließt die Handles als Teil der Prozessbereinigung, also zündet es. Wenn Sie jedoch das Job-Handle vom Kindprozess erben lassen, überlebt das Handle, das das Kind hält, den Tod des Elternprozesses, es ist also nicht das letzte Handle und es zündet nicht. Lassen Sie das Job-Handle nicht erben.
Werden Benachrichtigungen des Abschlussports immer zugestellt?
Nicht immer. Die offizielle Dokumentation stellt ausdrücklich fest, dass außer den Benachrichtigungen der mit JobObjectNotificationLimitInformation gesetzten Grenzen die Zustellung von Nachrichten an den Abschlussport nicht garantiert ist. Eine Benachrichtigung, die nicht ankam, bedeutet nicht, dass das Ereignis nicht stattfand. Für eine Überwachung, die Gewissheit braucht, kombinieren Sie sie mit dem Abfragen der Abrechnungsinformationen über QueryInformationJobObject, und halten Sie die Prozesshandles selbst, um festzustellen, ob ein Prozess lebt oder tot ist.
Hat .NET eine offizielle Job-Object-API?
Nein. System.Diagnostics.Process hat keinen Begriff eines Jobs, und die BCL hat auch keinen Wrapper. Die praktische Antwort ist, CreateJobObject / SetInformationJobObject / AssignProcessToJobObject über P/Invoke aufzurufen oder die Signaturen mit CsWin32, dem Quellgenerator von Microsoft, zu erzeugen und einen dünnen Wrapper zu schreiben. Wenn Sie das Job-Handle in ein SafeHandle packen und in IDisposables Dispose schließen, erscheint die Bedeutung von KillOnJobClose, "Lebensdauer des Wrappers = Lebensdauer des Kindprozessbaums", direkt im Code.
Wie sollte ich für einen Geräte-PC unter Windows 7 entwerfen?
Unter Windows 7 und früher kann ein Prozess nur zu einem Job gehören, und Nested Jobs sind nicht möglich. Wenn das SDK der Gegenseite einen eigenen Job verwendet, schlägt Ihr AssignProcessToJobObject fehl. JOB_OBJECT_LIMIT_BREAKAWAY_OK ist ein Flag, das einem Prozess, der bereits in Ihrem Job ist, erlaubt, mit CREATE_BREAKAWAY_FROM_JOB ein Kind außerhalb des Jobs zu erzeugen; es ist keine Magie, die ein zweites Assign durchlässt, nachdem der Prozess schon drin ist. Mit anderen Worten: Der Ausweg existiert nur, wenn die SDK-Seite Breakaway zur Erzeugungszeit anfordert. Ist das nicht zu erwarten, bleibt nur, den Entwurf vor dem Start unter der Annahme zu ändern, dass es nur einen Job gibt, Ihren eigenen. Die Dokumentation von Microsoft zeigt auch, wie man den Baum in Umgebungen ohne Nested Jobs mit den zwei Breakaway-Begrenzungsflags verwaltet. Allerdings ist es ein Betriebssystem außer Support, also kommt, wenn irgend möglich, die Migration zuerst.

Autorenprofil

Profilseite des Artikelautors.

Go Komura

Geschäftsführer von KomuraSoft LLC

Spezialisiert auf Windows-Softwareentwicklung, technische Beratung und Fehleranalyse, insbesondere bei bestehenden Systemen und schwer reproduzierbaren Störungen.

Zurück zum Blog