Pobierz prezentację
Pobieranie prezentacji. Proszę czekać
1
Wykład 6b Wątki, mechanizmy IPC
Systemy operacyjne Wykład 6b Wątki, mechanizmy IPC dr inż. Wojciech Bieniecki Instytut Nauk Ekonomicznych i Informatyki
2
Procesy są od siebie izolowane, wątki nie !
Wielowątkowość Jeden proces może wykonywać się w wielu współbieżnych wątkach (ang. thread). Każdy wątek (inna nazwa: proces lekki, ang. lightweight process) – Ma swój własny stan (Aktywny, Gotowy, Zablokowany, ... ) – Ma swoje wartości rejestrów i licznika rozkazów. – Ma swój własny stos (zmienne lokalne funkcji). – Ma dostęp do przestrzeni adresowej, plików i innych zasobów procesu. Wszystkie wątki procesu współdzielą te zasoby. Procesy są od siebie izolowane, wątki nie ! Operacje zakończenia, zawieszenia procesu dotyczą wszystkich wątków. Przełączanie pomiędzy równoprawnymi wątkami jest „tanie” (szybsze) w porównaniu z przełączaniem pomiędzy tradycyjnymi procesami (nie trzeba przełączać kontekstu pamięci). Wątki zyskują na popularności ponieważ – mając pewne cechy ciężkich procesów – są efektywniejsze w działaniu.
3
Schemat procesu i wątków
Przestrzeń adresowa Otwarte pliki Procesy potomne Obsługa sygnałów Sprawozdawczość Zmienne globalne Wątek 1 Licznik rozkazów Rejestry Stos i wskaźnik stosu Stan Wątek 2 Licznik rozkazów Rejestry Stos i wskaźnik stosu Stan Wątek 2 Licznik rozkazów Rejestry Stos i wskaźnik stosu Stan
4
Proces z wątkami Standardowy Unix MS-DOS
Linux, MS-Windows, POSIX, OS/2, Solaris
5
Cechy wątków Zalety Wady
– Utworzenie i zakończenie wątku zajmuje znacznie mniej czasu niż w przypadku procesu – Możliwość szybkiego przełączania kontekstu pomiędzy wątkami tego samego procesu – Możliwość komunikacji wątków bez pośrednictwa systemu operacyjnego – Możliwość wykorzystania maszyn wieloprocesorowych Wady Źle zachowujący się wątek może zakłócić pracę innych wątków tego samego procesu. W przypadku dwóch procesów o odrębnych przestrzeniach adresowych nie jest to możliwe
6
Wątki na poziomie użytkownika ang. user-level threads
System operacyjny nie jest świadom istnienia wątków. Zarządzanie wątkami jest przeprowadzane przez bibliotekę w przestrzeni użytkownika. Przykład: Wątek A wywołuje funkcję read. Standardowo funkcja systemowa read jest synchroniczna (usypia do momentu zakończenia operacji). Jednak implementacja w bibliotece wywołuje wersję asynchroniczną i przełącza się do wątku B. Rozwiązanie to jest szybkie, ma jednak wady: – Dwa wątki nie mogą się wykonywać współbieżnie na dwóch różnych procesorach. – Nie można odebrać procesora jednemu wątkowi i przekazać drugiemu
7
Wątki na poziomie jądra ang. kernel-level threads
Wątek jest jednostką systemu operacyjnego. Wątki podlegają szeregowaniu przez jądro. W systemie SMP* wątki mogą się wykonywać na różnych procesorach – przetwarzanie równoległe. Windows i Linux wykorzystują tę metodę. *) SMP (ang. Symmetric Multiprocessing, przetwarzanie symetryczne) - architektura komputerowa, która pozwala na znaczne zwiększenie mocy obliczeniowej systemu komputerowego poprzez wykorzystanie dwóch lub więcej procesorów do jednoczesnego wykonywania zadań.
8
Przykład użycia wątków: Serwer WWW
while(TRUE) { getNextRequest(&buf); handoffWork(&buf); } while(TRUE) { waitForWork(&buf); lookForPageInCache(&buf,&page); if(pageNotInCache(&page)) { readPageFromDisk(&buf,&page); } returnPage(&page); Technika puli wątków: Zamiast tworzyć nowy wątek do obsługi kolejnego żądania, mamy zbiór wątków stale gotowych do działania. Nie ponosimy kosztu tworzenia i usuwania wątków.
9
Możliwe problemy z wątkami
Przykład: errno to uniksowy mechanizm zgłaszania błędów przez funkcje libc, a w szczególności jądra. Jeśli funkcja zakończy się błędem, sygnalizuje to zwracając zwykle -1 lub NULL. Program powinien wtedy zajrzeć do zmiennej globalnej errno, żeby dowiedzieć się jaki dokładnie błąd wystąpił. Jeśli funkcja zakończy się pomyślnie zawartość errno nie jest zdefiniowana.
10
Możliwe problemy z wątkami
#include <stdio.h> /* fprintf */ #include <errno.h> /* errno */ #include <stdlib.h> /* malloc, free, exit */ #include <string.h> /* strerror */ extern int errno; int main( void ) { /* deklaracja wskaźnika do tablicy o pojemności 2GB */ char *ptr = malloc( UL ); if ( ptr == NULL ){ puts("malloc failed"); puts(strerror(errno)); } else /* Pomyślnie zaalokowano tablicę free( ptr ); exit(EXIT_SUCCESS); /* exiting program */
11
Możliwe problemy z wątkami
W standardowej bibliotece C, w wersji wielowątkowej, errno jest implementowane jako prywatna zmienna globalna (nie współdzielona z innymi wątkami) Gdy kilka wątków jednocześnie wywołuje funkcje malloc/free - może dojść do uszkodzenia globalnych struktur danych (listy wolnych bloków pamięci) Potrzeba synchronizacji => może prowadzić do spadku wydajności
12
Inne problemy z wątkami
Proces otrzymuje sygnał: – Wszystkie wątki otrzymują sygnał – Wybrany wątek otrzymuje sygnał – Wątek aktualnie aktywny otrzymuje sygnał Proces wykonuje fork. – Czy duplikować jedynie działający wątek, czy też wszystkie wątki ? Proces wywołuje exit. – Zakończyć proces czy też jedynie aktywny wątek ? Anulowanie wątku (ang. cancellation). – Wykonać natychmiast . – Wątek co jakiś czas sprawdza czy nie został anulowany Standard POSIX zawiera odpowiedzi na powyższe problemy.
13
Implementacje wątków – POSIX
Funkcja pthread_create tworzy nowy wątek. Rozpoczyna on pracę od funkcji, której adres przekazano jako trzeci argument. void *thread(void *param) { // tu kod wątku // możemy przekazać wynik return NULL; } Funkcja pthread_join usypia wywołujący ją wątek do momentu, kiedy wątek o identyfikatorze przekazanym jako pierwszy argument zakończy pracę. int main() { pthread_t id; // Parametr przekazywany wątkowi void *param=NULL; pthread_create(&id,NULL,&thread,param); // Funkcja thread w odrębnym wątku współbieżnie // z main. id przechowuje identyfikator wątku void *result; // Czekaj na zakończenie wątku pthread_join(id,&result); // Wynik w result, zamiast &result można // przekazać NULL } Zakończenie pracy wątku – powrót z funkcji, która go rozpoczyna.
14
Obsługa wątków w POSIX W Linuxie wątki mogą być tworzone poprzez funkcję pthread_create(). Podobnie jak fork() funkcja pthread_create() tworzy nowy kontekst wykonywania. Nowy wątek dzieli z tworzącym go procesem PID, przestrzeń adresową, deskryptory plików itp. Wątek kończy swoje działanie w momencie kiedy skończy się wykonywanie funkcji przekazanej jako parametr do pthread_create(). W Linuxie wątki mogą być także tworzone poprzez funkcję clone(). Różnica pomiędzy pthread_create() a clone(). Można wskazać: które typy zasobów będą a które nie będą współdzielone (np. identyfikatory plików, uchwyty sygnałów itp.). jaki będzie posiadała PID procesu macierzystego jaki sygnał (jeśli w ogóle) będzie dostarczał informacji twórcy wątku o zakończeniu wątku . gdzie będzie umieszczony stos nowego wątku. Wątek kończy swoje działanie w momencie kiedy skończy się wykonywanie funkcji przekazanej jako parametr do clone().
15
Włókna – Win32 API Włókna (ang. fibers) koncepcyjnie są to miniwątki, podobnie jak wątki dysponują własnym kontekstem wykonawczym – posiadają własny stos i chronią zawartość rejestrów procesora. Włókna w odróżnieniu od wątków nie są wywłaszczane przez system operacyjny z czasu procesora – za przełączanie włókien jest odpowiedzialna sama aplikacja.
16
BOOL AssignProcessToJobObject(HANDLE hJob, HANDLE hProcess);
Zadania - Windows W systemie Windows 2000 i późniejszych istnieje możliwość zgrupowania pewnej liczby procesów i zarządzania nimi w ramach tak zwanego zadania (ang. job). Funkcje pozwalające zarządzać zadaniami dają większe możliwości kontroli niż standardowe funkcje dedykowane procesom. Zadaniom można nakładać ograniczenia takie jak określenie maksymalnego czasu, w trybie użytkownika, jaki jest przyznawany każdemu procesowi (proces używający więcej czasu jest kończony), maksymalny czas, w trybie użytkownika, przyznany wszystkim procesom zadania itp. Do umieszczenia procesu w zadaniu służy specjalna funkcja systemu: BOOL AssignProcessToJobObject(HANDLE hJob, HANDLE hProcess); Umieszczenie kilku procesów w zadaniu umożliwia między innymi ich równoczesne zakończenie. Dodatkowo istnieje możliwość powiadamiania o tym co się dzieje w zadaniu, np. który z procesów się zakończył i czemu.
17
Powinowactwo Powinowactwo (ang. thread affinity) – powiązanie procesu/wątku ze zdefiniowaną jednostką centralną Istnieją dwa modele powinowactwa: słabe powinowactwo – nie ma gwarancji, że wątek, który wykonywał się na procesorze X będzie ponownie wykonywany na tym samym procesorze; silne powinowactwo – system gwarantuje, że dany proces będzie się wykonywał tylko na procesorach wskazanych przez użytkownika. System Windows 2000 standardowo stosuje słabe powinowactwo. Istnieje grupa funkcji systemowych, która pozwala wymusić silne powinowactwo. Silne powinowactwo jest szczególnie pożądane w komputerach o architekturze NUMA (ang. Non-Uniform Memory Access – nie jednolity dostęp do pamięci).
18
Metody komunikacji międzyprocesowej
Komunikacja międzyprocesowa (ang. Inter-Process Communication — IPC) – sposoby komunikacji pomiędzy procesami systemu operacyjnego. Pojęcie IPC może odnosić się do wymiany informacji w systemach rozproszonych (klastrów, systemów odległych połączonych siecią). Mechanizmy IPC opierają się na budowaniu w pamięci lub na dysku dynamicznych struktur, używanych do transmisji komunikatów pomiędzy procesami
19
Metody komunikacji międzyprocesowej
Lista metod IPC obejmuje: pliki i blokady – najprostsza i najstarsza forma IPC sygnały (ang. signals) – czasami znane jako przerwania programowe łącza nienazwane (ang. pipes) – znane też jako łącza komunikacyjne łącza nazwane (ang. named pipes) – znane też jako nazwane łącza komunikacyjne kolejki komunikatów (ang. message queues) umożliwiają przekazywanie określonych porcji danych pamięć dzielona (ang. shared memory) umożliwiają współdzielenie kilku procesom tego samego fragmentu wirtualnej przestrzeni adresowej semafory (ang. semaphores) umożliwiają synchronizacje procesów w dostępie do współdzielonych zasobów (np. do pamięci współdzielonej) gniazda Uniksa (ang. Unix domain sockets) gniazda (ang. sockets) RPC (ang. Remote Procedure Call) – zdalne wywoływanie procedur.
20
Pliki zwykłe w UNIX/LINUX
Jądro systemu operacyjnego UNIX udostępnia dwie operacje na plikach realizowane odpowiednio przez funkcje systemowe Odczyt – funkcja read Zapis – funkcja write Z punktu widzenia jądra w systemie UNIX plik nie ma żadnej struktury – jest traktowany jako tablica bajtów. Operacje odczytu lub zapisu mogą dotyczyć dowolnego fragmentu pliku, określonego z dokładnością do bajtu. Plik w UNIX identyfikowany jest przez nazwę. Podawanie nazwy pliku przy każdym odwołaniu do niego wymagałoby przeszukiwania katalogów w celu jego odnalezienia. Dlatego wprowadzono została funkcję systemową open, której zadaniem jest zaalokowanie niezbędnych zasobów w jądrze, umożliwiających wykonywanie dalszych operacji na pliku bez potrzeby przeszukiwania katalogów.
21
Pliki zwykłe w UNIX Funkcja open zwraca deskryptor (liczba), który będzie przekazywany jako parametrem dla funkcji systemowych związanych z operacjami na otwartych plikach. Standardowo zajęte są deskryptory: 0 – standardowe wejście, 1 – standardowe wyjście 2 –standardowe wyjście diagnostyczne Przy otwieraniu pliku przekazywany jest tryb otwarcia, określający dopuszczalne operacje, jakie można wykonać w związku z tym otwarciem, np. tylko zapis, tylko odczyt lub zapis i odczyt. Tryb otwarcia może mieć również wpływ na sposób wykonania tych operacji, np. każda operacja zapisu Jądro systemu operacyjnego dostarcza też mechanizm tworzenia plików – dostępny przez funkcję systemową creat, która tworzy plik i otwiera go w trybie do zapisu, zwracając odpowiedni deskryptor.
22
Pliki zwykłe w UNIX Tworzenie i otwieranie plików realizowane jest za pomocą funkcji: open - otwarcie pliku (uogólniona funkcja open umożliwia również utworzenie pliku) creat - utworzenie pliku i otwarcie do zapisu dup - utworzenie kopii deskryptora i nadanie jej pierwszego wolnego numeru z tablicy otwartych plików dup2 - utworzenie kopii deskryptora, umożliwiające określenie jej identyfikatora przez użytkownika close - zamknięcie deskryptora otwartego pliku, unlink - usunięcie dowiązania do pliku Operacje na plikach realizowane są za pomocą funkcji: read - odczyt fragmentu pliku, write - zapis fragmentu pliku, lseek - przesunięcie wskaźnika bieżącej pozycji Powyższe funkcje zdefiniowane są w pliku nagłówkowym fcntl.h. 22
23
Struktury danych w jądrze systemu związane z otwartymi plikami
24
Łącza komunikacyjne Łącza w UNIX są plikami specjalnymi.
Są podobne do plików zwykłych – posiadają swój i-węzeł, posiadają bloki z danymi, na otwartych łączach można wykonywać operacje zapisu i odczytu. Czym różnią się od plików zwykłych • ograniczona liczba bloków – łącza mają rozmiar 4KB – 8KB w zależności od konkretnego systemu, • dostęp sekwencyjny – na łączach można wykonywać tylko operacje zapisu i odczytu, ale nie można wykonywać funkcji lseek) • sposób wykonywania operacji zapisu i odczytu – dane odczytywane z łącza są zarazem usuwane (nie można ich odczytać ponownie) • proces jest blokowany w funkcji read na pustym łączu i w funkcji write, jeśli w łączu nie ma wystarczającej ilości wolnego miejsca, żeby zmieścić zapisywany blok
25
Łącza komunikacyjne – cechy wspólne i różnice
Łącze nazwane (tzw kolejki FIFO). Łącze nienazwane (tzw. potok) Posiada dowiązanie w systemie plików (istnieje jako plik w jakimś katalogu) nie ma dowiązania w systemie plików – istnieje tylko tak długo, jak jest otwarte Może być identyfikowane przez nazwę Jest identyfikowane tylko przez deskryptory Po zamknięciu pozostaje przydzielony i-węzeł, ale wszystkie jego bloki na dysku są zwalniane. Po zamknięciu wszystkich jego deskryptorów przestaje istnieć (zwalniany jest jego i-węzeł i wszystkie bloki) Procesy, które chcą komunikować się za pomocą łącza nienazwanego, muszą znać jego deskryptor.
26
Przykład użycia łącza nazwanego
Utworzone metodą mkfifo łącze musi zostać otwarte przez użycie funkcji open. Funkcja ta musi zostać wywołana przynajmniej przez dwa procesy w sposób komplementarny, tzn. jeden z nich musi otworzyć łącze do zapisu, a drugi do odczytu. Odczyt i zapis danych z łącza nazwanego odbywa się za pomocą funkcji read i write mkfifo("kolejka",0666); desc = open("kolejka" , O_RDONLY); desc = open("kolejka" , O_WRONLY); read(desc); write(desc); close(desc); close(desc); unlink("kolejka");
27
Użycie łączy nienazwanych
28
Sygnały Sygnałem nazywamy asynchroniczne zdarzenie skierowane do procesu. Otrzymanie sygnału przez proces oznacza, że pojawiło się jakieś zdarzenie wyjątkowe, wymagające natychmiastowej reakcji ze strony procesu. od innego procesu (np. SIGINT, SIGKILL) od jądra (np. SIGPIPE, SIGCHLD, SIGALRM) od jądra, ale przez wyjątek sprzętowy (np. SIGSEGV, SIGBUS) Reakcja procesu na otrzymany sygnał Wykonanie akcji domyślnej – najczęściej zakończenie procesu z ewentualnym zrzutem zawartości segmentów pamięci na dysk, czasami zignorowanie sygnału, Zignorowanie sygnału Przechwycenie sygnału tj. podjęcie akcji zdefiniowanej przez użytkownika
29
Sygnały w systemie Unix
Funkcje obsługujące sygnały zawarte są w bibliotece <signal.h>
30
Sygnały czasu rzeczywistego
W Posix dostępne są również sygnały czasu rzeczywistego (realtime, RT) W linuxie mają one wartości 34 – 64. Sygnały czasu rzeczywistego charakteryzują się tym, że: ● Sygnały są kolejkowane ● Wielokrotne wystąpienie tego samego sygnału nie powoduje „zlepiania” w jeden sygnał ● Gdy do kolejki trafiają wielokrotne, nieblokowane sygnały czasu rzeczywistego to sygnały z niższym numerem mają wyższy priorytet. ● Sygnały RT przekazują nie tylko nr sygnału ale także: strukturę siginfo_t oraz kontekst.
31
Ważniejsze funkcje systemowe dla sygnałów
32
Kolejka komunikatów Kolejki komunikatów to specjalne listy (kolejki) w jądrze, zawierające odpowiednio sformatowane dane i umożliwiające ich wymianę poprzez dowolne procesy w systemie. Istnieje możliwość umieszczania komunikatów w określonych kolejkach (z zachowaniem kolejności ich wysyłania przez procesy) oraz odbierania komunikatu na parę rożnych sposobów (zależnie od typu, czasu przybycia itp.).
33
Kolejka komunikatów Za każdą kolejkę komunikatów odpowiada jedna struktura typu msqid_ds Komunikaty danej kolejki przechowywane są na liście, której elementami są struktury typu msg każda z nich posiada informacje o typie komunikatu, wskaźnik do następnej struktury msg oraz wskaźnik do miejsca w pamięci, gdzie przechowywana jest właściwa treść komunikatu Dodatkowo, każdej kolejce komunikatów przydziela sie dwie kolejki typu wait_queue, na których śpią procesy zawieszone podczas wykonywania operacji czytania bądź pisania do danej kolejki.
34
Obsługa kolejki komunikatów
Kolejki komunikatów umożliwiają przesyłanie pakietów danych, nazywanych komunikatami, pomiędzy różnymi procesami. Sam komunikat jest zbudowany jako struktura msgbuf, jego definicja znajduje się w pliku <sys/msg.h>: /* message buffer for msgsnd and msgrcv calls */ struct msgbuf { long mtype; /* typ komunikatu */ char mtext[1]; /* tresc komunikatu */ }; Każdy komunikat ma określony typ i długość. Typ komunikatu nadaje proces inicjujący komunikat. Komunikaty są umieszczane w kolejce w kolejności ich wysyłania. Nadawca może wysyłać komunikaty, nawet gdy żaden z potencjalnych odbiorców nie jest gotów do ich odbioru. Komunikaty są w takich przypadkach buforowane w kolejce oczekiwania na odebranie. Przy odbiorze komunikatu, odbiorca może oczekiwać na pierwszy przybyły komunikat lub na pierwszy komunikat określonego typu. Komunikaty w kolejce są przechowywane nawet po zakończeniu procesu nadawcy, tak długo, aż nie zostaną odebrane lub kolejka nie zostanie zlikwidowana.
35
Pamięć współdzielona Schemat korzystania z pamięci współdzielonej
jest specjalnie utworzonym segmentem wirtualnej przestrzeni adresowej, do którego dostęp może mieć wiele procesów. Jest to najszybszy sposób komunikacji pomiędzy procesami. Schemat korzystania z pamięci współdzielonej jeden z procesów tworzy segment pamięci współdzielonej, dowiązuje go powodując jego odwzorowanie w bieżący obszar danych procesu, opcjonalnie zapisuje w stworzonym segmencie dane. inne procesy mogą odczytywać i/lub zapisywać wartości w pamięci współdzielonej. Każdy proces uzyskuje dostęp do pamięci współdzielonej względem miejsca wyznaczonego przez jego adres dowiązania, stąd każdy proces korzystając z tych samych danych używa innego adresu dowiązania. Kończąc korzystanie z segmentu pamięci proces może ten segment odwiązać, czyli usunąć jego dowiązanie. Kiedy wszystkie procesy zakończą korzystanie z segmentu pamięci współdzielonej, za jego usunięcie najczęściej odpowiedzialny jest proces, który segment utworzył.
36
Semafory Semafor to struktura danych wspólna dla kilku procesów.
Służy do synchronizowania kilku procesów korzystających ze wspólnego zasobu – zapobiega sytuacjom hazardowym – może zapobiec zakleszczeniu lub zagłodzeniu procesów. Semafor to liczba całkowita nieujemna przechowywana w jądrze systemu skojarzona z zasobem o wartości równej liczbie dostępnych zasobów tego typu. Proces, który żąda zasobu sprawdza wartość semafora – wartość dodatnia – zasób jest dostępny. Przed rozpoczęciem korzystania z zasobu proces zmniejsza wartość semafora. – wartość zerowa – nie ma wolnych zasobów i proces musi czekać. – zwolnienie zasobu przez proces – zwiększenie wartości semafora i wysłanie powiadomienia do kolejki procesów oczekujących na zasób W Uniksie mamy dostępne Semafory Systemu V Semafory Posiksowe nazwane Semafory Posiksowe w pamięci wspólnej
37
Porównanie semaforów i muteksów
pthread_mutex_lock(&mutex); /*********************/ /* sekcja krytyczna */ pthread_mutex_unlock(&mutex); sem_wait(&sem); /*********************/ /* sekcja krytyczna */ sem_post(&sem); Niemal identyczne zachowanie. Jednak odblokowanie muteksu może dokonać tylko wątek który go zablokował. Wywołanie odblokowania muteksu więcej niż 1 raz nie jest pamiętane. Muteksy używamy tylko do synchronizacji wątków. Semafory używamy do synchronizacji wątków i procesów
38
Przypadki wykorzystania semaforów
39
Literatura Ważniak – laboratorium z systemów operacyjnych
Wojciech Kwedlo, Wykład z Systemów Operacyjnych Wydział Informatyki Politechniki Białostockiej dr inż. Jerzy Ułasiewicz Programowanie aplikacji współbieżnych – Sygnały i ich obsługa dr Anna Kobusińska, Politechnika Poznańska. Materiały dydaktyczne dr inż. Paweł Paduch, Katedra Informatyki Politechniki Świętokrzyskiej w Kielcach Wykład z Programowania współbieżnego Beej's Guide to Unix IPC Robert Love: Linux. Programowanie systemowe. Helion 2008 Stevens R.W.: Programowanie w środowisku systemu UNIX. WNT, 2002 Havilland K., Gray D., Salama B.: Unix - programowanie systemowe. ReadMe, 1999
Podobne prezentacje
© 2024 SlidePlayer.pl Inc.
All rights reserved.