Was kommt Windows am nächsten an fork ()?


124

Ich denke, die Frage sagt alles.

Ich möchte unter Windows gabeln. Was ist die ähnlichste Operation und wie verwende ich sie?

Antworten:


86

Cygwin hat fork () unter Windows voll ausgestattet. Wenn die Verwendung von Cygwin für Sie akzeptabel ist, ist das Problem gelöst, falls die Leistung kein Problem darstellt.

Ansonsten können Sie sich ansehen, wie Cygwin fork () implementiert. Aus einer ganz alten Cygwin Architektur doc :

5.6. Prozesserstellung Der Fork-Aufruf in Cygwin ist besonders interessant, da er nicht gut auf der Win32-API abgebildet wird. Dies macht es sehr schwierig, richtig zu implementieren. Derzeit handelt es sich bei der Cygwin-Gabel um eine Implementierung, die nicht kopiert und geschrieben werden kann, ähnlich wie dies in früheren UNIX-Versionen der Fall war.

Das erste, was passiert, wenn ein übergeordneter Prozess einen untergeordneten Prozess teilt, ist, dass der übergeordnete Prozess ein Leerzeichen in der Cygwin-Prozesstabelle für das untergeordnete Element initialisiert. Anschließend wird mit dem Win32 CreateProcess-Aufruf ein angehaltener untergeordneter Prozess erstellt. Als Nächstes ruft der übergeordnete Prozess setjmp auf, um seinen eigenen Kontext zu speichern, und setzt einen Zeiger darauf in einem gemeinsam genutzten Cygwin-Speicherbereich (gemeinsam genutzt von allen Cygwin-Tasks). Anschließend werden die Abschnitte .data und .bss des Kindes ausgefüllt, indem aus dem eigenen Adressraum in den Adressraum des suspendierten Kindes kopiert wird. Nachdem der Adressraum des Kindes initialisiert wurde, wird das Kind ausgeführt, während das Elternteil auf einen Mutex wartet. Das Kind entdeckt mithilfe des gespeicherten Sprungpuffers, dass es gegabelt wurde, und springt weit. Das Kind setzt dann den Mutex, auf den das Elternteil wartet, und blockiert einen anderen Mutex. Dies ist das Signal für das übergeordnete Element, seinen Stapel und Heap in das untergeordnete Element zu kopieren. Danach gibt es den Mutex frei, auf den das untergeordnete Element wartet, und kehrt vom Fork-Aufruf zurück. Schließlich erwacht das Kind vom Blockieren des letzten Mutex, erstellt alle Speicherbereiche neu, die über den gemeinsam genutzten Bereich an es übergeben wurden, und kehrt von der Verzweigung selbst zurück.

Während wir einige Ideen haben, wie wir unsere Fork-Implementierung beschleunigen können, indem wir die Anzahl der Kontextwechsel zwischen dem übergeordneten und dem untergeordneten Prozess reduzieren, wird Fork unter Win32 mit ziemlicher Sicherheit immer ineffizient sein. Glücklicherweise kann in den meisten Fällen die von Cygwin bereitgestellte Spawn-Familie von Anrufen mit nur geringem Aufwand durch ein Fork / Exec-Paar ersetzt werden. Diese Aufrufe werden sauber über der Win32-API zugeordnet. Dadurch sind sie viel effizienter. Das Ändern des Treiberprogramms des Compilers, um Spawn anstelle von Fork aufzurufen, war eine triviale Änderung und erhöhte die Kompilierungsgeschwindigkeit in unseren Tests um 20 bis 30 Prozent.

Spawn und Exec haben jedoch ihre eigenen Schwierigkeiten. Da es unter Win32 keine Möglichkeit gibt, eine tatsächliche Ausführung durchzuführen, muss Cygwin eigene Prozess-IDs (PIDs) erfinden. Wenn ein Prozess mehrere Exec-Aufrufe ausführt, sind daher mehrere Windows-PIDs mit einer einzelnen Cygwin-PID verknüpft. In einigen Fällen können Stubs jedes dieser Win32-Prozesse verweilen und darauf warten, dass der ausgeführte Cygwin-Prozess beendet wird.

Klingt nach viel Arbeit, nicht wahr? Und ja, es ist langsam.

BEARBEITEN: Das Dokument ist veraltet. Bitte lesen Sie diese hervorragende Antwort für ein Update


11
Dies ist eine gute Antwort, wenn Sie eine Cygwin-App unter Windows schreiben möchten. Aber im Allgemeinen ist es nicht das Beste. Grundsätzlich sind die Prozess- und Thread-Modelle * nix und Windows sehr unterschiedlich. CreateProcess () und CreateThread () sind die im Allgemeinen äquivalenten APIs
Foredecker

2
Entwickler sollten bedenken, dass dies ein nicht unterstützter Mechanismus ist und IIRC tatsächlich dazu neigt, zu brechen, wenn ein anderer Prozess auf dem System Code-Injection verwendet.
Harry Johnston

1
Der unterschiedliche Implementierungslink ist nicht mehr gültig.
PythonNut

Bearbeitet, um nur den anderen Antwortlink zu verlassen
Laurynas Biveinis

@Foredecker, Eigentlich solltest du es nicht tun, auch wenn du versuchst eine "Cygwin App" zu schreiben. Es versucht, Unix zu imitieren fork, erreicht dies jedoch mit einer undichten Lösung, und Sie müssen auf unerwartete Situationen vorbereitet sein.
Pacerier

66

Ich kenne die Details dazu sicherlich nicht, weil ich es noch nie gemacht habe, aber die native NT-API kann einen Prozess verzweigen (das POSIX-Subsystem unter Windows benötigt diese Funktion - ich bin nicht sicher, ob das POSIX-Subsystem wird sogar noch unterstützt).

Eine Suche nach ZwCreateProcess () sollte Ihnen weitere Details liefern - zum Beispiel diese Informationen von Maxim Shatskih :

Der wichtigste Parameter hier ist SectionHandle. Wenn dieser Parameter NULL ist, teilt der Kernel den aktuellen Prozess auf. Andernfalls muss dieser Parameter ein Handle des Abschnittsobjekts SEC_IMAGE sein, das in der EXE-Datei erstellt wurde, bevor ZwCreateProcess () aufgerufen wird.

Beachten Sie jedoch, dass Corinna Vinschen angibt, dass Cygwin die Verwendung von ZwCreateProcess () immer noch als unzuverlässig empfunden hat :

Iker Arizmendi schrieb:

> Because the Cygwin project relied solely on Win32 APIs its fork
> implementation is non-COW and inefficient in those cases where a fork
> is not followed by exec.  It's also rather complex. See here (section
> 5.6) for details:
>  
> http://www.redhat.com/support/wpapers/cygnus/cygnus_cygwin/architecture.html

Dieses Dokument ist ziemlich alt, ungefähr 10 Jahre. Während wir noch Win32-Aufrufe zum Emulieren von Fork verwenden, hat sich die Methode merklich geändert. Insbesondere erstellen wir den untergeordneten Prozess nicht mehr im angehaltenen Zustand, es sei denn, bestimmte Datenstrukturen benötigen eine spezielle Behandlung im übergeordneten Prozess, bevor sie in das untergeordnete Element kopiert werden. In der aktuellen Version 1.5.25 sind offene Sockets im Elternteil der einzige Fall für ein suspendiertes Kind. Die kommende Version 1.7.0 wird überhaupt nicht ausgesetzt.

Ein Grund, ZwCreateProcess nicht zu verwenden, war, dass wir bis zur Version 1.5.25 immer noch Windows 9x-Benutzer unterstützen. Zwei Versuche, ZwCreateProcess auf NT-basierten Systemen zu verwenden, schlugen jedoch aus dem einen oder anderen Grund fehl.

Es wäre wirklich schön, wenn dieses Zeug besser oder überhaupt dokumentiert wäre, insbesondere ein paar Datenstrukturen und wie man einen Prozess mit einem Subsystem verbindet. Obwohl Fork kein Win32-Konzept ist, sehe ich nicht, dass es eine schlechte Sache wäre, die Implementierung von Fork zu vereinfachen.


Das ist die falsche Antwort. CreateProcess () und CreateThread () sind die allgemeinen Entsprechungen.
Foredecker

2
Interix ist in Windows Vista Enterprise / Ultimate als "Subsystem für UNIX-Anwendungen"
verfügbar

15
@Foredecker - Dies ist möglicherweise eine falsche Antwort, aber CreateProcess () / CreateThread () ist möglicherweise auch falsch. Es hängt davon ab, ob man nach "der Win32-Methode" oder "so nah wie möglich an der fork () -Semantik" sucht. CreateProcess () verhält sich erheblich anders als fork (), weshalb cygwin viel Arbeit leisten musste, um es zu unterstützen.
Michael Burr

1
@jon: Ich habe versucht, die Links zu reparieren und den relevanten Text in die Antwort zu kopieren (so dass zukünftige defekte Links kein Problem darstellen). Diese Antwort stammt jedoch schon lange genug, dass ich nicht 100% sicher bin, dass das Zitat, das ich heute gefunden habe, das ist, worauf ich mich 2009 bezog.
Michael Burr

4
Wenn Leute " forkmit sofort exec" wollen, dann ist CreateProcess vielleicht ein Kandidat. Aber forkohne execist oft wünschenswert und das ist es, was die Leute von einem echten Fahrer verlangen fork.
Aaron McDaid

37

Nun, Windows hat so etwas nicht wirklich. Zumal Fork verwendet werden kann, um einen Thread oder einen Prozess in * nix konzeptionell zu erstellen.

Also muss ich sagen:

CreateProcess()/.CreateProcessEx()

und

CreateThread()(Ich habe gehört, dass für C-Anwendungen _beginthreadex()besser ist).


17

Die Leute haben versucht, Fork unter Windows zu implementieren. Dies ist das Nächste, was ich finden kann:

Entnommen aus: http://doxygen.scilab.org/5.3/d0/d8f/forkWindows_8c_source.html#l00216

static BOOL haveLoadedFunctionsForFork(void);

int fork(void) 
{
    HANDLE hProcess = 0, hThread = 0;
    OBJECT_ATTRIBUTES oa = { sizeof(oa) };
    MEMORY_BASIC_INFORMATION mbi;
    CLIENT_ID cid;
    USER_STACK stack;
    PNT_TIB tib;
    THREAD_BASIC_INFORMATION tbi;

    CONTEXT context = {
        CONTEXT_FULL | 
        CONTEXT_DEBUG_REGISTERS | 
        CONTEXT_FLOATING_POINT
    };

    if (setjmp(jenv) != 0) return 0; /* return as a child */

    /* check whether the entry points are 
       initilized and get them if necessary */
    if (!ZwCreateProcess && !haveLoadedFunctionsForFork()) return -1;

    /* create forked process */
    ZwCreateProcess(&hProcess, PROCESS_ALL_ACCESS, &oa,
        NtCurrentProcess(), TRUE, 0, 0, 0);

    /* set the Eip for the child process to our child function */
    ZwGetContextThread(NtCurrentThread(), &context);

    /* In x64 the Eip and Esp are not present, 
       their x64 counterparts are Rip and Rsp respectively. */
#if _WIN64
    context.Rip = (ULONG)child_entry;
#else
    context.Eip = (ULONG)child_entry;
#endif

#if _WIN64
    ZwQueryVirtualMemory(NtCurrentProcess(), (PVOID)context.Rsp,
        MemoryBasicInformation, &mbi, sizeof mbi, 0);
#else
    ZwQueryVirtualMemory(NtCurrentProcess(), (PVOID)context.Esp,
        MemoryBasicInformation, &mbi, sizeof mbi, 0);
#endif

    stack.FixedStackBase = 0;
    stack.FixedStackLimit = 0;
    stack.ExpandableStackBase = (PCHAR)mbi.BaseAddress + mbi.RegionSize;
    stack.ExpandableStackLimit = mbi.BaseAddress;
    stack.ExpandableStackBottom = mbi.AllocationBase;

    /* create thread using the modified context and stack */
    ZwCreateThread(&hThread, THREAD_ALL_ACCESS, &oa, hProcess,
        &cid, &context, &stack, TRUE);

    /* copy exception table */
    ZwQueryInformationThread(NtCurrentThread(), ThreadBasicInformation,
        &tbi, sizeof tbi, 0);
    tib = (PNT_TIB)tbi.TebBaseAddress;
    ZwQueryInformationThread(hThread, ThreadBasicInformation,
        &tbi, sizeof tbi, 0);
    ZwWriteVirtualMemory(hProcess, tbi.TebBaseAddress, 
        &tib->ExceptionList, sizeof tib->ExceptionList, 0);

    /* start (resume really) the child */
    ZwResumeThread(hThread, 0);

    /* clean up */
    ZwClose(hThread);
    ZwClose(hProcess);

    /* exit with child's pid */
    return (int)cid.UniqueProcess;
}
static BOOL haveLoadedFunctionsForFork(void)
{
    HANDLE ntdll = GetModuleHandle("ntdll");
    if (ntdll == NULL) return FALSE;

    if (ZwCreateProcess && ZwQuerySystemInformation && ZwQueryVirtualMemory &&
        ZwCreateThread && ZwGetContextThread && ZwResumeThread &&
        ZwQueryInformationThread && ZwWriteVirtualMemory && ZwClose)
    {
        return TRUE;
    }

    ZwCreateProcess = (ZwCreateProcess_t) GetProcAddress(ntdll,
        "ZwCreateProcess");
    ZwQuerySystemInformation = (ZwQuerySystemInformation_t)
        GetProcAddress(ntdll, "ZwQuerySystemInformation");
    ZwQueryVirtualMemory = (ZwQueryVirtualMemory_t)
        GetProcAddress(ntdll, "ZwQueryVirtualMemory");
    ZwCreateThread = (ZwCreateThread_t)
        GetProcAddress(ntdll, "ZwCreateThread");
    ZwGetContextThread = (ZwGetContextThread_t)
        GetProcAddress(ntdll, "ZwGetContextThread");
    ZwResumeThread = (ZwResumeThread_t)
        GetProcAddress(ntdll, "ZwResumeThread");
    ZwQueryInformationThread = (ZwQueryInformationThread_t)
        GetProcAddress(ntdll, "ZwQueryInformationThread");
    ZwWriteVirtualMemory = (ZwWriteVirtualMemory_t)
        GetProcAddress(ntdll, "ZwWriteVirtualMemory");
    ZwClose = (ZwClose_t) GetProcAddress(ntdll, "ZwClose");

    if (ZwCreateProcess && ZwQuerySystemInformation && ZwQueryVirtualMemory &&
        ZwCreateThread && ZwGetContextThread && ZwResumeThread &&
        ZwQueryInformationThread && ZwWriteVirtualMemory && ZwClose)
    {
        return TRUE;
    }
    else
    {
        ZwCreateProcess = NULL;
        ZwQuerySystemInformation = NULL;
        ZwQueryVirtualMemory = NULL;
        ZwCreateThread = NULL;
        ZwGetContextThread = NULL;
        ZwResumeThread = NULL;
        ZwQueryInformationThread = NULL;
        ZwWriteVirtualMemory = NULL;
        ZwClose = NULL;
    }
    return FALSE;
}

4
Beachten Sie, dass die meisten Fehlerprüfungen fehlen - z. B. gibt ZwCreateThread einen NTSTATUS-Wert zurück, der mit den Makros SUCCEEDED und FAILED überprüft werden kann.
BCran

1
Was passiert, wenn der forkAbsturz, das Programm oder der Thread nur abstürzt? Wenn das Programm abstürzt, ist dies nicht wirklich eine Gabelung. Nur neugierig, weil ich nach einer echten Lösung suche und hoffe, dass dies eine anständige Alternative ist.
leetNightshade

1
Ich möchte darauf hinweisen, dass der bereitgestellte Code einen Fehler enthält. haveLoadedFunctionsForFork ist eine globale Funktion im Header, aber eine statische Funktion in der c-Datei. Sie sollten beide global sein. Derzeit stürzt die Gabel ab und fügt jetzt eine Fehlerprüfung hinzu.
leetNightshade

Die Seite ist tot und ich weiß nicht, wie ich das Beispiel auf meinem eigenen System kompilieren kann. Ich gehe davon aus, dass mir einige Header fehlen oder die falschen enthalten, oder? (Das Beispiel zeigt sie nicht.)
Paul Stelian

6

Vor der Einführung der neuen Option "Linux-Subsystem für Windows" durch Microsoft war Windows CreateProcess()das Nächste, was Windows tun mussfork() , aber Windows erfordert, dass Sie eine ausführbare Datei angeben, die in diesem Prozess ausgeführt werden soll.

Die Erstellung des UNIX-Prozesses unterscheidet sich erheblich von Windows. Der fork()Aufruf dupliziert den aktuellen Prozess im Wesentlichen fast vollständig, jeder in seinem eigenen Adressraum, und führt sie separat weiter aus. Obwohl die Prozesse selbst unterschiedlich sind, führen sie immer noch dasselbe Programm aus. Sehen Sie hier für einen guten Überblick über das fork/execModell.

Möchten Sie in die andere Richtung, das Äquivalent von Windows CreateProcess()ist das fork()/exec() Paar von Funktionen in UNIX.

Wenn Sie Software auf Windows portiert haben und eine Übersetzungsschicht nichts ausmacht, hat Cygwin die gewünschte Funktion bereitgestellt, die jedoch ziemlich schwierig war.

Natürlich mit dem neuen Linux - Subsystem , die nächste Sache, hat Windows fork()ist eigentlich fork() :-)


2
Kann ich forkbei gegebener WSL eine durchschnittliche Nicht-WSL-Anwendung verwenden?
Caesar

6

Das folgende Dokument enthält einige Informationen zum Portieren von Code von UNIX nach Win32: https://msdn.microsoft.com/en-us/library/y23kc048.aspx

Dies weist unter anderem darauf hin, dass das Prozessmodell zwischen den beiden Systemen sehr unterschiedlich ist, und empfiehlt die Berücksichtigung von CreateProcess und CreateThread, wenn ein fork () -ähnliches Verhalten erforderlich ist.


4

"Sobald Sie Dateizugriff oder Druck ausführen möchten, wird io abgelehnt."

  • Sie können Ihren Kuchen nicht haben und ihn auch nicht essen ... In msvcrt.dll basiert printf () auf der Konsolen-API, die selbst lpc verwendet, um mit dem Konsolensubsystem (csrss.exe) zu kommunizieren. Die Verbindung mit csrss wird beim Start des Prozesses initiiert. Dies bedeutet, dass bei jedem Prozess, der seine Ausführung "in der Mitte" beginnt, dieser Schritt übersprungen wird. Wenn Sie keinen Zugriff auf den Quellcode des Betriebssystems haben, ist es sinnlos, manuell eine Verbindung zu csrss herzustellen. Stattdessen sollten Sie Ihr eigenes Subsystem erstellen und dementsprechend die Konsolenfunktionen in Anwendungen vermeiden, die fork () verwenden.

  • Wenn Sie Ihr eigenes Subsystem implementiert haben, vergessen Sie nicht, auch alle Handles der Eltern für den untergeordneten Prozess zu duplizieren ;-)

"Außerdem sollten Sie die Zw * -Funktionen wahrscheinlich nicht verwenden, es sei denn, Sie befinden sich im Kernelmodus. Sie sollten stattdessen wahrscheinlich die Nt * -Funktionen verwenden."

  • Das ist falsch. Beim Zugriff im Benutzermodus gibt es absolut keinen Unterschied zwischen Zw *** Nt ***; Dies sind lediglich zwei verschiedene (ntdll.dll) exportierte Namen, die sich auf dieselbe (relative) virtuelle Adresse beziehen.

ZwGetContextThread (NtCurrentThread (), & context);

  • Das Abrufen des Kontexts des aktuellen (laufenden) Threads durch Aufrufen von ZwGetContextThread ist falsch, stürzt wahrscheinlich ab und ist (aufgrund des zusätzlichen Systemaufrufs) auch nicht der schnellste Weg, um die Aufgabe auszuführen.

2
Dies scheint nicht die Hauptfrage zu beantworten, sondern auf einige andere unterschiedliche Antworten zu antworten, und es wäre wahrscheinlich besser, direkt auf jede zu antworten, um Klarheit zu schaffen und es einfacher zu machen, zu verfolgen, was los ist.
Leigh

Sie scheinen anzunehmen, dass printf immer auf die Konsole schreibt.
Jasen

3

Die Semantik von fork () ist erforderlich, wenn das Kind ab dem Zeitpunkt, zu dem fork () aufgerufen wird, Zugriff auf den tatsächlichen Speicherstatus des Elternteils benötigt. Ich habe eine Software, die sich auf den impliziten Mutex des Speicherkopierens ab dem Zeitpunkt des Aufrufs von fork () stützt, wodurch die Verwendung von Threads unmöglich wird. (Dies wird auf modernen * nix-Plattformen über die Semantik des Kopierens beim Schreiben / Aktualisieren der Speichertabelle emuliert.)

Der nächste, der unter Windows als Systemaufruf vorhanden ist, ist CreateProcess. Das Beste, was getan werden kann, ist, dass das übergeordnete Element alle anderen Threads während der Zeit, in der es Speicher in den Speicher des neuen Prozesses kopiert, einfriert und sie dann auftaut. Weder die Cygwin-Frok-Klasse noch der Scilab-Code, den Eric des Courtis veröffentlicht hat, führen das Einfrieren von Threads durch, wie ich sehen kann.

Außerdem sollten Sie die Zw * -Funktionen wahrscheinlich nicht verwenden, es sei denn, Sie befinden sich im Kernelmodus. Sie sollten stattdessen wahrscheinlich die Nt * -Funktionen verwenden. Es gibt einen zusätzlichen Zweig, der prüft, ob Sie sich im Kernelmodus befinden, und andernfalls alle Grenzüberprüfungen und Parameterüberprüfungen durchführt, die Nt * immer durchführt. Daher ist es etwas weniger effizient, sie aus dem Benutzermodus aufzurufen.


Sehr interessante Informationen zu Zw * exportierten Symbolen, danke.
Andon M. Coleman

Beachten Sie, dass die Zw * -Funktionen aus dem Benutzerbereich aus Sicherheitsgründen weiterhin Nt * -Funktionen im Kernelbereich zugeordnet sind. Oder zumindest sollten sie.
Paul Stelian


2

Es gibt keine einfache Möglichkeit, fork () unter Windows zu emulieren.

Ich empfehle Ihnen, stattdessen Threads zu verwenden.


Nun, in der Fairness, Umsetzung forkwar genau das, was CygWin tat. Aber wenn Sie jemals nachlesen, wie sie es gemacht haben, ist Ihr "kein einfacher Weg" ein grobes Missverständnis :-)
paxdiablo


2

Wie bereits in anderen Antworten erwähnt, entspricht NT (der Kernel, der modernen Windows-Versionen zugrunde liegt) Unix fork (). Das ist nicht das Problem.

Das Problem ist, dass das Klonen des gesamten Status eines Prozesses im Allgemeinen nicht sinnvoll ist. Dies gilt in der Unix-Welt genauso wie in Windows, aber in der Unix-Welt wird fork () ständig verwendet, und Bibliotheken sind darauf ausgelegt, damit umzugehen. Windows-Bibliotheken sind nicht.

Beispielsweise unterhalten die System-DLLs kernel32.dll und user32.dll eine private Verbindung zum Win32-Serverprozess csrss.exe. Nach einem Fork gibt es zwei Prozesse auf dem Client-Ende dieser Verbindung, die Probleme verursachen werden. Der untergeordnete Prozess sollte csrss.exe über seine Existenz informieren und eine neue Verbindung herstellen. Dafür gibt es jedoch keine Schnittstelle, da diese Bibliotheken nicht für fork () entwickelt wurden.

Sie haben also zwei Möglichkeiten. Eine besteht darin, die Verwendung von Kernel32 und User32 sowie anderer Bibliotheken zu verbieten, die nicht zum Verzweigen bestimmt sind - einschließlich aller Bibliotheken, die direkt oder indirekt mit Kernel32 oder User32 verknüpft sind, was praktisch alle sind. Dies bedeutet, dass Sie überhaupt nicht mit dem Windows-Desktop interagieren können und in Ihrer eigenen Unixy-Welt stecken bleiben. Dies ist der Ansatz der verschiedenen Unix-Subsysteme für NT.

Die andere Möglichkeit besteht darin, auf einen schrecklichen Hack zurückzugreifen, um unbewusste Bibliotheken dazu zu bringen, mit fork () zu arbeiten. Das macht Cygwin. Es erstellt einen neuen Prozess, lässt ihn initialisieren (einschließlich der Registrierung bei csrss.exe), kopiert dann den größten Teil des dynamischen Status aus dem alten Prozess und hofft auf das Beste. Es wundert mich, dass dies jemals funktioniert. Es funktioniert sicherlich nicht zuverlässig - selbst wenn es nicht zufällig aufgrund eines Adressraumkonflikts fehlschlägt, kann jede Bibliothek, die Sie verwenden, stillschweigend in einem fehlerhaften Zustand belassen werden. Die Behauptung der derzeit akzeptierten Antwort, dass Cygwin eine "voll ausgestattete Gabel ()" hat, ist ... zweifelhaft.

Zusammenfassung: In einer Interix-ähnlichen Umgebung können Sie Fork durch Aufrufen von Fork () erstellen. Andernfalls versuchen Sie bitte, sich von dem Wunsch zu entwöhnen, dies zu tun. Selbst wenn Sie auf Cygwin abzielen, verwenden Sie fork () nur, wenn Sie dies unbedingt müssen.


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.