Myślę, że to pytanie mówi wszystko.
Chcę rozwidlać w systemie Windows. Jaka jest najbardziej podobna operacja i jak z niej korzystam.
Cygwin ma w pełni funkcjonalny fork () w systemie Windows. Tak więc, jeśli używanie Cygwin jest dla Ciebie akceptowalne, problem zostanie rozwiązany w przypadku, gdy wydajność nie jest problemem.
W przeciwnym razie możesz przyjrzeć się, jak Cygwin implementuje fork (). Z dość starego dokumentu architektury Cygwin :
5.6. Tworzenie procesu Wywołanie fork w Cygwin jest szczególnie interesujące, ponieważ nie mapuje dobrze w górnej części API Win32. To bardzo utrudnia prawidłowe wdrożenie. Obecnie rozwidlenie Cygwin jest implementacją bez kopiowania przy zapisie, podobną do tej, która była obecna we wczesnych wersjach UNIX.
Pierwszą rzeczą, która się dzieje, gdy proces nadrzędny forkuje proces potomny, jest inicjowanie przez niego spacji w tabeli procesów Cygwin. Następnie tworzy zawieszony proces potomny przy użyciu wywołania Win32 CreateProcess. Następnie proces nadrzędny wywołuje setjmp, aby zapisać swój własny kontekst i ustawia wskaźnik do tego w obszarze pamięci współdzielonej Cygwin (współdzielonej przez wszystkie zadania Cygwin). Następnie wypełnia sekcje .data i .bss dziecka, kopiując z własnej przestrzeni adresowej do przestrzeni adresowej zawieszonego dziecka. Po zainicjowaniu przestrzeni adresowej dziecka, dziecko jest uruchamiane, podczas gdy rodzic czeka na muteks. Dziecko odkrywa, że został rozwidlony i wykonuje długie skoki, korzystając z zapisanego bufora skoku. Następnie dziecko ustawia muteks, na który czeka rodzic, i blokuje inny muteks. Jest to sygnał dla rodzica, aby skopiował swój stos i stertę do dziecka, po czym zwalnia muteks, na który czeka dziecko, i wraca z wywołania fork. Wreszcie, dziecko budzi się z blokowania na ostatnim muteksie, odtwarza wszystkie obszary mapowane w pamięci przekazane do niego przez wspólny obszar i wraca z samego forka.
Chociaż mamy kilka pomysłów, jak przyspieszyć implementację rozwidlenia poprzez zmniejszenie liczby przełączeń kontekstu między procesem nadrzędnym i podrzędnym, rozwidlenie prawie na pewno zawsze będzie nieefektywne pod Win32. Na szczęście w większości przypadków rodzina wywołań spawn dostarczana przez Cygwin może zostać zastąpiona parą fork / exec przy niewielkim wysiłku. Te wywołania są dokładnie odwzorowywane w górnej części interfejsu API Win32. Dzięki temu są znacznie wydajniejsze. Zmiana programu sterownika kompilatora tak, aby wywoływał spawn zamiast fork, była trywialną zmianą i zwiększyła prędkość kompilacji o dwadzieścia do trzydziestu procent w naszych testach.
Jednak spawn i exec stwarzają własny zestaw trudności. Ponieważ nie ma sposobu na wykonanie faktycznego exec pod Win32, Cygwin musi wymyślić własne identyfikatory procesów (PID). W rezultacie, gdy proces wykonuje wiele wywołań exec, z jednym PID Cygwin będzie powiązanych wiele identyfikatorów PID systemu Windows. W niektórych przypadkach, kody pośredniczące każdego z tych procesów Win32 mogą pozostawać w oczekiwaniu na zamknięcie ich wykonanego procesu Cygwin.
Brzmi jak dużo pracy, prawda? I tak, jest powolny.
EDYTUJ: dokument jest nieaktualny, zapoznaj się z tą doskonałą odpowiedzią na aktualizację
fork
ale osiąga to za pomocą nieszczelnego rozwiązania i musisz być przygotowany na nieoczekiwane sytuacje.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ż nawet obsługiwany).
Poszukiwanie ZwCreateProcess () powinno dać ci więcej szczegółów - na przykład ta informacja od Maxima Shatskiha :
Chociaż zauważ, że Corinna Vinschen wskazuje, że Cygwin stwierdził, że używanie ZwCreateProcess () jest nadal niewiarygodne :
źródło
fork
natychmiastowegoexec
”, być może kandydatem jest CreateProcess. Alefork
bezexec
jest często pożądane i właśnie o to kierowcy ludzie proszą o prawdziwefork
.Cóż, okna tak naprawdę nie mają czegoś podobnego. Zwłaszcza, że fork może służyć do koncepcyjnego tworzenia wątku lub procesu w * nix.
Muszę więc powiedzieć:
CreateProcess()
/CreateProcessEx()
i
CreateThread()
(Słyszałem, że dla aplikacji C_beginthreadex()
jest lepsze).źródło
Ludzie próbowali wdrożyć fork w systemie Windows. To jest najbliższa rzecz, jaką mogę znaleźć:
Zaczerpnięte z: http://doxygen.scilab.org/5.3/d0/d8f/forkWindows_8c_source.html#l00216
źródło
fork
awaria ulegnie awarii, spowoduje awarię programu lub czy wątek po prostu się zawiesi? Jeśli to zawiesza program, to tak naprawdę nie jest rozwidlenie. Po prostu ciekawy, ponieważ szukam prawdziwego rozwiązania i mam nadzieję, że to może być przyzwoita alternatywa.Zanim Microsoft wprowadził nową opcję „podsystemu Linux dla Windows”,
CreateProcess()
była to najbliższa rzecz, jaką musiał wykonaćfork()
system Windows, ale system Windows wymaga określenia pliku wykonywalnego do uruchomienia w tym procesie.Tworzenie procesu w systemie UNIX przebiega zupełnie inaczej niż w systemie Windows. Jego
fork()
wywołanie zasadniczo powiela bieżący proces prawie w całości, każdy w swojej własnej przestrzeni adresowej i kontynuuje uruchamianie ich oddzielnie. Chociaż same procesy są różne, nadal korzystają z tego samego programu. Zobacz tutaj dobry przeglądfork/exec
modelu.Wracając w drugą stronę, odpowiednikiem Windowsa
CreateProcess()
jestfork()/exec()
para funkcji w systemie UNIX.Jeśli przenosisz oprogramowanie na Windows i nie masz nic przeciwko warstwie tłumaczeniowej, Cygwin zapewnił ci możliwość, ale była raczej niezdarna.
Oczywiście, z nowego podsystemu Linux , najbliższa rzecz Windows ma się
fork()
to rzeczywiściefork()
:-)źródło
fork
w przeciętnej aplikacji innej niż WSL?Poniższy dokument zawiera informacje na temat przenoszenia kodu z systemu UNIX na Win32: https://msdn.microsoft.com/en-us/library/y23kc048.aspx
Między innymi wskazuje, że model procesu jest zupełnie inny między dwoma systemami i zaleca rozważenie CreateProcess i CreateThread, gdzie wymagane jest zachowanie podobne do fork ().
źródło
„gdy tylko zechcesz uzyskać dostęp do pliku lub printf, odmówi się dostępu we / wy”
Nie możesz mieć ciastka i też go zjeść ... w msvcrt.dll printf () jest oparte 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 starcie procesu, co oznacza, że każdy proces rozpoczynający wykonywanie „w środku” będzie miał pominięty ten krok. Jeśli nie masz dostępu do kodu źródłowego systemu operacyjnego, nie ma sensu próbować łączyć się ręcznie z csrss. Zamiast tego powinieneś stworzyć swój własny podsystem i odpowiednio unikać funkcji konsoli w aplikacjach używających fork ().
po zaimplementowaniu własnego podsystemu nie zapomnij również zduplikować wszystkich uchwytów rodzica dla procesu potomnego ;-)
„Poza tym prawdopodobnie nie powinieneś używać funkcji Zw *, chyba że jesteś w trybie jądra, prawdopodobnie powinieneś zamiast tego używać funkcji Nt *”.
ZwGetContextThread (NtCurrentThread (), & kontekst);
źródło
Semantyka fork () jest konieczna, gdy dziecko potrzebuje dostępu do aktualnego stanu pamięci rodzica w momencie wywołania funkcji fork (). Mam oprogramowanie, które opiera się na niejawnym muteksie kopiowania pamięci od momentu wywołania natychmiastowego fork (), co uniemożliwia użycie wątków. (Jest to emulowane na nowoczesnych platformach * nix za pomocą semantyki kopiowania przy zapisie / aktualizacji pamięci tabeli).
Najbliższy, który istnieje w systemie Windows jako wywołanie systemowe, to CreateProcess. Najlepsze, co można zrobić, to zamrozić wszystkie inne wątki w czasie kopiowania pamięci do przestrzeni pamięci nowego procesu, a następnie je rozmrozić. Ani klasa Cygwin frok [sic], ani kod Scilab, który opublikował Eric des Courtis, nie zamrażają wątków, które widzę.
Ponadto prawdopodobnie nie powinieneś używać funkcji Zw *, chyba że jesteś w trybie jądra, prawdopodobnie powinieneś zamiast tego używać funkcji Nt *. Istnieje 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 wywoływanie ich z trybu użytkownika jest nieco mniej wydajne.
źródło
Twoje najlepsze opcje to CreateProcess () lub CreateThread () . Więcej informacji na temat przenoszenia można znaleźć tutaj .
źródło
Nie ma łatwego sposobu na emulację fork () w systemie Windows.
Proponuję zamiast tego używać wątków.
źródło
fork
było dokładnie tym , co zrobił CygWin. Ale jeśli kiedykolwiek przeczytasz, jak oni to zrobili, twój „niełatwy sposób” to rażące nieporozumienie :-)Najbliżej mówisz ... Niech pomyślę ... To musi być fork () chyba :)
Aby uzyskać szczegółowe informacje, zobacz Czy Interix implementuje fork ()?
źródło
Jak wspomniały inne odpowiedzi, NT (jądro będące podstawą nowoczesnych wersji Windows) ma odpowiednik Unix fork (). To nie jest problem.
Problem polega na tym, że klonowanie całego stanu procesu nie jest na ogół rozsądną rzeczą. Jest to tak samo prawdziwe w świecie uniksowym, jak w systemie Windows, ale w świecie uniksowym fork () jest używany przez cały czas i biblioteki są zaprojektowane tak, aby sobie z tym radzić. Biblioteki systemu Windows nie są.
Na przykład systemowe biblioteki DLL kernel32.dll i user32.dll utrzymują prywatne połączenie z procesem serwera Win32 csrss.exe. Po rozwidleniu istnieją dwa procesy po stronie klienta tego połączenia, co spowoduje problemy. Proces potomny powinien poinformować csrss.exe o swoim istnieniu i nawiązać nowe połączenie - ale nie ma takiego interfejsu, ponieważ te biblioteki nie zostały zaprojektowane z myślą o fork ().
Masz więc dwie możliwości. Jednym z nich jest zakaz korzystania z kernel32 i user32 oraz innych bibliotek, które nie są przeznaczone do rozwidlania - w tym bibliotek, które bezpośrednio lub pośrednio łączą się z kernel32 lub user32, czyli praktycznie wszystkimi z nich. Oznacza to, że nie możesz w ogóle wchodzić w interakcję z pulpitem Windows i utkniesz w swoim własnym, oddzielnym świecie Unixy. Jest to podejście przyjęte przez różne podsystemy Unix dla NT.
Inną opcją jest uciekanie się do jakiegoś okropnego hackowania, aby spróbować zmusić nieświadome biblioteki do pracy z fork (). To właśnie robi Cygwin. Tworzy nowy proces, pozwala mu się zainicjować (w tym rejestruje się w csrss.exe), a następnie kopiuje większość stanu dynamicznego ze starego procesu i ma nadzieję na najlepsze. Zadziwia mnie, że to zawsze działa. Z pewnością nie działa niezawodnie - nawet jeśli nie przypadkowo zawiedzie z powodu konfliktu przestrzeni adresowej, każda używana biblioteka może po cichu zostać zepsuta. Twierdzenie obecnie akceptowanej odpowiedzi, że Cygwin ma „w pełni funkcjonalny fork ()” jest ... wątpliwe.
Podsumowanie: W środowisku podobnym do Interix można forować, wywołując fork (). W przeciwnym razie spróbuj odzwyczaić się od chęci zrobienia tego. Nawet jeśli celujesz w Cygwin, nie używaj fork (), chyba że absolutnie musisz.
źródło
Jeśli zależy Ci tylko na utworzeniu podprocesu i czekaniu na niego, być może API _spawn * jest w toku.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
źródło