Upload
others
View
1
Download
0
Embed Size (px)
Citation preview
Programowanie Współbieżne
Tomasz Surmacz
Niniejsza publikacja stanowi materiały do wykładu prowadzonego na Politechnice Wrocławskiej i ma charakter autorski. Rozpowszechnianie
dozwolone wyłącznie do użytku prywatnego wśród studentów uczęszczających na wykład, jako materiały uzupełniające.
c©Tomasz Surmacz, Wrocław, 22 listopada 2010
Zagadnienia wstępne
Zagadnienia wstępne
• Semestr 9: 30h laboratorium (013/c3) w cyklu 4h co 2 tygodnie
• Semestr 9: 16h wykładu:
czwartki 11:15-12:45, 23/c3 przez 8 tygodni
Konsultacje i formy kontaktu
• Konsultacje: wtorki 13-15, środy 13-15
• e-mail: [email protected], Subject: PW lub INE5242
• WWW: http://pleple.ict.pwr.wroc.pl/pw/
• Poczta studencka: http://student.pwr.wroc.pl/
Literatura
• Stevens, W. Richard – Programowanie zastosowań sieciowych w systemie UNIX
• Bach, Maurice J. – Budowa systemu operacyjnego UNIX
• Silberschatz, Abraham – Podstawy systemów operacyjnych
• Ben-Ari, M. – Podstawy programowania współbieżnego
Programowanie Współbieżne, INE 5242, 2010/2011 2
Zagadnienia wstępne
Zebr. zakl.
17:00
19:00
17:00
15:00
13:00
11:00
9:00
PN WT SR CZ PT
15:00
13:00
11:00
19:00
17:00
15:00
13:00
11:00
17:00
15:00
13:00
11:0011:00
PN
BUS P
013/c3
9:00
17:00
19:00 19:00
17:00
9:00
konsultacje12:45-14:00 105/c3 13:00
19:00
BUS P17:00-19:00
013/c3
BUS P013/c3
konsultacje17-19 105/c3
SSO/L
18:00-20:35 127P/c3
15:00
SSO/W
15:00-17:45 23/c3
P.Wsp.lab
17.05-20.05 013/c3
P.Wsp.lab
14:15-17:00 013/c3
11:15-13:00
P.Wsp/W23/c3
P.Wsp.lab
17.05-20.05013/c3
P.Wsp.lab
17.05-20.05013/c3
9:009:00
BUS W
23/c3
Kalendarz akademicki (tyg. nieparzyste itp.): http://www.pwr.wroc.pl/14884.xml
Programowanie Współbieżne, INE 5242, 2010/2011 3
Program kursu
Program kursu
• Podstawy systemu UNIX
• System plików i prawa dostępu
• Wejście i wyjście w systemie UNIX
• Procesy, kontekst procesu, sterowanie procesami
• Sygnały, grupy procesów
• Strumienie pipe i FIFO
• Kolejki komunikatów
• Semafory, problemy współbieżności
• Pamięć wirtualna, stronicowanie i segmentacja
• Wątki
Programowanie Współbieżne, INE 5242, 2010/2011 4
1. Podstawy systemu UNIX System UNIX
Podstawy systemu UNIX
System UNIX
Początki – 1970: Ken Thompson, Dennis Ritchie (Bell Laboratories) na maszynie PDP-7, następnie PDP-11/20
• 1973 – pierwsza wersja z jądrem napisanym w C
• 1979 – BSD Unix (University of California, Berkeley)
• 1983 – Unix System V
• 1990 – Unix System V Release 4
• XINU (XINU Is Not UNIX) – system „edukacyjny”
• 1987 – Minix (A. Tanenbaum)
• 1991 – Linux 0.02 (Linus Torvalds)
Brian Kernighan – język C, powstał specjalnie w celu zapewnienia przenośności pisanego oprogramowania
Programowanie Współbieżne, INE 5242, 2010/2011 1-1
1. Podstawy systemu UNIX System UNIX
Podstawowe cechy systemu
• system wieloużytkownikowy
• system wielozadaniowy (wiele procesów)
• podział czasu
• wywłaszczanie procesów
• podsystem plików
• zarządzanie pamięcią
• biblioteki systemowe
• wszystkie urządzenia dostępne przez pliki specjalne
3 podstawowe wersje: (1988)
• Version 7 (wczesny odłam komercyjny)
• Berkeley – BSD 4.1 vs. BSD 4.2 (BSD4.4) (wersja „akademicka”)
• System V – SysV Rel.3, SysV Rel.2 (SVR4) (wersja komercyjna)
Obecnie: Pochodne BSD lub SysV (lub obu)
Programowanie Współbieżne, INE 5242, 2010/2011 1-2
1. Podstawy systemu UNIX System UNIX
Podstawowe różnice:
• parametry wykonania komend (np. ps -ef lub ps -aux)
• różne koncepcje dostępu do terminali (strumienie w SysV)
• inna obsługa przerwań systemowych (sygnałów)
• inne umiejscowienie standardowych programów
Standardy:
• ANSI C
• POSIX (Portable Operating System Interface)
• X/Open
• „Jedną z najwspanialszych zalet związanych ze standaryzacją jest to, że obowiązujących standardów jest takwiele, że zawsze znajdzie się taki, który nam odpowiada.”
Licencje:
• Systemy komercyjne: SysV, SCO, Solaris, HPUX, OSF, ...
• FreeBSD, Linux
• FSF & GNU
• Licencja GPL
Programowanie Współbieżne, INE 5242, 2010/2011 1-3
1. Podstawy systemu UNIX Fundamentalne cechy wszystkich systemów UNIX
Fundamentalne cechy wszystkich systemów UNIX
• Mechanizm ochrony bezpieczeństwa rozgraniczający użytkowników i administratora systemu
• Podział na pracę w trybie użytkownika i w trybie jądra systemu(wspomagany mechanizmami ochrony procesora)
• W trybie jądra wykonują się wszystkie funkcje systemowe
• Przejście do trybu jądra realizowane przez przerwania programowe
• System sterowany zdarzeniami (a nie „polling”) – aplikacje nie tracą czasu, oddając go innym procesom, a samesą usypiane, biernie czekając na wystąpienie oczekiwanego zdarzenia
• Wszystkie1 urządzenia, mechanizmy komunikacji wewnętrznej i sieciowej itp. dostępne są przez pliki i deskryp-tory plików
• System plików umożliwiający kontrolę dostępu do plików i blokowanie prawa do zapisu pomiędzy procesami.
• Pamięć wirtualna i system plików stosują te same mechanizmy – pełna integracja.
• Prosty i spójny interfejs zapewniany przez system operacyjny – około 300 funkcji systemowych (MS-Windows– kilkadziesiąt tysięcy)
1prawie
Programowanie Współbieżne, INE 5242, 2010/2011 1-4
1. Podstawy systemu UNIX System plików
System plików
• Separatorem ścieżki jest znak „/”
• Katalog / to „korzeń” (root) systemuplików
• Wszystkie katalogi/dyski są podłączonejako gałęzie drzewa katalogów – zarównolokalne, jak i zdalne.
• Różne rodzaje systemów plików: UFS,SysV, minix, ext2, MSDOS, NFS
• Komendy związane z plikami: ls, cd,pwd, mkdir, rmdir, cat, mv, cp, rm,mount, umount, df, du, quota
• Pliki: /etc/fstab, /etc/mntab,/etc/exports
• Dziurawe pliki (sparse files – man tar,man rsync)
/
usr bin
local
lib
etcbin
lib
var
spool
bin
log
/dev/dsk/c0t0d1s1
/dev/dsk/c1t2d0s4home
user1user2
cyber:/export/home
/dev/dsk/c0t0d1s0
dev
Programowanie Współbieżne, INE 5242, 2010/2011 1-5
1. Podstawy systemu UNIX Zarządzanie pamięcią
Zarządzanie pamięcią
• Pamięć fizyczna systemu podzielona jest na strony
• Rozmiar strony – z reguły 4-8-32 kB
• Dostęp do stron pamięci realizowany jest przez tablicę deskryptorów pamięci, z pomocą sprzętowych układówMMU
• Pamięć wirtualna
– Swoboda adresowania
– Możliwość zaadresowania większego obszaru pamięci niż dostępny
– Stronicowanie pamięci/plik wymiany – dokonywane automatycznie
Podział pamięci w systemie UNIX
• Pamięć jądra systemu
• Bufory dyskowe
• Plik(i) wymiany (swap)
• Pamięć procesów użytkowników
– Kod programu (TEXT)
– Dane
– Stos
– Stos wywołań systemowych
Programowanie Współbieżne, INE 5242, 2010/2011 1-6
1. Podstawy systemu UNIX Biblioteki
Biblioteki
• Język C określa konstrukcje sterujące wykonaniem programu
• Funkcje nie są częścią języka C, lecz bibliotek systemowych lub programów – nawet najbardziej podstawowe,jak printf ()
• Funkcje systemowe znajdują się w bibliotekach dynamicznych, takich jak libc.so
• Biblioteki statyczne (np. /usr/lib/libc.a)
– dołączane w całości lub częściowo do kodu kompilowanego programu, w trakcie kompilacji
– programy linkowane statycznie są większe, lecz nie wymagają działającego linkera ld
• Biblioteki dynamiczne (np. /usr/lib/libsocket.so.2 )
– dołączane dynamicznie do kodu programu w trakcie jego uruchamiania
– zajmują region pamięci współdzielony z innymi procesami
– mniejsze zapotrzebowanie na pamięć
– mniejsze programy
– dynamiczny linker – programy ld i ldd
• Zmienna środowiskowa LD LIBRARY PATH
Programowanie Współbieżne, INE 5242, 2010/2011 1-7
1. Podstawy systemu UNIX Biblioteki
Podstawowe biblioteki systemowe:
• libc.so – standardowe funkcje wejścia/wyjścia, zarządzania pamięcią, itp.
• libm.so – funkcje matematyczne
• ld.so – linker dynamiczny
• libsocket.so – funkcje sieciowe (SysV)
• libnsl.so – „name server library” – tłumaczenie nazw komputerów na adresy
• libX11.so – funkcje systemu okienkowego X11
Narzędzia przydatne do tworzenia/badania bibliotek dynamicznych
• ldd
• nm, ar, ranlib
• objdump, readelf
Programowanie Współbieżne, INE 5242, 2010/2011 1-8
1. Podstawy systemu UNIX Użytkownicy
Użytkownicy
• System wielozadaniowy (multitasking)
• System wieloużytkownikowy (multiuser)
• Konieczność ochrony użytkowników przed sobą nawzajem
Atrybuty użytkownika – /etc/passwd :
root:x:0:1:Super-User:/root:/sbin/shts:x:103:10:Tomasz Surmacz:/home/ts:/usr/local/bin/tcshnobody:x:60001:60001:Nobody:/:
• Nazwa użytkownika
• Identyfikator (uid)
• Grupa (nazwa i identyfikator – gid)
• Komentarz (pole „GECOS” – imię/nazwisko, nr telefonu, itp.)
• Hasło dostępu – obecnie przeniesione do /etc/shadow
• Katalog domowy
• Interpreter poleceń (shell)
Programowanie Współbieżne, INE 5242, 2010/2011 1-9
1. Podstawy systemu UNIX Użytkownicy
Dla jądra systemu istotne są:
• uid procesu
• gid procesu
• prawa dostępu do pliku
• uprawnienia specjalne (uid==0)
Polecenia operujące na uid/gid:
• newgrp, su, login, id
Programowanie Współbieżne, INE 5242, 2010/2011 1-10
1. Podstawy systemu UNIX Prawa dostępu
Prawa dostępu
• Każdy plik w systemie ma swojego właściciela i właściciela grupowego
• Każdy plik opisany jest przez 3 grupy praw dostępu:
– dla właściciela (user)
– dla grupy (group)
– dla pozostałych użytkowników (other)
• Podstawowe prawa dostępu to
– (r)ead – odczyt
– (w)rite – pisanie
– e(x)ecute – wykonanie
drwxr-x--x 3
ogu
pozostali
grupa
własciciel
111101001 7 5 1
typ (katalog/plik/...)
root wheel
liczba dowiazan
Programowanie Współbieżne, INE 5242, 2010/2011 1-11
1. Podstawy systemu UNIX Prawa dostępu
Przykładowa zawartość katalogu domowego:
drwxr-x--x 44 ts users 2048 Feb 27 16:39 .drwxr-xr-x 47 ts users 3072 Feb 28 00:28 ..-rw------- 1 ts users 3476 May 25 2007 .cshrc-rw-r--r-- 1 ts users 16 Jun 28 2007 .forwarddrwx------ 2 ts mail 1024 Feb 27 03:44 Maildrwxr-xr-x 3 ts users 1024 Jun 2 2007 News-rw-r--r-- 1 ts wheel 4029 Feb 27 03:13 back.tar.gzdrwxr-xr-x 3 ts users 1024 Dec 1 14:31 hgttgdrwxr-xr-x 3 ts users 1024 Feb 27 03:44 ssodrwxr-xr-x 3 ts users 1024 Feb 27 03:19 src
Komendy pozwalające zmieniać prawa dostępu:
• chmod, chgrp
• chown (tylko użytkownik root!)
• umask
Programowanie Współbieżne, INE 5242, 2010/2011 1-12
1. Podstawy systemu UNIX Prawa dostępu
System plików UFS
• Sztywny podział na tablicęi-węzłów i bloki dyskowe
• I-węzeł numer 1 jest zawszekatalogiem głównym systemuplików
• I-węzeł zawiera prawa dostępu,liczbę dowiązań, daty (mtime,ctime, atime) i wskaźnik na numerbloku zawierającego plik
• Katalog jest plikiem specjalnym,zawierającym nazwy plikówi odpowiadające im numeryi-węzłów
• Tablica i-węzłów w rzeczywistościzajmuje około 0.5-2% pojemnościdysku
• Plik znika z dysku dopiero gdyliczba dowiązań zmaleje do 0
• df -k, df -i
/
1 . 1 .. 2 tmp
blok 23450
70 usr11 home 3 lib95 var
ala ma kota. ala ma kota. ala ma kota ala ma kota. ala ma kota. ala ma kota. ala
blok 39834
81 398340644 83 2125.09.2003
f
3 23450 d0755 0 013.10.2003
2 33220 d1777 0 010.10.2003
1 23450 d0755 0 013.09.2003
2 . 1 ..81 kot-ali
blok 33220
93 plik
Programowanie Współbieżne, INE 5242, 2010/2011 1-13
1. Podstawy systemu UNIX Prawa dostępu
Bity specjalne:
• -rwsr-xr-x – set-user-id
• -rwxr-sr-x – set-group-id
• -rw------T – sticky bit
• drwxr-sr-x – set-group-id
• drwxrwxrwt – sticky bit
• prw-rw-rw- – strumień (named pipe)
• srw-rw-rw- – gniazdo w domenie UNIX (UNIX-domain socket)
• brw-rw---- – urządzenie blokowe (block device)
• crw-rw---- – urządzenie znakowe (character device)
• lrwxrwxrwx – dowiązanie symboliczne (symbolic link)
Programowanie Współbieżne, INE 5242, 2010/2011 1-14
1. Podstawy systemu UNIX Wejście/wyjście
Wejście/wyjście
• standardowe wejście – stdin
• standardowe wyjście – stdout
• wyjście błędów – stderr
• strumień pipe
• przekierowanie we/wy: < > << >> | >& |&
grep cat -n sort -t: +1
/etc/passwd
tee abc
abc
grep :0: < /etc/passwd | cat -n | tee abc | sort -t: 1+
• system plików
• katalogi, podkatalogi, pliki
Programowanie Współbieżne, INE 5242, 2010/2011 1-15
1. Podstawy systemu UNIX Środowisko pracy i środowisko programistyczne
Środowisko pracy i środowisko programistyczne
Interpreter poleceń – shell
• /bin/csh
• /bin/sh
• /sbin/sh
• bash
• tcsh
• Inne (zsh, ksh, emacs ;-))
Edytory tekstu
• vi
• emacs
• jed
• joe
Kompilatory
• cc
• gcc
• g++
• Program make wywołujący odpowiednie kompilatory i linker
• Linkowanie – ld lub gcc
• Pliki nagłówkowe: /usr/include, /usr/local/include
• Biblioteki: /usr/lib, /usr/local/lib
Uruchamianie programów
• Debugger systemowy adb (debugowanie i zmiana parametrówjądra działającego systemu)
• Debugger GNU gdb
• Ścieżka wykonania – katalog . (kropka)
Programowanie Współbieżne, INE 5242, 2010/2011 1-16
2. Procesy
Procesy
• tworzone funkcją fork()
• unikalny pid
• przodek (rodzic)—potomek (dziecko)
• grupa procesów i przewodnik grupy
• przełączanie procesów/przełączaniekontekstu
• tablica procesów
• priorytety
• nice
Stany procesów:
t�ry� b
� u� zy� t
�k.�
t�ry� b
� j
�a� d
�ra� .�
u� s�´p io ny� g� o t�o w� y�
Programowanie Współbieżne, INE 5242, 2010/2011 2-1
2. Procesy Kontekst procesu
Kontekst procesu
• Każdy proces posiada własne segmenty kodu, danych i stosu.
• Jądro systemu, poprzez system obsługi pamięci wirtualnej, w chwili wykonywania procesu ma dostęp do jednegosegmentu kodu, jednego segmentu danych i jednego(∗) segmentu stosu.
• Tablica procesów zawiera wskaźniki do zapamiętanych kontekstów poszczególnych procesów – zawierającychich prywatne mapy odwzorowujące wszystkie trzy segmenty programu na segmenty pamięci fizycznej.
CPU
1
2
3
4
5
RAM
CPU
1
2
3
4
5
RAMproces 1
proces 2
proces 1
proces 2
kod
dane
stos
kod
dane
stos
kod
dane
stos
kod
dane
stos
kod
dane
stos
kod
dane
stos
• Przełączenie kontekstu polega na zapamiętaniu aktualnego stanu procesu (odwzorowanie segmentów pamięci,stan procesora) w jego obszarze kontekstu, po czym wczytaniu do procesora kontekstu innego procesu.
Programowanie Współbieżne, INE 5242, 2010/2011 2-2
2. Procesy Sterowanie procesami z powłoki użytkownika
Sterowanie procesami z powłoki użytkownika
• Każdy proces zwraca wartość: 0 – ok, >0 – błąd
• Separator komend: znak ; lub & lub znak nowej linii
• Uruchamianie w tle: proces &
• Łączenie procesów strumieniem: proces1 | proces2
• Warunkowe wykonanie drugiego procesu:
– jeśli pierwszy zakończył się poprawnie: proces1 && proces2
– jeśli pierwszy zakończył się błędem: proces1 || proces2
• Przekierowanie wyjścia błędów:
– csh: proces >& plik oraz proces1 |& proces2
– sh: proces > plik 2>&1
• Zatrzymanie procesu: stop %nazwa, Klawisz Ctrl-Z, kill -STOP, kill -SUSP
• Zabicie procesu lub wysłanie sygnału: kill, kill -WINCH, kill -HUP, kill -KILL itp.
• Ponowne uruchomienie zatrzymanego procesu: fg
• Ponowne uruchomienie procesu w tle: bg
• Sprawdzenie listy działających procesów: jobs, a także ps
Programowanie Współbieżne, INE 5242, 2010/2011 2-3
2. Procesy Tworzenie nowych procesów
Tworzenie nowych procesów
• Do tworzenia nowych procesów służy funkcja fork() ze standardowej biblioteki libc.so.
• Nowy proces, utworzony funkcją fork() jest dokładną kopią rodzica i wykonuje się w tym samym miejscu – zawywołaniem funkcji fork()
printf("ok\n");a=7;p=fork();if (p==-1) { ...
printf("ok\n");a=7;p=fork();if (p==-1) { ...
printf("ok\n");a=7;p=fork();if (p==-1) { ...
printf("ok\n");a=7;p=fork();if (p==-1) { ...
pid=1234
pid=1234 pid=1240
• Oba procesy nie różnią się niczym, poza wartością zwróconą przez funkcję fork():
– rodzic: numer procesu potomnego
– dziecko: wartość 0
Programowanie Współbieżne, INE 5242, 2010/2011 2-4
2. Procesy Tworzenie nowych procesów
Tworzenie nowych procesów (c.d.)
• Sposobem na rozróżnienie obu procesów jest zbadanie wartości zwracanejprzez fork().
pid=fork();switch (pid) {case -1: fprintf(stderr, "Błąd fork!\n");
exit(1);
case 0: /* dziecko / child */printf("Tu pisze dziecko. pid rodzica=%d\n", getppid());break;
default: /* rodzic / parent */printf("To drukuje rodzic. pid dziecka=%d\n", pid);
}
• Inne funkcje: getpid(), getppid(), wait(), kill(), exec()
printf("ok\n");a=7;p=fork();if (p==-1) { ...
printf("ok\n");a=7;p=fork();if (p==-1) { ...
printf("ok\n");a=7;p=fork();if (p==-1) { ...
printf("ok\n");a=7;p=fork();if (p==-1) { ...
pid=1234
pid=1234 pid=1240
Programowanie Współbieżne, INE 5242, 2010/2011 2-5
2. Procesy Tworzenie nowych procesów
Funkcja fork()
• przydziela nowemu procesowi pozycjęw tablicy procesów
• przydziela nowemu procesowi nowy,unikalny identyfikator
• tworzy kopię kontekstu procesumacierzystego, kopiując segmentypamięci lub zwiększając licznik odwołańdo segmentu (np. współdzielonegosegmentu kodu)
• otwarte pliki rodzica pozostawia otwartew dziecku, a więc zwiększa liczniki wtablicy plików i i-węzłów
• przekazuje rodzicowi pid dziecka,a dziecku – wartość 0.
• nowy proces, choć nie był jeszcze nigdywykonywany, „budzi się” tak, jakbyzasnął w oczekiwaniu na zasób(wychodzi ze stanu uśpienia)
t�ry� b
� u� zy� t
�k.�
t�ry� b
� j
�a� d
�ra� .�
u� s�´p io ny� g� o t�o w� y�
fork()
w� y� w� ła� s� zc z.�
u� t�w� o rzo ny�
zo mb�ie�
Programowanie Współbieżne, INE 5242, 2010/2011 2-6
2. Procesy Tworzenie nowych procesów
Funkcje exec()
• Rodzina funkcji: execl(), execv(), execle(),execve(), execlp(), execvp()
• nazwa funkcji określa jednocześnie zakresprzekazywanych parametrów do nowegoprogramu (wektor argumentów, zmienneśrodowiskowe) oraz to, czy przeszukiwać ścieżkę(zmienną $PATH)
• 1. argument – rzeczywista ścieżka douruchamianego programu lub skryptu
• 2. argument i dalsze – paramentry wywołania
• Wywołanie nadpisuje w aktualnym kontekścieprocesu segmenty kodu, danych i stosu nowymprogramem i wykonanie funkcji main()
if ((pid=fork())==0) {execl("/bin/ls", "ls", "-la", "/tmp", NULL);printf("Błąd!!! Ta instrukcja nie ma prawa się wykonać!\n");
} else {wait(NULL);
}
Nowy program dziedziczy:
• numer procesu i numer procesu rodzicaoraz przynależność do grupy procesów
• wartość nice
• wartość umask
• priorytet
• uid, gid i przynależność do grup użyt-kowników
• katalog bieżący
• limity zasobów
• otwarte plikia
• kilka innych wartości dotyczących blokadna plikach, semaforów i obsługi sygnałów(man exec)
aniekoniecznie! – uwaga na fcntl() i FD CLOEXEC
Programowanie Współbieżne, INE 5242, 2010/2011 2-7
2. Procesy Sygnały
Sygnały
Pozwalają zasygnalizować wystąpienie sytuacji specjalnej, takiej jak:
• śmierć potomka (przez wywołanie exit() lub w inny sposób)
• wyjątek – np. próba dostępu do pamięci poza przyznany zakres adresów, próba zapisu do pamięci z atrybutemread-only, itp.
• niespodziewany błąd podczas wykonywania programu (np. pisanie do łącza, którego już nikt nie czyta)
• błąd, z którym system nie potrafi sobie poradzić, np. brakpamięci na wykonanie exec() gdy już zostały zwolnione staresegmenty programu
• pobudka, czyli SIGALARM wysyłany na życzenie procesuprzez system
• interakcja z terminalem – klawisz BREAK, SUSPEND, itp.
• wysłanie sygnału przez inny proces
• wykonywanie programu krok po kroku przez debugger
Sygnały są obsługiwane tylko przy przejściu między trybem jądrasystemu a trybem użytkownika
tryb u¿ytk.
tryb j–dra.
u¶piony gotowy
fork()
wyw‡aszcz.
utworzony
zombie
sprawd…
sprawd… i obs‡u¿
Programowanie Współbieżne, INE 5242, 2010/2011 2-8
2. Procesy Sygnały
• Listę dostępnych sygnałów można w każdym systemie sprawdzić pisząc kill -list lub przeglądając plik/usr/include/sys/signal.h lub /usr/include/signum.h
#define SIGHUP 1 /* hangup */#define SIGINT 2 /* interrupt (rubout) */#define SIGQUIT 3 /* quit (ASCII FS) */...#define SIGFPE 8 /* floating point exception */#define SIGKILL 9 /* kill (cannot be caught or ignored) */#define SIGBUS 10 /* bus error */#define SIGSEGV 11 /* segmentation violation */#define SIGSYS 12 /* bad argument to system call */#define SIGPIPE 13 /* write on a pipe with no one to read it */#define SIGALRM 14 /* alarm clock */#define SIGTERM 15 /* software termination signal from kill */#define SIGUSR1 16 /* user defined signal 1 */#define SIGUSR2 17 /* user defined signal 2 */...#define SIGLOST 37 /* resource lost (eg, record-lock lost) */...
• Część sygnałów jest domyślnie ignorowana, część powoduje zakończenie procesu
• Każdy sygnał z wyjątkiem SIGKILL i SIGSTOP można przechwycić, rejestrując odpowiednią funkcję obsługisygnału za pomocą signal() lub sigset()
• Jeśli funkcja przechwytująca ma ignorować sygnał, wystarczy wywołać signal(SIG IGN) lub sigignore()
• Funkcją sigpause(numer sygnału) można zawiesić proces aż do momentu otrzymania żądanego sygnału
• Na czas obsługi sygnału przyjmowanie sygnałów tego samego typu zostaje zablokowane automatycznie (adodatkowo inne sygnały można blokować i odblokowywać wołając sighold() i sigrelse())
Programowanie Współbieżne, INE 5242, 2010/2011 2-9
2. Procesy Kończenie procesu
Grupy procesów
• grupę może stanowić np. interpreter poleceń (shell) i wszystkie procesy przez niego uruchamiane – naciśnięcieBREAK itp. wysyła sygnał nie tylko do wykonywanego procesu, ale i do interpretera.
• ustawienie grupy – funkcja setpgrp()
Kończenie procesu
• wait()
• exit()
• Zakończony proces staje się zombie i powoduje wysłanie do rodzica sygnału SIGCLD
• Wywołanie przez rodzica funkcji wait() pozwala odebrać status zwrócony przez exit()
• „uwolniony” zombie ostatecznie znika z systemu
• wywołanie wait(), gdy nie ma żadnego zombie, zawiesza proces, do momentu gdy któreś z dzieci zakończyżywot lub gdy już nie będzie żadnego. (Zawieszenie kończy się także po odebraniu sygnału przez proces).
Różne funkcje wait():
pid_t wait (int *status);pid_t waitpid (pid_t pid, int *status, int options);pid_t wait3 (int *status, int options, struct rusage *rusage);pid_t wait4 (pid_t pid, int *status, int options, struct rusage *rusage);
Programowanie Współbieżne, INE 5242, 2010/2011 2-10
3. Strumienie pipe i FIFO Strumienie pipe
Strumienie pipe i FIFO
Strumienie pipe
• pozwalają łączyć spokrewnione ze sobąprocesy
• transmisja jednokierunkowa
• z reguły łączą wyjście stdout jednegoprocesu z wejściem stdin innego
• tworzone funkcją pipe(int fd[2]) –wypełnia 2-elementową tablicędeskryptorów
• fd[1] jest końcem otwartym do pisania
proces 2
jadro systemuj–dro systemu
fd[1] fd[0]
proces 1
• fd[0] jest końcem otwartym do czytania
• oba końce to „zwykłe” deskryptory plików – można na nich operować takimi funkcjami jak read(), write(),fprintf (), czy close().
• jądro systemu gwarantuje, że operacje zapisu nie przekraczające rozmiaru strumienia są niepodzielne
Programowanie Współbieżne, INE 5242, 2010/2011 3-1
3. Strumienie pipe i FIFO Pisanie do strumienia pipe
Pisanie do strumienia pipe
proces 2
jadro systemuj–dro systemu
fd[1] fd[0]
proces 1
• strumień pipe pełni jednocześnie rolę bufora o pojemności 4kB
• zapisanie danych, gdy jest miejsce w strumieniu, powoduje natychmiastowy powrót z funkcji write() itp.
• zapisanie danych powyżej maksymalnego rozmiaru powoduje zablokowanie procesu, do czasu aż zwolni sięmiejsce w strumieniu (ktoś te dane przeczyta)
• za pomocą odpowiednich funkcji fcntl() lub ioctl() można ustawić opcję O_NDELAY, lecz wówczas proces piszącymusi być przygotowany na to, że może mu się udać zapis tylko części wysyłanych danych
• pisanie do strumienia, którego nikt nie czyta (jego drugi koniec został zamknięty) powoduje wysłanie doprocesu piszącego sygnału SIGPIPE
• zamknięcie zapisywanego końca powoduje, że proces czytający w wyniku wywołania funkcji read() otrzymawartość 0
Programowanie Współbieżne, INE 5242, 2010/2011 3-2
3. Strumienie pipe i FIFO Czytanie ze strumienia pipe
Czytanie ze strumienia pipe
• otrzymywane dane stanowią strumień nie podzielony na żadnepakiety lub inne fragmenty
• czytanie ze strumienia może zwrócić mniej danych, niżzażądano
• jeśli strumień jest pusty i zamknięty do zapisu, funkcja read()zwraca wartość 0, oznaczającą EOF.
• jeśli strumień jest pusty, ale otwarty do zapisu, funkcja read()zostaje zablokowana, do momentu gdy można będzie cośprzeczytać
• blokowania można uniknąć stosując opcję O_NDELAY w funkcjifcntl() lub korzystając z funkcji select()
• zamknięcie strumienia do odczytu, gdy ciągle znajdowały sięw nim nie przeczytane dane, generuje sygnał SIGPIPEwysyłany do procesu piszącego
• zamknięcie pustego strumienia danych nie powoduje żadnychkonsekwencji dla procesu piszącego – dopóki nie spróbujeczegoś zapisać.
• dzięki blokowaniu możliwe jest wykorzystanie strumieni dowzajemnej synchronizacji procesów
proces 2
jadro systemuj–dro systemu
fd[1] fd[0]
proces 1
Programowanie Współbieżne, INE 5242, 2010/2011 3-3
3. Strumienie pipe i FIFO Łączenie dwóch procesów strumieniem pipe
Łączenie dwóch procesów strumieniem pipe
fd[1] fd[0]
proces potomny
jadro systemu
proces
proces
j–dro systemu
fd[1] fd[1]
fd[0] fd[0]
fd[1] fd[0]
j–dro systemu
int fd[2];if (pipe(fd)==-1) perror("pipe");...
switch (p=fork()){ case 0: /*dziecko*/ ... break; default: /*rodzic*/ ...}
Programowanie Współbieżne, INE 5242, 2010/2011 3-4
3. Strumienie pipe i FIFO Łączenie dwóch procesów strumieniem pipe
Łączenie dwóch procesów strumieniem pipe (c.d.)
proces potomny
jadro systemu
proces
j±dro systemu
fd[1]
fd[0]
fd[1] fd[0]
/*rodzic*/
close(fd[0]);write(fd[1], buf, strlen(buf)+1);
/*dziecko*/
close(fd[1]);n=read(fd[0], buf, BUFSIZE);
proces potomny
jadro systemujadro systemu
proces
j±dro systemu
fd[1] fd[1]
fd[0] fd[0]
fd[1] fd[0]
switch (p=fork()){ case 0: /*dziecko*/ ... break; default: /*rodzic*/ ...}
Programowanie Współbieżne, INE 5242, 2010/2011 3-5
3. Strumienie pipe i FIFO Tworzenie strumieni stdout-stdin
Tworzenie strumieni stdout-stdin
• Do kopiowania otwartych deskryptorów służą funkcje dup() i dup2 ()
• dup(n) kopiuje deskryptor n na najniższy wolny numer deskryptora
• Poniższy program:
int fdes;fdes=open("/tmp/test", "rt");close(0);dup(fdes);
spowoduje przepisanie deskryptora fdes do deskryptora nr 0.
• to samo można osiągnąć za pomocą dup2 (fdes, 0)
Jak korzystając z dup() uzyskać efekt taki sam jak w dup2 (fdes, 10)?
Programowanie Współbieżne, INE 5242, 2010/2011 3-6
3. Strumienie pipe i FIFO Tworzenie strumieni stdout-stdin
Tworzenie strumieni stdout-stdin
Najczęstszym zastosowaniem jest zastąpienie standardowego wejścia (lub wyjścia) strumieniem pipe, przed wyko-naniem funkcji exec():
int fd[2];
pipe(fd);if ((f=fork()) == 0) {/* dziecko -- będzie czytać */close(fd[1]);close(0);dup(fd[0]);execlp("cat", "cat", "-n", NULL);fprintf(stderr, "exec się nie udał!\n");exit(1);
} else {/* rodzic lub błąd fork() */...wait();
}
Programowanie Współbieżne, INE 5242, 2010/2011 3-7
3. Strumienie pipe i FIFO Funkcja popen()
Funkcja popen()
Podobny efekt można osiągnąć za pomocą funkcji popen():
#include <stdio.h>FILE *popen(const char *command, const char *mode);int pclose(FILE *stream);
• popen() tworzy strumień pipe oraz proces potomny, w którym wykonuje /bin/sh, przekazując mu jako para-mentr nazwę programu do wykonania
• jeśli *type==’r’, to standardowe wyjście uruchomionego podprocesu jest połączone ze strumieniem, a procesgłówny może czytać ze zwróconego deskryptora
• jeśli *type==’w’, zwrócony koniec jest otwarty do zapisu i połączony ze standardowym wejściem uruchomio-nego podprocesu
• zakończenie komunikacji może wymagać użycia pclose(), konieczna też jest synchronizacja za pomocą wait().
Wady stosowania popen():
• za każdym razem niepotrzebnie uruchamiany jest /bin/sh;
• brak pełnej kontroli nad wejściem i wyjściem z podprocesu;
• nie jest możliwe jednoczesne przechwycenie wejścia i wyjścia ani rozdzielenie std. wyjścia od wyjścia błędów;
• funkcja popen() NIGDY i pod żadnym pozorem nie powinna być stosowana w programach typu set-user-id(zbyt łatwo przeoczyć złe dane przekazywane do interpretera sh).
Programowanie Współbieżne, INE 5242, 2010/2011 3-8
3. Strumienie pipe i FIFO Strumienie FIFO
Strumienie FIFO
Zasadnicze różnice w stosunku do strumieni pipe:
• Posiadają dowiązanie w systemie plików – tworzone funkcją mknod() lub komendą mknod.
• Mogą być używane przez procesy nie spokrewnione ze sobą, a nawet procesy różnych użytkowników.
• Funkcja open() używana do otwierania kolejki FIFO do zapisu blokuje proces, aż do momentu otwarcia kolejkido odczytu, i odwrotnie. Dopiero gdy kolejka otwarta jest jednocześnie do zapisu i odczytu obie funkcje open()powracają ze stanu uśpienia;
• „Sprzątanie” tymczasowych kolejek może wymagać usunięcia ich z systemu plików funkcją unlink().
• Kolejki FIFO mają z reguły większy rozmiar bufora (4–16 kB)
Podobieństwa:
• Zamknięcie końca używanego do pisania (dokładniej: wszystkich końców) generuje u czytelników EOF;
• Zamknięcie końca używanego do czytania generuje sygnał SIGPIPE wysyłany do wszystkich piszących dostrumienia;
• Zapis i odczyt przez standardowe funkcje I/O, takie jak write() czy read();
• Niepodzielność zapisów mniejszych niż rozmiar strumienia;
• Blokowanie procesów w przypadku przepełnienia lub pustego strumienia (i możliwość zastosowania O_NDELAY).
Programowanie Współbieżne, INE 5242, 2010/2011 3-9
3. Strumienie pipe i FIFO Korzystanie ze strumieni FIFO
Korzystanie ze strumieni FIFO
#include <stdio.h>#include <errno.h>
main(){ int fd;int err;
err=mknod("/tmp/fifo1", S_IFIFO | 0660, 0)if (err<0 && errno!=EEXIST) {fprintf(stderr, "Nie mogę utworzyć FIFO\n");exit (1);
}
fd=open("/tmp/fifo1, O_RDONLY); /* blokada? */if (fd<0) {fprintf(stderr, "Nie mogę otworzyć FIFO do czytania\n");exit(2);
}
read(fd, ... ...);...close(fd);unlink("/tmp/fifo1");
}
Programowanie Współbieżne, INE 5242, 2010/2011 3-1
4. Kolejki komunikatów
Kolejki komunikatów
• lista (jednokierunkowa) zawierająca komunikaty o określonym maksymalnym rozmiarze;
• nowe komunikaty dodawane są na końcu listy, zachowując kolejność ich wysyłania
• każdy komunikat ma dodatkowy parametr zwany typem, co pozwala na obsługę kilku „strumieni” komunikatóww ramach jednej kolejki (poprzez selektywne odbieranie komunikatów wybranego typu).
msgsnd()
msgrcv()
kolejkakomunikat�w(w przestrzenij–dra systemu)
5
5
3
2
2
2
15
37
2
3
3
2
7
2
2
Programowanie Współbieżne, INE 5242, 2010/2011 4-2
4. Kolejki komunikatów Struktura kolejki w jądrze systemu
Struktura kolejki w jądrze systemu
• cuid i cgid – proces, który utworzyłkolejkę (creator uid, creator gid);
• uid i gid – aktualny właścicieli właściciel grupowy kolejki;
• mode – prawa dostępu do kolejki;
• key – klucz identyfikujący kolejkę;
• first, last – wskaźniki na pierwszyi ostatni komunikat w kolejce;
• wielkość całej kolejki nie możeprzekraczać msg_qbytes, a aktualnawielkość to msg_cbytes;
• aktualna liczba komunikatóww kolejce to msg_qnum.
type: 7
next:
len: 5
data
type: 3
next:
len: 10
data
type: 2
next:
len: 3
data
NULL
nag‡�wki komunikat�w
obszar danychmsg 1
msg 2
msg 3
struct ipc_perm msg_perm;
struct msg *first;
struct msg *last;
time_t msg_rtime;
time_t msg_ctime;
...
uid_t cuid; gid_t cgid; uid_t uid; gid_t gid; mode_t mode; ... key_t key;
ulong_t msg_cbytes;
ulong_t msg_qnum;
ulong_t msg_qbytes;
struktura msqid
Programowanie Współbieżne, INE 5242, 2010/2011 4-3
4. Kolejki komunikatów Tworzenie kolejek
Tworzenie kolejek
int msgget(key_t key, int msgflg);
Funkcja msgget() tworzy nową kolejkę lub znajduje już istniejącą
• Jeśli klucz ma wartość IPC_PRIVATE, zawsze jest tworzona nowa kolejka.
• Jeśli kolejka o podanym kluczu już istnieje, zostaje zwrócony jej identyfikator, o ile użytkownik ma odpowiednieprawa dostępu, w przeciwnym razie zwracany jest (poprzez zmienną errno) błąd ENOENT;
• Jeśli kolejka nie istnieje a wśród opcji msgflg ustawiona była IPC_CREAT, zostaje utworzona nowa kolejkai zwrócony jej identyfikator;
• Użycie opcji IPC_EXCL wymuszautworzenie nowej kolejki – jeżeli byłyustawione obie opcje: IPC_CREATi IPC_EXCL, a kolejka już istniała,zostaje zwrócony błąd EEXIST.
Jeśli tworzona jest nowa kolejka, najmłodsze9 bitów w opcjach oznacza prawa dostępu dotworzonej kolejki, np. 0644 albo 0666.
#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>
#define KEY ((key_t) 12345L)#define PERM 0600
main(){int msqid;
if ( (msqid=msgget(KEY, PERM | IPC_CREAT)) < 0) {fprintf(stderr, "Nie można utworzyć kolejki\n");exit (1);
}...
}
Programowanie Współbieżne, INE 5242, 2010/2011 4-4
4. Kolejki komunikatów Usuwanie kolejek
Usuwanie kolejek
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
Funkcja msgctl() pozwala usunąć kolejkę, ale także sprawdzić jej stan lub zmienić pewne wartości związane zkolejką. Sposób działania zależy od parametru cmd:
• IPC_STAT – umieszcza dane o kolejce w strukturze wskazywanej przez buf;
• IPC_SET – zmienia parametry kolejki na parametry przekazane w strukturze wskazywanej przez buf. Zmienionemogą zostać: właściciel i grupa (o ile wołający proces ma uid=0 lub taki sam, jak proces, który utworzyłkolejkę), prawa dostępu do kolejki i maksymalny rozmiar kolejki;
• IPC_RMID – usuwa kolejkę z systemu.
Polecenia systemowe związane z kolejkami komunikatów:
• ipcrm – usuwa kolejkę
• ipcs -q – sprawdza status kolejki
Programowanie Współbieżne, INE 5242, 2010/2011 4-5
4. Kolejki komunikatów Wysyłanie komunikatów
Wysyłanie komunikatów
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
struct mymsg { /* przykładowa struktura komunikatu */long mtype; /* typ komunikatu */char mtext[1]; /* zawartość komunikatu */
}
• Wysyłając komunikat należy określić jego długość i adres początku;
• msgflg może mieć ustawiony bit IPC_NOWAIT, oznaczający, że funkcja nie będzie blokowana, jeśli komunikatunie można wysłać natychmiast;
• Wysyłany komunikat musi mieć określony typ – liczba dodatnia na samym początku struktury przechowującejkomunikat;
• Struktura zawierająca komunikat może być dowolna, ale pierwsze pole musi być liczbą typu long, zawierającątyp komunikatu.
struct komunikat {long type;int x;int y;char opis[30];
}
Programowanie Współbieżne, INE 5242, 2010/2011 4-6
4. Kolejki komunikatów Wysyłanie komunikatów
Wysyłanie komunikatów – przykład
#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>
#define KEY ((key_t) 12345L)#define PERM 0600
struct komunikat {long type;int x;int y;char opis[30];
}
main(){int mq;int res;
struct komunikat msg={12, 1, 2, "abc");
mq=msgget(KEY, IPC_CREAT | 0644);if (mq==-1) {.... /* błąd utworzenia kolejki */
}res=msgsnd(mq, &msg, sizeof(msg)-sizeof(long int), 0);
}
Programowanie Współbieżne, INE 5242, 2010/2011 4-7
4. Kolejki komunikatów Odbieranie komunikatów
Odbieranie komunikatów
int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
Wartość msgtyp określa jakie komunikaty mogą zostać odebrane:
• 0 – pierwszy komunikat z kolejki, dowolnego typu;
• > 0 – pierwszy komunikat o podanym typie;
• < 0 – spośród komunikatów, których typ jest mniejszy lub równy wartości bezwzględnej msgtyp, wybieranyjest komunikat o najniższym typie (a jeśli jest ich kilka – pierwszy z nich).
Wartość msgflg określa sposób zachowania funkcji:
• jeśli ustawiony jest znacznik IPC_NOWAIT, a w kolejce nie ma żądanego komunikatu, funkcja wróci natychmiast,zwracając wartość -1 i ustawiając zmienną errno na ENOMSG;
• jeśli IPC_NOWAIT nie jest ustawiony, a w kolejce brak żądanego komunikatu, funkcja usypia proces, do czasu gdy:
– zostanie wysłany odpowiedni komunikat;
– kolejka zostanie usunięta z systemu (błąd EIDRM);
– zostanie odebrany sygnał, który nie jest ignorowany. Komunikat nie jest pobierany z kolejki a funkcjamsgrcv() zwraca błąd EINTR lub jest automatycznie restartowana (sigaction(), parametr SA_RESTART).
• jeśli ustawiony jest znacznik MSG_NOERROR, funkcja nie będzie sygnalizować błędu, jeśli odbierany komunikatjest większy niż długość przekazanego bufora, przycinając go do żądanego rozmiaru. W przeciwnym raziekomunikat nie zostanie odebrany a zmienna errno zostanie ustawiona na E2BIG.
Programowanie Współbieżne, INE 5242, 2010/2011 4-8
4. Kolejki komunikatów Selektywne przesyłanie komunikatów
Selektywne przesyłanie komunikatów
k�o� l
�e� j
�k
�a�
k�o� m� u� n� i
k
�a� t �w�
2
3
2
2
2
15
37
2
3
3
2
7
klie� nt 1 klie� nt
2 klie� nt
3
�
s� e� rw� e� r
2 7 2 3 2 15
23715
• klienci przesyłają komunikaty do serwera oznaczając je typem 2 (lub np. 1 dla danych o wyższym priorytecie,2 – normalnym);
• serwer odbiera wyłącznie komunikaty o typie 2 (lub 1–2), a wysyła komunikaty innych typów;
• każdy klient ma osobny numer dla komunikatów, które odbiera (np. swój numer procesu).
Programowanie Współbieżne, INE 5242, 2010/2011 4-9
5. Semafory Sekcja krytyczna
Semafory
Sekcja krytyczna
Dwa procesy niezależnie modyfikująwspólną zmienną „a”, jeden po drugim
a� == 5�0�0�
int a,b;
b=a;b+=100;a=b;
500
600 int a,c;
c=a;c+=300;a=c;
900
600a� == 6�0�0�
a� == 9�0�0�
a� == 5�0�0
�
int a,c;
c=a;c+=300;a=b;
500
600
int a,b;
b=a;
800
500
a� == 8�0�0
�
a� == 6�0�0
�
b+=100;a=b;
Dwa procesy niezależnie modyfikująwspólną zmienną „a”, lecz system ope-racyjny powoduje chwilowe wstrzymaniepierwszego procesu w trakcie wykonywa-nia tej operacji.
Programowanie Współbieżne, INE 5242, 2010/2011 5-1
5. Semafory Sekcja krytyczna
• Od momentu pobrania wartości zmiennej „a” do momentu jej odesłania żaden inny proces nie powinien miećdo niej dostępu
• Fragment programu potrzebujący wyłączności działania nazwiemy sekcją krytyczną.
int a,b;
b=a;
b+=100;a=b;
int a,c;
c=a;c+=300;a=c;
Rozwiązanie?
static int wolny=1; /* semafor */
Zajmij(int *wolny) { Zwolnij(int *wolny) { main() {while (*wolny<=0) *wolny++; int a, b;sleep(1); } Zajmij(&sem); // Dlaczego*wolny--; b=a; // to nie} b+=100; // działa ?
a=b;Zwolnij(&sem);}
Programowanie Współbieżne, INE 5242, 2010/2011 5-2
5. Semafory Sekcja krytyczna
To będzie działać, jeśli operacje Zajmij () i Zwolnij () będą niepodzielne.
int a,b;
Zajmij(s);
int a,c;
Zajmij(s);
Zwolnij(s);
Zwolnij(s);
c=a;c+=300;a=c;
b=a;
b+=100;a=b;
• Operacja Zajmij () jest wejściem do sekcji krytycznej – usypia proces, jeśli zasób jużjest zajęty, aż do momentu, gdy inny proces go zwolni. Proces zostaje umieszczonyw kolejce procesów oczekujących na zwolnienie zasobu (semafora);
• Operacja Zwolnij () stanowi wyjście z sekcji krytycznej – nigdy nie usypia procesu, zato może spowodować obudzenie innego (przeniesienie go z kolejki oczekujących).
• Obie operacje są niepodzielne (atomowe) i działają na poziomie jądra systemu.
t�ry� b
� u� zy� t
�k.�
t�ry� b
� j
�a� d
�ra� .�
u� s�´p io ny� g� o t�o w� y�
Programowanie Współbieżne, INE 5242, 2010/2011 5-3
5. Semafory Sekcja krytyczna
Semafor możemy traktować jak abstrakcyjny typ danych składający się ze zmiennej i kolejki procesów:
• Zajmij (S)
Jeśli S > 0, to S ← S − 1 i idź dalej.
Jeśli S = 0, to umieść proces w kolejce oczekujących na zwolnienie semafora i zaśnij.
• Zwolnij (S)
Jeśli kolejka oczekujących jest pusta, to S ← S + 1:
Jeśli ktoś czeka, to usuń go z kolejki oczekujących i obudź go, nie zmieniając wartości S.
Semafor binarny – przyjmujący jedynie wartości 0 i 1.
Semafor ogólny – przymujące dowolne (lub wybrane) wartości nieujemne
• Bieżąca wartość semafora odpowiada liczbie „wolnych” zasobów;
• Osiągnięcie wartości 0 oznacza, że kolejny proces żądający zasobu zostanie uśpiony;
• Możliwe jest zmniejszenie lub zwiększenie wartości semafora o wartość inną niż 1 (o ile zmniejszenie niedoprowadziłoby do osiągnięcia wartości ujemnej).
Programowanie Współbieżne, INE 5242, 2010/2011 5-4
5. Semafory Sekcja krytyczna
Inne przykłady wymagające ochrony dostępu do sekcji krytycznej:
• modyfikacja wspólnej zmiennej w pamięci lub pliku (konto bankowe?);
• zapis pliku przez kilka procesów;
• modyfikacja rekordu w bazie danych;
• dostęp do urządzeń zewnętrznych (terminale, modemy, itp.);
Inne przykłady synchronizacji za pomocą semaforów:
• Producent-konsument
– jeden proces „produkuje” dane i sygnalizuje to zwalniając semafor:
– drugi „konsumuje” je, oczekując na nie przez zajęcie semafora.
• Synchronizacja kilku procesów w oczekiwaniu na jedno wspólne wydarzenie
Programowanie Współbieżne, INE 5242, 2010/2011 5-5
5. Semafory Problem pięciu filozofów
Problem pięciu filozofów
• Życie filozofa to medytacje, ale to wyczerpującezajęcie, więc filozof czasami robi się głodny;
• Głodny filozof udaje się do jadalni i zajmuje miejsceprzy stole;
• Filozof je tylko wtedy, gdy ma dwa widelce;
• Dwóch filozofów nie może jednocześnie trzymaćtego samego widelca;
• Najedzony filozof wychodzi z jadalni i wracamedytować.
Problemy:
• Żaden filozof nie powinien zostać zagłodzony.
• Nie można dopuścić do blokady.
• Problem higieny pomijamy, jako nie wnoszący nicdo rozwiązania.
1
2
3
4
5
Programowanie Współbieżne, INE 5242, 2010/2011 5-6
5. Semafory Tworzenie semaforów
Tworzenie semaforów
int semget(key_t key, int n_sems, int semflg);
Funkcja semget() tworzy nowy zbiór semaforów lub znajduje już istniejący:
• Jeśli klucz ma wartość IPC_PRIVATE, zawsze jest tworzony nowy zbiór semaforów;
• Jeśli semafor o podanym kluczu już istnieje, zostaje zwrócony jego identyfikator, o ile użytkownik ma odpo-wiednie prawa dostępu do niego, w przeciwnym razie zwracany jest (poprzez zmienną errno) błąd ENOENT;
• Jeśli semafor nie istnieje, a wśród opcji semflg ustawiona była IPC_CREAT, zostaje utworzony nowy semafori zwrócony jego identyfikator;
• Użycie opcji IPC_EXCL wymuszautworzenie nowego semafora – jeżelibyły ustawione obie opcje: IPC_CREATi IPC_EXCL, a semafor już istniał, zostajezwrócony błąd EEXIST.
Jeśli tworzony jest nowy semafor, najmłodsze9 bitów w opcjach oznacza prawa dostępu dotworzonego semafora, np. 0644 albo 0666.
#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>
#define KEY ((key_t) 12345L)#define PERM 0600
main(){int semid;if ( (semid=semget(KEY, 1, PERM | IPC_CREAT)) < 0) {fprintf(stderr, "Nie można utworzyć semafora\n");exit (1);
}...
}
Programowanie Współbieżne, INE 5242, 2010/2011 5-7
5. Semafory Usuwanie semaforów
Usuwanie semaforów
int semctl(int semid, int semnum, int cmd, union semun arg);
union semun {int val;struct semid_ds *buf;ushort_t *array;} arg ;
Funkcja semctl() pozwala usunąć semafor, ale także wykonać na nim różne operacje. Sposób działania zależy odparametru cmd, a parametr semnum określa numer semafora w grupie, którego dotyczy operacja (przy pierwszych 3operacjach, dotyczących całej grupy, ten parametr jest nieistotny):
• IPC_STAT – umieszcza dane o semaforze (właściciel, prawa dostępu, itp.) w strukturze wskazywanej przez buf;
• IPC_SET – zmienia parametry grupy semaforów na parametry przekazane w strukturze wskazywanej przez buf.Zmienione mogą zostać: właściciel i grupa (o ile wołający proces ma uid=0 lub taki sam, jak proces, któryutworzył semafor) i prawa dostępu do semafora.
• IPC_RMID – usuwa grupę semaforów z systemu.
• GETVAL – pobiera i zwraca wartość semafora semnum;
• SETVAL – ustawia wartość semafora semnum na wartość val unii semun przekazanej jako argument arg;
Polecenia systemowe związane z semaforami:
• ipcrm – usuwa semafor (grupę semaforów) z systemu; ipcs -s – sprawdza status semafora.
Programowanie Współbieżne, INE 5242, 2010/2011 5-8
5. Semafory Zajmowanie i zwalnianie semaforów
Zajmowanie i zwalnianie semaforów
int semop(int semid, struct sembuf *sops, size_t nsops);
struct sembuf {short sem_num; /* numer semafora */short sem_op; /* operacja na semaforze */short sem_flg; /* opcje */}
Ogólnie:
• sem_op < 0 – zajęcie semafora
• sem_op > 0 – zwolnienie semafora
• sem_op == 0 – synchronizacja z semaforem (oczekiwanie, aż jego wartość osiągnie 0)
Opcje sem_flg:
• IPC_NOWAITJeżeli nie można wykonać żądanej operacji, proces nie zostaje uśpiony, lecz funkcja wraca z błędem EAGAIN
• SEM_UNDOJeżeli semafor zostaje przydzielony, liczba, o jaką została zmniejszona jego wartość, zostaje dodana do zmiennejsemadj procesu (a przy zwalnianiu semafora – odjęta od tej zmiennej).
Jeśli proces zostaje zakończony z niezerową wartością semadj (np. po otrzymaniu sygnału itp.), funkcja exit()spowoduje dodanie tej zmiennej do wartości semafora (czyli zwolnienie zajętych zasobów).
Programowanie Współbieżne, INE 5242, 2010/2011 5-9
5. Semafory Zajęcie semafora
Zajęcie semafora
struct sembuf op_zajm={0, /* semafor nr 0 */-1, /* zajmij == odejmij wartość |N| (tutaj: 1) */0 /* opcje */
};int semid;
semid=semget(IPC_PRIVATE, 1, IPC_CREAT);if (semid==-1)... (błąd) ...
semop(semid, &op_zajm, 1);/* sekcja krytyczna */
Algorytm zajmowania semafora (N < 0):
• jeśli wartość semafora jest większa lub równa wartości bezwzględnej N , to zmniejsz ją o tyle (a jeśli użytoopcji SEM_UNDO, to dodatkowo zwiększ o |N | zmienną semadj).
• jeśli nie, i nie została użyta opcja IPC_NOWAIT, zwiększ licznik procesów oczekujących na semafor i zawieśproces, aż do czasu, gdy nastąpi jedno ze zdarzeń:
– zostanie spełniony ten warunek. Wówczas wartość semafora jest pomniejszana o |N | (i ew. zwiększana o|N | wartość semadj), zmniejszany jest licznik procesów oczekujących, a proces zostaje obudzony.
– semafor zostanie usunięty z systemu;
– proces otrzyma sygnał (funkcja zwróci błąd EINTR lub zostanie automatycznie wznowiona).
• jeśli nie było możliwe zajęcie semafora, ale użyto opcji IPC_NOWAIT, wróć natychmiast z błędem EAGAIN.
Programowanie Współbieżne, INE 5242, 2010/2011 5-10
5. Semafory Zwolnienie semafora
Zwolnienie semafora
struct sembuf op_tab[2]={0, /* semafor nr 0 */-1, /* zajmij == odejmij wartość |N| (tutaj: 1) */0 /* opcje */
}, {0, /* semafor nr 0 */1, /* zwolnij == dodaj wartość |N| (tutaj: 1) */0 /* opcje */
};int semid;
semid=semget(IPC_PRIVATE, 1, IPC_CREAT);if (semid==-1)... (błąd) ...
semop(semid, op_tab, 1);/* sekcja krytyczna *//* ... */
semop(semid, &(op_tab[1]), 1);
Algorytm zwalniania semafora przez system operacyjny (N > 0):
• wartość semafora zostaje zwiększona o N ,
• jeśli ustawiona jest opcja SEM_UNDO, wartość ta jest również odejmowana od zmiennej semadj procesu związanejz tym semaforem,
• jeśli licznik procesów oczekujących na semafor ma wartość większą od zera, zostaje obudzony pierwszy procesz kolejki.
Programowanie Współbieżne, INE 5242, 2010/2011 5-11
5. Semafory Oczekiwanie na zajęcie semafora przez inny proces
Oczekiwanie na zajęcie semafora przez inny proces
Jeśli wartość N jest równa 0, proces oczekuje aż semafor zostanie zajęty przez inny proces:
• jeśli wartość semafora wynosi 0, funkcja semop() wraca natychmiast;
• jeśli wartość semafora jest większa od 0, ale użyto opcji IPC_NOWAIT, funkcja natychmiast wraca z błędemEAGAIN;
• jeśli wartość semafora jest większa od 0 i nie użyto opcji IPC_NOWAIT, zostaje zwiększona wartość semzcntsemafora, a proces zostaje uśpiony, do czasu, gdy:
– wartość semafora (semval) osiągnie zero;
– semafor zostanie usunięty z systemu;
– proces otrzyma sygnał, który ma zostać przechwycony. Wówczas wartość semzcnt jest zmniejszana i wołanaprocedura obsługi sygnału zarejestrowana wcześniej wywołaniem funkcji signal().
Zastosowania dla tego typu postępowania:
• Rozwiązanie problemu wyścigu podczas inicjowania wartości semafora.
• Synchronizacja z procesem żądającym dostępu do zasobu (jednoczesna operacja typu „czekaj, aż osiągnie 0, agdy się to stanie, zwolnij”).
Programowanie Współbieżne, INE 5242, 2010/2011 5-12
6. Pamięć wspólna Stronicowanie pamięci
Pamięć wspólna
Stronicowanie pamięci
• Adresy wkompilowane w programy nie mogąbyć adresami pamięci fizycznej, bo nie byłobymożliwe współbieżne wykonywanie kilkuprocesów.
• Kompilatory generują kod operujący wwirtualnej przestrzeni adresowej (ograniczonejz góry, np. do 4GB), a tłumaczenia adresówwirtualnych na rzeczywiste dokonujejednostka MMU procesora.
• System może wykonywać kilka kopii tegosamego programu, operującego tymi samymiadresami pamięci wirtualnej, więc tłumaczenieadresów wirtualnych na fizyczne jest ściślezwiązane z kontekstem procesu.
• Pamięć wirtualna uzyskiwana jest przezsegmentację i stronicowanie. Jednostkowymobszarem pamięci jest strona, zwykle wielkościod 8 do 32 kB (stały rozmiar zależny odsystemu).
20� b
�i�t� 12 b
�i�t�
0 0 1 A 7 3 F 0
8F345000 RWX
n� r s� t� ro� n� y� o� f�f
�s� e t�
t� a b�l�i�c� a s� t� ro� n�
8CF04000 R--81212000 R--8DD22000 R-X... RWX8CF1A000 RW-8CF1B000 RW-
1A7
8CF1A000
8CF1B000
8CF19000
3F0
8C000000
p a m� i�Œ�
� f�i
�z� y� c� z� n� a
o� b�s� z� a r
p ro� c� e s� u�
s� t� r� on� ap�am� iŒ
ci
Programowanie Współbieżne, INE 5242, 2010/2011 6-1
6. Pamięć wspólna Segmentacja pamięci
Segmentacja pamięci
W rzeczywistości dostęp do pamięci jest trochę bardziej skomplikowany. Dostęp do pamięci fizycznej odbywa sięw pierwszym rzędzie poprzez segmenty (danych, kodu, stosu), a dopiero potem – strony:
20� b
�i
�t� 12 b
�i
�t�
1 A 7 3 F 0
s� e� g� m� e� n� t� i� n� r s� t� ro n� y o f
�f
�s� e� t�
t� a� b�l i
�c� a� s� t� ro n�
8CF04000 R--
... ...8CF1A000 RW-8CF1B000 RW-
8CF1A000
8CF1B000
8CF19000
3F0
8C000000
p� a� m� i�Œ�
� f�i�z� y c� z� n� a�
o b�s� z� a� r
p� ro c� e� s� u�
tablica s� eg� m� en� tów�
8F345000 RWX
81212000 R--
8DD22000 R-X... ...8CF1B000 R-X
8FAA5000 RW-8FAA6000 RW-... ...8FAD1000 RW-
0 0
k�od
�
d�
an� e
s� tos�
k�od
�
d�
an� e
s� tos�
• Każdy segment posiada własną tablicę stron (adres tablicy i jej długość);
• segment reprezentuje ciągły obszar pamięci;
Programowanie Współbieżne, INE 5242, 2010/2011 6-2
6. Pamięć wspólna Segmentacja pamięci
• przy każdym dostępie do pamięci starsza część adresu wyznacza używany segment, a jednocześnie jest porów-nywana z jego wielkością, w celu wykrycia dostępu poza przydzielony segment;
• starsza część adresu wyznacza indeks w tablicy stron segmentu i pozwala odczytać adres strony pamięcifizycznej;
• ostateczny adres powstaje przez zsumowanie adresu strony i offsetu.
20� b
�i
�t� 12 b
�i
�t�
1 A 7 3 F 0
s� e� g� m� e� n� t� i� n� r s� t� ro n� y o f
�f
�s� e� t�
t� a� b�l i�c� a� s� t� ro n�
8CF04000 R--
... ...8CF1A000 RW-8CF1B000 RW-
8CF1A000
8CF1B000
8CF19000
3F0
8C000000
p� a� m� i�Œ�
� f�i�z� y c� z� n� a�
o b�s� z� a� r
p� ro c� e� s� u�
tablica s� eg� m� en� tów�
8F345000 RWX
81212000 R--
8DD22000 R-X... ...8CF1B000 R-X
8FAA5000 RW-8FAA6000 RW-... ...8FAD1000 RW-
0 0
k�od
�
d�
an� e
s� tos�
k�od
�
d�
an� e
s� tos�
Programowanie Współbieżne, INE 5242, 2010/2011 6-3
6. Pamięć wspólna Tworzenie segmentu pamięci wspólnej
Tworzenie segmentu pamięci wspólnej
int shmget(key_t key, int size, int shmflg);
Funkcja shmget() tworzy nowy segment pamięci wspólnej lub znajduje już istniejący:
• Jeśli klucz ma wartość IPC_PRIVATE, tworzony jest nowy segment;
• Jeśli segment o podanym kluczu już istnieje, zostaje zwrócony jego identyfikator, o ile użytkownik ma odpo-wiednie prawa dostępu do niego, w przeciwnym razie zwracany jest (poprzez zmienną errno) błąd ENOENT;
• Jeśli segment pamięci wspólnej nie istnieje,a wśród opcji shmflg ustawiona byłaIPC_CREAT, zostaje utworzony nowy blokpamięci i zwrócony jego identyfikator;
• Użycie opcji IPC_EXCL wymusza utworzenienowego segmentu pamięci – jeżeli byłyustawione obie opcje: IPC_CREAT iIPC_EXCL, a segment już istniał, zostajezwrócony błąd EEXIST.
Jeśli tworzony jest nowy segment pamięci,najmłodsze 9 bitów w opcjach oznacza prawadostępu do tworzonego bloku pamięci, np. 0644albo 0666.
#include <sys/types.h>#include <sys/ipc.h>#include <sys/shm.h>#define KEY ((key_t) 12345L)#define PERM 0600#define SIZE 4096
main(){int shmid;
if ( (shmid=shmget(KEY, SIZE, PERM | IPC_CREAT)) < 0) {fprintf(stderr, "Nie można utworzyć bloku pamięci wspólnej\n");exit (1);
}...
}
Programowanie Współbieżne, INE 5242, 2010/2011 6-4
6. Pamięć wspólna Usuwanie pamięci wspólnej
Usuwanie pamięci wspólnej
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
Funkcja shmctl() pozwala usunąć segment pamięci wspólnej, ale także wykonać na nim różne operacje. Sposóbdziałania zależy od parametru cmd:
• IPC_STAT – umieszcza dane o bloku pamięci (takie jak właściciel, prawa dostępu, itp.) w strukturze wskazy-wanej przez buf;
• IPC_SET – zmienia parametry segmentu pamięci wspólnej na parametry przekazane w strukturze wskazywanejprzez buf. Zmienione mogą zostać: właściciel i grupa (o ile wołający proces ma uid==0 lub taki sam, jakproces, który utworzył segment pamięci wspólnej) i prawa dostępu do segmentu.
• IPC_RMID – usuwa segment pamięci wspólnej z systemu.
• SHM_LOCK – zablokowanie w pamięci segmentu pamięci wspólnej (operacja może być wykonana wyłącznie przezużytkownika posiadającego euid==0);
• SHM_UNLOCK – odblokowanie segmentu.
Polecenia systemowe związane z pamięcią wspólną:
• ipcrm – usuwa segment pamięci wspólnej z systemu;
• ipcs -m – sprawdza status pamięci wspólnej w systemie.
Programowanie Współbieżne, INE 5242, 2010/2011 6-5
6. Pamięć wspólna Korzystanie z pamięci wspólnej
Korzystanie z pamięci wspólnej
Zanim proces będzie mógł odczytać coś z pamięci wspólnej lub do niej zapisać, musi zażądać od systemu dołączeniasegmentu pamięci wspólnej do własnej przestrzeni adresowej (widząc jakby przez „okienko” swojej pamięci segmentpamięci wspólnej). Po zakończeniu korzystania z pamięci wspólnej, proces powinien odłączyć ją od swojej przestrzeniadresowej. Służą do tego odpowiednio funkcje shmat() i shmdt().
void *shmat(int shmid, const void *shmaddr, int shmflg);int shmdt(const void *shmaddr);
Funkcja shmat() zwraca adres przyłączonego segmentu. Adres ten zależy od użytych parametrów shmaddr i shmflg:
• Jeśli shmaddr==NULL (czyli (void*)0), to przydzielany jest pierwszy wolny adres znaleziony przez system;
• Jeśli shmaddr jest niezerowy, wówczas system podłącza segment pamięci wspólnej pod podanym adresem,chyba że została użyta także opcja SHM_RND, wówczas adres zostaje wyrównany w dół, do najbliższego adresupodzielnego przez SHMLBA.
• Użycie opcji SHM_RDONLY powoduje podłączenie pamięci w trybie wyłącznie do odczytu, w przeciwnym raziedostęp jest do czytania i pisania.
Funkcja shmdt() odłącza od przestrzeni adresowej procesu segment pamięci wspólnej wskazywany przez shmaddr.
Programowanie Współbieżne, INE 5242, 2010/2011 6-6
6. Pamięć wspólna Pamięć wspólna widziana przez 2 procesy
Pamięć wspólna widziana przez 2 procesy
8CF1A000 RW-
20 bit 12 bit
0 0 1 A 7 3 F 0
8F345000 RWX
nr strony offset
tablica stron
8CF04000 R--81212000 R--8DD22000 R-X... ...8CF1A000 RW-8CF1B000 RW-
1A7
8C000000
pamiŒ� fizyczna
20 bit 12 bit
0 0 1 0 E 3 F 0
8F355000 R-X
nr strony offset
tablica stron
... ...
8DC10000 RW-8CFD4000 RW-8CFBB000 R--8CFA0000 R--
10E
proces 1
proces 2
8CF1A000
8CF1B000
8CF19000
3F0
8CF18000
Programowanie Współbieżne, INE 5242, 2010/2011 6-7
6. Pamięć wspólna Pamięć wirtualna
Pamięć wirtualna
W rzeczywistości dostęp do pamięcijest trochę bardziej skomplikowany.(Deja Vu – to takie dziwne uczucie, ...)
• Tablica stron może zawieraćinformacje o stronach, których niema w pamięci fizycznej;
• w chwili dostępu do takiej stronygenerowany jest wyjątekpowodujący zachowanie kontekstuprocesu, ew. przeniesienie dokolejki procesów oczekujących nasprowadzenie strony z pamięciswap, oraz sprowadzenie strony, poczym wznowienie procesu tak,jakby nic się nie stało;
• Dodatkowo tablica zawiera licznikodwołań, automatyczniezmniejszany, a służący do szukaniastron, które mogą być wyrzuconedo pamięci swap.
20� b
�i
�t� 12 b
�i
�t�
1 A 7 3 F 0
s� e� g� m� e� n� t� i� n� r s� t� ro n� y o f
�f
�s� e� t�
t� a� b�l i
�c� a� s� t� ro n�
8CF04000 R--V
... ...8CF1A000 RW-V8CF1B000 RW-V
8CF1A000
8CF1B000
8CF19000
3F0
8C000000
p� a� m� i�Œ
�� f
�i�z� y c� z� n� a�
o b�s� z� a� r
p� ro c� e� s� u�
tablica s� eg� m� en� tów�
8F345000 RWXV
41212000 R---
8DD22000 R-XV... ...8CF1B000 R-XV
8FAA5000 RW-V8FAA6000 RW-V... ...8FAD1000 RW-V
0 0
k�od
�
d�
an� e
s� tos�
k�od
�
d�
an� e
s� tos�12
1
31
1
20
31
s� w� a� p�
p� l i�k� w� y m� i
�a� n� y
Programowanie Współbieżne, INE 5242, 2010/2011 6-8
6. Pamięć wspólna Pełny diagram stanów procesów
Pełny diagram stanów procesów
t�ry� b
� u� zy� t
�k.�
t�ry� b
� j
�a� d
�ra� .�
u� s�´p io ny� g� o t�o w� y�
u� s�´p io ny� / s� w� a� p g� o t
�o w� y� /
s� w� a� p
f�ork
�()
w� y� w� ła� s� zc� z.�
u� t�w� o rzo ny�
zo mb�ie�
s� w� a� p� -o� u� t�
s� w� a� p� -i�n�
ex� it()k
�ill()
p� rz� erw� an� ie
w� y w� łas! z� cz� .
p"o# w$ r% ó& t'
f
( . blo
kuj) ac
a
obud*
z� en� ie
Programowanie Współbieżne, INE 5242, 2010/2011 6-9
6. Pamięć wspólna fork() jeszcze raz
fork() jeszcze raz
• Tworzenie nowego procesu funkcją fork() wymaga skopiowania do nowego procesu segmentu danych i stosu.
• Tworzenie nowych segmentów, alokowanie na nie stron pamięci i skopiowanie zawartości procesu macierzystegoto kosztowne operacje.
• Jeśli fork() jest wykonywany tylko po to, by chwilę później wykonać exec(), jest to niepotrzebnie wykonywanapraca i strata czasu.
Do poprawy efektywności wykonywania fork()+exec() służy „uproszczona” funkcja vfork(), która:
• Tworzy nowy proces, podobnie jak fork()
• Nie rozdziela segmentów danych i stosu tych procesów
• Wstrzymuje proces rodzica do momentu gdy proces potomny wykona którąś z funkcji exec() lub się zakończy.
• Proces potomny dziedziczy otwarte pliki podobnie jak w fork() – ich zamykanie jest niezależne od zamykaniaich w rodzicu.
Proces potomny tworzony przez vfork() ma bardzo ograniczone pole działania:
• Może pozamykać niepotrzebne deskryptory plików
• Nie może modyfikować żadnych zmiennych
• Nie może wołać żadnych własnych funkcji ani wykonać return
• Nie może wołać żadnych funkcji systemowych, z wyjątkiem funkcji z rodziny exec() i funkcji exit()
Programowanie Współbieżne, INE 5242, 2010/2011 6-10
7. Wątki
Wątki
Istnieje kilka bibliotek do tworzenia wątków, wzajemnie niekompatybilnych. Najpopularniejsze to:
• Solaris threads
• POSIX Threads (pthreads)
Wątki zdefiniowane standardem POSIX są przenośne, system zapewnia wymuszanie opcji schedulera. Wątki systemuSolaris mogą być wstrzymywane i wznawiane, można w nich korzystać ze zoptymalizowanych operacji na semaforachi blokad czytania/pisania, a w systemach wieloprocesorowych mogą być uruchamiane równolegle.
Podstawowe cechy wątków:
• Tworzenie wątków jest szybsze i tańsze niż tworzenie nowego procesu
• Wszystkie wątki dzielą między sobą przestrzeń adresową procesu – dane i kod programu;
• Każdy wątek ma osobny stos i pewne atrybuty dotyczące wykonywania
• Wszystkie mają wspólny jeden numer procesu, rozróżniane są przez ID wątku uzyskiwany przy tworzeniunowego wątku
• Synchronizacja odbywa się przez specjalne funkcje związane z wątkami, operujące na semaforach, monitorachlub zmiennych warunkowych.
• Nie wszystkich funkcji systemowych można używać w wątkach w bezpieczny sposób („MT-Safe”).
Programowanie Współbieżne, INE 5242, 2010/2011 7-1
7. Wątki
Podstawowe funkcje
• pthread create() (POSIX), thr create() (Solaris)
• pthread exit(), thr exit()
• pthread join(), thr join()
• pthread detach(), –
• pthread mutex init(), mutex init()pthread mutex trylock(), mutex trylock()pthread mutex lock(), mutex lock()pthread mutex unlock(), mutex unlock()pthread mutex destroy(), mutex destroy() – operacje na monitorach
• sem init(), sem wait(), sem trywait() i inne – operacje na semaforach
• pthread cond init(), pthread cond wait(), pthread cond timedwait(), pthread cond signal() i inne
• sched yield(), thr yield()
Programowanie Współbieżne, INE 5242, 2010/2011 7-2
7. Wątki
Tworzenie wątków
#include <pthread.h>int pthread_create(pthread_t *new_thread_ID,
const pthread_attr_t *attr,void * (*start_func)(void *), void *arg);
#include <thread.h>int thr_create(void *stack_base, size_t stack_size,
void *(*start_func)(void *), void *arg, long flags,thread_t *new_thread_ID);
• Funkcja tworzy nowy wątek. Nowy wątek po utworzeniu zaczyna swoje życie na początku funkcji start_func.
• Wątek kończy się automatycznie po wyjściu z funkcji start_func, po wywołaniu pthread exit() lub thr exit(),odwołaniu wątku (pthread cancel()) lub zakończeniu przez exit() głównego procesu.
• pthread join() jest odpowiednikiem funkcji wait() – czeka na zakończenie określonego lub dowolnego wątku.
Tylko jeden z wątków może wykonać pthread join() podając ten sam wątek jako argument funkcji (pozostałekończone są natychmiast z błędem ESRCH).
Dalsze rozważania – na przykładzie wątków POSIX.
Programowanie Współbieżne, INE 5242, 2010/2011 7-3
7. Wątki
Synchronizacja wątków
• Specjalne mechanizmy semaforów dostosowane do specyfiki programów wielowątkowych (Standard POSIX1003.1b)
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);int sem_wait(sem_t * sem);int sem_trywait(sem_t * sem);int sem_post(sem_t * sem);int sem_getvalue(sem_t * sem, int * sval);int sem_destroy(sem_t * sem);
• Mechanizm monitorów i zmiennych warunkowych (pthread mutex lock(), pthread cond wait() i inne)
– Monitor w najprostszej formie (bez zmiennej warunkowej) można traktować jak semafor binarny
– Z monitorem może być związana jedna lub kilka zmiennych warunkowych
– Zmienne warunkowe, inne (zwykłe) zmienne oraz sam monitor można zagregować we wspólny obiekt – np.w postaci klasy języka C++.
– Monitor służy do zapewnienia atomiczności operacji mogących mieć wpływ na zmienne z nim związane
Programowanie Współbieżne, INE 5242, 2010/2011 7-4
7. Wątki
Zmienne warunkowe
• Zmienna warunkowa jest zawsze związana z monitorem (w celu uniknięcia wyścigu pomiędzy czekaniem nazajście warunku a sygnalizowaniem jego zajścia)
• Wątek może zostać wstrzymany do momentu spełnienia żądanego warunku (np. oczekiwanego wyniku porów-nania dwóch zmiennych itp.)
• Wykonanie pthread cond wait(), zawiesza wątek w oczekiwaniu na spełnienie warunku i automatycznie zwalniamonitor związany ze zmienną warunkową
• Wątek ponownie otrzymuje sterowanie po tym, gdy inny wątek wykona pthread cond broadcast() lub pth-read cond signal(): Wywołanie pthread cond signal() budzi dokładnie jeden wątek czekający na spełnieniewarunku, pthread cond signal() – wszystkie.
• W obudzonym wątku monitor jest ponownie zajęty, więc można kontynuować działanie w sekcji krytycznejdotyczącej zmiennej warunkowej, pamiętając także o konieczności wywołania pthread mutex unlock()
Przykład – ponownie problem pięciu filozofów:
• Za pomocą zmiennej warunkowej uzyskamy „bramkarza” nie wpuszczającego do jadalni więcej niż 3 filozofówjednocześnie
• Widelce (pałeczki) również można zaimplementować za pomocą zmiennych warunkowych, prościej jednakbędzie użyć samych monitorów (bez zmiennych) lub semaforów (binarnych)
Programowanie Współbieżne, INE 5242, 2010/2011 7-5
7. Wątki
Monitory i zmienne warunkowe – przykład
/* ... zainicjowanie zmiennych warunkowych i monitorów ... */pthread_mutex_t bramka = PTHREAD_MUTEX_INITIALIZER;pthread_cond_t miejsce = PTHREAD_COND_INITIALIZER;int ilu_je = 0;
void filozof(int nr) {while(1) {/* filozof medytuje */...
/* filozof się budzi i chce wejść do jadalni */pthread_mutex_lock(&bramka)while (ilu_je>=3) {pthread_cond_wait(&miejsce, &bramka); /* oczekiwanie na wolne miejsce */}/* ... wchodzi: */ilu_je++;pthread_mutex_unlock(&bramka);
... /* teraz można próbować wziąć widelce */
... /* oraz jeść */
/* koniec jedzenia i wyjście z jadalni */pthread_mutex_lock(&bramka);if (--ilu_je < 3)pthread_cond_broadcast(&miejsce); /* obudzenie filozofów czekających na wolne miejsce w jadalni */pthread_mutex_unlock(&bramka);}}
1
2
3
4
5
Programowanie Współbieżne, INE 5242, 2010/2011 7-6
7. Wątki
Monitory i zmienne warunkowe – przykład (c.d.)
Sam sposób brania widelców można rozwiązać w „tradycyjny” sposób, traktując monitory jak semafory binarne:
/* inicjowanie mechanizmów -- gdzieś na początku */
pthread_mutex_t widelec[5]; /* tablica pięciu monitorów/semaforów binarnych */for (i=0; i<5; i++) {widelec[i]=PTHREAD_MUTEX_INITIALIZER;pthread_mutex_init(widelec+i);}
/* pobieranie widelców przez filozofa */
void filozof(int nr) {while(1) {...... /* wejście do jadalni */
pthread_mutex_lock(widelec+nr); /* zajmowanie widelców */pthread_mutex_lock(widelec+((nr+1)%5));/* filozof je */pthread_mutex_unlock(widelec+nr);pthread_mutex_unlock(widelec+((nr+1)%5));
... /* wyjście z jadalni */}}
Programowanie Współbieżne, INE 5242, 2010/2011 7-7
7. Wątki
Monitory i zmienne warunkowe – przykład (c.d.)
Inny sposób pobierania widelców – z użyciem semaforów:
/* inicjowanie mechanizmów -- gdzieś na początku */
sem_t widelec[5]; /* tablica pięciu monitorów/semaforów binarnych */for (i=0; i<5; i++) {sem_init(widelec+i, 0, 1); /* pshared=0, początkowa wartość semafora = 1 */}
void filozof(int nr) {while(1) {...... /* wejście do jadalni */
sem_wait(widelec+nr); /* zajmowanie widelców */sem_wait(widelec+((nr+1)%5));/* filozof je */sem_post(widelec+nr);sem_post(widelec+((nr+1)%5));
... /* wyjście z jadalni */}}
/* gdzieś na końcu ... */for (i=0; i<5; i++) {sem_destroy(widelec+i);}
Programowanie Współbieżne, INE 5242, 2010/2011 7-8
7. Wątki
Jeszcze jeden przykład problemu pięciu filozofów
• Monitory pozwalają w elegancki sposób rozwiązać problem wiążąc wszystkie wymagane mechanizmy w jednejstrukturze
• Zamiast implementować widelce w postaci semaforów wystarczy 5-elementowa tablica oznaczająca dostępnośćposzczególnych widelców
• Rolę semaforów mogą spełniać zmienne warunkowe – zmienna ok[i] oznacza, że filozof nr i może aktualniejeść (bo oba jego widelce leżą na stole i są wolne).
• Monitor służy do wzajemnego wykluczania operacji prowadzonych na tablicy widelców i zmiennych warunko-wych.
Algorytm postępowania filozofa:
• Zajmuje monitor, sprawdza, czy oba jego widelce są wolne.
• Jeśli nie są, zasypia na zmiennej warunkowej, czekając aż zostanie zasygnalizowany fakt, że oba są wolne.
Oczekiwanie na zmiennej warunkowej zwalnia monitor, dzięki czemu inni filozofowie są w stanie zwolnić widelcei zasygnalizować ten fakt.
• Jeśli (wreszcie) oba widelce są wolne, filozof zajmuje je (odnotowując w tablicy widelców) i zaczyna jeść.(zwalniając monitor)
• Po zakończeniu, ponownie zajmuje monitor i zwalnia widelce oraz sygnalizuje swoim sąsiadom, że (być może)mogą jeść (po czym zwalnia monitor i idzie medytować).
Programowanie Współbieżne, INE 5242, 2010/2011 7-9
7. Wątki
Implementacja ostatniego rozwiązania
#define C_INIT PTHREAD_COND_INITIALIZERstruct {pthread_mutex_t mut;pthread_cond_t ok[5];int widelec[5];
} f={PTHREAD_MUTEX_INITIALIZER, {C_INIT, C_INIT, C_INIT, C_INIT, C_INIT}, {1,1,1,1,1} };
void filozof(int nr) {while(1) {pthread_mutex_lock(&f.mut); /* zajmowanie widelców - najpierw zajmij monitor*/while (f.widelec[nr]+f.widelec[(nr+1)%5] < 2) /* czy oba są wolne? */pthread_cond_wait(&f.ok[nr], &f.mut); /* nie są ==> czekaj */
f.widelec[nr]=0; /* zajmij oba naraz */f.widelec[(nr+1)%5]=0;pthread_mutex_unlock(&f.mut); /* oraz zwolnij monitor */
... /* filozof je */
pthread_mutex_lock(&f.mut); /* zwalnianie widelców - najpierw zajmij monitor*/f.widelec[nr]=1;f.widelec[(nr+1)%5]=1;
pthread_cond_signal(&f.[(nr-1+5)%5]; /* "szturchnij" lewego sąsiada */pthread_cond_signal(&f.[(nr+1)%5]; /* i prawego */pthread_mutex_unlock(&f.mut); /* oraz zwolnij monitor */
... /* idź medytować */}}
Programowanie Współbieżne, INE 5242, 2010/2011 7-10
7. Wątki
Problemy z funkcjami systemowymi w wątkach
• Niektóre z funkcji systemowych operują na danych zapisywanych w statycznie przydzielanym obszarze pamięci
• Wywoływanie tych funkcji z różnych wątków powodowałoby nadpisywanie tego obszaru
Przykład: czytanie katalogu funkcją opendir() i readdir():
#include <stdio.h>#include <dirent.h>main(){
DIR *dirp;struct dirent *direntp;
dirp = opendir(".");while ( (direntp=readdir(dirp)) != NULL )
printf( "%s\n", direntp->d_name );closedir(dirp);return (0);
}
Większość funkcji takich jak readdir() posiada swoje odpowiedniki pozwalające na korzystanie z wątków, nazywanezwykle przez dodanie sufiksu _r (pochodzącego od słowa reentrant), np. readdir r().
Podobnie np. funkcje dotyczące tłumaczenia nazw DNS: gethostbyname() i gethostbyname r(), gethostbyname r(),gethostent r(), gethostbyaddr r() itp.
Programowanie Współbieżne, INE 5242, 2010/2011 7-11
8. Różności Blokowanie dostępu do plików
Różności
Blokowanie dostępu do plików
• fcntl() z opcjami F_SETLK, F_SETLKW, F_GETLK.
– możliwa blokada zapisu lub blokada odczytu, całego pliku lub jego fragmentu.
• lockf ()
Podobna funkcjonalność, inne wywołanie. Poprzez F_TLOCK i F_TEST można sprawdzić możliwość uzyskaniablokady nie blokując procesu w celu jej uzyskania.
• BSD: flock()
Możliwość tworzenia „advisory lock” czyli blokad nie wymuszanych przez system blokowaniem procesu (procesmoże w międzyczasie zająć się innymi rzeczami.
Funkcja select()
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *errorfds, struct timeval *timeout);
void FD_ZERO(fd_set *fdset);void FD_SET(int fd, fd_set *fdset);void FD_CLR(int fd, fd_set *fdset);
int FD_ISSET(int fd, fd_set *fdset);
• Listy deskryptorów są upakowane w zmiennych readfds, writefds i errorfds, po 1 bit na deskryptor;
Programowanie Współbieżne, INE 5242, 2010/2011 8-1
8. Różności Blokowanie dostępu do plików
• Do manipulowania tymi listami służą makra takie, jak FD_ZERO, zerujące listę, czy FD_SET, ustawiające bitodpowiedający podanemu deskryptorowi pliku, gniazda, strumienia FIFO itp.
• Jeśli lista jest pusta, można zamiast niej podać wskaźnik NULL;
• Struktura timeout określa maksymalny czas oczekiwania na dostępność określonych deskryptorów.
struct timeval {long tv_sec; /* sekundy */long tv_usec; /* mikrosekundy */};
Sposób interpretowania tego czasu:
– Powróć natychmiast, jeśli żaden deskryptor nie jest gotowy: struktura *timeout wypełniona zerami;
– Czekaj zadany czas: jeśli wartości w *timeout nie są obie równe zero;
– Czekaj w nieskończoność (aż któryś z deskryptorów będzie gotów): timeout == NULL.
Programowanie Współbieżne, INE 5242, 2010/2011 8-2
8. Różności Programowanie w systemie UNIX – podsumowanie
Programowanie w systemie UNIX – podsumowanie
• Funkcje systemowe w przypadku błędów zwracają wartość −1, a informacja o przyczynie błędu zostaje umiesz-czona w zmiennej errno
• Standardowo funkcje we/wy są blokujące – jeśli żądanej czynności nie można wykonać od razu, proces jestusypiany, nie zajmując przez to czasu procesora w aktywnej pętli oczekiwania.
• Nieblokujące wejście/wyjście można uzyskać na różne sposoby:
– Ustawiając odpowiednie opcje deskryptora pliku (fcntl() – od tego momentu wszystkie operacje na de-skryptorze są nieblokujące).
– Poprzez użycie odpowiednich opcji w funkcji (np. IPC_NOWAIT w funkcjach IPC).
– Pozwalając na ewentualne blokowanie funkcji, ale przerwanie jej za pomocą sygnału (np. SIGALARM).
• Problem jednoczesnej obsługi wielu deskryptorów plików lepiej powierzyć funkcjom takim jak select() lubpoll() blokując proces w oczekiwaniu na jeden z wielu warunków, niż stosować nieblokujące we/wy.
Programowanie Współbieżne, INE 5242, 2010/2011 8-3
Literatura
[1] Maurice J. Bach. Budowa systemu operacyjnego UNIX. Wydawnictwa Naukowo-Techniczne, 2 edition, 1995.oryginał: “The Design of the UNIX Operating System”, Prentice Hall, Inc., 1986.
[2] M. Ben-Ari. Podstawy programowania współbieżnego. Wydawnictwa Naukowo-Techniczne, 1 edition, 1989.oryginał: “Principles of Concurrent Programming”, Prentice-Hall International (UK) Ltd., 1983.
[3] Abraham Silberschats, James L. Peterson, and Peter B. Galvin. Podstawy systemów operacyjnych. WydawnictwaNaukowo-Techniczne, 2 edition, 1993. oryginał: “Operating System Concepts”, Addison-Wesley PublishingCompany, Inc., 1991.
[4] W. Richard Stevens. Programowanie zastosowań sieciowych w systemie Unix. Wydawnictwa Naukowo-Techniczne, 1 edition, 1995. oryginał: “UNIX Network Programming”, Prentice Hall, Inc., 1990.
Programowanie Współbieżne, INE 5242, 2010/2011 9-1
Skorowidz
., 1-16/ , 1-5/etc/exports , 1-5/etc/fstab, 1-5/etc/mntab, 1-5/etc/passwd , 1-9/etc/shadow , 1-9/usr/include, 1-16/usr/include/signum.h, 2-9/usr/include/sys/signal.h, 2-9/usr/lib, 1-16/usr/lib/libc.a, 1-7/usr/lib/libsocket.so.2 , 1-7/usr/local/include, 1-16/usr/local/lib, 1-16exit(), 6-13
bg, 2-3
close(), 3-1
dup(), 3-6dup2 (), 3-6
exec(), 2-5, 2-7, 2-8, 3-7, 6-7, 6-13
execl(), 2-7execle(), 2-7execlp(), 2-7execv(), 2-7execve(), 2-7execvp(), 2-7exit(), 2-8, 2-10, 5-9, 6-7, 7-3
fcntl(), 2-7, 3-2, 3-3, 8-1, 8-5fg, 2-3flock(), 8-1fork(), 2-1, 2-4–2-6, 6-7, 6-13fprintf (), 3-1fread(), 6-10fseek(), 6-10ftok(), 4-9fwrite(), 6-10
gethostbyaddr r(), 7-11gethostbyname(), 7-11gethostbyname r(), 7-11gethostent r(), 7-11getpid(), 2-5getppid(), 2-5
9-2
ioctl(), 3-2ipcrm, 4-4ipcs, 4-4
jobs, 2-3
kill, 2-3kill(), 2-5
ld.so, 1-8libc.so, 1-7, 1-8, 2-4libm.so, 1-8libnsl.so, 1-8libsocket.so, 1-8libX11.so, 1-8lockf (), 8-1
main(), 2-7mknod, 3-9mknod(), 3-9mmap(), 6-10msgctl(), 4-4msgget(), 4-3msgrcv(), 4-7msync(), 6-10munmap(), 6-10mutex destroy(), 7-2
mutex init(), 7-2mutex lock(), 7-2mutex trylock(), 7-2mutex unlock(), 7-2
nice, 2-1, 2-7
open(), 3-9opendir(), 7-11
pclose(), 3-8pipe(), 3-1poll(), 8-5popen(), 3-8printf (), 1-7ps, 2-3pthread cancel(), 7-3pthread cond broadcast(), 7-5pthread cond init(), 7-2pthread cond signal(), 7-2, 7-5pthread cond timedwait(), 7-2pthread cond wait(), 7-2, 7-4, 7-5pthread create(), 7-2pthread detach(), 7-2pthread exit(), 7-2, 7-3pthread join(), 7-2, 7-3pthread mutex destroy(), 7-2
Programowanie Współbieżne, INE 5242, 2010/2011 9-3
pthread mutex init(), 7-2pthread mutex lock(), 7-2, 7-4pthread mutex trylock(), 7-2pthread mutex unlock(), 7-2, 7-5
read(), 3-1–3-3, 3-9readdir(), 7-11readdir r(), 7-11
sbrk(), 6-7sched yield(), 7-2select(), 3-3, 8-2–8-5sem init(), 7-2sem trywait(), 7-2sem wait(), 7-2semctl(), 5-8semget(), 5-7semop(), 5-12setpgrp(), 2-10sh, 3-8shmat(), 6-6, 6-7shmctl(), 6-5, 6-8shmdt(), 6-6, 6-8shmget(), 6-4, 6-7sigaction(), 4-7sighold(), 2-9sigignore(), 2-9
signal(), 2-9, 5-12sigpause(), 2-9sigrelse(), 2-9sigset(), 2-9
thr create(), 7-2thr exit(), 7-2, 7-3thr join(), 7-2thr yield(), 7-2
umask, 2-7unlink(), 3-9
vfork(), 6-13
wait(), 2-5, 2-10, 3-8, 7-3write(), 3-1, 3-2, 3-9
Zajmij (), 5-3, 5-4Zwolnij (), 5-3, 5-4
Programowanie Współbieżne, INE 5242, 2010/2011 9-4