Pobieranie prezentacji. Proszę czekać

Pobieranie prezentacji. Proszę czekać

UNIX jesień/zima 2013 Wykład 6 Programowanie systemowe w Linux: Wątki i ich synchronizacja dr inż. Wojciech Bieniecki Instytut Nauk Ekonomicznych i Informatyki.

Podobne prezentacje


Prezentacja na temat: "UNIX jesień/zima 2013 Wykład 6 Programowanie systemowe w Linux: Wątki i ich synchronizacja dr inż. Wojciech Bieniecki Instytut Nauk Ekonomicznych i Informatyki."— Zapis prezentacji:

1 UNIX jesień/zima 2013 Wykład 6 Programowanie systemowe w Linux: Wątki i ich synchronizacja dr inż. Wojciech Bieniecki Instytut Nauk Ekonomicznych i Informatyki 1

2 Wielowątkowość – powtórzenie 2 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. 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. Procesy są od siebie izolowane, wątki nie !

3 Schemat procesu i wątków 3 Proces Przestrzeń adresowa Otwarte pliki Procesy potomne Obsługa sygnałów Sprawozdawczość Zmienne globalne Proces 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 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 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 Diagram przejść pomiędzy stanami wątku w UNIX 4

5 Proces z wątkami – w zależności od implementacji 5 Standardowy Unix MS-DOS Linux, MS-Windows, POSIX, OS/2, Solaris

6 Wątki na poziomie użytkownika ang. user-level threads 6 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 7 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 Implementacja wątków w POSIX 8 Program korzystający z funkcji operujących na wątkach POSIX musi zawierać dyrektywę #include Kompilując przy użyciu gcc programy korzystające z tej biblioteki, należy wymusić jej dołączenie, przez użycie opcji -lpthread: gcc -lpthread program.c Każdy proces zawiera przynajmniej jeden główny wątek początkowy, tworzony przez system operacyjny w momencie stworzenia procesu. Aby do procesu dodać nowy wątek należy wywołać funkcję pthread_create. Nowo utworzony wątek zaczyna się od wykonania funkcji użytkownika przekazanej jako argument pthread_create. Wątek działa aż do czasu wystąpienia jednego z następujących zdarzeń: zakończenia funkcji, wywołania funkcji pthread_exit, anulowania wątku za pomocą funkcji pthread_cancel, zakończenia procesu macierzystego wątku, wywołania funkcji exec przez jeden z wątków.

9 Przykład użycia funkcji pthread_create 9 void *wat_fun(void *param) { // tu kod wątku // możemy przekazać wynik return NULL; } int main() { pthread_t id; // Parametr przekazywany wątkowi void *param=NULL; pthread_create(&id,NULL,&wat_fun,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 } Funkcja pthread_create tworzy nowy wątek. Rozpoczyna on pracę od funkcji, której adres przekazano jako trzeci argument. Funkcja pthread_join usypia wywołujący ją wątek do momentu, kiedy wątek o identyfikatorze przekazanym jako pierwszy argument zakończy pracę. Zakończenie pracy wątku – powrót z funkcji, która go rozpoczyna.

10 Opis funkcji systemowych 10 pthread_create(pthread_t *thread, pthread_attr_t *attr, void*(*start_routine) (void*),void *arg) Parametry funkcji wskazywanej przez start_routine czyli parametr arg muszą być przekazane przez wskaźnik na obszar pamięci (zmienną prostą lub strukturę), który zawiera odpowiednie wartości. W Linuxie wątki mogą być tworzone poprzez funkcję pthread_create(). Podobnie jak fork() funkcja pthread_create() tworzy nowy kontekst wykonywania. Nowy wątek współ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 wskazywanej przez start_routine przekazanej jako parametr do pthread_create(). Parametr attr wskazuje na atrybuty wątku, a przez wskaźnik thread zwracany jest identyfikator wątku.

11 Opis funkcji systemowych 11 W Linuxie wątki mogą być także tworzone poprzez funkcję clone(). W rzeczywistości funkcja ta tworzy nowy proces, tak jak fork(), ale pozwala procesowi potomnemu współdzielić z procesem wywołującym część kontekstu wykonania (obszar pamięci, tablica deskryptorów plików, tablica programów obsługi sygnałów) Różnica pomiędzy pthread_create() a clone(). Można wskazać: Wątek kończy swoje działanie w momencie kiedy skończy się wykonywanie funkcji przekazanej jako parametr do clone(). 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. int clone(int (*fn)(void*), void*child_stack, int flags, void*arg); int clone(int (*fn)(void*), void*child_stack, int flags, void*arg);

12 Opis funkcji systemowych 12 pthread_exit(void *retval) oczekiwanie na zakończenie wątku. Funkcja umożliwia zablokowanie wątku w oczekiwaniu na zakończenie innego wątku, identyfikowanego przez parametr th. Jeśli oczekiwany wątek zakończył się wcześniej, funkcja zakończy się natychmiast. Funkcja przekazuje przez parametr thread_return wskaźnik na wynik wątku (wykonywanej przez niego funkcji), przekazany jako parametr funkcji pthread_exit wywołanej w zakończonym wątku. zakończenie wątku. Funkcja powoduje zakończenie wątku i przekazanie retval, jako wskaźnika na wynik. Wskaźnik ten może zostać przejęty przez inny wątek, który będzie wykonywał funkcję pthread_join zakończenie wykonywania innego wątku. Funkcja umożliwia wątkowi usunięcie z systemu innego wątku, identyfikowanego przez parametr thread. pthread_join(pthread_t th, void **thread_return) pthread_cancel(pthread_t thread)

13 Przykład programu z wątkiem 13 #include void *Hello(void *arg) { int i; for ( i=0; i<20; i++ ) { printf("Wątek pisze na ekranie!\n"); sleep(1); } return NULL; } int main(void) { pthread_t mojwątek; if ( pthread_create( &mojwątek, NULL, Hello, NULL)){ printf("błąd przy tworzeniu wątku\n"); abort(); } if ( pthread_join ( mojwatek, NULL ) ) { printf("błąd w kończeniu wątku\n"); exit(); } return 0; } #include void *Hello(void *arg) { int i; for ( i=0; i<20; i++ ) { printf("Wątek pisze na ekranie!\n"); sleep(1); } return NULL; } int main(void) { pthread_t mojwątek; if ( pthread_create( &mojwątek, NULL, Hello, NULL)){ printf("błąd przy tworzeniu wątku\n"); abort(); } if ( pthread_join ( mojwatek, NULL ) ) { printf("błąd w kończeniu wątku\n"); exit(); } return 0; }

14 Problem synchronizacji wątków 14 Architektura RISC: ładuj do rejestru, zwiększ wartość, zapisz wynik. Niech x oznacza jest modyfikowaną zmienną counter. Przyjmijmy, że x=5 Rozważmy dwie możliwe kolejności wykonywania instrukcji poszczególnych procesów. – a) Poprawna wartość 5. – b) Niepoprawna wartość 4. Wybór jednej z tych wielkości niedeterministyczny. Sytuacja wyścigu

15 Problem z synchronizacją 15 #include int X=0; void *fun(void *arg) { int i,j; for (i=0; i<20; i++) { j=X; j++; printf("O"); fflush(stdout); sleep(1); X=j; } return NULL; } #include int X=0; void *fun(void *arg) { int i,j; for (i=0; i<20; i++) { j=X; j++; printf("O"); fflush(stdout); sleep(1); X=j; } return NULL; } int main(void) { pthread_t id_w; int i; if(pthread_create(&id_w,NULL,fun,NULL)) { printf("błąd przy tworzeniu wątku."); abort(); } for ( i=0; i<20; i++) { X++; printf("X"); fflush(stdout); sleep(1); } if (pthread_join(id_w, NULL)) { printf("błąd przy kończeniu wątku."); abort(); } printf("\nX=%d\n", X); exit(0); } int main(void) { pthread_t id_w; int i; if(pthread_create(&id_w,NULL,fun,NULL)) { printf("błąd przy tworzeniu wątku."); abort(); } for ( i=0; i<20; i++) { X++; printf("X"); fflush(stdout); sleep(1); } if (pthread_join(id_w, NULL)) { printf("błąd przy kończeniu wątku."); abort(); } printf("\nX=%d\n", X); exit(0); } Przykład wyścigu

16 Cechy wątków 16 Zalety – Utworzenie i zakończenie wątku zajmuje znacznie mniej czasu niż w przypadku procesu 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 – 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

17 Synchronizacja wątków 17 Stosowane są dwie metody zapewnienia odpowiedniej koordynacji wątków: korzystanie z zamków czyli blokad wzajemnie wykluczających, tzw. muteksów korzystanie z konstrukcji nazywanych zmiennymi warunkowymi. Mutex to inaczej semafor binarny (zezwala na dostęp lub zabrania). Zamknięcia muteksu może dokonać dowolny wątek znajdujący się w jego zasięgu, natomiast otworzyć go może tylko wątek który go zamknął. Wątki, które nie mogą uzyskać dostępu do muteksu są blokowane w oczekiwaniu na niego. Operacje wykonywane na muteksach są niepodzielne. Jeśli za pomocą muteksów trzeba synchronizować wątki kilku procesów, należy odwzorować mutex w obszar pamięci współdzielonej dostępny dla wszystkich procesów.

18 Synchronizacja wątków 18 Gdy niezbędne jest synchronizowanie wątków za pomocą bieżących wartości danych chronionych muteksami, można użyć konstrukcji nazywanych zmiennymi warunkowymi. Zmienna warunkowa jest kojarzona z konkretnym muteksem i predykatem. Podobnie jak mutex może ona być odwzorowana w pamięć współdzieloną, dzięki czemu może być używana przez parę procesów. Głównym zadaniem zmiennych warunkowych jest powiadamianie innych procesów o tym, że dany warunek został spełniony, lub do blokowania procesu w oczekiwaniu na otrzymanie powiadomienia. W momencie kiedy wątek jest blokowany na zmiennej warunkowej, skojarzony z nim mutex jest zwalniany. W oczekiwaniu na to samo powiadomienie zablokowanych może być kilka wątków. Wątek zgłasza powiadamianie wysyłając sygnał do skojarzonej zmiennej warunkowej.

19 Opis funkcji systemowych 19 Do zapewnienia wzajemnego wykluczania używana jest zmienna (np. mutex), zadeklarowana następująco: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(pthread_mutex_t *mutex) zajęcie zamka (zajęcie sekcji krytycznej). Funkcja powoduje zajęcie zamka wskazywanego przez parametr mutex, poprzedzone ewentualnym zablokowaniem wątku do czasu zwolnienia zamka, jeśli został on wcześniej zajęty przez inny wątek. pthread_mutex_unlock(pthread_mutex_t *mutex) zwolnienie zamka (zwolnienie sekcji krytycznej). Funkcja powoduje zwolnienie zamka wskazywanego przez parametr mutex, umożliwiając jego zajęcie innemu wątkowi. pthread_mutex_trylock(pthread_mutex_t *mutex) próba zajęcia zamka. Funkcja powoduje zajęcie zamka wskazywanego przez parametr mutex, jeśli nie jest zajęty przez inny wątek. W przeciwnym przypadku zwraca błąd, nie blokując tym samym procesu.

20 Opis funkcji systemowych 20 Synchronizacja za pomocą zmiennych warunkowych polega na usypianiu i budzeniu wątku w sekcji krytycznej. W tym celu używana jest zmienna warunkowa (tu cond), zadeklarowana następująco: pthread_cond_t cond = PTHREAD_COND_INITIALIZER; pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) oczekiwanie na sygnał. Funkcja powoduje uśpienie wątku na zmiennej warunkowej, wskazywanej przez parametr cond. Na czas uśpienia wątek zwalnia zamek, wskazywany przez parametr mutex, udostępniając tym samym sekcję krytyczną innym wątkom. Po obudzeniu i wyjściu z funkcji (na skutek odebrania sygnału wysłanego przez pthread_cond_signal) zamek zajmowany jest ponownie. pthread_cond_signal(pthread_cond_t *cond) wysłanie sygnału (obudzenie) do jednego z wątków oczekujących na zmiennej warunkowej wskazywanej przez cond.

21 Przykład synchronizacji z muteksem 21 #include int X; pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; void *fun(void *arg) { int i,j; for ( i=0; i<20; i++ ) { pthread_mutex_lock(&mut); j = X; j++; printf("O"); fflush(stdout); sleep(1); X = j; pthread_mutex_unlock(&m); } return NULL; } #include int X; pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; void *fun(void *arg) { int i,j; for ( i=0; i<20; i++ ) { pthread_mutex_lock(&mut); j = X; j++; printf("O"); fflush(stdout); sleep(1); X = j; pthread_mutex_unlock(&m); } return NULL; } int main(void) { pthread_t id_w; int i; if(pthread_create(&id_w,NULL,fun,NULL)) { printf("błąd tworzenia wątku."); abort(); } for ( i=0; i<20; i++) { pthread_mutex_lock(&mut); X++; pthread_mutex_unlock(&mut); printf("o"); fflush(stdout); sleep(1); } if(pthread_join(id_w, NULL ) ) { printf("błąd kończenia wątku."); abort(); } printf("\nX = %d\n", X); exit(0); } int main(void) { pthread_t id_w; int i; if(pthread_create(&id_w,NULL,fun,NULL)) { printf("błąd tworzenia wątku."); abort(); } for ( i=0; i<20; i++) { pthread_mutex_lock(&mut); X++; pthread_mutex_unlock(&mut); printf("o"); fflush(stdout); sleep(1); } if(pthread_join(id_w, NULL ) ) { printf("błąd kończenia wątku."); abort(); } printf("\nX = %d\n", X); exit(0); }

22 Możliwe problemy z wątkami 22 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.

23 Możliwe problemy z wątkami 23 #include /* fprintf */ #include /* errno */ #include /* malloc, free, exit */ #include /* 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 */ }

24 Możliwe problemy z wątkami 24 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

25 Inne problemy z wątkami 25 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

26 Literatura 26 Wojciech Kwedlo, Wykład z Systemów Operacyjnych Wydział Informatyki Politechniki Białostockiej Ważniak – laboratorium z systemów operacyjnych


Pobierz ppt "UNIX jesień/zima 2013 Wykład 6 Programowanie systemowe w Linux: Wątki i ich synchronizacja dr inż. Wojciech Bieniecki Instytut Nauk Ekonomicznych i Informatyki."

Podobne prezentacje


Reklamy Google