Wątki a procesy w systemie Linux


253

Ostatnio słyszałem, jak kilka osób mówi, że w Linuksie prawie zawsze lepiej jest używać procesów zamiast wątków, ponieważ Linux jest bardzo wydajny w przetwarzaniu procesów i ponieważ istnieje wiele problemów (takich jak blokowanie) związanych z wątkami. Jestem jednak podejrzliwy, ponieważ wydaje się, że wątki mogą dać całkiem duży wzrost wydajności w niektórych sytuacjach.

Więc moje pytanie brzmi: w obliczu sytuacji, w której wątki i procesy mogłyby całkiem dobrze sobie poradzić, czy powinienem używać procesów lub wątków? Na przykład, jeśli piszę serwer WWW, czy powinienem używać procesów lub wątków (lub kombinacji)?


Czy jest jakaś różnica w stosunku do Linuksa 2.4?
mouviciel

3
Różnica między procesami i wątkami w Linuksie 2.4 polega na tym, że wątki dzielą więcej części swojego stanu (przestrzeń adresowa, uchwyty plików itp.) Niż procesy, które zwykle nie. NPTL pod Linuksem 2.6 czyni to nieco jaśniejszym, dając im „grupy wątków”, które są trochę jak „procesy” w win32 i Solaris.
MarkR

6
Współbieżne programowanie jest trudne. O ile nie potrzebujesz bardzo wysokiej wydajności, najważniejszym aspektem w kompromisie będzie często trudność debugowania . Procesy są pod tym względem znacznie łatwiejszym rozwiązaniem, ponieważ cała komunikacja jest jawna (łatwa do sprawdzenia, zalogowania itp.). Natomiast wspólna pamięć wątków tworzy gazilliony miejsc, w których jedna nić może błędnie wpłynąć na drugą.
Lutz Prechelt,

1
@LutzPrechelt - Programowanie współbieżne może być wielowątkowe, jak również wieloprocesowe. Nie rozumiem, dlaczego zakładasz, że programowanie współbieżne jest wielowątkowe. Może to wynikać z pewnych szczególnych ograniczeń językowych, ale ogólnie może to być jedno i drugie.
iankit

2
Łączę Lutz stwierdził po prostu, że programowanie współbieżne jest trudne, niezależnie od tego, który zostanie wybrany - proces lub wątki - ale że programowanie współbieżne przy użyciu procesów ułatwia debugowanie w wielu przypadkach.
user2692263,

Odpowiedzi:


322

Linux używa modelu wątków 1-1, bez (do jądra) bez rozróżnienia między procesami i wątkami - wszystko jest po prostu zadaniem wykonalnym. *

W Linuksie wywołanie systemowe cloneklonuje zadanie z konfigurowalnym poziomem udostępniania, między innymi:

  • CLONE_FILES: udostępnij tę samą tabelę deskryptorów plików (zamiast tworzyć kopię)
  • CLONE_PARENT: nie konfiguruj relacji rodzic-dziecko między nowym zadaniem a starym (w przeciwnym razie child getppid()= rodzic getpid())
  • CLONE_VM: udostępnij to samo miejsce w pamięci (zamiast tworzyć kopię COW )

fork()Połączenia clone(najmniej dzielą się, )a pthread_create()połączenia clone(dzielą najwięcej ). **

forking kosztuje nieco więcej niż pthread_createing z powodu kopiowania tabel i tworzenia mapowań COW dla pamięci, ale programiści jądra Linuksa próbowali (i udało się) zminimalizować te koszty.

Przełączanie między zadaniami, jeśli współużytkują tę samą przestrzeń pamięci i różne tabele, będzie nieco tańsze niż w przypadku, gdy nie zostaną one udostępnione, ponieważ dane mogą być już załadowane do pamięci podręcznej. Jednak przełączanie zadań jest nadal bardzo szybkie, nawet jeśli nic nie jest współużytkowane - jest to coś, co programiści jądra Linuksa starają się zapewnić (i zapewnić to).

W rzeczywistości, jeśli jesteś w systemie multi-procesora, nie dzielenie może faktycznie być korzystne dla wydajności: jeśli każde zadanie jest uruchomiony na innym procesorze, synchronizacja pamięci współdzielonej jest drogie.


* Uproszczony. CLONE_THREADpowoduje, że dostarczanie sygnałów jest współużytkowane (co wymaga CLONE_SIGHAND, co dzieli tabelę obsługi sygnałów).

** Uproszczony. Istnieją zarówno SYS_forki SYS_clonewywołań systemowych, ale w jądrze, sys_forki sys_clonesą bardzo cienkie obwolut wokół tej samej do_forkfunkcji, która sama jest cienka otoki wokół copy_process. Tak, terminy process, threadi tasksą używane zamiennie raczej w jądrze Linuksa ...


6
Myślę, że brakuje nam 1 punktu. Jeśli wykonujesz wiele procesów dla swojego serwera WWW, musisz napisać inny proces, aby otworzyć gniazdo i przekazać „pracę” do różnych wątków. Gwintowanie oferuje w jednym procesie wiele wątków, czysty design. W wielu sytuacjach wątek jest po prostu naturalny, aw innym przypadku nowy proces jest po prostu naturalny. Gdy problem pojawia się w szarym obszarze, inne kompromisy, jak wyjaśniono w efemmiecie, stają się ważne.
Saurabh

26
@Saurabh Nie bardzo. Można łatwo socket, bind, listen, fork, a następnie mieć wiele procesów acceptpołączeń w tym samym gnieździe odsłuchu. Proces może przestać akceptować, jeśli jest zajęty, a jądro przekieruje połączenia przychodzące do innego procesu (jeśli nikt nie nasłuchuje, jądro będzie kolejkować lub upuszczać, w zależności od listenzaległości). Nie masz dużo większej kontroli nad podziałem pracy, ale zwykle to wystarczy!
ephemient

2
@Bloodcount Wszystkie procesy / wątki w systemie Linux są tworzone przez ten sam mechanizm, który klonuje istniejący proces / wątek. Przekazano flagi, aby clone()określić, które zasoby są udostępniane. Zadanie może również unshare()zawierać zasoby w dowolnym późniejszym czasie.
ephemient

4
@KarthikBalaguru W samym jądrze jest jedno task_structzadanie dla każdego. Jest to często nazywane „procesem” w całym kodzie jądra, ale odpowiada każdemu uruchamialnemu wątkowi. Nie ma process_struct; jeśli kilka task_structs jest połączonych ze sobą przez ich thread_grouplistę, to są one tym samym „procesem” do przestrzeni użytkownika. Jest trochę specjalnej obsługi „wątków”, np. Wszystkie wątki rodzeństwa są zatrzymywane na fork i exec i pojawia się tylko wątek „główny” ls /proc. Każdy wątek jest dostępny za pośrednictwem /proc/pid, niezależnie od tego, czy jest wymieniony na liście, /procczy nie.
ephemient

5
@KarthikBalaguru Jądro obsługuje ciągłość zachowania między wątkami i procesami; na przykład, clone(CLONE_THREAD | CLONE_VM | CLONE_SIGHAND))dałby ci nowy „wątek”, który nie udostępnia katalogu roboczego, plików lub blokad, a jednocześnie clone(CLONE_FILES | CLONE_FS | CLONE_IO)„proces”, który to robi. Podstawowy system tworzy zadania poprzez klonowanie; fork()i pthread_create()są tylko funkcjami bibliotecznymi, które wywołują clone()inaczej (jak napisałem w tej odpowiedzi).
ephemient

60

Linux (a nawet Unix) daje trzecią opcję.

Opcja 1 - procesy

Utwórz samodzielny plik wykonywalny, który obsługuje część (lub wszystkie części) aplikacji, i wywołaj ją osobno dla każdego procesu, np. Program uruchamia swoje kopie w celu delegowania zadań.

Opcja 2 - wątki

Utwórz autonomiczny plik wykonywalny, który uruchamia się z jednym wątkiem i utwórz dodatkowe wątki do wykonania niektórych zadań

Opcja 3 - widelec

Dostępne tylko w systemie Linux / Unix, jest to nieco inne. Rozwidlony proces jest tak naprawdę własnym procesem z własną przestrzenią adresową - dziecko nie może (normalnie) wpłynąć na przestrzeń adresową rodzica lub rodzeństwa (w przeciwieństwie do wątku) - dzięki czemu zyskujesz większą niezawodność.

Jednak strony pamięci nie są kopiowane, są one kopiowane przy zapisie, więc zwykle używa się mniej pamięci, niż można sobie wyobrazić.

Rozważ program serwera WWW, który składa się z dwóch kroków:

  1. Czytaj dane konfiguracji i środowiska wykonawczego
  2. Obsługuj żądania strony

Jeśli użyłeś wątków, krok 1 zostałby wykonany raz, a krok 2 w wielu wątkach. Jeśli używałeś „tradycyjnych” procesów, kroki 1 i 2 musiałyby zostać powtórzone dla każdego procesu, a pamięć do przechowywania konfiguracji i danych wykonawczych powielona. Jeśli użyłeś fork (), możesz zrobić krok 1 raz, a następnie fork (), pozostawiając dane środowiska wykonawczego i konfigurację w pamięci, bez zmian, bez kopiowania.

Istnieją więc naprawdę trzy opcje.


7
@ Rozwidlenie Qwertie nie jest takie fajne, łamie wiele bibliotek w subtelny sposób (jeśli używasz ich w procesie nadrzędnym). Tworzy nieoczekiwane zachowanie, które dezorientuje nawet doświadczonych programistów.
MarkR

2
@ MarkR czy mógłbyś podać kilka przykładów lub link do tego, jak rozwidlanie psuje bibliotekę i tworzy nieoczekiwane zachowanie?
Ehtesh Choudhury

18
Jeśli proces rozwidla się przy otwartym połączeniu mysql, zdarzają się złe rzeczy, ponieważ gniazdo jest współużytkowane przez dwa procesy. Nawet jeśli tylko jeden proces korzysta z połączenia, drugi zatrzymuje je przed zamknięciem.
MarkR

1
Wywołanie systemowe fork () jest określane przez POSIX (co oznacza, że ​​jest dostępne w dowolnym systemie Unix), jeśli użyłeś bazowego interfejsu API Linuxa, którym jest wywołanie systemowe clone (), to w rzeczywistości masz jeszcze więcej możliwości w Linuksie niż tylko trzy .
Lie Ryan,

2
@ MarkR Udostępnianie gniazda jest zgodne z projektem. Poza tym jeden z procesów może zamknąć gniazdo za pomocą linux.die.net/man/2/shutdown przed wywołaniem close () na gnieździe.
Lelanthran

53

To zależy od wielu czynników. Procesy są ważniejsze niż wątki i mają wyższy koszt uruchomienia i zamknięcia. Komunikacja międzyprocesowa (IPC) jest również trudniejsza i wolniejsza niż komunikacja między wątkami.

I odwrotnie, procesy są bezpieczniejsze i bardziej bezpieczne niż wątki, ponieważ każdy proces działa we własnej wirtualnej przestrzeni adresowej. Jeśli jeden proces ulega awarii lub przepełnienie bufora, w ogóle nie wpływa na żaden inny proces, podczas gdy wątek ulega awarii, usuwa wszystkie pozostałe wątki w procesie, a jeśli wątek ma przepełnienie bufora, otwiera się dziura bezpieczeństwa we wszystkich wątkach.

Tak więc, jeśli moduły aplikacji mogą działać głównie niezależnie przy niewielkiej komunikacji, prawdopodobnie powinieneś użyć procesów, jeśli możesz sobie pozwolić na koszty uruchomienia i zamknięcia. Wydajność IPC będzie minimalna, a Ty będziesz nieco bezpieczniejszy przed błędami i dziurami w zabezpieczeniach. Jeśli potrzebujesz każdej wydajności, którą możesz uzyskać lub mieć dużo wspólnych danych (takich jak złożone struktury danych), idź z wątkami.


9
Odpowiedź Adama posłuży również jako instruktaż. Aby uzyskać więcej szczegółów, MarkR i efhemient zapewniają dobre wyjaśnienia. Bardzo szczegółowe wyjaśnienie z przykładami można znaleźć na stronie cs.cf.ac.uk/Dave/C/node29.html, ale wydaje się, że jest nieco przestarzałe.
CyberFonic

2
CyberFonic's odnosi się do systemu Windows. Jak mówi ephemient pod Linuksem procesy nie są cięższe. W Linuksie wszystkie mechanizmy komunikacji między wątkami (futex, pamięć współdzielona, ​​potoki, IPC) są również dostępne dla procesów i działają z tą samą prędkością.
Russell Stuart

Korzystanie z IPC jest trudniejsze, ale co się stanie, jeśli ktoś użyje „pamięci współdzielonej”?
abhiarora

11

Inni omawiali te względy.

Być może istotną różnicą jest to, że w systemie Windows procesy są ciężkie i kosztowne w porównaniu do wątków, aw Linuksie różnica jest znacznie mniejsza, więc równanie równoważy się w innym punkcie.


9

Dawno, dawno temu istniał Unix, a w tym starym, dobrym Unixie było mnóstwo narzutów związanych z procesami, więc niektórzy sprytni ludzie zrobili tworzenie wątków, które będą miały tę samą przestrzeń adresową z procesem nadrzędnym i potrzebowali jedynie zredukowanego kontekstu przełącznik, dzięki czemu zmiana kontekstu byłaby bardziej wydajna.

We współczesnym Linuksie (2.6.x) wydajność przełączania kontekstu procesu nie różni się znacznie od wątku (tylko wątek MMU jest dodatkowy dla wątku). Występuje problem ze wspólną przestrzenią adresową, co oznacza, że ​​wadliwy wskaźnik w wątku może uszkodzić pamięć procesu nadrzędnego lub innego wątku w tej samej przestrzeni adresowej.

Proces jest chroniony przez MMU, więc wadliwy wskaźnik spowoduje tylko sygnał 11 i nie spowoduje uszkodzenia.

Zasadniczo używałbym procesów (niewiele obciążeń związanych z przełączaniem kontekstu w Linuksie, ale ochrona pamięci z powodu MMU), ale pthreads, gdybym potrzebował klasy harmonogramu w czasie rzeczywistym, która razem jest inną filiżanką herbaty.

Jak myślisz, dlaczego wątki mają tak duży wzrost wydajności w systemie Linux? Czy masz na to jakieś dane, czy to tylko mit?


1
Tak, mam trochę danych. Przeprowadziłem test, który tworzy 100 000 procesów i test, który tworzy 100 000 wątków. Wersja wątku działała około 9 razy szybciej (17,38 sekund dla procesów, 1,93 dla wątków). Teraz testuje to tylko czas tworzenia, ale w przypadku zadań krótkotrwałych kluczem może być czas tworzenia.
user17918

4
@ user17918 - Czy możesz udostępnić kod użyty do obliczenia wyżej wymienionych czasów ..
codingfreak

jedna wielka inna, z procesami jądro tworzy tabelę stron dla każdego procesu, a teady używają tylko jednej tabeli stron, więc myślę, że to normalne, że wątki są szybsze niż procesy
c4f4t0r

Innym prostym sposobem spojrzenia na to jest to, że TCB jest dość mniejsze niż PCB, więc oczywiste jest, że zmiana kontekstu procesu, która obejmuje PCB, zajmie nieco więcej czasu niż przełączenie wątków.
Karthik Balaguru

5

Jak ściśle powiązane są twoje zadania?

Jeśli mogą żyć niezależnie od siebie, użyj procesów. Jeśli polegają na sobie, użyj wątków. W ten sposób możesz zabić i zrestartować zły proces bez zakłócania działania innych zadań.


4

Aby jeszcze bardziej skomplikować sprawy, istnieje coś takiego jak pamięć lokalna wątków i pamięć współdzielona Unix.

Lokalne przechowywanie wątków pozwala, aby każdy wątek miał osobne wystąpienie obiektów globalnych. Użyłem go tylko podczas tworzenia środowiska emulacji na systemie Linux / Windows dla kodu aplikacji działającego w systemie RTOS. W RTOS każde zadanie było procesem z własną przestrzenią adresową, w środowisku emulacji każde zadanie było wątkiem (ze wspólną przestrzenią adresową). Używając TLS do takich rzeczy jak singletony, byliśmy w stanie mieć osobną instancję dla każdego wątku, tak jak w „prawdziwym” środowisku RTOS.

Pamięć współdzielona może (oczywiście) przynieść korzyści w zakresie wydajności związane z dostępem wielu procesów do tej samej pamięci, ale kosztem / ryzykiem związanym z koniecznością prawidłowej synchronizacji procesów. Jednym ze sposobów na to jest utworzenie przez jeden proces struktury danych we wspólnej pamięci, a następnie wysłanie uchwytu do tej struktury poprzez tradycyjną komunikację między procesami (jak nazwany potok).


1
Użyłem lokalnego przechowywania wątków do zbierania statystyk, kiedy ostatnio pisałem program sieci wątkowych: każdy wątek zapisywał swoje własne liczniki, żadne blokady nie były potrzebne i tylko wtedy, gdy wysłano wiadomość, każdy wątek połączyłby swoje statystyki w globalne sumy. Ale tak, TLS nie jest zbyt często używane ani konieczne. Z drugiej strony pamięć współdzielona ... oprócz wydajnego wysyłania danych, możesz również udostępniać semafory POSIX między procesami, umieszczając je we wspólnej pamięci. To jest niesamowite.
ephemient

4

W mojej ostatniej pracy z LINUX należy pamiętać o bibliotekach. Jeśli używasz wątków, upewnij się, że wszystkie biblioteki, których możesz używać w wątkach, są bezpieczne dla wątków. To mnie piekło kilka razy. W szczególności libxml2 nie jest fabrycznie bezpieczny dla wątków. Można go skompilować z wątkiem bezpiecznym, ale to nie to, co otrzymujesz dzięki aptitude install.


3

Musiałbym się zgodzić z tym, co słyszeliście. Kiedy porównujemy nasz klaster ( xhpli takie), zawsze uzyskujemy znacznie lepszą wydajność dzięki procesom nad wątkami.</anecdote>


3

Decyzja między wątkiem / procesem zależy trochę od tego, do czego będziesz go używać. Jedną z zalet tego procesu jest to, że ma PID i może zostać zabity bez zakończenia rodzica.

Na przykład serwera WWW w świecie rzeczywistym apache 1.3 służył tylko do obsługi wielu procesów, ale w wersji 2.0 dodano abstrakcję, dzięki czemu można przełączać się między nimi. Komentarze wydają się zgadzać, że procesy są bardziej niezawodne, ale wątki mogą dać nieco lepszą wydajność (z wyjątkiem okien, w których wydajność procesów jest do bani i chcesz używać tylko wątków).


2

W większości przypadków wolę procesy niż wątki. wątki mogą być przydatne, gdy masz stosunkowo mniejsze zadanie (narzut procesu >> czas zajęty przez każdą podzieloną jednostkę zadania) i istnieje potrzeba dzielenia pamięci między nimi. Pomyśl o dużej tablicy. Również (offtopic), zauważ, że jeśli twoje wykorzystanie procesora wynosi 100 procent lub jest blisko niego, nie będzie żadnych korzyści z wielowątkowości lub przetwarzania. (w rzeczywistości pogorszy się)


Co masz na myśli mówiąc o braku korzyści? Co powiesz na wykonywanie ciężkich obliczeń w wątku GUI? Przeniesienie ich do wątku równoległego będzie znacznie lepsze z punktu widzenia doświadczenia użytkownika, bez względu na obciążenie procesora.
olegst

2

Wątki -> Wątki dzielą przestrzeń pamięci, są abstrakcją procesora, są lekkie. Procesy -> Procesy mają własną przestrzeń pamięci, jest to abstrakcja komputera. Aby wykonać zadanie równoległe, musisz wyodrębnić procesor. Zaletą stosowania procesu nad wątkiem jest jednak bezpieczeństwo, stabilność, podczas gdy wątek zużywa mniej pamięci niż proces i oferuje mniejsze opóźnienia. Przykładem sieciowym jest Chrome i Firefox. W przypadku Chrome każda karta jest nowym procesem, dlatego użycie pamięci chrome jest wyższe niż firefox, a zapewnione bezpieczeństwo i stabilność jest lepsze niż firefox. Bezpieczeństwo zapewniane przez chrome jest lepsze, ponieważ każda zakładka jest nowym procesem, inna zakładka nie może wtargnąć w przestrzeń pamięci danego procesu.


2

Myślę, że wszyscy wykonali świetną robotę odpowiadając na twoje pytanie. Właśnie dodam więcej informacji o wątku w porównaniu do procesu w systemie Linux, aby wyjaśnić i podsumować niektóre poprzednie odpowiedzi w kontekście jądra. Tak więc moja odpowiedź dotyczy kodu specyficznego dla jądra w systemie Linux. Zgodnie z dokumentacją jądra systemu Linux nie ma wyraźnego rozróżnienia między wątkiem a procesem, z wyjątkiem tego, że wątek używa wspólnej wirtualnej przestrzeni adresowej w przeciwieństwie do procesu. Zauważ też, że jądro Linux używa terminu „zadanie” w odniesieniu do procesu i wątku w ogóle.

„Brak wewnętrznych struktur implementujących procesy lub wątki, zamiast tego istnieje struktura task_struct, która opisuje abstrakcyjną jednostkę planowania o nazwie task”

Również według Linusa Torvaldsa NIE powinieneś w ogóle myśleć o procesie kontra wątek, a ponieważ jest on zbyt ograniczający, a jedyną różnicą jest COE lub kontekst wykonania w kategoriach „oddziel przestrzeń adresową od obiektu nadrzędnego” lub wspólną przestrzeń adresową. W rzeczywistości używa on przykładem serwera WWW, aby jego punkt tutaj (który bardzo polecam czytanie).

Pełny kredyt na dokumentację jądra systemu Linux


-3

Jeśli chcesz współdzielić zasoby, naprawdę powinieneś używać wątków.

Weź również pod uwagę fakt, że przełączanie kontekstu między wątkami jest znacznie tańsze niż przełączanie kontekstu między procesami.

Nie widzę powodu, aby jawnie stosować osobne procesy, chyba że masz ku temu dobry powód (bezpieczeństwo, sprawdzone testy wydajności itp.)


3
Mam przedstawiciela do edycji, ale nie do końca się zgadzam. Przełączanie kontekstu między procesami w systemie Linux jest prawie tak tanie, jak przełączanie kontekstu między wątkami.
efhemient
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.