Wątki są tzw. "lekkimi procesami" - "light weight processes" (LWPs) w przeciwieństwie do normalnych "ciężkich" procesów - "Heavy-weight processes" (HWPs). Każdy proces składa sie z 5 części: kodu programu, danych, stosu, tablicy plików i tablicy sygnałów. HWP nie potrafiąc współdzielić żadnej ze swoich części (jeżeli HWP tworzy potomka poprzez wywołanie funkcji fork(), jedyną częścią procesu, która jest współdzielona przez rodzica i potomka jest kod programu.), mają znacząco większy czas przełączania procesu, bo za każdym razem trzeba zapamiętać każdej części procesu (nie mówiąc o stanie procesora). Także jedyną możliwością dostępu przez nie do wspólnych zmiennych jest wykorzystanie łącz nienazwanych lub pamięci dzielonej.
Wątki redukują narzut systemowy przez współdzielenie swoich części. Przy braku potrzeby zapamiętywania za każdym razem części procesu, wymiana następuje bardziej efektywnie i może być przeprowadzana znacznie częściej. Ze względu na sposób implementacji, wyróżniamy dwa typy wątków: wątki poziomu użytkownika (user-space threads) i wątki poziomu jądra (kernel-space threads).
Wątki poziomu użytkownika rezygnują z zarządzania wykonaniem przez jądro i robią to same. Często jest to nazywane "wielowątkowością spółdzielczą", gdyż proces definiuje zbiór wykonywalnych procedur, które są "wymieniane" poprzez operacje na wskaźniku stosu. Zwykle każdy wątek "rezygnuje" z procesora poprzez bezpośrednie wywołanie żądania wymiany (wysłanie sygnału i zainicjowanie mechanizmu zarządzającego) albo przez odebranie sygnału zegara systemowego . Obecnie wątki poziomu użytkownika mogą szybciej dokonać wymiany niż wątki poziomu jądra, ale ciągły rozwój tych drugich powoduje, że ta różnica jest bardzo mała.
Implementacje wątków poziomu użytkownika napotykają na liczne problemy, które trudno obejść, np:
Różne rozwiązania tych problemów zależą najczęściej bezpośrednio od sprzętowej strony systemu: kontrola monopolizacji czasu poprzez monitor używający swojego własnego zegara, uruchamianie procesów na określonych procesorach i tam startowanie wątków, czy też nadpisywanie funkcji systemowych, żeby były wielowejściowe (nieblokujące operacje wejścia/wyjścia itp.).
Wątki poziomu jądra są często implementowane poprzez dolączenie do każdego procesu tabeli/listy jego wątków. W tym rozwiązaniu system zarządza każdym wątkiem wykorzystując kwant czasu przyznany dla jego procesu-rodzica.
Zaletą takiej implementacji jest zniknięcie zjawiska "kradzenia" czasu wykonania innych wątków przez "zachłanny" wątek, bo zegar systemowy tyka niezależnie i system wydziedzicza "niesforny" wątek. Także blokowanie operacji wejścia/wyjścia nie jest już problemem. Ponadto przy poprawnym zakodowaniu programu, proces może automatycznie wyciągnąć korzyści z istnienia SMP przyśpieszając swoje wykonanie przy każdym dodatkowym procesorze.
Kombinacje obu rozwiązań
Niektóre implementacje używają obu rozwiązań: wątków poziomu użytkownika powiązanych z wątkami jądra. Takie polączenie przynosi korzyści wynikające z obu wersji wątków, choć rozwój wątków jądra spowoduje, że jedyną zaletą tego rozwiązania będzie możliwość współdziałania wielu wątków.
Pierwsze biblioteki wątków pojawiły się dostępne dla jądra w wersji 1.0.9. Były to wątki poziomu użytkownika, bo jądro zaczęło udostępniać wątki dopiero w wersji 1.3.56. Od tego momentu nastąpił stały rozwój idei wielowątkowości (wprowadzenie wielowejściowości funkcji jądra, poprawienie funkcji blokujących) aż do jądra w wersji 2.1 które domyślnie ma wbudowany mechanizm obsługi wątków.
Powrót do spisu treści
Większość bibliotek wykorzystujących wątki poziomu jądra zawiera w sobie własną implementację funkcji clone(). Ponieważ jednak jądro w wersji 2.1 będzie pierwotnie zawierać wątki, więc nieobecna obecnie w bibliotece libc5 definicja clone() będzie obecna w bibliotece glibc.
Obecna biblioteka glibc zawiera następującą definicję funkcji clone():
int clone(int (*fn)(), void **stack, int flags, int argc,... /* args */); gdzie fn: funkcja wykonywana przez wątek stack: stos wątku flags: flagi mówiące co jest współdzielone między wątkiem a rodzicem argc: liczba parametrów przekazywanych do fn /* args */: parametry wymagane przez fn
Znaczenie poszczególnych flag występujących w clone():
CLONE_VM współdzielenie danych i stosu CLONE_FS współdzielenie tablicy plików CLONE_FILES współdzielenie informacji o otwartych plikach CLONE_SIGHAND współdzielenie tablicy sygnałóe;w CLONE_PID (W PLANACH) współdzielenie PIDPowrót do spisu treści
Z powodu istnienia nierównoważnych implementacji wątków, nie powstał dotąd żaden program umożliwiający bezpośrednie obserwowanie działania poszczególnych wątków. Programiści, aby zbadać działanie swojego programu, zmuszeni są więc do stosowania różnych sztuczek:
Poniższa tabela została opracowana na podstawie Thread-FAQ
Nazwa: |
Bare-Bones Threads |
---|---|
Autor: |
Christopher Neufeld [neufeld@physics.utoronto.ca] |
Źródło: |
Dokumentacja: [brak danych] Kod źródłowy: [ftp://caliban.physics.utoronto.ca/pub/linux/bb_threads.tar.gz] |
Opis: |
Ta biblioteka zawiera tylko podstawowe funkcje pozwalające na pracę na pojedynczym procesorze jak i na SMP. Oparta na clone(). |
Nazwa: |
CLthreads |
---|---|
Autor: |
Pavel Krauz [kra@fsid.cvut.cz] |
Źródło: |
Dokumentacja: [brak danych] Kod źródłowy: [ftp://lin.fsid.cvut.cz/pub/linux/clthreads/clthreads-0.1.1.tgz] |
Opis: |
Biblioteka oparta na clone(). Częściowo "poprawia" libc i jest zgodna ze standartem POSIX 1003.1b. |
Nazwa: |
DCEthreads |
---|---|
Autor: |
Michael T. Peterson [mtp@big.aa.net] |
Źródło: |
Dokumentacja: [http://www.aa.net/~mtp/PCthreads.html] Kod źródłowy: [ftp://sunsite.unc.edu/pub/Linux/devel/lang/c/dcethreads-1.0.tar.gz] |
Opis: |
Biblioteka oparta na standardzie POSIX 1003.1c. Zawiera kod źródłowy, środowisko do utworzenia biblioteki w wersji zarówno ELF jak i A.OUT, oraz pełen zestaw stron man-a dotyczących funkcji POSIX.1c. |
Nazwa: |
FSU Pthreads |
---|---|
Autor: |
Frank Mueller [mueller@informatik.hu-berlin.de] |
Źródło: |
Dokumentacja projektu: [http://www.cs.fsu.edu/~mueller/projects.html] Dokumentacja interfejsu: [file://ftp.cs.fsu.edu/pub/PART/publications/pthreads_interface.ps.gz] Dokumentacja projektu (kopia): [http://www.informatik.hu-berlin.de/~mueller/projects.html] Kod źródłowy: [ftp://ftp.cs.fsu.edu/pub/PART/PTHREADS/pthreads.tar.gz] Kod źródłowy (mirror): [http://www.informatik.hu-berlin.de/~mueller/ftp/pub/PART/pthreads.tar.gz] |
Opis: |
Pthreads jest biblioteką dla języka C. Implementuje standard POSIX 1003.4 dla platform: SunOS 4.1.x, Solaris 2.x, SCO UNIX, FreeBSD i Linux. Częściowo "poprawia" libc. Biblioteka wątków użytkownika. |
Nazwa: |
JKthread |
---|---|
Autor: |
Jeff Koftinoff [jeffk@awinc.com] |
Źródło: |
Dokumentacja: [http://turnercom.com/jdk/linux3.html] Kod źródłowy: [http://turnercom.com/jdk/jkthread-1.1.tar.gz] |
Opis: |
Ta biblioteka implementuje wątki poziomu jądra przy użyciu funkcji clone() z wersji 2.0 Linuxa. Posiada niestandardowe API, zawiera C++ wrappers. |
Nazwa: |
LinuxThreads |
---|---|
Autor: |
Xavier Leroy [Xavier.Leroy@inria.fr] |
Źródło: |
Dokumentacja: [http://pauillac.inria.fr/~xleroy/linuxthreads/] Kod źródłowy: [ftp://ftp.inria.fr/INRIA/Projects/cristal/Xavier.Leroy/linuxthreads.tar.gz] |
Opis: |
Biblioteka jest zgodna ze standardem POSIX 1003.1c. W przeciwieństwie do innych implementacji tego standardu, używa wątków poziomu jądra: wątki są tworzone przy pomocy clone() i są zarządzane przez jądro. Częściowo "poprawia" libc. |
Nazwa: |
LWP |
---|---|
Autor: |
Stephen Crane [jsc@doc.ic.ac.uk] |
Źródło: |
Dokumentacja: [ftp://gummo.doc.ic.ac.uk/rex/lwp.ps.gz] Kod źródłowy: [ftp://gummo.doc.ic.ac.uk/rex/lwp.tar.gz] |
Opis: |
Mała, przenośna biblioteka wątków użytkownika przeznaczona dla platform: SUN3/4, mips-ultrix, 386BSD, HP-UX and Linux. Zawiera strony man-a. |
Nazwa: |
NThreads (Numerical Threads) |
---|---|
Autor: |
Thomas Radke [Thomas.Radke@informatik.tu-chemnitz.de] |
Źródło: |
Dokumentacja: [ftp://ftp.tu-chemnitz.de/pub/Local/informatik/linux] Kod źródłowy: [ftp://ftp.tu-chemnitz.de/pub/Local/informatik/linux/nthreads.tgz] |
Opis: |
Biblioteka wątków użytkownika. Zawiera poprawki do jądra umożliwiające wielowątkowość, a także strony man-a. |
Nazwa: |
PCthreads |
---|---|
Autor: |
Michael T. Peterson [mtp@big.aa.net] |
Źródło: |
Dokumentacja: [http://www.aa.net/~mtp/PCthreads.html] Kod źródłowy: [ftp://sunsite.unc.edu:/pub/Linux/devel/lang/c/pthreads-1.0.tar.gz] |
Opis: |
Biblioteka wątków użytkownika - zawiera nieblokujące select(), read() i write(), strony man-a. Wymaga do działania DCEThreads. |
Nazwa: |
Provenzano Pthreads |
---|---|
Autor: |
Christopher A. Provenzano [proven@mit.edu] |
Źródło: |
Dokumentacja: [http://www.mit.edu:8001/people/proven/pthreads.html] Kod źródłowy: [ftp://sipb.mit.edu/pub/pthreads] Kod źródłowy: [ftp://ftp://sunsite.unc.edu/pub/Linux/devel/lang/c/GCC/libc-5.3.12.tar.gz] |
Opis: |
Biblioteka wątków użytkownika z blokującymi funkcjami systemowymi (read, write, connect, accept, sleep, wait, etc.) i "bezpieczną wielowątkowo" biblioteką C (stdio, net, itp.). Oddzielna wersja biblioteki jest rozpowszechniana z kodem źródłowym libc, ale nie musi być wkompilowana domyślnie. |
Nazwa: |
QuickThreads |
---|---|
Autor: |
David Keppel [pardo@cs.washington.edu] |
Źródło: |
Dokumentacja: [ftp://ftp.cs.washington.edu/tr/1993/05/UW-CSE-93-05-06.PS.Z] Kod źródłowy: [ftp://ftp.cs.washington.edu/pub/qt-002.tar.Z] |
Opis: |
Zestaw narzędzi do łatwej budowy biblioteki wątków użytkownika - nie zawiera wielu funkcji wysokiego poziomu! |
Nazwa: |
RexThreads |
---|---|
Autor: |
Stephen Crane [jsc@doc.ic.ac.uk] |
Źródło: |
Dokumentacja: [ftp://gummo.doc.ic.ac.uk/rex/lwp.ps.gz] Kod źródłowy: [ftp://gummo.doc.ic.ac.uk/rex/lwp.tar.gz] |
Opis: |
Biblioteka wątków użytkownika. Posiada niestandardowe API, tworzone są wrappers'y do C++. |
Odpowiedzią Linusa Torvaldsa na często zadawane pytanie "Czy istnieje jakiś przykład użycia clone() ?" jest clone example [clone.c].
Powrót do spisu treści
SMP - Symmetric MultiProcessor System - system wieloprocesorowy
API - Application Programming Interface - zbiór funkcji udostępnianych przez bibliotki czy jądro i pozwalających na ich używanie
Thread - wątek - lekki proces
Joinable thread - wątek, który może zostać przyłączony przez inny wątek
Detached thread - wątek odłączony - nie może zostać przyłączony przez inny wątek
Reentrant - wielowejściowość - możliwość wykonywania tego samego kodu przez wiele wątków na raz
Wrappers - "opakowanie" istniejących bibliotek w API zapewniające odpowiednie właściwości i poziom bezpieczeństwa (u nas wielowejściowości)
Powrót do spisu treściAutorem tej biblioteki jest Xavier Leroy. Dokumentację ściągnąć można z http://pauillac.infria.fr/xleroy/linuxthreads/, a źródła z ftp://ftp.inria.fr/INRIA/Projects/cristal/Xavier.Leroy/linuxthreads.tar.gz.
Biblioteka ta jest zgodna ze standardem POSIX 1003.1c, chociaż nie do końca implementuje zawarte w tym standardzie ustalenia:
Inna cechą charakterystyczną tej biblioteki jest wykorzystywanie sygnałów SIGUSR1 i SIGUSR2. Powoduje to niemożliwość ich używania przez programistę, a także może być przyczyną dosyć niejasnych konfliktów z innymi bibliotekami jądra (np. Xlib czy SVGAlib).
Biblioteka posiada własne wersje niektórych funkcji z libc5, które nie były "bezpieczne" dla wątków (m.in. printf!!!), gdyż albo nie miały wielowejściowości (blokowanie się na operacjach wejścia/wyjścia) albo miały problem ze zmienną errno.
Powrót do spisu treści
Działanie biblioteki Linuxthreads oparte jest na systemie klient-serwer: klient zglasza zapotrzebowanie na utworzenie, zakończenie lub usunięcie wątku do specjalnego wątku-zarządcy, a on wykonuje całą "czarną robotę".
Tak więc każdy proces, który tworzy N wątków, tworzy na prawdę N+2 procesy: jeden wątek główny, od którego poprzez funkcję clone() powstają pozostałe wątki, jeden wątek-zarządca i N właściwych wątków. Ponieważ jednak wątek-zarządca włącza się bardzo rzadko, więc efektywność tego rozwiązania niewiele na tym traci.
Powrót do spisu treści
Tworzenie
Algorytm ten wykonuje się podczas pierwszego wywołania funkcji pthread_create(). /* pthread_initialize_manager() */
Działanie
Algorytm ten wykonuje; się cały czas aż do zakończenia programu. /* __pthread_manager() */
Zakończenie
Algorytm ten wykonuje się kiedy główny wątek kończy pracę. /* __pthread_manager() */