Jaka jest najbliższa rzecz, jaką windows ma do fork ()?

Myślę, że pytanie mówi wszystko.

Chcę rozwidlić okna. Jaka jest najbardziej podobna operacja i jak z niej korzystać.

 108
Author: rlbond, 2009-06-12

12 answers

Cygwin ma w pełni funkcjonalną funkcję fork () w systemie Windows. Tak więc, jeśli użycie Cygwina jest dla Ciebie akceptowalne, to problem zostanie rozwiązany w przypadku, gdy wydajność nie jest problemem.

W przeciwnym razie możesz przyjrzeć się, jak Cygwin implementuje fork (). Z dość starej architektury Cygwina doc :

5.6. Tworzenie Procesów Wywołanie widelca w Cygwinie jest szczególnie interesujące ponieważ nie mapuje dobrze na szczycie API Win32. To sprawia, że bardzo trudne do poprawnie zaimplementuj. Obecnie widelec Cygwin jest implementacja non-copy-on-write podobne do tego, co było obecne na początku smaki Uniksa.

Pierwszą rzeczą, która się dzieje, gdy proces rodzica rozwidla proces potomny czy rodzic inicjalizuje spację w tabeli procesów Cygwin dla dziecko. Następnie tworzy zawieszony proces potomny przy użyciu Win32 CreateProcess call. Następnie rodzic wywołania procesu setjmp, aby zapisać własne kontekst i ustawia wskaźnik na to w a Cygwin shared memory area (shared wśród wszystkich zadań Cygwina). Następnie wypełnia u dziecka .Dane i .sekcje bss kopiując z własnej przestrzeni adresowej na adres zawieszonego dziecka miejsce. Po przestrzeni adresowej dziecka jest inicjowana, dziecko jest uruchamiane podczas rodzic czeka na mutex. Dziecko odkrywa, że został rozwidlony i longjumps używając zapisanego bufora skoku. Dziecko następnie ustawia mutex rodzic czeka i blokuje kolejny mutex. To jest sygnałem dla rodzica, aby skopiować swój stos i stertę do dziecka, po czym uwalnia mutex, którym jest dziecko czeka i wraca z widelca sprawdzam. Wreszcie dziecko budzi się z blokowanie na ostatnim mutexie, odtwarza wszelkie obszary odwzorowane w pamięci przekazywane do niego przez obszar współdzielony, i zwraca z widelec.

Choć mamy kilka pomysłów jak przyspieszyć realizację naszych forków poprzez zmniejszenie liczby kontekstów przełącza się między rodzicem a dziecko proces, widelec będzie prawie na pewno zawsze bądź nieefektywny pod Win32. Na szczęście w większości sytuacji spawn rodzina połączeń dostarczanych przez Cygwin można zastąpić widelec / exec para z tylko trochę wysiłek. Te połączenia Mapa czysto na górze API Win32. W rezultacie, oni są znacznie bardziej wydajne. Zmiana program sterownika kompilatora do wywołania spawn zamiast widelca był trywialny zmiana i zwiększenie kompilacji prędkości o dwadzieścia do trzydziestu procent w nasze testy.

Jednak spawn i exec prezentują swoje własny zestaw trudności. Bo tam nie da się zrobić rzeczywistego exec pod Win32, Cygwin musi wymyślić własne Identyfikatory Procesów (Pid). W rezultacie, gdy proces wykonuje wiele exec połączeń, będzie wiele okien Pidy związane z pojedynczym Cygwinem PID. W niektórych przypadkach, stuby każdego z te procesy Win32 mogą pozostać, czekając na ich Exec ' d Cygwin proces do wyjścia.

Brzmi jak dużo pracy, prawda? I tak, to jest sloooooow.

EDIT: doc jest nieaktualny, proszę zobaczyć tę doskonałą odpowiedź dla aktualizacji

 74
Author: Laurynas Biveinis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:37

Na pewno nie znam szczegółów na ten temat, ponieważ nigdy tego nie robiłem, ale natywne API NT ma możliwość rozwidlenia procesu(podsystem POSIX w Windows potrzebuje tej możliwości - nie jestem pewien, czy podsystem POSIX jest już w ogóle obsługiwany).

Wyszukiwanie ZwCreateProcess () powinno dać ci więcej szczegółów - na przykład ten fragment informacji z Maxim Shatskih :

Najważniejszym parametrem jest tutaj SectionHandle. Jeśli ten parametr na NULL, jądro rozwidli bieżący proces. W przeciwnym razie to parametr musi być uchwytem obiektu sekcji SEC_IMAGE utworzonego na plik EXE przed wywołaniem ZwCreateProcess ().

Chociaż zauważ, że Corinna Vinschen wskazuje, że Cygwin znaleziony przy użyciu ZwCreateProcess() nadal nierzetelny :

Iker Arizmendi napisał (a):

> 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
Ten dokument jest dość stary, około 10 lat. Póki jeszcze używamy Wywołania Win32 do emulowania forka, metody znacznie się zmienił. Szczególnie, nie tworzymy procesu potomnego w stanie zawieszonym już, chyba że konkretne dane wymagają specjalnej obsługi w rodzica, zanim zostaną skopiowane do dziecka. W obecnej wersji 1.5.25 release jedynym futerałem dla zawieszonego dziecka są otwarte gniazda w rodzic. Nadchodząca Wersja 1.7.0 w ogóle się nie zawiesi.

Jednym z powodów, aby nie używać ZwCreateProcess było to, że do 1.5.25 release nadal wspieramy użytkowników Windows 9X. Jednak dwa próby użycia ZwCreateProcess w systemach opartych na NT nie powiodły się dla jednego powód czy inny.

Byłoby naprawdę miło, gdyby to coś było lepsze lub w ogóle udokumentowane, zwłaszcza kilka struktur danych i jak podłączyć proces do podsystemu. O ile fork nie jest koncepcją Win32, to nie zobacz, że byłoby źle, aby fork łatwiejsze do wdrożenia.
 54
Author: Michael Burr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-24 06:21:10

Cóż, windows nie ma nic podobnego. Zwłaszcza, że fork może być używany do koncepcyjnego tworzenia wątku lub procesu w *nix.

Więc muszę powiedzieć:

CreateProcess()/CreateProcessEx()

I

CreateThread() (słyszałem, że dla aplikacji C, _beginthreadex() jest lepszy).

 35
Author: Evan Teran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-23 14:59:44

Ludzie próbowali zaimplementować fork na Windows. To jest najbliższa rzecz jaką mogę znaleźć:

Pochodzi z: 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;
}
 15
Author: Eric des Courtis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-01 16:02:22

Poniższy dokument zawiera kilka informacji o przenoszeniu kodu z Uniksa na Win32: https://msdn.microsoft.com/en-us/library/y23kc048.aspx

Wskazuje między innymi, że model procesu jest zupełnie inny między tymi dwoma systemami i zaleca rozważenie CreateProcess i CreateThread, gdzie wymagane jest zachowanie podobne do fork ().

 6
Author: Brandon E Taylor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-23 15:36:21

Przed wprowadzeniem przez Microsoft nowej opcji "Linux subsystem for Windows", CreateProcess() była najbliższą rzeczą, jaką Windows ma do fork(), Ale Windows wymaga podania pliku wykonywalnego do uruchomienia w tym procesie.

Tworzenie procesów uniksowych jest zupełnie inne niż Windows. Jego wywołanie fork() zasadniczo powiela bieżący proces prawie w całości, każdy w swojej własnej przestrzeni adresowej i kontynuuje ich uruchamianie oddzielnie. Podczas gdy same procesy są różne, nadal działają ten sam program. Zobacz tutaj aby uzyskać dobry przegląd modelu fork/exec.

Wracając w drugą stronę, odpowiednikiem okien CreateProcess() jest fork()/exec() para funkcji w Uniksie.

Jeśli przenosiłeś oprogramowanie do Windows i nie masz nic przeciwko warstwie tłumaczeniowej, Cygwin dostarczył Ci taką możliwość, jaką chcesz, ale była to raczej kludgey.

Oczywiście, z nowym podsystemem Linux , najbliższą rzeczą, jaką Windows ma do fork() jest właściwie fork() :-)

 5
Author: paxdiablo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-05 03:30:20

Semantyka Fork () jest niezbędna, gdy dziecko potrzebuje dostępu do rzeczywistego stanu pamięci rodzica od chwili wywołania Fork (). Mam program, który opiera się na niejawnym mutexie kopiowania pamięci od momentu wywołania instant fork (), co uniemożliwia użycie wątków. (Jest to emulowane na nowoczesnych platformach *nix poprzez semantykę copy-on-write/update-memory-table.)

Najbliższy, który istnieje w systemie Windows jako syscall, to CreateProcess. Najlepsze, co można zrobić jest dla rodzica, aby zamrozić wszystkie inne wątki w czasie kopiowania pamięci do przestrzeni pamięci nowego procesu, a następnie rozmrozić je. Ani Klasa Cygwin frok [sic], ani Kod Scilab, który opublikował Eric des Courtis, nie powodują zamrożenia wątku, co widzę.

Poza tym, prawdopodobnie nie powinieneś używać funkcji Zw*, chyba że jesteś w trybie jądra, powinieneś raczej używać funkcji Nt*. Jest dodatkowa gałąź, która sprawdza czy jesteś w trybie jądra, a jeśli nie, wykonuje wszystkie sprawdzanie granic i weryfikację parametrów, które zawsze wykonuje Nt*. W związku z tym bardzo mniej wydajne jest wywoływanie ich z trybu użytkownika.

 3
Author: sjcaged,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-06 19:59:16

"gdy tylko chcesz zrobić dostęp do plików lub printf, io są odrzucane"

  • Nie możesz zjeść swojego ciasta... w msvcrt.dll, printf() opiera się na API konsoli, które samo w sobie używa lpc do komunikacji z podsystemem konsoli (csrss).exe). Połączenie z csrss jest inicjowane przy uruchomieniu procesu, co oznacza, że każdy proces, który rozpoczyna jego wykonanie "w środku", będzie miał ten krok pominięty. Chyba że masz dostęp do kodu źródłowego systemu operacyjnego system, to nie ma sensu próbować połączyć się z csrss ręcznie. Zamiast tego powinieneś utworzyć swój własny podsystem i odpowiednio unikać funkcji konsolowych w aplikacjach, które używają fork ().

  • Po zaimplementowaniu własnego podsystemu nie zapomnij również powielić wszystkich uchwytów rodzica dla procesu potomnego;-)

" ponadto prawdopodobnie nie powinieneś używać funkcji Zw*, chyba że jesteś w trybie jądra, powinieneś prawdopodobnie używać funkcji Nt* zamiast tego."

  • to jest niepoprawne. Gdy dostępne w trybie użytkownika, nie ma absolutnie żadnej różnicy między Zw * * * Nt***; są to tylko dwa różne (ntdll.dll) eksportowane nazwy, które odnoszą się do tego samego (względnego) wirtualnego adresu.

ZwGetContextThread (NtCurrentThread (), &context);

  • uzyskanie kontekstu bieżącego (uruchomionego) wątku przez wywołanie ZwGetContextThread jest błędne, może ulec awarii i (z powodu dodatkowego wywołania systemowego) nie jest również najszybszym sposobem za wykonanie zadania.
 3
Author: user3502619,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-06 01:53:56

Najlepsze opcje to CreateProcess () lub CreateThread () . Więcej informacji na temat portowania znajdziesz tutaj.

 2
Author: John T,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-12 06:40:43

Nie ma łatwego sposobu emulowania fork () w systemie Windows.

Proponuję zamiast tego użyć wątków.

 2
Author: VVS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-12 06:44:53

Najbliżej mówisz... Niech pomyślę... To musi być fork () chyba :)

Po szczegóły zobacz Czy Interix implementuje fork ()?

 2
Author: Piotr Dobrogost,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:40

Jeśli zależy Ci tylko na stworzeniu podprocesu i czekaniu na niego, być może _spawn * API jest w trakcie.h są wystarczające. Oto więcej informacji na ten temat:

Https://docs.microsoft.com/en-us/cpp/c-runtime-library/process-and-environment-control https://en.wikipedia.org/wiki/Process.h

 1
Author: solstice333,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-15 09:24:30