Dlaczego tworzenie nowego procesu w systemie Windows jest droższe niż w systemie Linux?

101

Słyszałem, że tworzenie nowego procesu na komputerze z systemem Windows jest droższe niż w systemie Linux. Czy to prawda? Czy ktoś może wyjaśnić techniczne powody, dla których jest droższy i podać jakiekolwiek historyczne powody decyzji projektowych stojących za tymi powodami?

Tylko czytać
źródło

Odpowiedzi:

68

mweerden: NT został zaprojektowany dla wielu użytkowników od pierwszego dnia, więc nie jest to tak naprawdę powód. Jednak masz rację co do tego, że tworzenie procesów odgrywa mniejszą rolę w NT niż w Uniksie, ponieważ NT, w przeciwieństwie do Uniksa, faworyzuje wielowątkowość nad wieloprocesowość.

Rob, to prawda, że ​​fork jest stosunkowo tani, gdy używa się COW, ale w rzeczywistości po fork jest przeważnie wykonywany przez exec. Ponadto program exec musi załadować wszystkie obrazy. Dlatego omawianie działania widelca jest tylko częścią prawdy.

Omawiając szybkość tworzenia procesów, prawdopodobnie dobrym pomysłem jest rozróżnienie między NT i Windows / Win32. Jeśli chodzi o NT (czyli samo jądro), nie sądzę, aby tworzenie procesów (NtCreateProcess) i wątków (NtCreateThread) było znacznie wolniejsze niż na przeciętnym Uniksie. Może się trochę więcej działo, ale nie widzę tutaj głównej przyczyny różnicy w wydajności.

Jeśli jednak spojrzysz na Win32, zauważysz, że dodaje on sporo narzutów do tworzenia procesów. Po pierwsze, wymaga powiadomienia CSRSS o utworzeniu procesu, który obejmuje LPC. Wymaga dodatkowego załadowania przynajmniej kernel32 i musi wykonać szereg dodatkowych czynności księgowych, które należy wykonać, zanim proces zostanie uznany za pełnoprawny proces Win32. I nie zapominajmy o wszystkich dodatkowych obciążeniach wynikających z analizowania manifestów, sprawdzania, czy obraz wymaga podkładki zgodności, sprawdzania, czy obowiązują zasady ograniczeń oprogramowania, yada yada.

To powiedziawszy, widzę ogólne spowolnienie w sumie tych wszystkich drobiazgów, które należy zrobić, oprócz surowego tworzenia procesu, przestrzeni VA i początkowego wątku. Ale jak powiedziano na początku - ze względu na faworyzowanie wielowątkowości nad wielozadaniowością, jedynym oprogramowaniem, które jest poważnie dotknięte tym dodatkowym kosztem, jest słabo przeportowane oprogramowanie Unix. Chociaż sytuacja ta zmienia się, gdy oprogramowanie takie jak Chrome i IE8 nagle odkrywa na nowo zalety przetwarzania wieloprocesowego i zaczyna często uruchamiać i przerywać procesy ...

Johannes Passing
źródło
8
Po fork nie zawsze występuje exec (), a ludzie dbają tylko o fork (). Apache 1.3 używa fork () (bez exec) w Linuksie i wątków w Windows, nawet jeśli w wielu przypadkach procesy są rozwidlane, zanim będą potrzebne i przechowywane w puli.
Blaisorblade
5
Nie zapominając oczywiście o poleceniu „vfork”, które jest przeznaczone do opisanego scenariusza „po prostu wywołaj exec”.
Chris Huang-Leaver
4
Innym rodzajem oprogramowania, na które ma to poważny wpływ, są wszelkiego rodzaju skrypty powłoki, które obejmują koordynację wielu procesów. Na przykład skrypty Bash w Cygwin bardzo cierpią z tego powodu. Rozważmy pętlę powłoki, która generuje dużo sed, awk i grep w potokach. Każde polecenie tworzy proces, a każdy potok tworzy podpowłokę i nowy proces w tej podpowłoce. Unix został zaprojektowany właśnie z myślą o takim zastosowaniu, dlatego szybkie tworzenie procesów pozostaje tam normą.
Dan Molding,
5
-1. Twierdzenie, że oprogramowanie jest „słabo przeportowane”, ponieważ nie działa dobrze na źle zaprojektowanym systemie operacyjnym pełnym błędów kompatybilności, które spowalniają tworzenie procesów, jest absurdalne.
Miles Rout
6
@MilesRout Celem przenoszenia jest zmodyfikowanie oprogramowania tak, aby działało w nowym systemie docelowym, mając na uwadze mocne i słabe strony tego systemu. Słabo działające oprogramowanie przeniesione to słabo przeniesione oprogramowanie, niezależnie od przeszkód, jakie zapewnia system operacyjny.
Dizzyspiral
28

Unix posiada wywołanie systemowe „fork”, które „dzieli” bieżący proces na dwie części i daje ci drugi proces, identyczny z pierwszym (modulo powrót z wywołania fork). Ponieważ przestrzeń adresowa nowego procesu jest już uruchomiona i działa, powinno to być tańsze niż wywołanie `` CreateProcess '' w systemie Windows i załadowanie obrazu exe, powiązanych bibliotek dll itp.

W przypadku rozwidlenia system operacyjny może używać semantyki „kopiuj przy zapisie” dla stron pamięci powiązanych z obydwoma nowymi procesami, aby zapewnić, że każdy z nich otrzyma własną kopię stron, które następnie modyfikuje.

Rob Walker
źródło
22
Ten argument jest ważny tylko wtedy, gdy naprawdę rozwidlasz. Jeśli zaczynasz nowy proces, w systemie Unix nadal musisz rozwidlić i wykonać. Zarówno Windows, jak i Unix mają kopię podczas zapisu. System Windows z pewnością ponownie wykorzysta załadowany plik EXE, jeśli uruchomisz drugą kopię aplikacji. Nie sądzę, że twoje wyjaśnienie jest prawidłowe, przepraszam.
Joel Spolsky
1
Więcej o exec () i fork () vipinkrsahu.blogspot.com/search/label/system%20programming
webkul
W mojej odpowiedzi dodałem dane dotyczące wydajności. stackoverflow.com/a/51396188/537980 Widać, że jest szybszy.
ctrl-alt-delor
25

Dodając do tego, co powiedział JP: większość narzutów należy do uruchamiania Win32 dla procesu.

Jądro Windows NT faktycznie obsługuje rozwidlenie COW. Używa ich SFU (środowisko Microsoft UNIX dla Windows). Jednak Win32 nie obsługuje rozwidlenia. Procesy SFU nie są procesami Win32. SFU jest ortogonalne do Win32: oba są podsystemami środowiska zbudowanymi na tym samym jądrze.

Oprócz wywołań LPC poza procesem CSRSS, w XP i nowszych występuje wywołanie poza procesem do silnika zgodności aplikacji w celu znalezienia programu w bazie danych zgodności aplikacji. Ten krok powoduje wystarczające obciążenie, że firma Microsoft udostępnia opcję zasad grupy, aby wyłączyć silnik zgodności w WS2003 ze względu na wydajność.

Biblioteki środowiska wykonawczego Win32 (kernel32.dll itp.) Również wykonują wiele odczytów rejestru i inicjalizacji podczas uruchamiania, które nie mają zastosowania do procesów UNIX, SFU lub natywnych.

Tworzenie procesów natywnych (bez podsystemu środowiska) jest bardzo szybkie. SFU robi dużo mniej niż Win32 przy tworzeniu procesów, więc jego procesy są również szybkie w tworzeniu.

AKTUALIZACJA NA 2019: dodaj LXSS: podsystem Windows dla systemu Linux

Zastąpienie SFU w Windows 10 to podsystem środowiska LXSS. Jest to tryb 100% jądra i nie wymaga żadnego IPC, które nadal ma Win32. Syscall dla tych procesów jest kierowany bezpośrednio do lxss.sys / lxcore.sys, więc fork () lub inny proces tworzący wywołanie kosztuje łącznie tylko 1 wywołanie systemowe dla twórcy. [Obszar danych zwany instancją] śledzi wszystkie procesy LX, wątki i stan środowiska wykonawczego.

Procesy LXSS są oparte na procesach natywnych, a nie procesach Win32. Wszystkie elementy specyficzne dla Win32, takie jak silnik zgodności, nie są w ogóle zaangażowane.

Chris Smith
źródło
16

Oprócz odpowiedzi Roba Walkera: Obecnie masz takie rzeczy, jak Native POSIX Thread Library - jeśli chcesz. Ale przez długi czas jedynym sposobem na „delegowanie” pracy w świecie unixowym było użycie fork () (i nadal jest preferowane w wielu, wielu okolicznościach). np. jakiś serwer gniazd

socket_accept ()
widelec()
jeśli (dziecko)
    handleRequest ()
jeszcze
    goOnBeingParent ()
Dlatego implementacja fork musiała być szybka iz biegiem czasu wprowadzono wiele optymalizacji. Microsoft poparł CreateThread, a nawet włókna, zamiast tworzyć nowe procesy i używać komunikacji międzyprocesowej. Myślę, że porównywanie CreateProcess z forkiem jest niesprawiedliwe, ponieważ nie są one wymienne. Prawdopodobnie bardziej odpowiednie jest porównanie fork / exec z CreateProcess.

VolkerK
źródło
2
O ostatnim punkcie: fork () nie jest wymienny z CreateProcess (), ale można też powiedzieć, że Windows powinien wtedy zaimplementować fork (), ponieważ daje to większą elastyczność.
Blaisorblade
Ach, czasownik To Bee.
acib708
Ale fork + exec w Linuksie jest szybszy niż CreateThread w MS-Windows. Linux może sam zrobić fork, aby być jeszcze szybszym. Jakkolwiek to porównać, SM jest wolniejsze.
ctrl-alt-delor
13

Myślę, że kluczem do tej sprawy jest historyczne wykorzystanie obu systemów. Windows (i wcześniej DOS) były pierwotnie systemami dla jednego użytkownika dla komputerów osobistych . W związku z tym systemy te zazwyczaj nie muszą cały czas tworzyć wielu procesów; (bardzo) mówiąc prosto, proces jest tworzony tylko wtedy, gdy zażąda tego jeden samotny użytkownik (a my, ludzie, nie działamy bardzo szybko, mówiąc względnie).

Systemy oparte na Uniksie były pierwotnie systemami i serwerami dla wielu użytkowników. Szczególnie w przypadku tych ostatnich nierzadko zdarza się, że procesy (np. Demony poczty lub http) rozdzielają procesy w celu obsługi określonych zadań (np. Obsługi jednego połączenia przychodzącego). Ważnym czynnikiem w tym jest tania forkmetoda (która, jak wspomniał Rob Walker ( 47865 ), początkowo wykorzystuje tę samą pamięć dla nowo utworzonego procesu), która jest bardzo przydatna, ponieważ nowy proces natychmiast otrzymuje wszystkie potrzebne informacje.

Jest oczywiste, że przynajmniej historycznie potrzeba szybkiego tworzenia procesów przez systemy Unix jest znacznie większa niż w przypadku systemów Windows. Myślę, że nadal tak jest, ponieważ systemy oparte na Uniksie są nadal bardzo zorientowane na procesy, podczas gdy Windows, ze względu na swoją historię, był prawdopodobnie bardziej zorientowany na wątki (wątki są przydatne do tworzenia responsywnych aplikacji).

Zastrzeżenie: w żadnym wypadku nie jestem ekspertem w tej sprawie, więc wybacz mi, jeśli się pomyliłem.

mweerden
źródło
9

Uh, wydaje się, że dzieje się wiele usprawiedliwień typu „tak jest lepiej”.

Myślę, że ludzie mogliby skorzystać na przeczytaniu „Showstopper”; książka o rozwoju Windows NT.

Głównym powodem, dla którego usługi działają jako biblioteki DLL w jednym procesie w systemie Windows NT, było to, że były zbyt wolne jako oddzielne procesy.

Jeśli upadłeś i zabrudziłeś, przekonasz się, że problem stanowi strategia ładowania biblioteki.

W systemach Unices (ogólnie) segmenty kodu bibliotek współużytkowanych (DLL) są faktycznie współużytkowane.

Windows NT ładuje kopię biblioteki DLL na proces, ponieważ manipuluje segmentem kodu biblioteki (i segmentem kodu wykonywalnego) po załadowaniu. (Mówi mu, gdzie są twoje dane?)

Skutkuje to segmentami kodu w bibliotekach, których nie można ponownie wykorzystać.

Tak więc tworzenie procesu NT jest w rzeczywistości dość kosztowne. Z drugiej strony sprawia, że ​​DLL nie ma znacznego zapisu w pamięci, ale jest szansą na problemy z zależnościami między aplikacjami.

Czasami w inżynierii opłaca się cofnąć się i powiedzieć: „Teraz, gdybyśmy mieli to zaprojektować tak, by było do niczego, jak by to wyglądało?”

Pracowałem z wbudowanym systemem, który był kiedyś dość temperamentny i pewnego dnia spojrzałem na niego i zdałem sobie sprawę, że to magnetron wnękowy z elektroniką w komorze mikrofalowej. Potem zrobiliśmy to dużo bardziej stabilnym (i mniej jak kuchenka mikrofalowa).

Tim Williscroft
źródło
3
Segmenty kodu są wielokrotnego użytku, o ile biblioteka DLL jest ładowana pod preferowanym adresem bazowym. Tradycyjnie powinieneś upewnić się, że ustawiłeś niesprzeczne adresy podstawowe dla wszystkich bibliotek DLL, które będą ładowane do twoich procesów, ale to nie działa z ASLR.
Mike Dimmick,
Jest jakieś narzędzie do ponownego bazowania wszystkich bibliotek DLL, prawda? Nie jestem pewien, co robi z ASLR.
Zan Lynx
3
Współdzielenie sekcji kodu działa również w systemach obsługujących ASLR.
Johannes przechodzi
@MikeDimmick, więc każdy, kto tworzy bibliotekę DLL, musi współpracować, aby upewnić się, że nie ma konfliktów, czy może je wszystkie łatać na poziomie systemu, przed załadowaniem?
ctrl-alt-delor
9

Krótka odpowiedź brzmi: „warstwy oprogramowania i komponenty”.

Architektura Windows SW ma kilka dodatkowych warstw i komponentów, które nie istnieją w Uniksie lub są uproszczone i obsługiwane wewnątrz jądra w Uniksie.

W Uniksie, fork i exec to bezpośrednie wywołania jądra.

W systemie Windows API jądra nie jest używane bezpośrednio, na jego szczycie znajduje się win32 i pewne inne komponenty, więc tworzenie procesu musi przejść przez dodatkowe warstwy, a następnie nowy proces musi się uruchomić lub połączyć się z tymi warstwami i komponentami.

Od dłuższego czasu badacze i korporacje próbują rozbić Uniksa w dość podobny sposób, zwykle opierając swoje eksperymenty na jądrze Mach ; dobrze znanym przykładem jest OS X .. Jednak za każdym razem, gdy próbują, robi się to tak wolno, że przynajmniej częściowo scalają elementy z powrotem do jądra na stałe lub w celu wysyłki produkcyjnej.

DigitalRoss
źródło
Warstwy niekoniecznie spowalniają: napisałem sterownik urządzenia, z wieloma warstwami, w C. Czysty kod, umiejętne programowanie, łatwe do odczytania. Była szybsza (marginalnie) niż wersja napisana w wysoce zoptymalizowanym asemblerze, bez warstw.
ctrl-alt-delor
Ironią jest to, że NT jest ogromne jądra (nie mikrojądro)
Ctrl-Alt-Delor
2

Ponieważ wydaje się, że w niektórych odpowiedziach istnieje uzasadnienie dla MS-Windows, np

  • „Jądro NT i Win32 to nie to samo. Jeśli programujesz na jądro NT, nie jest tak źle ”- Prawda, ale jeśli nie piszesz podsystemu Posix, to kogo to obchodzi. Będziesz pisać do win32.
  • „To niesprawiedliwe porównywanie fork z ProcessCreate, ponieważ oni robią różne rzeczy, a Windows nie ma forka” - Prawda, więc porównuję podobnie. Jednak porównam również fork, ponieważ ma wiele zastosowań, takich jak izolacja procesów (np. Każda karta przeglądarki internetowej działa w innym procesie).

Spójrzmy teraz na fakty, jaka jest różnica w wydajności?

Dane podsumowano z http://www.bitsnbites.eu/benchmarking-os-primitives/ .
Ponieważ stronniczość jest nieunikniona, podsumowując, zrobiłem to na korzyść sprzętu MS-Windows
w większości testów i7 8 core 3.2GHz. Z wyjątkiem Raspberry-Pi z Gnu / Linux

Porównanie różnych podstawowych operacji na Gnu / Linux, Apple-Mac i Microsoft Windows (mniejszy jest lepszy)

Porównanie procesu tworzenia w MS-Windows i Linux

Uwagi: W Linuksie forkjest szybsza metoda niż preferowana metoda MS-Window CreateThread.

Liczby operacji typu tworzenia procesów (ponieważ trudno jest zobaczyć wartość dla Linuksa na wykresie).

W kolejności według szybkości od najszybszej do najwolniejszej (liczby oznaczają czas, mała oznacza lepszą).

  • Linux CreateThread 12
  • Mac CreateThread 15
  • Linux Fork 19
  • Windows CreateThread 25
  • Linux CreateProcess (fork + exec) 45
  • Widelec Mac 105
  • Mac CreateProcess (fork + exec) 453
  • Raspberry-Pi CreateProcess (fork + exec) 501
  • Windows CreateProcess 787
  • Windows CreateProcess ze skanerem antywirusowym 2850
  • Windows Fork (symulacja z CreateProcess + fixup) większy niż 2850

Liczby dla innych pomiarów

  • Tworzenie pliku.
    • Linux 13
    • Mac 113
    • Windows 225
    • Raspberry-Pi (z wolną kartą SD) 241
    • Windows z defenderem i skanerem antywirusowym itp. 12950
  • Przydzielanie pamięci
    • Linux 79
    • Windows 93
    • Mac 152
ctrl-alt-delor
źródło
1

Wszystko to plus fakt, że na komputerze z Winem najprawdopodobniej włączy się oprogramowanie antywirusowe podczas CreateProcess ... To zwykle największe spowolnienie.

gabr
źródło
1
Tak, jest to największe, ale nie jedyne istotne spowolnienie.
ctrl-alt-delor
1

Warto również zauważyć, że model zabezpieczeń w systemie Windows jest znacznie bardziej skomplikowany niż w systemach operacyjnych opartych na systemie UNIX, co zwiększa obciążenie podczas tworzenia procesów. Jeszcze jeden powód, dla którego wielowątkowość jest preferowana od wieloprocesowości w systemie Windows.

hacksoncode
źródło
1
Spodziewałbym się, że bardziej skomplikowany model bezpieczeństwa będzie bezpieczniejszy; ale fakty pokazują inaczej.
Lie Ryan
4
SELinux jest również bardzo złożonym modelem bezpieczeństwa i nie nakłada znaczących narzutówfork()
Spudd86
6
@LieRyan, W projektowaniu oprogramowania (z mojego doświadczenia), bardziej skomplikowane, bardzo rzadko oznacza bezpieczniejsze.
Woodrow Douglass