Przeglądam tę książkę , Advanced Linux Programming, Mark Mitchell, Jeffrey Oldham i Alex Samuel. Jest z 2001 roku, więc trochę stary. Ale i tak uważam to za całkiem dobre.
Doszedłem jednak do momentu, gdy odbiega on od tego, co mój Linux produkuje w wynikach powłoki. Na stronie 92 (116 w przeglądarce) rozdział 4.5 Implementacja wątku GNU / Linux zaczyna się od akapitu zawierającego to oświadczenie:
Implementacja wątków POSIX w GNU / Linux różni się od implementacji wątków w wielu innych systemach podobnych do UNIX w istotny sposób: w GNU / Linux wątki są implementowane jako procesy.
To wydaje się kluczowe, a później zilustrowane kodem C. Dane wyjściowe w książce to:
main thread pid is 14608
child thread pid is 14610
A w moim Ubuntu 16.04 jest to:
main thread pid is 3615
child thread pid is 3615
ps
wyjście obsługuje to.
Wydaje mi się, że coś musiało się zmienić od 2001 roku do teraz.
Następny podrozdział na następnej stronie, 4.5.1 Obsługa sygnałów, stanowi rozwinięcie poprzedniej instrukcji:
Zachowanie interakcji między sygnałami i wątkami różni się w zależności od systemu uniksowego. W GNU / Linux zachowanie jest podyktowane faktem, że wątki są implementowane jako procesy.
Wygląda na to, że będzie to jeszcze ważniejsze w dalszej części książki. Czy ktoś mógłby wyjaśnić, co się tutaj dzieje?
Widziałem to. Czy wątki jądra Linuksa naprawdę są procesami jądra? , ale to niewiele pomaga. Jestem zmieszany.
To jest kod C:
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void* thread_function (void* arg)
{
fprintf (stderr, "child thread pid is %d\n", (int) getpid ());
/* Spin forever. */
while (1);
return NULL;
}
int main ()
{
pthread_t thread;
fprintf (stderr, "main thread pid is %d\n", (int) getpid ());
pthread_create (&thread, NULL, &thread_function, NULL);
/* Spin forever. */
while (1);
return 0;
}
getpid
zwraca to, co byłoby nazywane identyfikatorem grupy wątków i aby uzyskać unikalny identyfikator dla procesu, którego musisz użyćgettid
. Jednak inne niż jądro, większość ludzi i narzędzi nazywa grupę wątków procesem, a proces nazywa wątkiem, aby zachować spójność z innymi systemami.Odpowiedzi:
Myślę, że ta część
clone(2)
strony człowieka może wyjaśnić różnicę re. PID:Wyrażenie „wątki są implementowane jako procesy” odnosi się do problemu wątków, które miały osobne PID w przeszłości. Zasadniczo Linux pierwotnie nie miał wątków w procesie, tylko oddzielne procesy (z oddzielnymi PID), które mogły mieć pewne wspólne zasoby, takie jak pamięć wirtualna lub deskryptory plików.
CLONE_THREAD
a oddzielenie identyfikatora procesu (*) i identyfikatora wątku sprawia, że zachowanie Linuksa bardziej przypomina inne systemy i bardziej przypomina wymagania POSIX w tym sensie. Chociaż technicznie system operacyjny wciąż nie ma osobnych implementacji wątków i procesów.Obsługa sygnałów była kolejnym problematycznym obszarem ze starą implementacją, co opisano bardziej szczegółowo w dokumencie @FooF, do którego odnosi się w ich odpowiedzi .
Jak zauważono w komentarzach, Linux 2.4 został wydany również w 2001 roku, w tym samym roku, co książka, więc nic dziwnego, że wiadomości nie dotarły do tego druku.
źródło
Masz rację, rzeczywiście „coś musiało się zmienić od 2001 roku do teraz”. Książka, którą czytasz, opisuje świat zgodnie z pierwszą historyczną implementacją wątków POSIX w Linuksie, zwaną LinuxThreads (zobacz także artykuł na Wikipedii ).
LinuxThreads miał pewne problemy ze zgodnością ze standardem POSIX - na przykład wątki nieposiadające PID-ów - i inne poważne problemy. Aby naprawić te wady, Red Hat kierował kolejną implementacją o nazwie NPTL (natywna biblioteka wątków POSIX) w celu dodania niezbędnej obsługi jądra i biblioteki przestrzeni użytkownika w celu osiągnięcia lepszej zgodności z POSIX (biorąc dobre części z innego konkurencyjnego projektu reimplementacji przez IBM o nazwie NGPT („ Posix Next Generation Threads ”), zobacz artykuł w Wikipedii na temat NPTL ). Dodatkowe flagi dodane do
clone(2)
wywołania systemowego (zwłaszczaCLONE_THREAD
to@ikkkachu
wskazuje w jego odpowiedzi ) jest prawdopodobnie najbardziej widoczną częścią modyfikacji jądra. Część pracy w przestrzeni użytkownika została ostatecznie włączona do biblioteki GNU C.Nadal obecnie niektóre wbudowane zestawy SDK systemu Linux używają starej implementacji LinuxThreads, ponieważ używają mniejszej wersji LibC zajmującej pamięć, zwanej uClibc (zwanej także µClibc) , i zajęło sporo czasu, zanim implementacja przestrzeni użytkownika NPTL z GNU LibC została przeniesiona i przyjęta jako domyślna implementacja wątków POSIX, ponieważ ogólnie rzecz biorąc, te specjalne platformy nie starają się podążać za najnowszymi modami z prędkością błyskawicy. Można to zaobserwować, zauważając, że PID dla różnych wątków na tych platformach są również różne w przeciwieństwie do specyfikacji standardu POSIX - tak jak opisuje czytana książka. Właściwie kiedy zadzwonisz
pthread_create()
, nagle zwiększyłeś liczbę procesów z jednego do trzech, ponieważ potrzebny był dodatkowy proces, aby utrzymać bałagan razem.Strona podręcznika Linux pthreads (7) zawiera wyczerpujący i interesujący przegląd różnic między nimi. Innym pouczającym, choć nieaktualnym, opisem różnic jest ten artykuł autorstwa Ulricha Deppera i Ingo Molnara na temat projektu NPTL.
Radzę, abyś nie traktował tej części książki zbyt poważnie. Zamiast tego polecam wątki programistyczne POSIX firmy Butenhof oraz strony podręczników POSIX i Linux na ten temat. Wiele samouczków na ten temat jest niedokładnych.
źródło
Wątki (Przestrzeń użytkownika) nie są implementowane jako takie procesy w systemie Linux, ponieważ nie mają własnej prywatnej przestrzeni adresowej, nadal współużytkują przestrzeń adresową procesu nadrzędnego.
Wątki te są jednak implementowane w celu korzystania z systemu rozliczania procesów jądra, dlatego przydzielane są im własne identyfikatory wątków (TID), ale otrzymują takie same PID i „ID grup wątków” (TGID) jak proces nadrzędny - w przeciwieństwie do rozwidlenie, w którym tworzone są nowe TGID i PID, a TID jest taki sam jak PID.
Wygląda więc na to, że najnowsze jądra miały osobny TID, który można zapytać, to jest inny dla wątków, odpowiedni fragment kodu pokazujący to w każdej z głównych () funkcji thread_funkcji powyżej:
Cały kod z tym to:
Podając przykładowy wynik:
źródło
Zasadniczo informacje w książce są historycznie dokładne, z powodu haniebnie złej historii implementacji wątków w systemie Linux. Ta odpowiedź przeze mnie na powiązane pytanie dotyczące SO służy również jako odpowiedź na Twoje pytanie:
https://stackoverflow.com/questions/9154671/distinction-between-processes-and-threads-in-linux/9154725#9154725
źródło
Wewnętrznie w jądrze Linuksa nie ma procesów ani wątków. Procesy i wątki są głównie koncepcją przestrzeni użytkownika, samo jądro widzi tylko „zadania”, które są planowalnym obiektem, który może nie dzielić żadnych, niektórych lub wszystkich swoich zasobów z innymi zadaniami. Wątki to zadania skonfigurowane do współdzielenia większości zasobów (przestrzeń adresowa, mmapy, potoki, otwarte programy obsługi plików, gniazda itp.) Z zadaniem nadrzędnym, a procesy to zadania skonfigurowane do współdzielenia minimalnych zasobów z zadaniem nadrzędnym .
Kiedy używasz Linux API bezpośrednio ( clone () , zamiast fork () i pthread_create () ), masz większą elastyczność w określaniu ilości zasobów do udostępnienia lub nie, i możesz tworzyć zadania, które nie są w pełni proces ani w pełni wątek. Jeśli używasz tych połączeń niskiego poziomu bezpośrednio, możliwe jest również utworzenie zadania z nowym TGID (traktowanym jako proces przez większość narzędzi użytkownika), które faktycznie współużytkują wszystkie swoje zasoby z zadaniem nadrzędnym lub odwrotnie, aby utworzyć zadanie ze współużytkowanym TGID (traktowane jako wątek przez większość narzędzi użytkownika), które nie współużytkuje zasobów z zadaniem nadrzędnym.
Podczas gdy Linux 2.4 implementuje TGID, jest to głównie tylko dla korzyści księgowania zasobów. Wielu użytkowników i narzędzie przestrzeni użytkownika uważają, że przydatne jest grupowanie powiązanych zadań i raportowanie zużycia zasobów.
Implementacja zadań w Linuksie jest znacznie płynniejsza niż światopogląd procesów i wątków prezentowany przez narzędzia przestrzeni użytkownika.
źródło
Linus Torvalds stwierdził w liście z listami dyskusyjnymi jądra w 1996 r., Że „zarówno wątki, jak i procesy są traktowane jako„ kontekst wykonania ”, który jest„ tylko konglomeratem całego stanu tego CoE .... obejmuje rzeczy takie jak CPU stan, stan MMU, uprawnienia i różne stany komunikacji (otwarte pliki, procedury obsługi sygnałów itp.) ”.
Jak widać, ten program odrodzi 25 wątków jednocześnie, z których każdy będzie spał przez 100 sekund, a następnie ponownie dołączy do programu głównego. Po ponownym dołączeniu wszystkich 25 wątków do programu, program jest gotowy i zakończy działanie.
Za pomocą
top
będziesz mógł zobaczyć 25 instancji programu „Thread2”. Ale nudząca nerka. Produkcjaps auwx
jest jeszcze mniej interesująca ... ALEps -eLf
staje się trochę ekscytująca.Możesz zobaczyć tutaj wszystkie 26 regionów Europy, które
thread2
program utworzył. Wszystkie mają ten sam identyfikator procesu (PID) i identyfikator procesu nadrzędnego (PPID), ale każdy z nich ma inny identyfikator LWP (proces lekki), a liczba LWP (NLWP) wskazuje, że jest 26 CoE - główny program i Odrodziło się 25 wątków.źródło
Jeśli chodzi o Linux, procesy i wątki są w pewnym sensie tym samym. To znaczy są one tworzone z tego samego wywołania systemowego:
clone
.Jeśli się nad tym zastanowić, różnica między wątkami i procesami polega na tym, w których obiektach jądra będą współużytkowane przez dziecko i rodzic. W przypadku procesów nie jest to wiele: otwarte deskryptory plików, segmenty pamięci, do których nie zapisano, prawdopodobnie kilka innych, o których nie mogę myśleć bez końca. W przypadku wątków współdzielonych jest znacznie więcej obiektów, ale nie wszystkie.
Tym, co zbliża wątki i obiekty w Linuksie, jest
unshare
wywołanie systemowe. Obiekty jądra, które zaczynają się jako współdzielone, mogą zostać udostępnione po utworzeniu wątku. Możesz na przykład mieć dwa wątki tego samego procesu, które mają inną przestrzeń deskryptorów plików (przez odwołanie udostępniania deskryptorów plików po utworzeniu wątków). Możesz to przetestować samodzielnie, tworząc wątek, wywołującunshare
oba wątki, a następnie zamykając wszystkie pliki i otwierając nowe pliki, potoki lub obiekty w obu wątkach. Następnie spójrz,/proc/your_proc_fd/task/*/fd
a zobaczysz, że każdytask
(który utworzyłeś jako wątek) będzie miał inne fd.W rzeczywistości zarówno tworzenie nowych wątków, jak i nowych procesów są procedurami bibliotecznymi, które wywołują
clone
poniżej i określają, który z obiektów jądra nowo utworzony proces-wątek-rzeczamajig (tj.task
) Będzie współdzielił z wywołującym procesem / wątkiem.źródło