close
Transkrypt
close
UXP – lato 2015, Grzegorz Blinowski UXP1A Unix – Programowanie i Architektura lato 2015 Grzegorz Blinowski Instytut Informatyki Politechniki Warszawskiej UXP – lato 2015, Grzegorz Blinowski Regulamin, itp. • Zasady ogólne: – Tryb zaliczenia – oceny są wystawione najpóźniej ostatniego dnia zajęć (przed rozpoczęciem sesji letniej), – 50 p. Projekt, 50 p. kolokwia, 50 p. do zaliczenia przedmiotu – Projekt i kolokwia niezbędne do zaliczenia - min. 25 p. za kolokwia, min. 25 p. za projekt – nie przewiduje się poprawkowego kolokwium – poprawka indywidualne rozmowy – Kolokwium – 1 godz. lekcyjna, bez notatek • Projekt: – Projekt rusza po c.a. 1 mies. wykładów – Zespoły 4-o osobowe, – propozycja własnych projektów - bez sztampowych rozwiązań – ujemne punkty za znaczne opóźnienie – Projekt wstępny - na papierze i projekt zasadniczy: demo + obszerna dokumentacja – Szczegółowe wymagania podane przy rozdawaniu zadań UXP – lato 2015, Grzegorz Blinowski Regulamin itp. • Inne – Wolne czwartki: 4.06 – Wykładu nie będzie 23.04, – Będzie wyznaczony termin zastępczy (pt 12-14 lub 14-16 sala 138) – Kolokiwum I – po omówieniu mechanizmów IPC (9.04?) – Kolokiwum II – po omówieniu całości materiału (28.05 - ?) – Oceny, wpisy, poprawa 11.06 (ostatnie zajęcia) – Projekt: start ok. 9.04, zaliczenie do 11.06 • Wymagania – Systemy operacyjne – Dobra znajomość języka C • Materiały – dostępne na stronie http://www.ii.pw.edu.pl/~gjb • Kontakt: [email protected], tel PW: 222347184; konsultacje wt. 10-12 UXP – lato 2015, Grzegorz Blinowski Literatura • W. Richard Stevens, Advanced Programming in the UNIX Environment • Uresh Vahalia, Jadro systemu UNIX, WNT; 2001 • Berny Goodhear, James Cox, Sekrety magicznego ogrodu UNIX System V Wersja 4 od środka (podręcznik), WNT 2001 Marc Rochkind, Programowanie w systemie Unix dla zawansowanych, WNT (wyd. 2; 2005) David R. Butenhof, Programming with Posix Threads, Addison-Wesley, 1997 Daniel P. Bovet, Marco Cesati, LINUX kernel, Wydawnictwo RM (O’Reilly) 2001 M. Bach, Budowa systemu operacyjnego Unix, WNT 1996 • • • • UXP – lato 2015, Grzegorz Blinowski Plan wykładów • Historia, Standardy • Procesy • Sygnały • Pliki - podstawy • Komunikacja IPC (potoki, FIFO, SYSV IPC) • Kolokwium nr 1 • Pliki – cd, zajmowanie plików i rekordów • VFS; systemy plików: UFS, Procfs/specfs, NFS (i RPC) • VM • Wątki •XTI (?) • boot / init • Kolokwium nr 2 UXP – lato 2015, Grzegorz Blinowski Historia • 1965-1969: GE&MIT&Bell Labs - MULTICS • 1969: Ken Thompson, Dennis Ritchie (Bell Labs): gra "Space Travel", komputer GE-645 • 1969: PDP-7 (Bell): środowisko systemu plików, obsługa procesow, 2 użytkowników -> PDP-11 • UNICS -> UNIX Brian Kernighan; pierwszy użytkownik - wydz. patentowy Bell Labs. • 1971: UNIX First Edition • 1972: język B; 1973 - język C (Thomson & Ritchie) • 1974 - artykuł o Unix-ie w Comm. of the ACM UXP – lato 2015, Grzegorz Blinowski Historia c.a. • 1975 -> Edycja 6 - V6 - pierwsza edycja używana poza Bell Labs • 1976: Ritchie - stdio • 1975-76 pierwsza wersja przeniesiona na inną maszynę niż PDP • 1979 - edycja v7 (kompilator C, sh) • 1978 - BSD (bazuje na v6) - Bill Joy, komputery VAX11 (32 bit); pierwsza implementacja TCP/IP w BSD (1980) • Linia "komercyjna": Unix System III, Unix System V AT&T; SVR4 (1989) UXP – lato 2015, Grzegorz Blinowski (Wikimedia Commons) UXP – lato 2015, Grzegorz Blinowski Główne innowacje • BSD: – TCP/IP – Sockets API – Job control – Symlinks – System plików UFS – Multi-group (przynależność do wielu grup) • System V – Biblioteki .so – TLI, STREAMS – IPC (pamięć dzielona, semafory, kolejki komunikatów) • SunOS – V-node – Funkcja mmap() – NFS, RPC, XDR UXP – lato 2015, Grzegorz Blinowski Standardy i organizacje • Lata 80-te: wiele wersji na licencji AT&T (od 1983 – podział Bell System) • Konsorcjum X/Open – 1984 • 1990: OSF/1 – BSD/Mach; Unix International (AT&T) • 1993: COSE, X/Open • Obecnie znak handlowy “UNIX” należy do “The Open Group” UXP – lato 2015, Grzegorz Blinowski Standardy • • • • • • • • SVID – System V Interface Definition (AT&T) SUS - Single Unix Specification 1988 – ... POSIX (IEEE) 1990+: Spec 1170 1997: SUS v2 (Open Group) 2001: POSIX:2001 – SUS v3 (3000+ stron) 2004: POSIX:2004 2008: POSIX:2008 UXP – lato 2015, Grzegorz Blinowski Standardy • Unix System V release 4 (SVID)– AT&T, Unix International, Novel • X/Open XPG3 (m.in. IPC, X-windows, lokalizacja programów, programy użytkowe, język C) • POSIX (wybrane): – IEEE P1003.1 – API (interfejs miedzy systemem operacyjnym a programami) – IEEE P1003.2 – Interpreter poleceń i programy użytkowe – IEEE P1003.3 – Testy zgodności – IEEE P1003.4a – Wątki UXP – lato 2015, Grzegorz Blinowski (Wikimedia Commons) UXP – lato 2015, Grzegorz Blinowski Cechy • • Przenośność - źródła C + kilka % kodu asamblera Wielozadaniowy i wielodostępny – Wiele procesów – każdy ma „złudzenie” posiadania maszyny na własność – Równoczesna praca wielu użytkowników • Pamięć wirtualna : – procesy mogą alokować więcej pamięci niż jest w systemie, – mapa pamięci procesu może być "stała" i obejmować całą przestrzeń adresową (4 GB w modelu 32b) • • • Wirtualny i rozproszony system plików Wirtualny - w jednym drzewie wiele typów systemów plików Rozproszony - obejmuje wiele maszyn w sieci UXP – lato 2015, Grzegorz Blinowski System procesów UXP – lato 2015, Grzegorz Blinowski Procesy- zagadnienia • Cykl życia: tworzenie, wykonanie, zakończenie • Tryb wykonania: user, kernel • Wejście do kernel: syscall, przerwanie, wyjątek • Szeregowanie: w jaki sposób proces jest wybierany z listy gotowych procesów, jak lista ta jest zarządzana i sortowana • Przełączanie / wywłaszczanie (contex switching) • Wykorzystanie pamięci (we współpracy z pod-systemem VM) • Wykorzystanie systemu plików (we współpracy z podsystemem VFS) • Obsługa wyjątków (sygnały) • Timing – statystyki, profilowanie, itp. UXP – lato 2015, Grzegorz Blinowski Diagram stanów procesu SONPROC syscal intr., fault exit() SZOMB SONPROC kernel preempt SRUN sleep schedule SSLEEP wakeup SRUN ten sam stan SIDL (idle) fork() UXP – lato 2015, Grzegorz Blinowski Diagram stanów procesu (stary) UXP – lato 2015, Grzegorz Blinowski Diagram stanów procesu - Linux • TASK_RUNNING – w trakcie wykonania lub gotowy • INTERRUPTIBLE lub UNINTERRUPTIBLE – odp. SSLEEP • TASK_STOPPED (nie pokazany – SIGTSTP i inne) UXP – lato 2015, Grzegorz Blinowski Deskryptor procesu • Proces opisany poprzez swój deskryptor • Deskr. częściowo zależny od architektury sprzętu • “Klasyczny” podział deskryptora na 2 części: – proc - /usr/include/sys/proc.h – u - u-area (u-obszar) - /usr/include/sys/user.h • Powiązania deskryptorów z innymi strukturami: – Struct pid – hierarchia procesów (także „orphaned flag”, odp. hash table, itp) – VFS: ufschunk, file, vnode – VM: as (address space), seg (segments) UXP – lato 2015, Grzegorz Blinowski struct proc • rejestry procesora: rejestry uniwersalne – odkładane na stosie kernelowym procesu w momencie wejścia w tryb jądra(!); rej. kontekstu, wskażniki stosów user i kernel • stan procesu (SRUN, SIDL, SZOMB, SONPROC, …) • PID, PPID, • zdarzenie, na które oczekuje proces • pamięć, mapowanie pam. wirt • informacje organizacyjne związane z listą procesów i kolejkami schedulara: priorytet, wartość nice, statystyki schedulera • proces group • wskażnik na u-area • limity • inf. związane z obsługą sygnałów: maski • liczniki czasu • inf. związane z obługą select() UXP – lato 2015, Grzegorz Blinowski struct u (user) • katalog aktualny • root fs • tablica otwartych plików • terminal sterujący • pole zwrotu f-kcji systemowej • liczniki czasu i inne dane statystyczne • inf. związane z debugowaniem i core dump-em UXP – lato 2015, Grzegorz Blinowski Deskryptor procesu w Linux • Struktura task: – struct task_struct <linux/sched.h> – ok 2 KB • przydzielany dynamicznie • w kernelu <=2.4 dostępny na końcu segmentu stosu jądra (x86) • obecnie na stosie jadra zlokalizowany thread_struct powiazany z task_struct UXP – lato 2015, Grzegorz Blinowski Hierarchia procesów, PID, PPID • Drzewiasta hierarchia procesów • Dziedziczenie procesów osieroconych przez proces init (ppid==1) • Powstawanie procesów “zombie” – deskryptor procesu, którego statusu nie odebrał (jeszcze) rodzic • “specjalne” procesy systemowe: – 0: swapper. scheduler – 1: init – 2,3,4: pagedaemon, pageout, vmdaemon, fsflush, update • Praktyczne znaczenie PID: identyfikacja, ustalenie hierarchii, zapisanie do pliku w celu wysłania sygnału UXP – lato 2015, Grzegorz Blinowski Polecenie ps ogg% ps -axl UID PID PPID CPU PRI NI VSZ RSS WCHAN STAT TT 0 0 0 0 -18 0 0 0 sched 0 1 0 0 10 0 456 DLs ?? 0:10.17 Is ?? 0:00.58 /sbin/init -- 0 2 0 1 -18 0 0 12 psleep DL ?? 0:00.07 (pagedaemon) 0 3 0 0 28 0 0 12 psleep DL ?? 0:00.00 (vmdaemon) 0 4 0 0 28 0 0 12 update DL ?? 62:25.89 0 27 1 4 18 0 200 80 pause Is ?? 0:00.01 adjkerntz -i 0 73 1 0 2 0 196 448 select Ss ?? 2:02.57 syslogd -s 0 100 1 0 2 0 196 472 select Is ?? 0:02.62 inetd 0 102 1 1 18 0 332 448 pause Is ?? 7:44.83 cron 0 110 1 0 on port 25 (sendmail) 2 0 620 708 select Ss ?? 0:42.97 sendmail: accepting connections 160 wait TIME COMMAND (swapper) (update) UXP – lato 2015, Grzegorz Blinowski Polecenie ps ogg% ps -axl UID PID PPID CPU PRI NI VSZ RSS WCHAN 0 167 1 2 2 0 12152 167 0 0 12154 167 0 12160 STAT 0 468 604 accept Is 28 0 0 0 - 2 28 0 0 167 1 28 0 0 12162 167 1 28 0 12164 167 2 0 18841 167 0 22953 TT TIME COMMAND ?? 6:27.12 /usr/local/bin/sshd Z ?? 0:00.00 (sshd) 0 - Z ?? 0:00.00 (sshd) 0 0 - Z ?? 0:00.00 (sshd) 0 0 0 - Z ?? 0:00.00 (sshd) 28 0 0 0 - Z ?? 0:00.00 (sshd) 1 2 0 836 1040 select S ?? 0:02.03 /usr/local/bin/sshd 167 0 28 0 0 Z ?? 0:00.00 100 18843 18841 0 18 0 452 324 pause Ss p0 0:00.23 -csh (csh) 100 19017 18843 1 28 0 636 272 - R+ p0 0:00.00 ps -axl 0 - (sshd) 0 14142 1 0 3 0 176 524 ttyin Is+ v0 0:00.09 /usr/libexec/getty Pc ttyv0 0 2315 1 0 3 0 176 596 ttyin Is+ v1 0:00.02 /usr/libexec/getty Pc ttyv1 100 19292 1 0 3 0 452 324 ttyin Is+ v2 0:00.26 -csh (csh) 0 172 1 0 3 0 176 516 ttyin Is+ v3 0:00.02 /usr/libexec/getty Pc ttyv3 0 173 1 0 3 0 176 516 ttyin Is+ v4 0:00.02 /usr/libexec/getty Pc ttyv4 UXP – lato 2015, Grzegorz Blinowski Mapa pamięci procesu UXP – lato 2015, Grzegorz Blinowski Mapa pamięci procesu • 2^32 lub 2^44 (16 TB w adresowaniu 64-o bitowym) • Stosy: – User stack – Kernel stack (pusty, jeśli w trybie user) – Syscall: wywołanie funkcji bibliotecznej w trybie user, samo wejście do trybu jądra: uprzywilejowana instrukcja powodująca wyjątek – Stos jądra – używany normalnie do przechowywania rekordów aktywacji, zmiennych lokalnych itd. Podczas wykonywania kolejnych funkcji w trybie jądra – Context switch: na stosie jądra odłożone rejestry itp, co pozwala na powrót przy ponownej aktywacji procesu UXP – lato 2015, Grzegorz Blinowski API #include <unistd.h> – pid_t getpid(void); – pid_t getppid(void); UXP – lato 2015, Grzegorz Blinowski Użytkownicy i grupy • UID: liczba (uid_t) • UID - nazwa - mapowanie przez /etc/passwd lub NIS/NIS+ • API: #include <sys/types.h> #include <pwd.h> struct passwd *getpwnam(const char *login); struct passwd *getpwuid(uid_t uid); struct passwd *getpwent(void); /* seq read */ int setpwent(void); /* rewind */ void endpwent(void); /* close */ UXP – lato 2015, Grzegorz Blinowski Użytkownicy i grupy root:x:0:0::/root:/bin/bash root:$1$xxxj0:13726:0::::: bin:x:1:1:bin:/bin: bin:*:9797:0::::: daemon:x:2:2:daemon:/sbin: daemon:*:9797:0::::: adm:x:3:4:adm:/var/log: adm:*:9797:0::::: lp:x:4:7:lp:/var/spool/lpd: lp:*:9797:0::::: mail:x:8:12:mail:/: mail:*:9797:0::::: news:x:9:13:news:/usr/lib/news: news:*:9797:0::::: mysql:x:27:27:MySQL:/var/lib/mysql:/bin/bash mysql:*:9797:0::::: pop:x:90:90:POP:/: pop:*:9797:0::::: nobody:x:99:99:nobody:/: nobody:*:9797:0::::: backup:x:3116:100:,,,:/home/backup:/bin/bash backup:$1$xxx/:13231:0:9999 ulam:x:3113:98:,,,:/home/ulam:/bin/bash ulam:$1xxx0:13231:0:99999:7 tadek:x:3120:100:,,,:/home/tadek:/bin/bash tadek:$xxxy1:14532:0:99999: UXP – lato 2015, Grzegorz Blinowski Użytkownicy i grupy (API) struct passwd { char *pw_name; /* user name */ char *pw_passwd; /* encrypted password */ int pw_uid; /* user uid */ int pw_gid; /* user gid */ time_t pw_change; /* password change time */ char *pw_class; /* user access class */ char *pw_gecos; /* Honeywell login info */ char *pw_dir; /* home directory */ char *pw_shell; /* default shell */ time_t pw_expire; /* account expiration */ }; UXP – lato 2015, Grzegorz Blinowski Grupy użytkowników • Grupa: – GID, nazwa, lista użytkowników, hasło • „Styl” BSD – użytkownik może należeć do wielu grup na raz – wg. tych przynależności ustalane są prawa dostepu do plików i innych zasobów – lista akt. grup inicjowana przez roota w momencie logowania się użytkownika do systemu • „Styl” SV – proces należy do jednej grupy – polecenie/funkcja newgrup() – zmiana grupy UXP – lato 2015, Grzegorz Blinowski /etc/group /etc/group: daemon:*:1:daemon kmem:*:2:root sys:*:3:root tty:*:4:root operator:*:5:root mail:*:6: bin:*:7: news:*:8: man:*:9: games:*:13: staff:*:20:root,gjb guest:*:31:root,guest uucp:*:66:uucp UXP – lato 2015, Grzegorz Blinowski API grup #include <sys/types.h> #include <grp.h> struct group * getgrnam(const char *name); struct group * getgrgid(gid_t gid); struct group * getgrent(void); int setgrent(void); void endgrent(void); UXP – lato 2015, Grzegorz Blinowski Real, effective, saved UID/GID • Real user / group ID: identyfikacja, rozliczenia (RUID, RGID) • Effective user / group ID: uprawnienia (EUID, EGID) • Potrzeba zmianu UID dla root (przy logowaniu użytkownika) • Potrzeba zmiany praw na innego użytkownika (typowo – innego niż root i powrotu do oryginalnych praw) - trzeba zachować “szczelny” mechanizm uprawnień • Wprowadzono “Saved user / group ID” UXP – lato 2015, Grzegorz Blinowski Z(a)miana RUID, EUID (RGID, EGID) • API – setuid( uid_t uid ); – setgid ( gid_t gid ); – Jeśli root to ustawia RUID, EUID, SVUID (tylko root ustawia RUID) – Jeśli nie root oraz uid==ruid || uid==svuid ustaw: euid na uid – Jesli nie spełnione powyższe to błąd – EUID może być ustawione przez exec() – SVUID początkowo takie jak EUID UXP – lato 2015, Grzegorz Blinowski fork(), wait(), exec() UXP – lato 2015, Grzegorz Blinowski Tworzenie procesów • Życie procesu może być rozpatrywane z punktu widzenia jądra systemu oraz z punktu widzenia programisty: – uruchomienie programu poprzez wywołanie exec...(), inicjalizację w crt0 i zakończenie procesu – Przekazywanie argumentów przez funkcje exec (różne odmiany funkcji exec() ): – main(int argc, char *argv[], char *envp[]) • argv[0] - nazwa programu • rozmiar argv - 5120 – 10240 B typowo (stosowanie programu xargs) • envp – środowisko • extern char **environ, getenv( char *par) UXP – lato 2015, Grzegorz Blinowski Funkcja systemowa fork() • fork() - Zwraca: 0 – dla potomka, > 0 czyli PID potomka dla proc. rodzica, < 0 w wypadku błędu (za dużo procesów) if ( (childpid = fork()) == 0 ) { /* proces potomny */ exec(....); } else { /* proces macierzysty */ } • Cele: – Tworzenie nowego procesu – Tworzenie demona: fork, exec, exit, fork, exec, exit – Tworzenie “farmy” procesów serwisowych – Tworzenie nadzorcy i procesu roboczego (demona) – Shell – wykonywanie poleceń, przetwarzanie w tle, przetwarzanie potokowe UXP – lato 2015, Grzegorz Blinowski fork() Duplikacja procesu: • • • Nowy Deskryptor, Nowe segmenty pamięci (stos, sterta, dane) – dla potomka takie same po wyjściu z fork() ale nie te sam! Pozostaje b.z.: text Deskryptor: • Zostaje częściowo skopiowany, • Zmienia się: PID, PPID, • Dziedziczone: – RUID, EUID, – tablica otwartych plików, – akt. katalog, – umask, – ustawienia dotyczące sygnałów UXP – lato 2015, Grzegorz Blinowski wait() <sys/wait.h> <sys/time.h> <sys/resource.h> int wait( int *status); • Oczekuje na zakończenie procesu potomnego (którego kolwiek), funkcja powolna (może być przerwana sygnałem) • zwraca –1 jeśli nie było potomka • zawiesza się gdy jest potomek, czeka na jego zakończenie • gdy potomek wykona jawny lub niejawny exit() wait() się odblokowuje • Zombie – nie było wait() u rodzica • exit() – generuje SIGCLD (domyślnie nie ma reakcji) • ustawienie konieczny signal( SIGCLD, SIG_IGN) powoduje, że wait() nie jest UXP – lato 2015, Grzegorz Blinowski Status zwracany przez wait() • Status – związany z przyczyna zakończenia procesu oraz argumentem dla exit() (return) Arg dla exit() 0x00 0x00 c:0x80 | nr-sygn nr-sygn 8 bit 0x7f 8 bit Zakończenie przez exit() Sygnał zakończyl potomka: Jeśli core to ustawiony Najstarszy bit mlodszego bajtu Proces zatrzymany – nie zakończony UXP – lato 2015, Grzegorz Blinowski Odmiany wait() int wait3(union wait *status, int options, struct rusage *rusage) • status – jak dla wait() • options – WNOHANG • zwraca: 0 jeśli nie blok i nic się nie stało, -1 błąd, PID pid_t waitpid(pid_t pid, int *stat_loc, int options); • pid == -1 - dowolny proces • pid > 0 - czekamy na konkretnego potomka o danym pid • pid < -1 czekamy na potomka z danego pgrp = abs(pid) • options: WCONTINUED, WNOHANG, WNOWAIT, WUNTRACED UXP – lato 2015, Grzegorz Blinowski execve() • Ładuje nowy program do istniejącego procesu • Wraca tylko w przypadku błędu • Deskryptor procesu po wywołaniu execve() – “nadpisanie” wybranych pól: dane, stos, sterta, text, rejestry, statystyki, liczniki czasu – Zachowanie b/z innych: PID, PPID, PGRP, grupy, tablica plików, aktualny katalog, terminal sterujący, rusage, root fs, umask, maska sygnałów (ale nie funkcje obsługi sygnałów! (dlaczego?)) • Typowe sytuację błędne: – [EACCES] – nie ma prawa dostępu do pliku programu, lub brak +x, lub zły typ pliku – [ENOENT] – brak pliku – [ENOEXEC] – nieznany lub zły format pliku programu – [ENOTDIR] – katalog w ścieżce nie jest katalogiem – [ETXTBSY] – plik programu otwarty do zapisu UXP – lato 2015, Grzegorz Blinowski Rodzina funkcji exec() #include <unistd.h> extern char **environ; int execl(const char *path, const char *arg0, ... /*, (char *)0 */); int execle(const char *path, const char *arg0, ... /*, (char *)0, char *const envp[] */); int execlp(const char *file, const char *arg0, ... /*, (char *)0 */); int execv(const char *path, char *const argv[]); int execve(const char *path, char *const argv[], char *const envp[]); int execvp(const char *file, char *const argv[]); int execvP(const char *file, const char *search_path, char *const argv[]); • Pierwotna funkcja to execve() • Uwzględnienie zmiennej środowiskowej PATH • Specjalne traktowanie plików z magiczną sekwencją #!shell • Może ustawić EUID, EGID (set user/group id) • Uwaga na relację path/file z arg0 ! UXP – lato 2015, Grzegorz Blinowski Środowisko (environment) gjb@skinner:~$ env MANPATH=/usr/local/man:/usr/man:/usr/lib/java/man TERM=xterm SHELL=/bin/bash SSH_CLIENT=172.22.103.92 60220 22 QTDIR=/usr/lib/qt SSH_TTY=/dev/pts/0 USER=gjb MAIL=/var/mail/gjb PATH=/usr/local/bin:/usr/bin:/bin:/usr/games:/usr/lib/java/bin:/usr/li b/java/jre /bin:/usr/lib/qt/bin:. LC_COLLATE=C PWD=/home/gjb LANG=en_US HOME=/home/gjb gjb@skinner:~$ UXP – lato 2015, Grzegorz Blinowski Środowisko (environment) #include <unistd.h> extern char **environ; char *getenv(const char *name); int putenv(char *string); int setenv(const char *name, const char *value, int overwrite); • Przechowywany jest wskaźnik (string nie jest kopiowany) • Konwencja “nazwa=wartość” nie jest narzucona • Ostatni element tablicy environ ma wartość NULL UXP – lato 2015, Grzegorz Blinowski Środowisko (environment) gjb@skinner:~$ env MANPATH=/usr/local/man:/usr/man:/usr/lib/java/man TERM=xterm SHELL=/bin/bash SSH_CLIENT=172.22.103.92 60220 22 QTDIR=/usr/lib/qt SSH_TTY=/dev/pts/0 USER=gjb MAIL=/var/mail/gjb PATH=/usr/local/bin:/usr/bin:/bin:/usr/games:/usr/lib/java/bin:/usr/li b/java/jre /bin:/usr/lib/qt/bin:. LC_COLLATE=C PWD=/home/gjb LANG=en_US HOME=/home/gjb gjb@skinner:~$ UXP – lato 2015, Grzegorz Blinowski Sygnały • Sygnał – informacja binarna o zajściu zdarzenia dostarczane przez jądro do procesu. • Sygnał określony jest przez jego numer (typ/kod sygnału – SIG…) • UWAGA – nie mylić sygnałów z przerwaniami! • Pojęcia: procedura obsługi, obsługa, instalacja proc. obsługi, blokowanie • Dostarczanie: – "Synchroniczne" – związane z akcją procesu – SIGSEGV, SIGBUS, SIGILL, SIGFPE, SIGTRAP, SIGXCPU, SIGEMT, – "Asynchroniczne" – pozostałe, np: SIGINT, SIGQUIT, SIGKILL, SIGPIPE, SIGALRM, SIGTERM, SIGCLD, SIGURG, SIGIO, SIGSTOP, SIGTTIO, SIGTTOU UXP – lato 2015, Grzegorz Blinowski Sygnały • Źródła sygnałów: – Sam proces – syg. synchr., n.p. SIGSEGV – Inny proces – dowolny proces (funkcja kill() lub polecenie kill) lub np. proces potomny (SIGCLD); proces z którym się komunikujemy (SIGPIPE) – Urządzenie – terminal lub pseudoterminal (połączenie sieciowe) – SIGINTR, SIGQUIT, SIGSTOP, SIGTTIO, SIGTTOU – Komunikacja (gniazda): SIGIO, SIGURG – Liczniki czasu: SIGALRM, SIGPROF • Konsekwencje sygnałów: – Wywołanie procedury obsługi – Przerwanie procesu (zakończenie) – Zatrzymanie (wstrzymanie procesu) – Zrzucenie pliku „core” UXP – lato 2015, Grzegorz Blinowski sygnały – lista POSIX.1 Signal Value Action Comment ------------------------------------------------------------------------SIGHUP 1 Term Hangup detected on controlling terminal or death of controlling process SIGINT 2 Term Interrupt from keyboard SIGQUIT 3 Core Quit from keyboard SIGILL 4 Core Illegal Instruction SIGABRT 6 Core Abort signal from abort(3) SIGFPE 8 Core Floating point exception SIGKILL 9 Term Kill signal SIGSEGV 11 Core Invalid memory reference SIGPIPE 13 Term Broken pipe: write to pipe with no readers SIGALRM 14 Term Timer signal from alarm(2) SIGTERM 15 Term Termination signal SIGUSR1 30,10,16 Term User-defined signal 1 SIGUSR2 31,12,17 Term User-defined signal 2 SIGCHLD 20,17,18 Ign Child stopped or terminated SIGCONT 19,18,25 Continue if stopped SIGSTOP 17,19,23 Stop Stop process SIGTSTP 18,20,24 Stop Stop typed at tty SIGTTIN 21,21,26 Stop tty input for background process SIGTTOU 22,22,27 Stop tty output for background process UXP – lato 2015, Grzegorz Blinowski sygnały - lista Sygnały zdefiniowane w POSIX 1003.1-2001 Signal Value Action Comment ------------------------------------------------------------------------SIGBUS 10,7,10 Core Bus error (bad memory access) SIGPOLL Term Pollable event (Sys V). Synonym of SIGIO SIGPROF 27,27,29 Term Profiling timer expired SIGSYS 12,-,12 Core Bad argument to routine (SVID) SIGTRAP 5 Core Trace/breakpoint trap SIGURG 16,23,21 Ign Urgent condition on socket (4.2 BSD) SIGVTALRM 26,26,28 Term Virtual alarm clock (4.2 BSD) SIGXCPU 24,24,30 Core CPU time limit exceeded (4.2 BSD) SIGXFSZ 25,25,31 Core File size limit exceeded (4.2 BSD) UXP – lato 2015, Grzegorz Blinowski funkcja systemowa kill() #include <signal.h> int kill(int pid_proc, int sig); • pid_proc > 0 - sygnał wysyłany do procesu o zadanym pid • pid_proc < -1 - sygnał wysyłany do grupy procesów o gid == abs(pid) • pid_proc == -1 - sygnał wysyłany do wszystkich procesów poza root (gdy wysyła root), wszystkich procesów o EUID=euid wysyłającego (gdy wysyła nie-root) • jeśli sig == 0 sprawdzana będzie możliwość wysłania sygnału • zwraca 0 - jeśli powodzenie (udało się dostarczyć sygnał do co najmniej jednego procesu), -1 w przypadku błędu • Uwaga - zachowanie kill dla pid_proc <0 może być nieco różne dla różnych wersji UNIX-a UXP – lato 2015, Grzegorz Blinowski Funkcja obsługi sygnału • int (*signal(int sig, void (*func)(int)))(int); lub: • void (*signal(int sig, void(*function)(int)))(int); • Zwraca wskażnik do funkcji int (int) – wywoływanej funkcji zostanie przekazany kod sygnału. • SIG_IGN, SIG_DFL – stałe oznaczające odpowiednio: ignorowanie sygnału oraz akcję domyślną (pierwotnie ustawioną) UXP – lato 2015, Grzegorz Blinowski sigaction • int sigaction(int sig, const struct sigaction *act, struct sigaction *oact); • Nowsza wersja funkcji signal(), act – akcja, oact – poprzednia akcja • struct sigaction { void(*) (int) sa_handler; // może być SIG_IGN/DFL sigset_t sa_mask ; // maska zablokowanych podczas obsługi int sa_flags ; ... } • Maska – określa sygnały wstrzymane (stosowana też w innych funkcjach) • Maska sygn jest częścią deskryptora procesu • Uwaga – rozróżnienie: sygnał ignorowany, sygnał wstrzymany UXP – lato 2015, Grzegorz Blinowski sygnały niezawodne Sig_handler() {flag=1;} . . . Sig_handler() {flag=1;} . . . for (;;) { for (;;) { sigprocmask( ); while (flag == 0) while (flag == 0) pause(); } . . . sigsuspend(mask); } UXP – lato 2015, Grzegorz Blinowski sigset • int sigemptyset( sigset_t *set ); • int sigfillset( sigset_t *set ); • int sigaddset( sigset_t *set, int signo ); • int sigdelset( sigset_t *set, int signo ); • int sigismember( const sigset_t *set, int signo ); • int sigprocmask(int how, const sigset_t *set, const sigset_t *oset); sigset_t: maska how: SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK UXP – lato 2015, Grzegorz Blinowski sigset • int sigpending( sigset_t *set ); Zwraca aktualnie wstrzymane sygnały • int sigsuspend( const sigset_t *set ); Zawiesza się i atomowo odblokowuje wskazane sygnały UXP – lato 2015, Grzegorz Blinowski Sygnały niezawodne • Wstrzymywanie sygnałów • Wstrzymywanie w czasie obsługi (patrz sigaction) • Kolejkowanie sygnałów – gdy sygnał dostarczony kilka razy zanim obsłużony (nie zawsze implementowane) • Kolejność dostarczenia nie jest określona, jednak należy oczekiwać że sygnały takie jak SIGSEGV będą miały wyższy priorytet UXP – lato 2015, Grzegorz Blinowski grupy procesów • Proces jest członkiem grupy procesów • Jeżeli PGRP == PID to proces jest liderem grupy procesów • Do grup procesów można dostarczać sygnały (tzn. do wszystkich procesów w grupie) #include <unistd.h> int getpgrp(); SYS V int getpgrp(int pid) BSD, pid == 0 to pgrp tego procesu pgrp może być zmieniany: int setpgrp(); SYS V – zmień PGRP na PID i zostań liderem int setpgrp(int pid, int pgrp); BSD, pid == 0 proces bieżący, <> 0 i EUID=0, pid==pgrp – tworzymy nową grupę i zostajemy liderem EUID!=0: pgrp można zmienić tylko sobie i potomkowi przed wywł. exec() UXP – lato 2015, Grzegorz Blinowski grupy procesów • Używane przez shell-a do zarządzania grupami poleceń (“potokami” poleceń) • Pozwalają na kontrolowanie, które programy otrzymują sygnały związane z terminalem • Tworzenie grupy procesów - przykład: % cat file.txt | grep abc | grep –v def | lp & • uruchomione procesy tworzą własną grupę, która zaczyna pracę w tle, ale może zostać przełączona na pierwszy plan, wtedy zacznie otrzymywać sygnały od terminala sterującego • Grupa procesów tworzona jest poprzez listę struktur proc w kernelu, lider jest na początku listy • Wszystkie grupy związane z terminalem: pierwszoplanowe i drugoplanowe - sesja UXP – lato 2015, Grzegorz Blinowski grupy procesów sterowanie pracami: • bash-3.00$ sleep 10 ^Z [1]+ Stopped sleep 10 bash-3.00$ bg [1]+ sleep 10 & bash-3.00$ fg sleep 10 bash-3.00$ bash-3.00$ read var & <<< Przykład SIGTTIN [1] 18445 [1]+ Stopped bash-3.00$ fg read var abcd efgh read var UXP – lato 2015, Grzegorz Blinowski Grupy procesów Terminal sterujący Sygnaly: SIGINT - ^c SIGQUIT - ^\ SIGSTP - ^z SIGCONT – fg, bg SIGSTOP – fg, bg SIGTTIN SIGTTOU Grupa procesów drugoplanowych Grupa procesów pierwszoplanowa UXP – lato 2015, Grzegorz Blinowski grupy i sesje procesów - c.d. • sesja (session) – grupa grup procesów • sesja typowo związana jest z login-shellem użytkownika i grupuje wszystkie prace (grupy procesów) – zarówno pierwszo- jak i drugoplanowe - “login session” • proces może zmieniać przynależność do grupy procesow tylko w obrębie sesji • grupa procesów i sesja jest zachowana przy exec...() • Terminal sterujący jest związany z sesją (podobnie jak z PG) #include <unistd.h> int setsid(void); // tworzy nową sesję i nową PG; // proces staje się liderem sesji i grupy procesów pid_t getsid(pid_t pid); // pobiera SID dla danego procesu UXP – lato 2015, Grzegorz Blinowski Grupy procesów • Identyfikator grupy terminali – Liczba całkowita > 0 – IGT == pid procesu przywódcy grupy procesów, ktory otworzył terminal, jest to proces sterujący terminala – Proces sterujący terminala to zwykle shell użytkownika, a pozostałe to programy dzialające w tle – Terminal sterujący: /dev/tty – Shell z sterowaniem pracami (job control) – zmienia t_pgrp na bieżącą pracę pierwszoplanową – Sygnały wysyłane przez terminal sterujący: INT, QUIT, HUP, IO, CONT, WINCH Pozbywanie sie term ster: BSD: ioctl( fd, TIOCNOTTY, ...) SVR: setpgrp dla procesu, kóry nie jest przywódcą grupy UXP – lato 2015, Grzegorz Blinowski Scheduler - podstawy • Scheduler decyduje, który z procesów gotowych do wykonania uruchomić. • Wkracza między wywłaszczeniem starego a uruchomieniem nowego procesu. • Uwaga: tylko procesy w stanie SRUN znajdują się w kolejkach schedulera, np. proces w stanie SSLEEP nie! • Wybierany jest gotowy do wykonania proces (SRUN) o największym priorytecie. • Priorytety mogą być dynamicznie zmienne • Priorytet zależy od wielu czynnikow ... UXP – lato 2015, Grzegorz Blinowski Scheduler • Klasy szeregowania (np SVR4) - w zależności od klasy procesy podlegają różnym regułom szeregowania, jednak typowo stosowana jest klasy timeshared (“z podziałem czasu”), systemy “niekomercyjne” implementuja zazwyczaj tylko szeregowanie timeshared (TS) • Kwant czasu – ilość czasu jaką ma procesu zanim procesor zostanie mu odebrany – Długość zależy od klasy szereg. i priorytetu, – może być krótki, długi, nieskończenie długi • Kolejki szeregowania: dla każdego priorytetu utrzymywana jest kolejka szeregowania. • SYSV: Flagi związane ze shedulerem runrun i krunrun, odpowiednio: wywłaszcz proces w trybie użytkownika (ściśle – tuż przed powrotem); wywłaszcz proces w kernelu (możliwe w określonych fragmentach kernela). UXP – lato 2015, Grzegorz Blinowski Priorytety i klasy 159 … 100 99 … 60 59 … 0 priorytet Klasa czasu rzeczywistego (realtime) Stały priorytet, może być zmieniony jawnei przez proces Proces musi dobrowolnie oddać procesor Klasa systemowa (system) Procesy takie jak: pageout, fsflush, itp. Stałe, tj. niezmienne priorytety, procesy zawsze wykonują się w trybie jądra Klasa z podziałem czasu (timeshared) Klasa ze zmiennymi priorytetami i zmiennym kwantem czasu UXP – lato 2015, Grzegorz Blinowski UXP – lato 2015, Grzegorz Blinowski Przełączenie kontekstu Makro: PREEMPT() Wywoływane w kernel-u w punktach możliwego wywłaszczenia { if (krunrun) preempt() } preempt() { Wylicz nową wartość priorytetu (w zależności od klasy szeregowania bieżącego procesu) Odłóż deskryptor bieżącego procesu do odpowiedniej kolejki schedulera swtch() } UXP – lato 2015, Grzegorz Blinowski Algorytm schedulera klasy TS • Zasada: sprawiedliwy i sprawny przydział czasu procesora • Realizowana poprzez dynamicznie zmienny priorytet procesu (0-59) oraz zmienny kwant czasu • Podstawowe reguły: – Priorytet jest obniżany gdy proces zużywa duże ilości czasu procesora (tzw. “proces obliczeniowy”) - tj. przekracza swój kwant czasu i musi być wywłaszczony. – Proces obliczeniowy otrzyma długi kwant czasu. – Priorytet jest podwyższany gdy proces nie zużywa całego wyznaczonego mu kwantu czasu, tj. przechodzi w stan oczekiwania SSLEEP (proces zorientowany na I/O). – Proces zorientowany na I/O otrzyma krótki kwant czasu. UXP – lato 2015, Grzegorz Blinowski Wyliczanie priorytetu • Suma: – Wartość użytkownika (nice) – Wartość dynamiczna wyliczana przez scheduler • Dla każdego priorytetu przechowywana jest struktura określająca m.in.: kwant czasu, priorytet po upłynięciu kwantu, priorytet po wyjściu ze stanu sleep, czas pracy z danym priorytetem • Po wyjściu ze sleep proces otrzymuje (na chwilę) systemowy priorytet (60-99) w zależności od typu zdarzenia, na które oczekiwał proces gdy był uśpiony (blokada pamięci, blokada i-węzła, blokowe I/O, pipe, mount, terminal, SIGCLD, inny sygnał) UXP – lato 2015, Grzegorz Blinowski Przełączenie kontekstu swtch() { save(); /* zapisz kontekst procesu */ pswtch(); /* znajdź nowy proces do wykonania */ /* curproc wskazuje obecnie wznowiony proces */ resume(); /* przywróć zachowany kontekst nowegoprocesu */ /* tu kernel wykonuje juz nowy proces */ } pswtch() { Uaktualnij statystyki if ( SZOMB ) { Zwolnij zasoby if ( NOWAIT ) { usuń deskryptor } } else { if (SONPROC ) ustaw stan na SRUN } Znajdź proces w stanie SRUN o najwyzszym priorytecie Usuń go z kolejki schedulera, ustaw jego status na SONPROC Zaktualizuj zmienne schedulera UXP – lato 2015, Grzegorz Blinowski Klasy priorytetów • Klasa określa algorytm szeregowania • Klasa jest dziedziczona • Nowy scheduler (tj. dla nowej klasy) może być dostarczony jako zbiór dobrze określonych funkcji kernela: – np. CL_FORK, CL_PREEMPT, CL_SLEEP, CL_STOP, CL_WAKEUP – #define CL_PREEMPT(classfuncs, arg)\ (*(classfuncs)->p_clfuncs->cl_preempt)(arg) – Takie rozwiązanie pozwala na wywołanie makra CL_* I przekazanie odpowiedniej funkcji argumentu niezaleznie od klasy szeregowania danego procesu UXP – lato 2015, Grzegorz Blinowski start systemu, init UXP – lato 2015, Grzegorz Blinowski start systemu - boot • Mikrokod uruchamia bootloader • bootloader wczytuje i wykonuje program boot • boot potrafi zlokalizować (device, file) kernel, przekazać do niego argumenty, zaladować (odpakować) go i przekazać mu sterowanie • mlsetup() - inicjalizacja sprzętu, przełączenie procesora w tryb uprzywilejowany, włączenie pamięci wirtualnej • dispinit() - uruchomienie schedulera i przerwań zegarowych • inicjalizacja systemu procesów, uruchomienie "szkieletowego" procesu pid==0 dla zadań inicjalizacji kernela • uruchomienie main() kernela: inicjalizacja: stronicowania, IO, alokacji pamięci dla jądra, VFS, IPC, zegarów, inicjalizacja urządzeń, itd., wł. obsługi przerwań • VFS_MOUNTROOT dla "/" (specjalna funkcja w VFS) • konfiguracja swap • uruchomienie /sbin/init (fork, specjalna postać exec()) • startuje pageout, fsflush i inne serwisy systemowe, init uruchamia skrypty rc UXP – lato 2015, Grzegorz Blinowski Runlevel • Zmienna kernela określająca “rodzaj pracy” systemu • Wprowadzona w SYSV, dostępne w Linux, nieobecne w BSD (choć symulowane w procesie init) • Na danym RL mogą działać tylko procesy o takim samym RL • Zmiana RL powoduje zabicie procesów związanych z poprzednim RL i uruchomienie nowych związanych z bieżącym RL • Zarządzaniem procesami w związku ze zmianą RL zajmuje się proces init • init - uruchamiany przy starcie systemu, pid==1, nadzoruje uruchamianie procesów systemowych, jest przodkiem wszystkich procesów • Plik konfguracyjny /etc/inittab demona init(d) określa programy, skrypty związane z danym RL (zob. dalej) UXP – lato 2015, Grzegorz Blinowski Runlevel RL Linux SysVR4 Solaris • 0 Halt shutdown ROM • 1 Single single/root-fs single/all-fs • 2 Multi Multi Multi/Net • 3 Multi/Net Multi/Net Multi/exportfs • 4 - Multi/user-def Multi/user-def • 5 3+DM Halt,firmware shutdown,pwroff • 6 reboot reboot reboot • s - ==1, current term==cons single/root-fs • DM – X Display manager • root-fs – tylko fs / zamontowane (RO) • all-fs – wszystkie systemy zamontowane • exportfs – systemy plików NFS eksportowane • ROM – interpreter wbudowany w hardware (SPARC) UXP – lato 2015, Grzegorz Blinowski inittab id:rlevel:action:process is:3:initdefault: p3:s1234:powerfail:/usr/sbin/shutdown -y -i5 -g0 >/dev/msglog 2<>/dev/msglog sS:s:wait:/sbin/rcS >/dev/msglog 2<>/dev/msglog </dev/console s0:0:wait:/sbin/rc0 >/dev/msglog 2<>/dev/msglog </dev/console s1:1:respawn:/sbin/rc1 >/dev/msglog 2<>/dev/msglog </dev/console s2:23:wait:/sbin/rc2 >/dev/msglog 2<>/dev/msglog </dev/console s3:3:wait:/sbin/rc3 >/dev/msglog 2<>/dev/msglog </dev/console s5:5:wait:/sbin/rc5 >/dev/msglog 2<>/dev/msglog </dev/console s6:6:wait:/sbin/rc6 >/dev/msglog 2<>/dev/msglog </dev/console fw:0:wait:/sbin/uadmin 2 0 >/dev/msglog 2<>/dev/msglog </dev/console of:5:wait:/sbin/uadmin 2 6 >/dev/msglog 2<>/dev/msglog </dev/console rb:6:wait:/sbin/uadmin 2 1 >/dev/msglog 2<>/dev/msglog </dev/console sc:234:respawn:/usr/lib/saf/sac -t 300 co:234:respawn:/usr/lib/saf/ttymon -g -h -p "`uname -n` console login: " -T sun -d /dev/console -l console -m ldterm,ttcompat UXP – lato 2015, Grzegorz Blinowski inittab • akcje w inittab: – respawn - urucham jeśli się zakończy – wait - uruchom, czekaj na koniec – once - uruchom asynchronicznie – boot, bootwait - przy starcie (RL bez znaczenia) – power, powerwait - jeśli awaria zasilania – off - wyślij SIGTERM, 5s, SIGKILL (domyślne zachowanie przy zmianie RL) – initdefalt - domyślny RL • skrypty rc (/etc/rcN.d): – katalog/zestaw skryptów dla wybranych RL (0,1,2,3) – 0,1: zabicie procesów – przykład dla RL==2: S01mountfs, S20syssetup, S69inet, S71rpc, S75cron – przykład dla RC==3: S10sshd, S20apache UXP – lato 2015, Grzegorz Blinowski Procesy typu demon • Poces demon (daemon) – proces drugo-planowy – nie ma terminala – nie ma shella zgłoszeniowego • Podstawowe usługi oferowane przez „demony”: – wywołanie programu w określonym czasie – drukowanie - lpd – poczta – Inna komunikacja - uucp – statystyka – śledzenie procesów - sendmail, postfix, ... – Wiele innych - acct - sar, profiler - crontab, at, batch UXP – lato 2015, Grzegorz Blinowski Procesy typu demon • Uruchamianie przez: – init lub skrypty rc – Cron - crond (plikicrontab) lub at – Bezpośrednio przez użytkownika • Podstawowe usługi oferowane przez „demony”: – raz uruchamiany – nie umiera i nie jest wznawiany (ale init respawn) – aktywowany zdarzeniem – Typowo powołuje procesy potomne obsługujące zdarzenia UXP – lato 2015, Grzegorz Blinowski Procesy typu demon 1. Zamknij deskryptory plików SV BSD #include <sys/param.h> /* _NFILE w <stdio.h>, getdtablesize() */ for (i=0; i<NOFILE; i++) close(i); 2. Zmień katalog roboczy chdir(”/”); 3. Wyzeruj maskę trybu dostępu umask(0); 4. Przejdź na do pracy drugo-planowej fork() exit() brak & 5. Odłącz się od grupy setpgrp(); /*SV*/ setpgrp(0, getpid()); /*BSD*/ 6. Ignoruj sygnały terminala wyniki procesu drugo-planowego na terminal ? #ifdef SIGTTOU /* SIGTTIN, SIDTSTP */ signal(SIGTTOU, SIG_IGN); #endif UXP – lato 2015, Grzegorz Blinowski Procesy typu demon 7. Odłącz się od terminala *SV*/ setpgrp(); if (fork() != 0) setpgrp(); wskaźnik do terminala = NULL <=> proces nie jest przywódcą exit(0); /* proces macierzysty*/ /* proces potomny*/ /*BSD*/ if ((fd = open(„/dev/tty”, O_RDWR)) >= 0) { ioctl(fd, TIOCNOTTY, (char*) 0); close(fd); } 8. Jeśli tworzymy procesy potomne to zadbajmy o poprawną obsługę ich zakończenia, tj. wait() lub: signal( SIGCLD, SIG_IGN); UXP – lato 2015, Grzegorz Blinowski System plików UXP – lato 2015, Grzegorz Blinowski System plików - podstawy • Drzewo plików • Korzeń drzewa: “File system root”, katalog aktualny “current dir” • Typy plików: zwykłe, katalogi (d), sterowniki znakowe (c) i blokowe (b), FIFO (p), sym-link (l), socket (s), inne • Atrybuty plików: – Nazwa: • znaki dozwolone – wszystkie prócz \0 i / • Niezalecane – znaki wzorce shella, spacja – Właściciel, grupa – prawa dostępu: owner (user), group, other; setuid, setgid, sticky – Daty-czasy: dostepu, modyfikacji, modyfikacji metryczki UXP – lato 2015, Grzegorz Blinowski System plików • pwd, cd, mkdir, rmdir, • ls, cp, mv, rm, • chown, chgrp, chmod • Linki symboliczne, ln, lchown • koncepcja wzorca w shellu: *, [], ?, eg: [a-z], [^a-z] - regexp UXP – lato 2015, Grzegorz Blinowski Atrybuty plików drwxr-xr-x 3 • gjb staff 1024 Mar 20 12:00 Prace Znaczenie pól: – Oznaczenie typu pliku: -, d, c, b, p, l, ... – Oznaczenie praw dostępu: rwxrwxrwx – Liczba (sztywnych) dowiązań do pliku (wskazań z katalogów) – User – Group – Rozmiar w B – Data-czas (3 warianty), typowo ost. modyfikacja – Nazwa pliku UXP – lato 2015, Grzegorz Blinowski user@host:/etc$ ls -la ls -la total 1896 drwxr-xr-x 47 root root 4096 2011-08-22 08:00 ./ drwxr-xr-x 20 root root 4096 2010-07-07 21:21 ../ ... drwxr-xr-x 10 root root 4096 2011-10-04 14:43 rc.d/ lrwxrwxrwx 1 root root 10 2008-08-06 09:42 rc0.d -> rc.d/rc0.d/ lrwxrwxrwx 1 root root 10 2008-08-06 09:42 rc1.d -> rc.d/rc1.d/ lrwxrwxrwx 1 root root 10 2008-08-06 09:42 rc2.d -> rc.d/rc2.d/ lrwxrwxrwx 1 root root 10 2008-08-06 09:42 rc3.d -> rc.d/rc3.d/ lrwxrwxrwx 1 root root 10 2008-08-06 09:42 rc4.d -> rc.d/rc4.d/ lrwxrwxrwx 1 root root 10 2008-08-06 09:42 rc5.d -> rc.d/rc5.d/ lrwxrwxrwx 1 root root 10 2008-08-06 09:42 rc6.d -> rc.d/rc6.d/ -rw-r--r-- 1 root root 64 2009-02-02 14:18 resolv.conf lrwxrwxrwx 1 root root 16 2008-08-06 09:42 rmt -> /usr/libexec/rmt* -rw-r--r-- 1 root root 1615 2008-04-20 08:08 rpc drwxr-xr-x 3 root root 4096 2008-05-28 22:17 samba/ -rw-r--r-- 1 root root 3302 2007-04-10 23:56 screenrc -rw-r--r-- 1 root root 73695 2009-02-02 11:55 services UXP – lato 2015, Grzegorz Blinowski Atrybuty plików c.d. • sticky bit ("lepki") – dawno - program ma pozostać w pamięci (nie być swapowany/pageowany) (tylko historyczne) – katalog - prawo +w nie pozwala na usuwanie plików (zastosowanie: /tmp), usuwać może: root, właściciel pliku, właściciel katalogu – tylko root może ustawiać s-b • Immutable bit (BSD, Linux) - dodatkowy bit, gdy ustawiony pliku nie można usunąć (nawet root), stosowany do chronienia ważnych plików systemowych UXP – lato 2015, Grzegorz Blinowski Atrybuty plików c.d. prawo G+S (SETGID) dla katalogu: – tworzone pliki beda dziedziczyć grupę katalogu, a nie użytkownika gjb@skinner:~$ mkdir test1 gjb@skinner:~$ ls -ld test1 drwxr-xr-x 2 gjb users 4096 2014-03-13 12:38 test1/ gjb@skinner:~$ chmod g+s test1 gjb@skinner:~$ ls -ld test1 drwxr-sr-x 2 gjb users 4096 2014-03-13 12:38 test1/ gjb@skinner:~$ chgrp cvs test1; cd test1 gjb@skinner:~/test1$ touch a; mkdir b gjb@skinner:~/test1$ chmod g-s . ; touch c gjb@skinner:~/test1$ ls -la total 12 drwxr-sr-x 3 gjb cvs 4096 2014-03-13 12:39 ./ drwx--x--x 24 gjb users 4096 2014-03-13 12:38 ../ -rw-r--r-- 1 gjb cvs drwxr-sr-x 2 gjb cvs -rw-r--r-- 1 gjb users 0 2014-03-13 12:39 a 4096 2014-03-13 12:39 b/ 0 2014-03-13 12:45 c UXP – lato 2015, Grzegorz Blinowski API systemu plików • chmod, chown, close, creat, dup, dup2, fcntl, link, lseek, open, read, stat, umask, write, unlink, stat(5), UXP – lato 2015, Grzegorz Blinowski Funkcja open() #include <sys/stat.h> #include <fcntl.h> int open(const char *path, int oflag, /* mode_t mode */...); • oflag: – O_RDONLY, O_WRONLY, O_RDWR – O_NDELAY - obiekty komunikacyjne – O_APPEND – dopisuj do końca – O_CREAT – utwórz jeśli nie ma – O_TRUNC – zmniejsz dług do 0 (nadpisz) – O_EXCL – razem z O_CREAT – utwórz tylko jeśli nie istnieje • mode – 32 bit, określa zarówno prawa dostępu (niższe bity, jak i typ oraz dodatkowe atrybuty – wyższe bity) UXP – lato 2015, Grzegorz Blinowski mode #define S_IRWXU 00700 /* read, write, execute: owner */ #define S_IRUSR 00400 /* read permission: owner */ #define S_IWUSR 00200 /* write permission: owner */ #define S_IXUSR 00100 /* execute permission: owner */ #define S_IRWXG 00070 /* read, write, execute: group */ #define S_IRGRP 00040 /* read permission: group */ #define S_IWGRP 00020 /* write permission: group */ #define S_IXGRP 00010 /* execute permission: group */ #define S_IRWXO 00007 /* read, write, execute: other */ #define S_IROTH 00004 /* read permission: other */ #define S_IWOTH 00002 /* write permission: other */ #define S_IXOTH 00001 /* execute permission: other */ UXP – lato 2015, Grzegorz Blinowski mode – typy, maski #define S_IFMT 0xF000 /* type of file */ #define S_IAMB 0x01FF /* access mode bits */ #define S_IFIFO 0x1000 /* fifo */ #define S_IFCHR 0x2000 /* character special */ #define S_IFDIR 0x4000 /* directory */ #define S_IFBLK 0x6000 /* block special */ #define S_IFREG 0x8000 /* regular */ #define S_IFLNK 0xA000 /* symbolic link */ #define S_IFSOCK 0xC000 /* socket */ #define S_IFDOOR 0xD000 /* door */ #define S_ISUID 0x0800 /* set user id on execution */ #define S_ISGID 0x0400 /* set group id on execution */ #define S_ISVTX 0x0200 /* save swapped text even after use */ #define S_IREAD 00400 /* read permission, owner */ #define S_IWRITE 00200 /* write permission, owner */ #define S_IEXEC 00100 /* execute/search permission, owner */ #define S_ENFMT S_ISGID /* record locking enforcement flag */ UXP – lato 2015, Grzegorz Blinowski mode – makra typu #define S_ISFIFO(mode) (((mode)&0xF000) == 0x1000) #define S_ISCHR(mode) (((mode)&0xF000) == 0x2000) #define S_ISDIR(mode) (((mode)&0xF000) == 0x4000) #define S_ISBLK(mode) (((mode)&0xF000) == 0x6000) #define S_ISREG(mode) (((mode)&0xF000) == 0x8000) #define S_ISLNK(mode) (((mode)&0xF000) == 0xa000) #define S_ISSOCK(mode) (((mode)&0xF000) == 0xc000) #define S_ISDOOR(mode) (((mode)&0xF000) == 0xd000) UXP – lato 2015, Grzegorz Blinowski creat(), dup(), dup2() #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int creat(const char *path, mode_t mode); • Zwracany deskryptor O_WRONLY • Jeśli istnieje jest skracany do 0 • Jest równoważny – open(path, O_WRONLY | O_CREAT | O_TRUNC, mode) #include <unistd.h> int dup(int fd); int dup2(int fd, fd2); • Duplikuje wskazany deskryptor, dup2() - nadpisuje fd2 • Dup(): zwrócony deksryptor będzie najniższy numerycznie z dostępnych • Równoważne fcntl(int fd, F_DUPFD, 0) UXP – lato 2015, Grzegorz Blinowski read(), write(), lseek() int read(int fd, char *buff, unsigned int nbytes); int write(int fd, char *buff, unsigned int nbytes); • Zwraca – liczbę odczytanych/zapisanych bajtów, 0/-1 – koniec pliku lub brak miejsca na fs long int lseek(int fd, long offset, int whence); off64_t lseek64(int fd, off64_t offset, int whence); whence: – 0 SEEK_SET – przesuń od początku pliku (suma) – 1 SEEK_CUR – przesuń od pozycji bieżącej (suma) – 2 SEEK_END – przesuń od końca pliku (suma – uwaga offset może być tu dodatni i ujemny) UXP – lato 2015, Grzegorz Blinowski stat() #include <sys/types.h> #include <sys/stat.h> int stat(const char *path, struct stat *buf); int lstat(const char *path, struct stat *buf); int fstat(int fildes, struct stat *buf); • lstat - działa na sym-linku (a nie na wskazanym pliku) • fstat działa na otwartym pliku UXP – lato 2015, Grzegorz Blinowski struct stat { struct stat ushort st_mode; /* typ i prawa dostępu */ ino_t st_ino; /* Inode number */ dev_t st_dev; /* ID of device containing */ /* a directory entry for this file */ dev_t st_rdev; /* ID of device */ /* This entry is defined only for */ /* char special or block special files */ nlink_t st_nlink; /* Number of links */ uid_t st_uid; /* User ID of the file's owner */ gid_t st_gid; /* Group ID of the file's group */ off_t st_size; /* File size in bytes */ time_t st_atime; /* Time of last access */ time_t st_mtime; /* Time of last data modification */ time_t st_ctime; /* Time of last file status change */ /* Times measured in seconds since */ /* 00:00:00 UTC, Jan. 1, 1970 */ long st_blksize; blkcnt_t st_blocks; /* Preferred I/O block size */ /* Number of 512 byte blocks allocated*/ UXP – lato 2015, Grzegorz Blinowski Błędy zwracane przez stat() • EACCES - Search permission is denied for a component of the path prefix. • EINTR function. A signal was caught during the execution of the stat() or lstat() • ELOOP path. Too many symbolic links were encountered in translating • ENAMETOOLONG The length of the path argument exceeds PATH_MAX, or the length of a path component exceeds NAME_MAX while _POSIX_NO_TRUNC is in effect. • ENOENT - The named file does not exist or is the null path- name. • ENOLINK - The path argument points to a remote machine and the link to that machine is no longer active. • ENOTDIR - A component of the path prefix is not a directory. • EBADF - The fildes argument is not a valid open file UXP – lato 2015, Grzegorz Blinowski Dowiązania sztywne • Organizacja logiczna systemu plików: – Katalogi – Metryczki (inode – information node) – z grubsza odpowiada strukturze stat – Dane czyli alokacja (plik może nie zawierać danych) • Katalog: – Mapuje nazwy na numery metryczek – Nie przechowuje więcej informacji o pliku! – do danej metryczki mogą występować odwolania z wielu katalogów! – Przykład: wpisy “.”, “..” – Dowiązanie sztywne (hard link) powiązanie: nazwametryczka – Numery metryczek sa uniklane w obrębie danego systemu plików, więc hard-link tylko w obrębie jednego fs UXP – lato 2015, Grzegorz Blinowski katalog (wiąże nazwy z nr inode) tablica inode- logicznie (zawiera atrybuty plików) nr inode - metryczka dir1 . .. file1.txt dir2 ... 100 1 110 111 / dir1 dir2 file1.txt 1 / ... 100 dir1 ... 110 file1 111 dir2 112 ... UXP – lato 2015, Grzegorz Blinowski Dowiązania symboliczne (soft- sym-links) • Dowiązanie symboliczne to specjalny plik, który zawiera nazwę wskazywanego pliku • Dowiązanie może mieć postać: “../../src”., “/usr/bin/ls”, “./files/abc.txt” • Kernel w typowych funkcjach przetwarzających ścieżki “podąża za” sym-linkami (zob. lstat() jako jeden z wyjatków) • Symlinki mogą przekraczać granice systemów plików! • Obsługa s-l jest wolniejsza od h-l int symlink(const char *oldpath, const char *newpath); int link(const char *oldpath, const char *newpath); UXP – lato 2015, Grzegorz Blinowski Wywołanie fcntl() #include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd); int fcntl(int fd, int cmd, long arg); int fcntl(int fd, int cmd, struct flock *lock); • “file control” - różnego rodzaju operacje na pliku reprezentowanym przez deskryptor – F_DUPFD – jak dup() – F_GETFD, F_SETFD – pobiera/ustawia flagi deskryptora (jest jedna: FD_CLOEXEC) – F_GETFL / F_SETFL – pobiera ustawia flagi: O_RD, O_WR, O_RDWR, O_APPEND, O_NONBLOCK, O_ASYNC, O_DIRECT (ale nie mode) – F_GETLK, F_SETLK, F_SETLKW – zob. Dalej – F_GETOWN, F_SETOWN, F_GETSIG and F_SETSIG – ustalają proces i sygnał wysyłany z gniazda BSD (socket) UXP – lato 2015, Grzegorz Blinowski Wywołanie fcntl() #include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd); int fcntl(int fd, int cmd, long arg); int fcntl(int fd, int cmd, struct flock *lock); • F_SETLEASE, F_GETLEASE – blokada plików, zob. dalej • F_NOTIFY – powiadomienie o zmianach w fs: – fd – musi reprezentować katalog – Poniższe flagi mogą być traktowane jak maski bitowe (|) • DN_ACCESS: read • DN_MODIFY: write,truncate • DN_CREATE: open, creat, mknod, mkdir, link, symlink, rename • DN_DELETE: unlink, rename - do innego kat., rmdir • DN_RENAME: rename - w tym kat. • DN_ATTRIB: chown, chmod – Zmiana sygnalizowana przez SIGIO; DN_MULTISHOT jeśli chcemy otrzymywać je stale UXP – lato 2015, Grzegorz Blinowski Wywołanie ioctl() #include <unistd.h> #include <sys/ioctl.h> int ioctl(int fd, int request, ...); DIOxxx FIOxxx MTIOxxx SIOxxx TIOxxx <disklabel.h> - niskopoziomowy dostep do etykiety i tabl partycji dysku <ioctl.h> - pliki <mtio.h> - pamięci taśmowe <ioctl.h> - gniazda <ioctl.h> - terminale • ioctl() - "catch all" dla pozostalych operacji I/O • ustawianie różnego rodzaju flag • obsługa urządzeń - np. linie sterujące portów szeregowych • niskopoziomowe operacje na dyskach - tablica partycji, itp. • niskopoziomowe operacje na taśmach - przewijanie, omijanie archiwów • operacje na sterownikach kart sieciowych • ... UXP – lato 2015, Grzegorz Blinowski umask, umask() #include <sys/types.h> #include <sys/stat.h> mode_t • • • umask(mode_t cmask); Ustawia zmienną umask w deskryptorze procesu Zwraca poprzednią maskę (nie ma sytuacji błędnej) maska: – taka jak prawa dostępu dla open/creat, gjb@fox:/tmp> umask 22 gjb@fox:/tmp> touch a; ls -l a -rw-r--r-- 1 gjb ... gjb@fox:/tmp> umask 777 gjb@fox:/tmp> touch b – używana przy tworzeniu nowego pliku gjb@fox:/tmp> ls -l b lub katalogu, ---------- 1 gjb ... – maska jest odejmowana od uprawnień w momencie tworzenia pliku, – np. dla maski 022 skasowane bedą bity w dla og (og-w) • Proces demon powinien ustawiać umask gjb@fox:/tmp> touch a; ls -l a -rw-r--r-- 1 gjb ... UXP – lato 2015, Grzegorz Blinowski Operacje na katalogach • Przypomnienie: katalog jest plikiem “prawie zwykłym” - kilka funkcji systemowych interpretuje go inaczej niż plik #include <unistd.h> int chdir(const char* path); zmienia aktualny katalog procesu int fchdir(int fd); zmienia aktualny katalog na “zapamiętany” w deskryptorze (katalogi można otwierać przy pomocy open() ale tylko w trybie R) #include <unistd.h> int mkdir (const char *path, mode_t perms); Tworzy katalog pusty katalog, dodaje pozycje . i .. Uwaga: zapewne chcemy ustawic co najmniej u+x (inaczej nie będzie dostepu do plików) UXP – lato 2015, Grzegorz Blinowski Operacje na katalogach • Przypomnienie: katalog mapuje nazwę na numer inode, faktyczna wewnętrzna struktura katalogu jest ukryta #include <dirent.h> • DIR* opendir(const char* dirname); zwraca wskaźnik na uchwyt do katalogu struct dirent* readdir(DIR* dirp); odczytuje kolejne struktury dirent struct dirent { ino_t d_ino; char d_name[]; . . . } void rewinddir(DIR* dirp); “przewija do początku” void seekdir(DIR* dirp, long int loc); long int telldir(DIR* dirp); zwraca katualną pozycję, “przewija” do wskazanej pozycji UXP – lato 2015, Grzegorz Blinowski Operacje na katalogach #include <dirent.h> int listdir(const char *path) { struct dirent *entry; DIR *dir_p; dir_p = opendir(path); if (dir_p == NULL) { /* ... */ return -1; } while((entry = readdir(dir_p))) printf(“%9.9d %s “, (long)entry->d_ino, entry->d_name); closedir(dir_p); return 0; } UXP – lato 2015, Grzegorz Blinowski Zajmowanie plików i rekordów • Atomowość operacji na plikach wykraczających poza read()/write() (np. read() -> write() ) wymaga mechanizmu synchronizacji • Można użyć jednego z wielu dostępnych mechanizmów, ale lepszy byłby mechanizm ściśle związany z plikami (a nie np. semafory IPC) • Istnieje kilka API (z przyczyn historycznych), w praktyce jeden mechanizm na poziomie kernela i ewentualnie kilka API • Niezależnie od rozwiązania API, dwa tryby zajmowania: – advisory locking (zalecane) - blokada dla tego kto jej używa, tj. używa funkcji blokad – mandatory locking (obowiązkowe) – nie da się obejść blokady (niedozwolone I/O będzie blokowane lub EAGAIN jeśli tryb NONBLOCK) – domyślnie: advisory, mandatory gdy prawa: SGID+,g-x UXP – lato 2015, Grzegorz Blinowski Po co zajmujemy pliki? #define SEQFILE “seqno” /*nazwa pliku*/ #define MAXBUFF 100 int main(int argc, char **argv) { int fd, i, n, pid, seqno; char buff[MAXBUFF +1]; pid = getpid(); if (( fd=open(SEQFILE,2) ) < 0 ) err_sys(„can’t open %s”, SEQFILE); for ( i=0; i<20; i++ ) { my_lock(fd); /*blokada*/ lseek(fd, 0L, 0); /*przewiń*/ if (( n=read(fd,buff,MAXBUFF) ) <=0 ) err_sys(“read error”); buff[n] = ‘\0’; if((n=sscanf(buff, “%d\n”, &seqno))!=1) err_sys(„sscanf error”); printf(“pid=%d, seq#=%d\n”,pid, seqno); seqno++; sprintf(buff, „%03d\n”, seqno); n=strlen(buff); lseek(fd, 0L, 0); /*przewiń*/ if (write (fd,buff,n)!=n) err_sys(„write error”); my_unlock(fd);/*zdjęcie blokady*/ } } wykonanie: pid = 186, 187, ... 187, 186, seq#=1 seq#=1 seq#=10 seq#=2 UXP – lato 2015, Grzegorz Blinowski Zajmowanie plików i rekordów Styl BSD – zajmowany jest cały plik #include <sys/file.h> int flock(int fd, int operation); • LOCK_SH shared lock. • LOCK_EX exclusive lock. • LOCK_UN remove an existing lock Styl SYSV – zajmowany jest fragment pliku od bieżącej pozycji, size B do przodu lub do tyłu (jeśli ujemne): #include <unistd.h> int lockf(int fildes, int function, off_t size); • • • • F_ULOCK F_LOCK F_TLOCK F_TEST unlock locked sections lock a section for exclusive use test and lock a section for exclusive use test a section for locks by other processes UXP – lato 2015, Grzegorz Blinowski Zajmowanie plików i rekordów • flock() zajmuje tylko całe pliki • lockf() ma tylko jeden typ blokady i zakłada, że plik otwarty do zapisu • Obydwa rozwiązania są niewystarczajace, dlatego stosujemy fcntl: #include <fcntl.h> int fcntl(int fildes, cmd, struct flock *lck); zwraca: EBADF, EAGAIN/EACCES (obszar zajęty), EINTR (blokada i sygnał), EDEADLK (cykl) cmd – F_GETLK, zwraca informacje o blokadach (jeśli są) F_SETLK, zakłada blokadę wskazanego typu i zakresu F_SETLKW, j.w. blokujące struct flock { short int short int off_t off_t pid_t } l_type; l_whence; l_start; l_len; l_pid; /* F_RDLCK, F_WRLCK, or F_UNLCK. */ /* SEEK_SET, SEEK_CUR, or SEEK_END */ /* zwrotnie */ UXP – lato 2015, Grzegorz Blinowski Zajmowanie plików i rekordów - fcntl() • l_whence - jak w lseek, • jeśli l_len==0 to zajęcie zawsze do końca pliku (także "przyszłego" końca), • można też zajmować poza aktualny koniec (analogicznie jak pozycjonowanie w lseek() ), • sąsiadujące zajęte obszary są sklejane, • zakończenie procesu zwalnia jego zajęcia, • zamknięcie deskryptora też zwalnia zajęcia (nawet jeśli proces ma inne deskryptory do tego pliku!), • zajęcia nie są dziedziczone przy fork(), ale nie są usuwane przy exec(), • zajęcia są globalne - na poziomie pliku. UXP – lato 2015, Grzegorz Blinowski Inne polecenia fcntl() - lease • Lease – Solaris, Linux od kernela 2.4 – F_SETLEASE: arg przyjmuje wartość: F_RDLCK, F_WRLCK, F_UNLCK – F_GETLEASE: zwraca wartości j.w. – Lease: jak flock() czyli R,...,R/W, ale: • Konfliktująca operacja open()/truncate() jest zawieszana, a do właściciela pliku wysyłany jest sygnał SIGIO – F_RDLCK (O_RD) – read lease, notyfikacja gdy write/trunc – F_WRLCK – write lease, notyfikacja gdy read/write/trunc • Właściciel w takiej sytuacji powinien oddać lease poprzez F_UNLOCK • Jeśli nie zrobi tego w czasie /proc/sys/fs/lease-break-time lease zostanie mu zabrany (“lease break”) • Po oddaniu lub zabraniu lease, konfliktująca operacja może być kontynuowana UXP – lato 2015, Grzegorz Blinowski Zaawansowane funkcje I/O #include <sys/uio.h> • ssize_t readv(int fildes, const struct iovec *iov, int iovcnt); • ssize_t writev(int fildes, const struct iovec *iov, int iovcnt); struct iovec { void size_t *iov_base; iov_len; } • scattered read / gather write • Zastosowanie: – atomowość operacji (istotne np. przy UDP) – unikamy kopiowania • ssize_t pread(int fildes, void *buf, size_t nbytes, off_t offset); • ssize_t pwrite(int fildes, void *buf, size_t cont, off_t offset); • Zastosowanie: – atomowe pozycjonowanie i odczyt lub zapis – lseek(); read() lub write() nie jest równoważny UXP – lato 2015, Grzegorz Blinowski Zaawansowane funkcje I/O #include <sys/types.h> #include <sys/uio.h> #include <unistd.h> iovcnt = sizeof(iov) / sizeof(struct iovec); ssize_t bytes_read; bytes_read = readv(fd, iov, int fd; char buf0[20]; iovcnt); char buf1[30]; ... char buf2[40]; int iovcnt; struct iovec iov[3]; iov[0].iov_base = buf0; iov[0].iov_len = sizeof(buf0); iov[1].iov_base = buf1; iov[1].iov_len = sizeof(buf1); iov[2].iov_base = buf2; iov[2].iov_len = sizeof(buf2); UXP – lato 2015, Grzegorz Blinowski Zawansowane I/O - poll • int poll(struct pollfd fds[], nfds_t nfds, int timeout); struct pollfd { int fd; short events; short revents; } • Funkcja jest zamiennikiem SYSV select() (z BSD) • Oczekuje na zdarzenia podane w tablicy fds[]; timeout określony jest w ms; -1 = niesk.; 0 – bez blokoania • Zdarzenie (pollfd) : deskryptor, oczekiwany typ zdarzenia, zdarzenie, które wystąpiło • Zwraca: liczbę deskryptorów, na których wystąpiły zdarzenia; 0 – timeout; -1 / errno -błąd • Zdarzenia (zob. też powiązanie ze STREAMS): – POLLIN można czytać – POLLRDNORM można czytać dane zwykłe – POLLRDBAND można czytać dane wysoko priorytetowe – POLLPRI j.w. – POLLOUT można pisać – POLLWRNORM j.w. – POLLWRBAND można pisać priorytetowe dane UXP – lato 2015, Grzegorz Blinowski Zawansowane I/O - poll • int poll(struct pollfd fds[], nfds_t nfds, int timeout); struct pollfd { int fd; short events; short revents; } • • Zdarzenia zwracane - dodatkowo: – POLLERR błąd – POLLHUP rozłączenie – POLLNVAL zły deskryptor Ustawienie flagi I_SETSIG poprzez ioctl(fd, I_SETSIG, arg) powoduje wysłanie sygnału SIGPOLL gdy zajdzie określone zdarzenie; pozwala to na realizację asynchronicznego I/O (sterowanego zdarzeniami); następujące zdarzenia mogą być przekazane jako maska bitowa w arg ioctl(): – S_RDNORM; S_RDBAND; S_HIPRI, S_OUTPUT, S_WRNORM, S_ERROR, S_HANGUP, S_BANDURG (gdy razem z S_RDBAND wysyła SIGURG) UXP – lato 2015, Grzegorz Blinowski Zawansowane I/O - poll • int poll(struct pollfd fds[], nfds_t nfds, int timeout); struct pollfd { int fd; short events; short revents; } • • Zdarzenia zwracane - dodatkowo: – POLLERR błąd – POLLHUP rozłączenie – POLLNVAL zły deskryptor Ustawienie flagi I_SETSIG poprzez ioctl(fd, I_SETSIG, arg) powoduje wysłanie sygnału SIGPOLL gdy zajdzie określone zdarzenie; pozwala to na realizację asynchronicznego I/O (sterowanego zdarzeniami); następujące zdarzenia mogą być przekazane jako maska bitowa w arg ioctl(): – S_RDNORM; S_RDBAND; S_HIPRI, S_OUTPUT, S_WRNORM, S_ERROR, S_HANGUP, S_BANDURG (gdy razem z S_RDBAND wysyła SIGURG) UXP – lato 2015, Grzegorz Blinowski VFS UXP – lato 2015, Grzegorz Blinowski Podstawy konstrukcji VFS • Tablica plików per-proces – odwołujemy się przez liczbowe deskryptory zwracane przez open(), pipe(), socket(), ... – dziedziczona przy fork(), – nie modyfikowana (poza close-on-exec) przez exec(), – przechowuje tylko flagę c-o-e oraz wskazanie na struct file. • Systemowa lista plików (struct file), – przechowuje informacje o otwartym pliku: pozycję, flagi (append, read/write, no-block) – wskazuje na strukturę vnode • VNODE Table - Systemowa tablica wirtualnych metryczek (struct vnode) – jeden używany plik odpowiada zawsze dokładnie jednemu vnode – vnode zapewnia wirtualizację czyli niezależność ioperacji od typu systemu UXP – lato 2015, Grzegorz Blinowski Tablica otwartych plików ufchunk user uf_ofile[] 0|1|2|. . proc *file lista otwartych plików w systemie N U L L ufchunk ufchunk 0|1|2|. . 0|1|2|. . NFPCHUNK = 24 tworzy open() create() dup() cred file file file file file vnode vnode vnode vnode vnode filock filock filock N U L L UXP – lato 2015, Grzegorz Blinowski struct file • Struktury file reprezentują otwarte przez procesy pliki • Struktury spięte są w listę (nie ma problemów wydajnościowych, bo lista nigdy nie jest przeszukiwana) struct file { struct file *f_next, *f_prev; off_t f_offset; cnt_t f_count; ushort f_flag; mode_t f_mode; struct vnode *f_vnode; struct cred *f_cred; } struct file { // Linux! mode_t f_mode; loff_t f_pos; unsigned short f_flags; unsigned short f_count; // pola związane z odczytem // z wyprzedzeniem unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin; struct file *f_next, *f_prev; int f_owner; struct inode * f_inode; struct file_operations *f_op; unsigned long f_version; void *private_data; }; UXP – lato 2015, Grzegorz Blinowski struct vnode • Uogólnienie inode • Dokładnie jeden VNode na używany plik (nie koniecznie otwarty!) struct vnode { u_short u_long struct vfs struct vnodeops struct vfs struct stdata struct page enum vtype dev_t caddr_t struct filock kmutex_t . . . } v_flag; v_count; *v_vfsmountedhere; *v_op; *v_vfsp; *v_stream; *v_pages; v_type; v_rdev; v_data; *v_filocks; v_lock; /* /* /* /* /* /* /* /* /* /* /* /* vnode flags */ reference count */ ptr to vfs mounted here */ vnode operations */ ptr to containing VFS */ associated stream */ vnode pages list */ vnode type */ device (VCHR, VBLK) */ private data for fs */ ptr to filock list */ protects vnode fields */ UXP – lato 2015, Grzegorz Blinowski Struktura vnode v_flag v_count - liczba odwołań - dowiązań *v_vfsmountedhere *v_op - opis typu systemu plików *v_vfsp - struktura wirtualnego systemu plików *v_stream *v_pages v_type VNON v_rdev - major & minor number VREG v_data - np. struct inode VDIR *v_filock VBLK VROOT VNOMAP VDUP VNOSWAP VNOMOUNT VVISSWAP regular VCHR VLINK VFIFO pipe VXNAM - XENIX shared data segment VBAD bad file UXP – lato 2015, Grzegorz Blinowski vnode->v_op • Pole v_op pozwala na realizację kernelowych funkcji plikowych niezależnie od faktycznej implementacji systemu plików • v_op pokazuje na tablicę operacji implementowanych przez dany system plików • Operacje są “przykryte” przez makra VOP_... • Dla argumentu arg kernel na każdym vnode może wykonać np. operację open: VOP_OPEN(vp, arg) co jest rozwijane na: (*(vp->v_op->vop_open))(vp, arg) • vnodeops: VOP_LOOKUP, VOP_CREATE, VOP_MKNOD, VOP_OPEN, VOP_CLOSE, VOP_ACCESS, VOP_GETATTR, VOP_SETATTR, VOP_READ, VOP_WRITE, VOP_IOCTL, VOP_FCNTL, VOP_POLL, VOP_KQFILTER, VOP_REVOKE, VOP_MMAP, VOP_FSYNC, VOP_SEEK, VOP_REMOVE, VOP_LINK, VOP_RENAME, VOP_MKDIR, VOP_RMDIR, VOP_SYMLINK, VOP_READDIR, VOP_READLINK, VOP_ABORTOP, VOP_INACTIVE, VOP_RECLAIM, VOP_LOCK, VOP_UNLOCK, VOP_ISLOCKED, VOP_BMAP, VOP_PRINT, VOP_PATHCONF, VOP_ADVLOCK, VOP_LEASE, VOP_WHITEOUT, VOP_GETPAGES, VOP_PUTPAGES, VOP_STRATEGY, VOP_BWRITE, VOP_GETEXTATTR, VOP_SETEXTATTR, VOP_LISTEXTATTR UXP – lato 2015, Grzegorz Blinowski vnode->v_op • int VOP_LOOKUP(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp); – Szuka pliku po nazwie; dvp: vnode katalogu, vpp: zwracany vnode; cnp: struktura używana do przetwarzania nazw scieżek • int VOP_OPEN(struct vnode *vp, int mode, kauth_cred_t cred); – Otwiera plik • int VOP_READ(struct vnode *vp, struct uio *uio, int ioflag, kauth_cred_t cred); – Czyta z pliku; uio: uogólniona struktura buforowa UXP – lato 2015, Grzegorz Blinowski vnode • przydzielany gdy open() (o ile już nie istnieje) • ale także: exec(), operacje na katalogach i inne sytuacje • nie są spięte w globalną listę; dostęp np. przez struct file; w celu przyśpieszenia dostępu do vn stosuje się też dodatkowe struktury danych - zob. DNLC, specfs) • blokady realizowane są poprzez listę struktur filock przywiązanych do vn UXP – lato 2015, Grzegorz Blinowski VFS • W systemie plików: – różne typy systemów (UFS, NFS, EXFS, XFS, tmpfs, procfs, sysfs, fdfs, ...) – różne zamontowane systemy (jednego lub różnych typów) – vnode pozwala na obsługę operacji w różnych typach systemów plików, ale jak organizujemy to na poziomie montowań? mount [-opcje] [-f typ.systemu] [-o opcje.tego.systemu] [urządzenie] [katalog] % mount -f ufs -o ro /dev/dsk/c0t0d0s0 /usr/local/files UXP – lato 2015, Grzegorz Blinowski montowania / / /bin /etc /bin /usr /etc /usr/local mount -f ufs /dev/hda0 /usr/local /usr /usr/local UXP – lato 2015, Grzegorz Blinowski VFS • mount - zamontowanie systemu plików • unmount - zdemontowanie systemu plików • fsck - sprawdzenie spójności i poprawności fs • format fmthard - formatowanie dysku • mkfs - tworzenie systemu plików struct vfssw - tablica przełączania VFS typy systemów plików i dostępne w nich operacje struct vfs - struktura VFS - dla każdego zamontowanego systemu plików UXP – lato 2015, Grzegorz Blinowski VFS • Tablica rozdzielcza wirtualnego systemu plików struct vfssw: typedef struct vfssw { char *vsw_name; int (*vsw_init)(); struct vfsops *vsw_vfsops; } vfssw_t; struct vfssw vfssw[] { {"ufs", ufsinit, &ufs_vfsops}, {"proc", prinit, &prvfsops} }; • vsw_vfsops definiuje funkcje danego fs (analogicznie do v_op): vfs_mount, vfs_unmount, vfs_root, vfs_statvfs, vfs_sync, vfs_vget, vfs_mountroot; makra: VFS_MOUNT, VFS_UMOUNT, VFS_ROOT, itd. UXP – lato 2015, Grzegorz Blinowski Operacje na vfs typedef struct vfsops { int (*vfs_mount)(); int (*vfs_unmount)(); int (*vfs_root)(); int (*vfs_statvfs)(); int (*vfs_sync)(); int (*vfs_vget)(); int (*vfs_mountroot)(); int (*vfs_swapvp)(); } vfsops_t; #define VFS_MOUNT(vfsp, mvp, uap, cr) (*(vfsp)->vfs_op->vfs_mount)(vfsp, mvp, uap, cr) • #define VFS_UNMOUNT(vfsp, cr) (*(vfsp)->vfs_op->vfs_unmount)(vfsp, cr) • #define VFS_ROOT(vfsp, vpp) (*(vfsp)->vfs_op->vfs_root)(vfsp, vpp) • #define VFS_STATVFS(vfsp, sp) (*(vfsp)->vfs_op->vfs_statvfs)(vfsp, sp) • #define VFS_SYNC(vfsp, flag, cr) (*(vfsp)->vfs_op->vfs_sync)(vfsp, flag, cr) • #define VFS_VGET(vfsp, vpp, fidp) (*(vfsp)->vfs_op->vfs_vget)(vfsp, vpp, fidp) • #define VFS_MOUNTROOT(vfsp, init) (*(vfsp)->vfs_op->vfs_mountroot)(vfsp, init) • #define VFS_SWAPVP(vfsp, vpp, nm) (*(vfsp)->vfs_op->vfs_swapvp)(vfsp, vpp, nm) UXP – lato 2015, Grzegorz Blinowski struct vfs • każda struct vfs opisuje zamontowany system plików • struktury vfs spięte w listę • nowa struktura tworzona w wyniku wywołania mount() • usuwana gdy umount • dla każdego vfs przechowujemy wskaźnik na przykryty vnode - i (co waźniejsze!) przykryty vnode musi zawierać wskaźnik na zamontowany vfs • dla każdego vfs musimy też przechować: typ, liczbę podmontowanych w nim vfs(!) oraz informację o fizycznym fs (device) UXP – lato 2015, Grzegorz Blinowski typedef struct vfs { struct vfs struct vfs *vfs_next; /* next VFS in VFS list */ struct vfsops *vfs_op; /* operations on VFS */ struct vnode *vfs_vnodecovered; /* vnode mounted on */ u_long vfs_flag; /* flags */ u_long vfs_bsize; /* native block size */ int vfs_fstype; /* file system type index */ fsid_t vfs_fsid; /* file system id */ caddr_t vfs_data; /* private data */ dev_t vfs_dev; /* device of mounted VFS */ u_long vfs_bcount; /* I/O count (accounting) */ u_short vfs_nsubmounts; /* immediate sub-mount count */ struct vfs *vfs_list; /* sync list pointer */ struct vfs *vfs_hash; /* hash list pointer */ kmutex_t } vfs_t; vfs_reflock; /* mount/unmount/sync lock */ UXP – lato 2015, Grzegorz Blinowski Struktury vfs Lista zamontowanych systemów plików VFS VFS vfs_next vfs_next vfs_nodecovered root vnode super block root vnode super block VNode v_vfsp Inode (v_data) vfs_op VNode vfsops vfsops v_vfsmountedhere v_vfsp vfs_vfsops vfssw Tablica dostępnych typów systemów plików Inode (v_data) V-węzeł katalogu, na którym zamontowano system plików UXP – lato 2015, Grzegorz Blinowski mount() - szkic • mount(spec, dir, flags, type, data_ptr, data_len); • sprawdzamy czy spec poprawny • lookuppn() - otrzymujemy v-node punktu montowania (dir) - mp • sprawdzamy czy dir poprawny, czy katalog, czy nie ma na nim innych montowań, itp. • znajdujemy vfs w vfssw po nazwie (type) • wywołujemy vfs_init() dla tego vfs • alokujemy struct vfs, i: – dodajemy ją do listy vfs – ustawiamy vfs_op wg vfssw – ustawiamy vfs_vnodecovered w vfs oraz vfs_mountedhere w vn • wywołujemy VFS_MOUNT – alokuje i inicjuje dane prywatne vfs, lokalizuje rootdir dla vfs i inicjuje jego vn (VFS_ROOT) • zwiększamy o 1 vfs_nsubmounts w vfs punktu montowania UXP – lato 2015, Grzegorz Blinowski DNLC – Directory Name Lookup Cache • Pamięć podręczna nazw • Przyśpiesza operacje przetwarzania scieżek / wyszukiwania w katalogach (VOP_LOOKUP) • pozwala na szybką lokalizację vnode po nazwie • struktury cache mapują nazwę na vnode • organizacja cache (struct ncache): – listy hash – listy LRU – nazwa, wskaźnik na vn, wskaźnik na katalog macierzysty • główna funkcja: dnlc_lookup() - zwraca wskaźnik na vnode po nazwie; dnlc_enter() - dodaje vnode do cache • uwaga: długie nazwy nie będą cachowane UXP – lato 2015, Grzegorz Blinowski DNLC – Directory Name Lookup Cache /* * Note namlen is a uchar_t to conserve space * and alignment padding. The max length of any * pathname component is defined as MAXNAMELEN * which is 256 (including the terminating null). * So provided this doesn't change, we don't include the null, * we always use bcmp to compare strings, and we don't start * storing full names, then we are ok. The space savings are worth it. */ typedef struct ncache { struct ncache *hash_next; /* hash chain, MUST BE FIRST */ struct ncache *hash_prev; struct vnode *vp; /* vnode the name refers to */ struct vnode *dp; /* vnode of parent of name */ int hash; /* hash signature */ uchar_t namlen; /* length of name */ char name[1]; /* segment name - null terminated */ } ncache_t; UXP – lato 2015, Grzegorz Blinowski DNLC – Directory Name Lookup Cache void void void vnode_t void void int void int void dnlc_init(void); dnlc_enter(vnode_t *, char *, vnode_t *); dnlc_update(vnode_t *, char *, vnode_t *); *dnlc_lookup(vnode_t *, char *); dnlc_purge(void); dnlc_purge_vp(vnode_t *); dnlc_purge_vfsp(vfs_t *, int); dnlc_remove(vnode_t *, char *); dnlc_fs_purge1(struct vnodeops *); dnlc_reduce_cache(void *); funkcje dnlc_purge_*() usuwają odwolania do danego vn (gdy przestaje być on używany LUB gdy odmontowany jest caly vfs) W Linuxie podobna organizacja, “Directory cache”: struct dir_cache_entry UXP – lato 2015, Grzegorz Blinowski Przykładowe typy systemów plików • UFS (FFS) – standardowy unixowy system “nowej” generacji, pochodzi z BSD, bazuje na nim m.in. system ext2, ext3 • Inne systemy dyskowe: S5 (historyczny), XFS (SGI), JFS (IBM), Raiserfs (Linux) • Tempfs – system w pamięci • Procfs – informacje o procesach parametry i struktury jądra (także sysfs) • fdfs – deskryptory procesu • NFS – Network File System (BSD/SunOS) • SPECFS (SVR4) - system obsługujący urządzenia UXP – lato 2015, Grzegorz Blinowski System plików UFS Alokacja I-node: • 12 bloków bezpośrednich (0-11) • jeden blok pośredni (12) 1-go poziomu pokazuje na blok w którym 2048 adresów bloków •jeden blok pośredni (13) 2-go poziomu pokazuje na blok w którym 2048 adresow bloków 1-go poziomu •jeden blok pośredni (24) 3-go poziomu pokazuje na lok w którym 2048 adresów bloków 2-go poziomu •Jednostka alokacji: blok 8 KB • Jednostka adresowania: long int (32 bit) przechowuje numery bloków (i_addr[]) • 8192 * 12 = 98304 B • 8192 * ( 12 + 2048) = 16 MB • 8192 * (12+2048 + 2048^2) = 3,4 ^ 10 B • 8192 * (12+2048 + 2048^2 + 2048^3) = 7,4 ^ 13 B = 70 trylionów B = 70 TB UXP – lato 2015, Grzegorz Blinowski UFS - superblock • wielkość sumaryczna, wielkość bloku, wielkość fragmentu • liczba inode, rozmiar inode • liczniki: wolnych bloków, wolnych inode • czas modyfikacji • grupy cylindrów: numer grupy tego SB, wielkość grypy cyl, inne parametry grupy cyl. • licznik montowań, czas ostatniego montowania, scieżka montowania (tylko informacyjnie) • flaga "czyste odmontowanie" • położenie: mapy wolnych blokow, tablicy inode, root-dir, bloków danych UXP – lato 2015, Grzegorz Blinowski ufs Informacje organizacyjne: • Super blok: parametry dysku: wielkość bloku, liczba bloków, położenie tabeli inode, root-dir, itd. • Mapa bitowa wolnych bloków • Tablica I-Node • Root-dir • W przypadku grup cylindrów informacje te są obecne w każdej grupie UXP – lato 2015, Grzegorz Blinowski Systemy plików - podsumowaniue • Tematy nie omawiane: – – – – VFS a VM (patrz dalej VM) VFS a SMP (multi-CPU) Media wymienne (CD/DVD, USB, ...) Specjalne montowania: loop, Squashfs, AUFS (LiveCD) • FS w różnych odmianach Unixa • Zaawansowane systemy plików (multi-wolumen, software-RAID, np. VxFS) UXP – lato 2015, Grzegorz Blinowski Inne dyskowe systemy plików • ext2 (Linux) – b. podobny do oryginalnego UFS • ext3 (Linux, 2001) – ext2 + dziennik, indeks Htree dla dużych katalogów, zgodny wstecznie z ext2 • ext4 (Linux, 2008) – obsługa b. dużych fs (do 1 EiB - 2^60B), obsługa extents, prealokacja miejsca (fallocate()), opóźniona alokacja (zapobiega fragmentacji gdy wielu piszących) inne udogodnienia związane z alokacją, lepsza obsługa dziennika, zgodny wstecznie z ext2/3 • ReiserFS – stworzony “od zera”, dziennik meta-danych, mechanizmy eliminujące fragmentację, brak kwot (quota) • JFS (IBM) – 64-o bitowy, nie tylko Linux (AIX, inne), dziennik metadanych, organizacja wewn. katalogów wykorzystuje B-drzewa, dynamiczna alokacja i-node, kompresja, “extents”, interesujący głównie z historycznego punktu widzenia • XFS (SGI), 64-o bitowy, nie tylko Linux (IRIX, inne), specjalizowany do wydajnoego I/O i dużych plików, extents, “write barriers”, “sparse files”, “direct I/O”, “guaranteed-rate I/O” UXP – lato 2015, Grzegorz Blinowski fdfs – przykład systemu “wirtualnego” • Przykład w skrypcie - łączy zawartość plików i stdin: cat ./myfile1.txt /dev/fd/0 ./myfile2.txt > ./outfile.txt • Pseudo-system nie mającego odzwierciedlenia w faktycznym systemie dyskowym • Pozwala na dostęp do deskryptorów (0,1,2, ...) - tak jak do plików • Pozwala na wygodny dostęp do deskryptorów takich jak np. stdio, stdout, stderr z poziomu skryptów • Uwaga: inny obraz zawartości dla każdego procesu(!) • Montowanie: mount -t fdfs /dev/fd /dev/fd • Przykład operacji: fd=open(''/dev/fd/0'', O_RD); UXP – lato 2015, Grzegorz Blinowski Procfs (sysfs) • “Pseudo file system” - /proc • Interfejs do struktur kernela (początkowo- deskryptorów procesów), tak aby można było ograniczyć konieczność stosowania mechanizmu setuid-root • Początkowo – jeden plik na każdy proces, nazwa taka jak pid, operacje na procesie poprzez ioctl() • Następnie dodano pliki i katalogi reprezentujące różne moduły i struktury systemowe • Obecny trend (BSD) to wyniesienie informacji o systemie do sysfs (w procfs zostaje tylko inf. o procesach) • Operacje na procfs to operacje na strukturach jądra systemu UXP – lato 2015, Grzegorz Blinowski Procfs – dane procesu • /proc/PID/cmdline, linia polecenia uruchomienia procesu gjb@skinner:/proc/29144$ more status Name: sshd State: S (sleeping) • /proc/PID/cwd, symlink do aktualnego katalogu Tgid: 29144 Pid: 29144 • /proc/PID/environ, zmienne środowiskowe PPid: 29136 TracerPid: 0 Uid: 1003 1003 1003 1003 • /proc/PID/exe, symlink do pliku wykonywalnego Gid: 100 100 100 100 • /proc/PID/fd/, deskryptory plików Groups: 100 108 112 • /proc/PID/root, symlink do korzenia fs • /proc/PID/status, status iI podstawowe statystyki • /proc/PID/task, katalog z linkami do utworzonych przez ten proces procesów FDSize: 32 VmPeak: 6512 kB VmSize: 6508 kB … Threads: 1 ... voluntary_ctxt_switches: 642 nonvoluntary_ctxt_switches: 1 UXP – lato 2015, Grzegorz Blinowski Procfs – dane systemu • /proc/cpuinfo contains information about the cpu, such as its vendor (and cpu family, model and model names which should allow you to identify the cpu) and its speed (cpu MHz), cache size, number of siblings and cores and cpu flags. It contains a value called "bogomips", which is frequently mistaken to measure CPU speed like a benchmark, while it doesn't actually measure any sensible (for endusers) value at all. It is just a side-effect of kernel timer calibration and yields highly varying values depending on CPU type, even at equal clock speeds. • /proc/devices is a list of character and block devices sorted by device id but giving the major part of the /dev name too. • /proc/diskstats gives some information (including device numbers) for each of the logical disk devices • /proc/filesystems is a list of the file systems supported by the kernel at the time of listing • /proc/interrupts, /proc/iomem, /proc/ioports and the directory /proc/irq give some self explanatory details about the devices (physical or logical) using the various system resources. UXP – lato 2015, Grzegorz Blinowski cat /proc/cpuinfo processor : 0 vendor_id : AuthenticAMD cpu family : 15 model : 43 model name : AMD Athlon(tm) 64 X2 Dual Core Processor 4200+ stepping : 1 cpu MHz : 2200.414 cache size : 512 KB physical id : 0 siblings : 2 core id : 0 cpu cores : 2 fdiv_bug : no hlt_bug : no f00f_bug : no coma_bug : no fpu : yes fpu_exception : yes cpuid level : 1 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt lm 3dnowext 3dnow pni lahf_lm cmp_legacy ts fid vid ttp bogomips : 4406.14 UXP – lato 2015, Grzegorz Blinowski cat /proc/diskstats 8 0 sda 12570396 284314 282056278 29556260 14478215 40908507 443503064 261844252 0 36204972 291402364 8 1 sda1 12854751 282056270 55437851 443502976 8 16 sdb 86787382 5178040 3452622634 201469292 26682139 51904455 630174030 489189356 0 157598096 690832496 8 17 sdb1 92037689 3452622626 78805247 630174014 Format: – Major, minor, nazwa, statystyki: – 1 - # of reads issued – 2 - # of reads merged, field 6 -- # of writes merged – 3 - # of sectors read – 4 - # of milliseconds spent reading – 5 - # of writes completed – 7 - # of sectors written – 8 - # of milliseconds spent writing – 9 - # of I/Os currently in progress – 10 - # of milliseconds spent doing I/Os – 11 - weighted # of milliseconds spent doing I/Os UXP – lato 2015, Grzegorz Blinowski cat /proc/interrupts CPU0 CPU1 0: 763911314 1187 1: 119 4 8: 0 1 9: 0 0 15: 0 13 21: 276202978 75 22: 140435125 5 24: 2620788380 1 NMI: 0 0 LOC: 763970150 763970214 ERR: 0 MIS: 74 IO-APIC-edge IO-APIC-edge IO-APIC-edge IO-APIC-level IO-APIC-edge IO-APIC-level IO-APIC-level IO-APIC-level Format: – numer IRQ – liczba obsłużonych przerwań per CPU – sprzętowy typ przerwania – urządzenie obsługujace timer i8042 rtc acpi ide1 eth0 libata VIA8237 UXP – lato 2015, Grzegorz Blinowski Procfs – dane systemu • /proc/meminfo contains a summary of how the kernel is managing its memory • /proc/modules is one of the most important files in /proc and contains a list of the kernel modules currently loaded and it gives some indication of dependencies (sometimes it isn't entirely correct) • /proc/mounts is a symlink to self/mounts which contains a list of the currently mounted devices and their mount points • /proc/net is a directory containing a lot of really useful information about the network stack • /proc/swaps is a list of the active swap partitions, their various sizes and priority • /proc/sysvipc contains memory sharing and IPC information • /proc/tty contains information about the current terminals; /proc/tty/driver looks to be a list of the different types of tty available each of which is a list of those of each type. • /proc/uptime is the length of time the kernel has been running since boot and spent in idle mode (both in seconds) • /proc/version contains the Linux kernel version, distribution number, gcc version number (used to build the kernel) and any other pertinent information relating to the version of the kernel currently running. UXP – lato 2015, Grzegorz Blinowski Pliki specjalne i SPECFS (device special files) UXP – lato 2015, Grzegorz Blinowski pliki specjalne • Mogą być zlokalizowane w dowolnym miejscu fs (np. /dev, /devices, pod-katalogach j.w.) • Przykłady: – /dev/hda, /dev/hdb <- dyski – /dev/hda0, hda1, ... <- partycje – /dev/dsk/sc4d2s3 <- dysk, kontroler, napęd, partycja – /dev/eth0 <- sterownik sieciowy – /dev/tcp, /dev/ip <- abstrakcyjne urządzenie do tworzenia gniazd w TLI – /dev/ttyS0, ttyS1, ... <- porty szeregowe – /dev/null, /dev/zero, /dev/mem <- “obiekty” kernela • plik specjalny jest abstrakcją urządzenia UXP – lato 2015, Grzegorz Blinowski pliki specjalne • dwa typy plików specjalnych: – block special – napędy dyskowe (interfejs blokowy) – character special – pozostałe (w tym dyski na niskim poziomie) • Dostęp do urządzenia następuje poprzez plik specjalny (w systemie plików), – w inode “spec” zapisany jest major oraz minor device number (dev_t) jednoznacznie identyfikujące typ i numer kolejnego) urządzenia • Operacje wykonywane są poprzez sterownik zlokalizowany w kernelu • UWAGA - operacje na pliku spec mogą dotyczyć: – tego pliku - np. unlink(), rename() – urządzenia: open(), read(), write(), ioctl() UXP – lato 2015, Grzegorz Blinowski pliki specjalne /dev/deviceX jądro block special struct bdevsw[major] 0 1 2 . . . Struktura cdevsw: int (*d_open)(); int (*d_close)(); int (*d_read)(); int (*d_write)(); int (*d_mmap)(); int (*d_segmap)(); int (*d_poll)(); int (*d_print)(); int (*d_size)(); int (*d_ioctl)(); char *d_name; struct streamtab *d_str; (*deviceX_open)(); (*deviceX_close)(); (*deviceX_read)(); (*deviceX_write)(); (*deviceX_ioctl)(); . . . character special struct cdevsw[major] 0 1 2 . . . minor Struktura bdevsw: int (*d_open)(); int (*d_close)(); int (*d_strategy)(); int (*d_print)(); int (*d_size)(); int (*d_ioctl)(); char *d_name; UXP – lato 2015, Grzegorz Blinowski pliki specjalne • Sterownik musi też obsługiwać przerwanie • Sterownik nie musi obsługiwać wszystkich funkcji (nodev()) • operacje we/wy na urządzeniu blokowym: – wykonywane są poprzez funkcję d_strategy() zarządzającą transferem buforów (do/z dysku) – są ściśle związane z implementacją podsystemu pamięci wirtualnej i stronicowania (VM – zob. dalej) – operacje read/write dla urządzenia blokowego realizowane są poprzez odczyt/zapis zamapowanych w pamięci kernela segmentów pamięci odpowiadających plikom (zob. mmap()) • urządzenie "character" dla dysku to tzw. "raw device" niskopoziomowy interfejs do dysku UXP – lato 2015, Grzegorz Blinowski pliki specjalne • Dla urządzeń znakowych (nie STREAMS) typowe operacje realizowane są poprzez sekwencję open(), read()/write(), ioctl() • np. ioctl() na porcie szeregowym realizuje wszystkie operacje pobrania i ustawienia stanu linii sterujacych (np. CD, CTS, DTR, DTS, itp.), zaś funkcje read i write operują na buforze portu • urzadzenia znakowe STREAMS to uniwersalny mechanizm (SVR4) realizujący obsługę "komunikatów" - sieć, pty - zob. STREAMS UXP – lato 2015, Grzegorz Blinowski SPECFS • SPECFS nie jest montowany, ale jest zdefiniowany w vfssw • zapewnia dostęp do urządzeń niezależnie od systemu plików, w którym znajduje się plik urządzenia • każdy vn związany z dev ma dodatkowo przywiązany vn tego urządzenia (czyli vn - "plikowy" i vn -"urządzenia") • vn urządzenia jest "opakowany” w SNODE • snode zawiera informacje związane z danym urządzeniem - np. nr bajtu do wczytania • różnica vn, sn: – vn - v_op dla systemu plików (np. ufs_vnodeops) – sn - v_op dla urządzenia (specfs_vnodeops) • Dodatkowo - jedno urządzenie może być reprezentowane w wielu różnych vfs różnych typów, dlatego: s_commonvp i "zbiorczy" snode • snode jest hashowany przez major i minor device number UXP – lato 2015, Grzegorz Blinowski snode / vnode UXP – lato 2015, Grzegorz Blinowski SPECFS VFS Otwarcie pliku specjalnego: • tworzymy vnode "dyskowy" • dołączamy sn zawierający vn typu specfs • szukamy i dołączamy unikalny sn/vn danego urzadzenia (s_commonvp) UXP – lato 2015, Grzegorz Blinowski Sieciowy system plików NFS • Wprowadzony przez Sun Microsystems w 1985 r (ówczesny SunOS == BSD). • De facto standard sieciowych systemów plików w Unixie (obecna wersja v 4, najbardziej powszechna wersja: v3) • Model klient-serwer, wykorzystuje zdalne wywołanie procedur (RPC) oraz standard XDR do reprezentacji danych, oryginalnie RPC zbudowano na UDP, obecnie wykorzystuje się głównie TCP • Główne założenia: – API identyczne jak dla lokalnych plików – semantyka maksymalnie zbliżona do lokalnego (dyskowego) systemu plików – wydajna implementacja UXP – lato 2015, Grzegorz Blinowski Sieciowy system plików NFS ● ● ● NFS v 1,2 – UDP/IP NFS v 3 – pliki > 4GB, zapisy asynchroniczne NFS v4 – łączenie operacji, zintegrowane blokowanie, UTF-8 w nazwach, klasy atrybutów plików (nie tylko Unix) UXP – lato 2015, Grzegorz Blinowski NFS – założenia • Serwer eksportuje systemy plików (NFS export) • Klient montuje zdalne systemy plików – Montowanie zdalnego systemu plików odbywa się tak jak lokalnej partycji, zamiast special podaje się adres serwera i scieżkę: mount -f nfs server:/usr/local/src2 /usr/local/src • Po stronie klienta (montującego) montowania NFS i nie-NFS mogą być nakładane tak jak “normalne” montowania zasobów lokalnych (czyli: np. przejście przez scieżkę może powodować przejście przez pliki lokalne, zdalne i znów lokalne) • Po stronie serwera mogą być eksportowane tylko zasoby lokalne (re-eksport stwarza problemy implementacyjne, ale przedewszystkim wydajnościowe, nie jest też uzasadniony praktycznie): share -F nfs /usr/local/src • System NFS jest odporny na awarie klienta i serwera: – Awaria serwera powoduje tylko opóźnienie po stronie klienta – Awaria klienta nie zakłóca pracy serwera, nie powoduje na nim wycieku zasobów – Założenie to wymusza bezstanowość protokołu (operacje są niezależne) UXP – lato 2015, Grzegorz Blinowski NFS – założenia • Zastosowanie NFS nie ogranicza się do systemu Unix • Awaria klienta lub serwera powinna być prosta do usunięcia • Aplikacje powinny mieć dostęp do plików NFS tak samo jak dla plików lokalnych: – To samo API – Ta sama semantyka – Taka sama konwencja nazewnicza (zwykłe nazwy scieżkowe) – Klienci Unix powinni móc stosować unixowe nazewnictwo i unixowe cechy systemu plików (prawa, rozszerzenia, dowiązania symboliczne) – np. powinna być możliwość zamontowania kompletnego systemu plików (wraz z /) dla maszyn bezdyskowych – Wysoka wydajność – Implementacja niezależna od warstwy transportowej UXP – lato 2015, Grzegorz Blinowski NFS – bezstanowość • Protokół NFS jest bezstanowy, co powoduje, że: – W każdej operacji (np. read, write) przekazany jest komplet niezbędnych danych, a więc także pozycja w pliku – operacje open i close nie modyfikują zasobów serwera – W razie awarii serwera klient ponawia żądania (lub zgłasza błąd) – Operacje muszą być synchroniczne: serwer musi zapisywać dane na dysk • “Anomalie” w stosunku do semantyki plików lokalnych: – Prawa dostępu są sprawdzane przy każdej operacji (a nie przy otwarciu); właściciel ma pełne prawa (prócz sprawdzenia gdy open()) – Usunięcie otwartego pliku (klient zmienia nazwę otwartego pliku jeśli to on go usuwa) – W systemie dyskowym pojedyncze lokalne read/write są zawsze atomowe (blokada na vnode), r/w w NFS wykonywane z różnych klientów mogą zostać wymieszane – Zajmowanie pliku/rekordów wymaga osobnego mechanizmu – nie może się odbywać na poziomie vnode klienta UXP – lato 2015, Grzegorz Blinowski NFS – implementacja • Protokół MOUNT służy do obsługi zdalnych montowań • Protokół RPC portmap pozwala na lokalizację portu usługi NFS • NFS (operacje plikowe) realizowany jest przez prot. NFS (na bazie RPC/XDR) • nfs_mount() tworzy i inicjuje strukturę vfs po stronie klienta, – zapamiętywany jest w niej adres sieciowy serwera, – jest sprawdzany i zapamiętywany vnode głównego katalogu montowania (po stronie serwera sprawdzana jest dostępność zasobu, ale nie jest tworzony żaden trwały obiekt) • “wejście” w zdalny system plików powoduje wykorzystnie VOP_LOOKUP i VFS_VGET w zdalnym systemie (wykonywane są operacje sieciowe NFS) – każde wejście do kolejnego katalogu scieżki NFS skutkuje operacją sieciową NFS(!) (ale klient może stosować buforowanie) – Prywatne dane vnode NFS (v_data) przechowane są w struct rnode (remote node) UXP – lato 2015, Grzegorz Blinowski NFS – portmap • Usługi RPC wykorzystywane przez NFS: $ rpcinfo -p program vers proto 100000 2 tcp 100000 2 udp 100003 2 udp 100003 3 udp 100003 4 udp 100003 2 tcp 100003 3 tcp 100003 4 tcp 100024 1 udp 100021 1 udp 100021 3 udp 100021 4 udp 100024 1 tcp 100021 1 tcp 100021 3 tcp 100021 4 tcp 100005 1 udp 100005 1 tcp 100005 2 udp 100005 2 tcp 100005 3 udp 100005 3 tcp port 111 111 2049 2049 2049 2049 2049 2049 32770 32770 32770 32770 32769 32769 32769 32769 644 645 644 645 644 645 portmapper portmapper nfs nfs nfs nfs nfs nfs status nlockmgr nlockmgr nlockmgr status nlockmgr nlockmgr nlockmgr mountd mountd mountd mountd mountd mountd UXP – lato 2015, Grzegorz Blinowski NFS – implementacja • W wyniku operacji LOOKUP (CREATE, MKDIR) serwer przekazuje do klient uchwyt (handle) • Uchwyt jest jednoznaczymym identyfikatorem pliku: bez struktury, nie interpretowanym przez klienta, zawiera: – Identyfikator systemu plików – Nr inode – Nr generacji inode – inode między operacjami klienckimi mógł zostać związany z innym plikiem - taką sytuację trzeba wykryć (błąd: “stale NFS file handle”) – Uwaga: tak jest w przypadku NFS dla systemów Unix, NFS w innym systemie może generować uchwyty inaczej • XID – unikalny identyfikator transakcji RPC (nie modyfikowany przy retransmisjach) UXP – lato 2015, Grzegorz Blinowski NFS – procedury RPC (v3) Procedure Procedure Procedure Procedure Procedure Procedure Procedure Procedure Procedure Procedure Procedure Procedure Procedure Procedure Procedure Procedure Procedure Procedure Procedure Procedure Procedure Procedure 0: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: NULL - Do nothing GETATTR - Get file attributes SETATTR - Set file attributes LOOKUP - Lookup filename ACCESS - Check Access Permission READLINK - Read from symbolic link READ - Read From file WRITE - Write to file CREATE - Create a file MKDIR - Create a directory SYMLINK - Create a symbolic link MKNOD - Create a special device REMOVE - Remove a File RMDIR - Remove a Directory RENAME - Rename a File or Directory LINK - Create Link to an object READDIR - Read From Directory READDIRPLUS - Extended read from directory FSSTAT - Get dynamic file system information FSINFO - Get static file system Information PATHCONF - Retrieve POSIX information COMMIT - Commit cached data on a server to stable storage UXP – lato 2015, Grzegorz Blinowski NFS – procedury i argumenty procedura argumenty wyniki GETATTR LOOKUP READLINK READ WRITE CREATE REMOVE RENAME READDIR STATFS fhandle dirfh, name status, fattr status, fhandle fattr fhandle, offset, cnt fhandle, offset, cnt, data dirfh, name, sattr drifh, name dirfh1, name1, dirfh2, name2 fhandle, cookie, count fhandle status, status, status, status status status, status, • • • • fattr/sattr – atrybuty pliku (do ustawienia) cookie – zwracane przez poprzednie READDIR, fhandle – uchwyt pliku, dirfh – uchwyt katalogu fattr, data fattr fhandle, fattr dir_entries filestats UXP – lato 2015, Grzegorz Blinowski NFS - operacje UXP – lato 2015, Grzegorz Blinowski NFS – zlecenia (nie)idempotentne • W razie braku potwierdzenia klient retransmituje zlecenie • Jeśli zaginie potwierdzenie to zlecenie może być wykonane więcej niż raz • Zlecenia idempotentne: można je wykonać kilka razy pod rząd bez skutków ubocznych • Zlecenia nieidempotentne: kilkukrotne wywołanie powoduje błąd, np: k:REMOVE, s:usunięcie, s:ack, ack ginie, k:REMOVE, s:błąd • Rozwiązanie: serwer utrzymuje cache niektórych zleceń w celu wykrywana duplikatów (tzw. pamięć podręczna retransmisji) – Rozwiązanie to eliminuje większość, ale nie wszystkie problemy związane z retransmisją – Pamiętane operacje: CREATE, REMOVE, LINK, MKDIR, RMDIR – Jeśli błąd operacji to serwer sprawdza czy to nie duplikat UXP – lato 2015, Grzegorz Blinowski NFS- użytkownicy i uprawnienia • Uprawnienia weryfikowane są przy opercji nfs_mount() i każdej operacji NFS – Uwaga: nfs_mount() nie wymaga uprawnień root-a(!) • Eksport systemów plików jest typowo limitowany dla konkretnych klientów (wg. adresów IP) • Użytkownicy i grupy muszą posiadać jednolitą numerację w ramach systemu NFS • Zaufanie do zdalnego root-a • Mapowanie UID, GID: dla każdego klienta serwer może stosować mapowanie identyfikatorów, uid==0 może być zawsze mapowany na inny • W wersji 4 NFS autoryzacja w oparciu o PKI UXP – lato 2015, Grzegorz Blinowski NFS – dalsze szczegóły implementacji • Kernel, klient – Musi obsługiwać vnode-y typu NFS, operacje RPC, stos TCP/IP, itd. – biod (block IO daemon) – wykonują operacje I/O z wyprzedzeniem, obecnie przy nowych wersjach i implementacjach protokołu NFS nie stosowane – Automountd – “auto mount daemon”: montuje zasoby na żądanie (gdy potrzebne): w przypadku próby odwołania do określonych katalogów zdalny system plików zostanie automatycznie zamontowany: przydatne w przypadku katalogów użytkowników • Serwer – nfsd – proces serwisowy prot. NFS, obsługuje żądania NFS • Zajmowanie plików i rekordów (NLM: network lock manager) – rpc.lockd – osobny serwis realizujący rozproszone zajmowanie zgodne z API fcntl-lock (operacje typu callback) – rpc.statd – odzyskiwanie informacji w razie awarii serwera lub klienta UXP – lato 2015, Grzegorz Blinowski Komunikacja międzyprocesowa UXP – lato 2015, Grzegorz Blinowski Potoki nienazwane #include <unistd.h> #include <sys/types.h> int pipe(int fd[2]); • Wywołanie systemowe pipe() tworzy potok komunikacyjny • Potok jest strukturą danych w jądrze • Dostęp procesu do potoku następuje poprzez dwa deskryptory typu plikowego: wejściowy fd[0] i wyjściowy fd[1] – dane zapisane do fd[1] mogą zostać odczytane z fd[0] • Dane zapisujemy / odczytujemy z potoku przy pomocy funkcji write() / read() • Potok ma ograniczoną pojemność - PIPE_BUF (zależne od wersji systemu, zawsze >= 512) • Zapisy i odczyty o wielkości nieprzekraczającej pojemności potoku są albo atomowe (dane z procesów nie mieszają się) albo blokujące (zobacz dalej) UXP – lato 2015, Grzegorz Blinowski Potoki - We/Wy • write(): – brak czytelników: -1, SIG_PIPE – danych <= PIPE_BUF • jest miejsce: atomowy zapis, nie blokujące • brak miejsca: blokada, op. atomowa – danych > PIPE_BUF • jest miejsce: zapis cześciowy. op. nieatomowa • potok pełen: blokuje, op. nieatomowa • read(): – brak danych, wszystkie deskr pisz. zamknięte • zwraca 0 – brak danych: blokuje – są dane: zwraca tyle ile jest, nie zwróci więcej niż PIPE_BUF UXP – lato 2015, Grzegorz Blinowski Potoki - We/Wy O_NONBLOCK • write(): – danych <= PIPE_BUF tam gdzie normalnie blokada zwraca EAGAIN – danych>PIPE_BUF, nie blokuje zapis częściowy lub EAGAIN • read(): – brak danych: EAGAIN, chyba, że koniec pliku – dane: zawsze zwraca to co jest UXP – lato 2015, Grzegorz Blinowski potoki • Proces potomny dziedziczy deskryptory, dziedziczy więc także deskryptory potoków! • Standardowy schemat komunikacji z procesem potomnym: utwórz potok(i); wywołaj fork(); pozamykaj nieużywane deskryptory; korzystaj z potoków do komunikacji fork() fd[0] fd[1] fd[0] fd[1] fd[1] fd[0] fd[0] fd[1] UXP – lato 2015, Grzegorz Blinowski Przykład: popen() #include <sddio.h> #define READ 0 #define WRITE 1 #define tst(a,b) (mode == READ ? (b) : (a)) int popen(char *cmd, int mode) { int p[2]; int popen_pid; if (pipe(p) < 0) return(NULL); if ((popen_pid = fork()) == 0) { /* proces potomny */ close(tst(p[WRITE], p[READ]); close(tst(0, 1)); /* 0 - stdin 1 - stdout */ dup(tst(p[READ], p[WRITE])); close(tst(p[READ], p[WRITE])); execl(”/bin/sh”, ”sh”, ”-c”, cmd, 0); exit(1); } if (popen_pid == -1) return(NULL); close(p[READ], p[WRITE]); return(tst(p[WRITE], p[READ]); } /* proces macierzysty */ UXP – lato 2015, Grzegorz Blinowski Przykład: popen2() #define READ 0 execl("/bin/sh", "sh", "-c", #define WRITE 1 command, NULL); pid_t popen2(const char *command, perror("execl"); int *infp, int *outfp) { exit(1); int p_stdin[2], p_stdout[2]; } /* pid==0 */ pid_t pid; if (pipe(p_stdin) != 0 || if (infp == NULL) pipe(p_stdout) != 0) close(p_stdin[WRITE]); return -1; else *infp = p_stdin[WRITE]; if ((pid = fork())<0) if (outfp == NULL) return pid; close(p_stdout[READ]); else if (pid == 0) { else *outfp = p_stdout[READ]; close(p_stdin[WRITE]); close(p_stdin[READ]); dup2(p_stdin[READ], READ); close(p_stdout[WRITE]); close(p_stdout[READ]); return pid; dup2(p_stdout[WRITE], WRITE); } UXP – lato 2015, Grzegorz Blinowski Przykład: shell • ls | grep abc | wc • Tworzymy dwa potoki, trzy procesy: int fds1[2], fds2[2]; pipe(fds1); pipe(fds2) if (fork()==0) { /* ls */ ls: fds1[1] grep: fds1[0] wc: fds2[1] close(fds2[0]); close(fds2[1]); close(fds1[0]); dup2(fds1[1],1 ); close(fds1[1]); execlp(“ls”, “ls”,0); } if (fork()==0) /* grep */ { close(fds1[1]); close(fds2[0]); dup2(fds1[0],0); close(fds1[0]); dup2(fds2[1],1); close(fds2[1]); execlp(“grep”, “grep”, “abc”, 0); } if (fork()==0) /* wc */ { close(fds1[0]); close(fds1[1]); close( fds2[1]); dup2(fds2[0],0); close(fds2[0]); execlp(“wc”, “wc”, 0); } fds2[0] UXP – lato 2015, Grzegorz Blinowski FIFO • Kolejki FIFO – “named pipes” - potoki nazwane • Identyfikowane przez pliki specjalne "widoczne" w syst. plików, • Semantyka read/write – identyczna jak dla potoków tworzonych przez pipe() • Inny sposób tworzenia i otwierania • Główna różnica w stosunku do pipe() - mogą używać procesy niespokrewnione – np. proces serwisowy i jego klient • Wywodzą się z SYSV, nie ma (?) w BSD • Tworzenie: % mknod name p int mknod(char *pathname, int mode, int dev); mode: S_IFIFO | prawa_dostępu int mkfifo(char *pathname, int mode); int unlink(char *pathname); UXP – lato 2015, Grzegorz Blinowski FIFO % mkfifo fifo1 % proga < fifo1 & % progb > fifo1 • Otwieranie, zamykanie FIFO: open(), close() (fopen(), itd.) • Jedna strona otwiera w trybie O_RD, druga O_WR (można też użyć O_RDWR) • pierwsze (w sensie czasowym otwarcie) zawiesza się w oczekiwaniu na otwarcie drugiej strony: – nie zawiesi się gdy O_RDWR – uwaga na ewentualny deadlock gdy więcej niż jeden FIFO! – Można wywołać open z flagą O_NDELAY UXP – lato 2015, Grzegorz Blinowski przykład FIFO client (int readfd, int writefd) { char buf[MAXBUF]; int n; /* wczytaj nazwę pliku i zapisz do IPC */ if ( fgets (buf, MAXBUF, stdin ) == NULL ) err_sys ("client: filename read error "); n = strlen (buf); /* ignoruj newline */ if ( buf[n-1] == '\n' ) n-- ; if ( write (writefd, buf, n) != n ) err_sys ("client: filename write error "); /* czytaj dane z IPC i prześlij na wyjście */ while ((n = read(readfd, buf, MAXBUF)) > 0) if (write(1, buf, n) != n) /* 1=stdout */ err_sys ("client: data write error "); if ( n < 0 ) err_sys ("client: data read error "); } UXP – lato 2015, Grzegorz Blinowski przykład FIFO server (int readfd, int writefd) { char buf[MAXBUF]; int n , fd; char errmesg[256], *sys_err_str(); /* wczytaj nazwę pliku z IPC */ if ((n = read(readfd, buf, MAXBUF)) <= 0) err_sys ("sever: filename read error "); buf[n] = '\0'; /* terminate filename */ if ((fd = open(buf, 0)) < 0) { /*wyślij komunikat o błędzie do klienta*/ sprintf(errmesg, "can't open, %s\n", sys_err_str()); strcat (buf, errmesg); n = strlen(buf); if (write ( writefd, buf, n) != n ) err_sys("server: errmesg write error"); } else { /* czytaj dane i zapisz do IPC */ while ((n = read(fd, buf, MAXBUF)) > 0) if (write(writefd, buf, n) != n) err_sys ("server: data write error"); if (n < 0) } err_sys("server: read error "); UXP – lato 2015, Grzegorz Blinowski przykład FIFO Plik fifo.h: #include <sys/types.h> #include <sys/stat.h> #include <sys/errno.h> #define FIFO1 "/tmp/fifo.1" #define FIFO2 "/tmp/fifo.2" #define PERMS 0666 UXP – lato 2015, Grzegorz Blinowski przykład FIFO KLIENT - main int main(int argc, char**argv) { int readfd, writefd; /* otwarcie FIFO utworzonego przez serwer */ if ((writefd = open(FIFO1, O_WR) < 0) err_sys("client: can't open wr fifo: %s", FIFO1); if ((readfd = open(FIFO2, O_RD) < 0) err_sys("client: can't open rd fifo: %s", FIFO2); client (readfd, writefd); close (readfd); close (writefd); /* usuń FIFO */ if ( unlink( FIFO1 ) < 0 ) err_sys("client: can't unlink %s",FIFO1); if (unlink( FIFO2 ) < 0 ) err_sys ("client: can't unlink %s",FIFO2); exit (0); } UXP – lato 2015, Grzegorz Blinowski SERWER - main int main(int argc, char **argv) { int readfd, writefd; /* otwórz FIFO do odczytu i zapisu */ if ((mknod(FIFO1, S_IFIFO|PERMS, 0) < 0 && ( errno != EEXIST )) err_sys ("can't create fifo: %s", FIFO1); if ((mknod(FIFO2, S_IFIFO|PERMS, 0) < 0 && ( errno != EEXIST )) unlink ( FIFO1 ); err_sys ("can't create fifo: %s", FIFO1); } if (( readfd = open (FIFO1, O_RD) < 0) err_sys("server: can't open rd fifo: %s", FIFO1); if (( writefd = open (FIFO2, O_WR) < 0) err_sys("server: can't open wr fifo: %s", FIFO2); server (readfd, writefd); close (readfd); close (writefd); exit (0); { UXP – lato 2015, Grzegorz Blinowski SYSV IPC potok kolejka FIFO kolejka komunikatów semafory pamięć dzielona widoczność ------ścieżka klucz (key_t 32 bity) klucz klucz dostęp deskryptyor (int) deskryptyor identyfikator (int) identyfikator identyfikator • Klucz -> identyfikator • Klucz jak nazwa pliku, identyfikator jak deskryptor pliku • Klucze identyfikujące obiekty IPC sa globalne i trwałe • Generowanie kluczy: key_t ftok(char *pathname, char proj); • Można też użyć klucza, który zapewni unikalny identyfikator: IPC_PRIVATE Funkcje systemowe: • otwierające „get” • sterujące „control” • operacyjne „operations” msgget() msgctl() msgsnd() msgrcv() • informacyjne: ipcrm(1), ipcs(1) semget() semctl() semop() shmget() shmctl() shmat() shmdt() UXP – lato 2015, Grzegorz Blinowski gjb@fox:~> ipcs ------ Shared Memory Segments -------key shmid owner perms bytes perms nsems perms used-bytes nattch status ------ Semaphore Arrays -------key semid owner ------ Message Queues -------key msqid owner messages root@fox:~# ipcs ------ Shared Memory Segments -------key shmid owner perms bytes nattch 0x0052ee79 0 pgsql8 600 169328640 2 0x0052f261 32769 pgsql83 600 38207488 4 0x0052e2c1 98306 postgres 600 168263680 1 ------ Semaphore Arrays -------key semid owner perms nsems 0x0052ee79 0 pgsql8 600 17 0x0052ee7a 32769 pgsql8 600 17 0x0052ee7b 65538 pgsql8 600 17 ------ Message Queues -------key root@fox:~# msqid owner perms used-bytes messages status UXP – lato 2015, Grzegorz Blinowski SYSV IPC – funkcje ...get() Tworzenie obiektów IPC: int msgget (key_t key, int msgflg); int semget (key_t key, int nsems, int semflg); int shmget (key_t key, int shmflg); klucz: IPC_CREATE IPC_CREATE | IPC_EXCL wolny errno = ENOENT O.K. O.K. zajęty O.K. errno = EEXIST UXP – lato 2015, Grzegorz Blinowski SYSV IPC – ipc_perm() Struktura ipc_perm jest wspólna dla wszystkich 3 typów obiektów IPC: struct ipc_perm { ushort uid; ushort gid; ushort cuid; ushort cgid; ushort mode; /* prawa dostępu */ ushort seq; key_t key; ipc_perm _______________ }; nagłówek: struct msqid_ds struct shmid_ds union semun dane UXP – lato 2015, Grzegorz Blinowski SYSV IPC – funkcje sterujące int msgctl (int msqid, int cmd, .../* struct msqid_ds *buf */); int shmctl (int shmid, int cmd, .../* struct shmid_ds *buf */); int semctl(int semid, int semnum, int cmd, .../*union semun *arg */); cmd: • IPC_STAT • IPC_SET • IPC_RMID - odczytaj nagłówek do bufora buf - zmodyfikuj dozwolone pola nagłówka - zwolnij obiekt UXP – lato 2015, Grzegorz Blinowski SYSV IPC – Kolejki komunikatów int msgsnd (int msqid, const void *msgp, size_t msgsz, int msgflg); int msgrcv (int msqid, const void *msgp, size_t msgsz, long msgtyp, int msgflg); • Zwracają: msgsnd(): 0,-1; msgrcv(): liczba komunikatów • Kolejka nie zawsze FIFO • Typ może określać adresata • Funkcje są domyślnie blokujące • Gdy flaga IPC_NOWAIT nie blokują (zwracają -1 i errno <- EAGAIN (snd) lub ENOMSG (rcv) • Odblokowanie gdy: pojawią się dane, lub usunięcie obiektu IPC lub sygnał struct msgbuf { long mtype; . . . }; msgrcv(): msgtyp = 0 msgtyp > 0 msgtyp < 0 pierwszy z kolejki pierwszy o zadanym typie typy o numerach niewiększych niż |msgtyp| UXP – lato 2015, Grzegorz Blinowski SYSV IPC – Kolejki komunikatów int msgsnd (int msqid, const void *msgp, size_t msgsz, int msgflg); int msgrcv (int msqid, const void *msgp, size_t msgsz, long msgtyp, int msgflg); struct msqid_ds { struct ipc_perm msg_perm; struct msg *msg_first; struct msg *msg_last; ushort msg_cbytes; /* ushort msg_gnum; /* ushort msg_gbytes; /* ushort msg_lspid; /* ushort msg_lrpid; /* } msqid msg_perm msg_first msg_last aktualna liczba bajtów w kolejce */ aktualna liczba komunikatow */ maks. liczba bajtów */ pid ostatniego msgsnd */ pid ostatniego msgrcv() */ typ długość dane typ długość dane typ (long) długość (>=0) dane UXP – lato 2015, Grzegorz Blinowski SYSV IPC – Kolejki komunikatów msgq.h: #include < sys/types.h > #include < sys/ipc.h > #include < sys/msg.h > #include < sys/errno.h > extern int errno; #define MKEY1 1234L #define MKEY2 2345L #define PERMS 0666 SERWER: #include "mesgq.h" int main (int argc, char **argv) { int rdid, wrid; /* utwórz kolejkę */ if ((rdid=msgget(MKEY1,PERMS|IPC_CREAT))<0) err_sys("server : can't get queue1"); if ((wrid=msgget(MKEY2,PERMS|IPC_CREAT))<0) err_sys("server : can't get queue2" ); server (rdid, wrid); exit(0); } UXP – lato 2015, Grzegorz Blinowski SYSV IPC – Kolejki komunikatów KLIENT - main #include "mesgq.h" int main (int argc, char **argv) { int reaid, wrid; /* otwórz kolejkę */ if ((wrid = msgget(MKEY1, 0 )) < 0 ) err_sys ("client : can't get queue1" ); if ((rdid = msgget(MKEY2, 1 )) < 0 ) err_sys ("client : can't get queue2" ); client (rdid, wrid); if (msgctl(rdid, IPC_RMID, (struct msqid_ds *)0 ) < 0 ) err_sys ("client: can't remove queue2" ); if (msgctl(wrid, IPC_RMID, (struct msqid_ds *)0 ) < 0 ) err_sys ("client: can't remove queue1" ); exit(0); } UXP – lato 2015, Grzegorz Blinowski SYSV IPC – Semafory int semget (key_t key, int nsems, int semflg); int semop (int semid, struct sembuf *sops, size_t nsops); • Operacje semaforowe są zawsze atomowe (wszystkie albo żadna) • Jesli nie można wykonać to blokada struct semid_ds struct sem semid struct ipc_perm sem_perm struct sem *sem_base ushort sem_nsem time_t sem_otime time_t sem_ctime [0] [sem_nsem-1] [1] ushort semval short sempid ushort semncnt ushort semzcnt semval sempid semncnt semzcnt semval sempid semncnt semzcnt UXP – lato 2015, Grzegorz Blinowski SYSV IPC – Semafory • sem_op > 0 dodaj do semval - uwolnienie zasobu • sem_op = 0 czekaj aż semval == 0 • sem_op < 0 czekaj aż semval >= |sem_op| po czym odejmij - zajmowanie zasobu struct sembuf op_lock[2] = { 2, 0, 0, /* zaczekaj aż semafor nr 2 będzie zerem */ 2, 1, SEM_UNDO /* zwiększ ten semafor o 1 */ }; static struct sembuf op_unlock [1] = { 2, -1, SEM_UNDO, /* zmniejsz [2] */ } ; struct sembuf [0] sops ushort sem_num short sem_op short sem_flg [nsops-1] [1] sem_num sem_op sem_flg sem_num sem_op sem_flg UXP – lato 2015, Grzegorz Blinowski SYSV IPC – Semafory int semctl(int semid, int semnum, int cmd, .../*union semun *arg*/); union semun { int val; /* GETVAL, SETVAL */ struct semid_ds *buff; /* IPC_STAT, IPC_SET */ ushort *array; /* GETALL, SETALL */ } arg; inne: /* IPC_RMID, GETPID, GETNCNT, GETZCNT */ Problemy • zabicie procesu zajmującego semafor : Jak przywrócić właściwą wartość semafora ? 1. obsługa sygnałów -> zwalnia blokadę ale SIGKILL 2. aktywne oczekiwanie (IPC_NOWAIT) i timeout 3. jądro uwalnia zajmowany semafor -> każdy proces posiada zmienną semadj • SEM_UNDO: zmienia wartość nastawiajacą (semadj) • przy kończeniu procesu semadj dodawana do wart semafora • usuwanie semafora z systemu - pamiętać o usunięciu przed wywołaniem exit() UXP – lato 2015, Grzegorz Blinowski SYSV IPC – pamięć dzielona int shmget (key_t key, int size, int shmflg); - zwraca shmid lub -1 void *shmat (int shmid, void *shmaddr, int shmflg); zwraca adres segmentu pamięci dzielonej lub -1 - adres określa system • shmaddr == 0 • shmaddr != 0 && shmflg & SHM_RND - zaokrąglony adres dowiązania (mod SHMLBA) • shmaddr != 0 && !(shmflg & SHM_RND) - adres dowiązania • shmflg - SHM_RDONLY int shmdt (void *shmaddr); zwraca 0 lub -1 int shmctl (int shmid, int cmd, .../* struct shmid_ds *buf */); • cmd: IPC_STAT, SHM_LOCK, IPC_SET, SHM_UNLOCK, IPC_RMID struct shmid_ds { struct ipc_perm struct anon_map int ushort ushort ushort ushort time_t time_t time_t }; shm_perm; *shm_amp; shm_segsz; shm_lpid; shm_cpid; shm_nattch; shm_cnattch; shm_atime; shm_dtime; shm_ctime; /* /* /* /* /* wskaźnik do str. kernela */ rozmiar segmentu */ pid ostatniego shmop */ pid twórcy */ liczba procesów dołączonych */ UXP – lato 2015, Grzegorz Blinowski Pamięć wirtualna - VM UXP – lato 2015, Grzegorz Blinowski Założenia • Pamięć wirtualna, przypomnienie: – Strony (pages), ramki (frames – w pamięci fizycznej) • Założenia: – Separacja przestrzeni adresowych – Jednolitość przestrzeni adresowej (ten sam schemat dla każdego procesu) – Ochrona obszarów: wykrycie odwołania do nielegalnego adresu, wykrycie nielegalnego odwołania (np. zapis do text, itp) – Uruchamiamy programy o przestrzeni adresowej większej od dostępnego RAM – Na raz w pamięci obecne są procesy o sumarycznej zajętości większej od dostępnego RAM – Wspólne segmenty (text) mogą być wspóldzielone – Procesy mogą współdzielić pamięć danych (IPC shmem) UXP – lato 2015, Grzegorz Blinowski Mechanizmy sprzętowe VM Schemat ogólny Uproszczony schemat procesorów Intel UXP – lato 2015, Grzegorz Blinowski Założenia • Zadania podsystemu VM: – Zapewnienie translacji adresów pamięci wirtualnej na fizyczne dla procesów i struktur kernela – Przydział i zwalnianie pamięci dla procesów i struktur kernela • Algorytmy przydziału pamięci (unikanie fragmentacji, scalanie obszarów) – Odzyskiwanie pamięci – Mapowanie plików zwykłych i specjalnych na obszary pamięci – Zarządzanie obszarami wymiany (swap) – Obsługa sprzętowego kontrolera pamięci (MMU- moduł HAT jądra) UXP – lato 2015, Grzegorz Blinowski Wymagania sprzętowe • Poziom ochrony zwykły i uprzywilejowany • Sprzętowa translacja adresów (MMU) – TLB – Tablice stron • Sprzętowa ochrona obszarów pamięci (generowanie wyjątków w razie błędnego odwołania): – adres poza zakresem – strona nieobecna – błąd ochrony • Bit odwołania i bit zapisu (reference, dirty) • Spójny restart operacji w razie wyjątku UXP – lato 2015, Grzegorz Blinowski Demand paging – stronicowanie na żądanie • System nie przydziela (w sensie fizycznym) nowemu procesowi kompletu jego stron • Podejście leniwe: strona jest przydzielna, gdy jest potrzebna (w ten sposób oszczędzamy pamięci oraz zasoby kernela takie jak tablica stron), oszczędzamy też czas: – Zostaną utworzone tylko te strony, który faktycznie zostały użyte, dotyczy to zarówno kodu jak i danych – Sygmenty dynamiczne: stos, sterta - rosną w miarę potrzeby – RS – Resident (page) Set (zob. RSS w ps) • W wypadku odwołania do nieistniejącej strony o poprawny adresie następuje wyjątek, w ramach jego obsługi strona jest tworzona i sprowadzana z dysku (np. strona segmentu text) lub alokowana anonimowo (np. stos) – Klastrowanie stron (clustering) – działamy na klastrach stron, odwołania są zwykle sekwencyjne – Prefetch – sprowadzanie z wyprzedzeniem UXP – lato 2015, Grzegorz Blinowski Segmenty pamięci struct seg { caddr_t s_base; /* base virtual address */ size_t s_size; /* size in bytes */ struct seg *s_next; /* list ptr */ struct seg *s_prev; /* list ptr */ struct as *s_as; /* containing as */ struct seg_ops *s_ops; /* ops vector */ void *s_data; /* private data */ }; • Struktura as (address space) organizuje mapę segmentów procesu • Każdy proces posiada listę segmentów (struct seg) podłączoną do as • Segmenty: text (kod, biblioteki so), dane zainicjowane, dane niezainicjowane, stos, sterta, segmenty współdzielone, ... • Segment ma określony typ wyznaczony przez s_ops (podobnie jak v_ops), operacje na segmencie są więc “zwritualizowane” UXP – lato 2015, Grzegorz Blinowski Operacje na segmentach Operacje zależne od typu segmentu: Makra SEGOP: #define SEGOP_DUP(s, n) struct seg_ops { (*(s)->s_ops->dup)((s), (n)) int (*dup)(struct seg *, struct seg *); #define SEGOP_UNMAP(s, a, l (*(s)->s_ops->unmap)((s), (a), int (*unmap)(struct seg *, caddr_t, (l)) size_t); #define SEGOP_FREE(s) void (*free)(struct seg *); (*(s)->s_ops->free)((s)) faultcode_t (*fault)(struct hat *, struct seg . . . *, caddr_t, size_t, enum fault_type, enum seg_rw); int (*setprot)(struct seg *, caddr_t, size_t, uint_t); Funkcje kernela dla segmentów: int (*checkprot)(struct seg *, caddr_t, size_t, uint_t); void seg_init(void); int (*sync)(struct seg *, caddr_t, size_t, int, uint_t); Struct seg *seg_alloc(struct as u_offset_t (*getoffset)(struct seg *, caddr_t); int (*gettype)(struct seg *, caddr_t); int (*getvp)(struct seg *, caddr_t, struct vnode **); void (*dump)(struct seg *); int (*pagelock)(struct seg *, caddr_t, size_t, struct page ***, enum lock_type, enum seg_rw); }; *as, caddr_t base, size_t size); int seg_attach(struct as *as, caddr_t base, size_t size, struct seg *seg); void seg_unmap(struct seg *seg); Void seg_free(struct seg *seg); UXP – lato 2015, Grzegorz Blinowski Segmenty kernela • seg_map obsługuje mapowania vnode na adresy wirtualne realizowane w celu obsługi I/O • seg_dev obsługuje mapowania urządzeń znakowych • seg_kmem obsługuje prywatne dane jądra UXP – lato 2015, Grzegorz Blinowski • Fizycznie segment opisany jest poprzez swoje dane prywatne: s_data • typowo s_data pokazuje na struct segvn_data • struct segvn_data określa dwa typu segmentów: – Związane z plikiem poprzez vnode – Anonimowe (anon) • Segmenty plikowe: text, data • Segmenty anonimowe: stos, sterta, współdzielone • Uwaga: segment anonimowy może być związany z plikiem wymiany, ale jest to inny typ powiązania niż dla segmentu plikowego UXP – lato 2015, Grzegorz Blinowski segvn_data segv_data wskazuje na strukturę: vnode (powiązany plik) typedef struct segvn_crargs { struct vnode *vp; /* vnode mapped from */ Struct cred *cred; /* credentials */ u_offset_t offset; /* starting offset of vnode */ uchar_t type; /* type of sharing done */ uchar_t prot; /* protections */ uint_t flags; /* flags MAP_PRIVATE, MAP_SHARED */ struct anon_map *amp; /* anon mapping to map to */ } segvn_crargs_t; Struktur anon_map opisuje strony anonimowe przydzielone do segmentu: struct anon_map { size_t size; /* size in bytes */ struct anon **anon; size_t swresv; /* swap space reserved for this map */ ulong_t refcnt; /* reference count }; */ anon tablica, lub lista stron (struct page *) UXP – lato 2015, Grzegorz Blinowski Struct page • Strona zawsze na jednej z list: – Hashowana lista vnode: strona pokazuje na vnode, vnode pokazuje na listę stron – Lista wolnych stron – Lista stron podręcznych – strony zwolnione, ale z aktualnymi danymi, proces może je odzyskać – Strony anonimowe – Strony kernela Struktura opisująca stronę typedef struct page { struct vnode *p_vnode;/* logical vnode this page is from */ struct page *p_hash; /* hash by [vnode, offset] */ struct page *p_vpnext;/* next page in vnode list */ struct page *p_vpprev;/* prev page in vnode list */ struct page *p_next; /* next page in free/intrans lists */ struct page *p_prev; /* prev page in free/intrans lists */ u_offset_t /* offset into vnode for this page */ p_offset; selock_t p_selock; /* shared/exclusive lock on the page */ ushort_t p_lckcnt; /* number of locks on page data */ ushort_t p_cowcnt; /* number of copy on write lock */ kcondvar_t p_cv; /* page struct's condition var */ kcondvar_t p_io_cv; /* for iolock */ . . . uchar_t p_state; } page_t; /* p_free, p_noreloc */ UXP – lato 2015, Grzegorz Blinowski HAT • HAT – hardware address translation • Silnie zależne od sprzętu, tj. od konstrukcji MMU: – Mapowanie fizycznej struktury opisu strony na systemową (PTE: Page Table Entry) – Obsługa cachowania (TLB) • HAT zwykle oprogramowane w asamblerze • Translacja jedno, dwu i trzy poziomowa (poziomy tablicy stron) UXP – lato 2015, Grzegorz Blinowski Swap (obszar wymiany) • Status strony procesu: – w pamięci – dostępna na żądanie (text lub wyzerowana strona danych) – obecna w swap • Do swap trafiają strony modyfikowane: dane, stos, sterta • Nie ma potrzeby wymiatania stron typu text • Różne podejścia do obszaru wymiany: – "Eager" - BSD, komercyjne: proces musi mieć zarezerwowane w swap tyle ile może potrzebować, rozmiar swap>=rozmiar RAM, dopiero swap>RAM zwiększa rozmiar dostępnej pamięci wirtualnej – "lazy" - Linux, nowsze odmiany komercyjne: swap nie musi być skonfigurowany, swap zawsze powiększa rozmiar dostępnej pamięci wirtualnej UXP – lato 2015, Grzegorz Blinowski Swap (obszar wymiany) • Wymianie podlegają strony dowiązane do struktury anon (anonmap) • W systemie można skonfigurować wiele obszarów wymiany – co najmniej jeden obszar wymiany musi być zdefiniowany, lub: – obszary wymiany jest fakultatywny • Obszar wymiany związany z vnode – może być partycją dyskową, zwykłym plikiem dyskowym lub na systemie NFS • Niektóre strony danych są zablokowane i nie podlegają wymianie (strony w trakcie operacji IO) • Struct swapinfo – deskryptor obszaru wymiany. Zawiera: – wskaźnik na: vnode obszaru wymiany, snode – jeśli partycja – wskaźnik do mapy przydzielonych stron - anon; – wskaźnik do tablicy wolnych stron - anon – wskaźniki listy kolejnych struktur swapinfo • Odpowiedni algorytm rozprasza strony po wszystkich dostępnych obszarach wymiany UXP – lato 2015, Grzegorz Blinowski konfiguracja swap • Informacje o swap: – pstat -T (BSD), swap -l (SYSV), swapon -s (linux) – cat /proc/swap • Konfigurowanie: – utworzenie pliku: mkfile 1024m /usr/swapfile1 – dd if=/dev/zero of=/usr/swapfile2 bs=1024 count=65536 (linux) – mkswap - na partycji lub pliku (Linux) • Dodanie obszaru wymiany: – swapon /usr/swapfile2 (Linux, BSD) – swapon /dev/sd3b (Linux, BSD) – swap -a /dev/dsk/c2t0d2s0 (Solaris) • Inne: swapoff UXP – lato 2015, Grzegorz Blinowski mmap() • odwzorowuje fragment pliku na obszar pamięci • ma specjalne zestosowanie w przypadku gdy plik jest urządzeniem • kernel wykorzystuje wewnętrzne mapowania w celu realizacji operacji read/write, n.p.: • • • /dev/mem - otwarcie dostępu do fizycznej pamięci systemu /dev/kmem - do pamięci jądra /dev/zero - dynamiczna alokacja segmentu wyzerowanych słów /dev/zero dynamiczna alokacja segmentu wyzerowanych słów • Korzyści: – spójny interfejs: pamięć, urządzenia, system plików – większa wydajność UXP – lato 2015, Grzegorz Blinowski Funkcja systemowa mmap() caddr_t mmap(addr, len, flags, fd, offset) • caddr_t addr - proponowany adres wirtualny • size_t len - długość segmentu =< od długości pliku • int prot - dostęp do segmentu: PROT_READ PROT_WRITE PROT_EXEC • int flags - używanie segmentu • • MAP_SHARED MAP_PRIVATE plik nie jest modyfikowany • MAP_FIXED gdy proponowany adres jest niedostępny • int fd - uchwyt do pliku • int offset - początek mapowania pliku (wielokrotność strony) UXP – lato 2015, Grzegorz Blinowski struct record { unsigned char contents[256]; unsigned accessed; }; /* open a file for reading and writing read the 24th record and alter it write it back again */ void function1(void) { int fd; struct record r; fd = open(„file”, O_RDWR, 0); lseek(fd, 24 * sizeof(struct record), SEEK_SET); read(fd, &r, sizeof(struct record))); /* alter fields of r */ r.contents = r.accessed = /* move back to correct point of the file */ lseek(fd, 24 * sizeof(struct record), SEEK_SET); write ( fd, &r sizeof(struct record)); close(fd); } UXP – lato 2015, Grzegorz Blinowski struct record { unsigned char contents[256]; unsigned accessed; }; /* open a file for reading and writing read the 24th record and alter it write it back again */ void function2(void) { int fd; struct record *rp; int len = e.g., the length of the file fd = open(„file”, O_RDWR, 0); rp = mmap( (caddr_t) 0, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); rp[24].contents = rp[24].accessed = close(fd); } UXP – lato 2015, Grzegorz Blinowski mmap() • System automatycznie synchronizuje zawartość segmentu z plikiem poprzez mechanizmy VM (na poziomie segvn_data lub anon_map) • Nie wszystkie typy systemów plików definiują VOP_MAP, zdefiniowane w systemach “dyskowych” oraz specfs • VOP_MAP(): – Tworzy nowy segment kernela – wywołanie as_map() – Odwołanie do brakującej strony generuje wyjątek braku strony i odwołanie do dysku – Dla specfs sterownik mapuje pamięć na bufor pamięci urzadzenia • Jak przebiega odczyt/zapis do plików na poziomie kernela: – Tworzone jest mapowanie na plik do specjalnego segmentu “tymczasowego” – Operacje odczytu, zapisu realizowane są poprzez ten segment – read/write musi posiadać implementację jednolitą z mmap(), gdyż inaczej powstaną problemy przy dostępie UXP – lato 2015, Grzegorz Blinowski Unifikacja dostępu do pliku • Kernel obsługuje segment seg_map • Funkcje: segmap_getmap(), segmap_release() - tworzą i usuwają mapowanie vnode na adres wirtualny • Gdy użytkownik wywołuje read()/write() kernel tworzy odwzrowanie fragmentu pliku na własny segment należący do seg_map • Dane prywatne segmap zawierają mapowanie poprzez tablice haszujące i tablicę smap na vnode-y plików • I/O realizowane jest przez iomove() z przestrzeni jądra do odpowiedniego segmentu użytkownika lub gdy brak danych w pamięci poprzez błąd braku strony UXP – lato 2015, Grzegorz Blinowski Unifikacja dostępu do pliku User: read() write() as Kernel tworzy krótkotrwałe mapowanie plik do specjalnego segmentu VOP_R E VOP_W AD RITE fault seg_map seg_vn wysokopoziomowe (niezależne of fs) operacje IO na vnode e() iomov VOP_GETPAGE VOP_PUTPAGE (gdy brak strony) Niskopoziomowe (zależne of fs) operacje IO na vnode VM VFS UXP – lato 2015, Grzegorz Blinowski root@skinner:/proc/11578# more maps address perm offset dev 08048000-080a0000 r-xp 00000000 09:01 080a0000-080a2000 rw-p 00058000 09:01 080a2000-080e9000 rw-p 080a2000 00:00 b79ce000-b7b0e000 rw-s 00000000 00:08 b7b12000-b7b14000 rw-p 00003000 09:01 b7b20000-b7c60000 rw-s 00000000 00:08 b7c79000-b7c7b000 rw-p 00008000 09:01 b7c7b000-b7c83000 r-xp 00000000 09:01 b7c85000-b7c8c000 r-xp 00000000 09:01 b7c8f000-b7c91000 r-xp 00000000 09:01 b7c93000-b7dd9000 r-xp 00000000 09:01 b7de9000-b7deb000 rw-p 00008000 09:01 b7e26000-b7e28000 rw-p 00013000 09:01 b7e3d000-b7e3e000 rw-p 00012000 09:01 b7e40000-b7e42000 rw-p 00001000 09:01 b7f66000-b7f7b000 rw-p 00123000 09:01 b7f8d000-b7f8f000 rw-p 0000f000 09:01 b7fba000-b7fbc000 rw-p 0001b000 09:01 bfbe9000-bfbfe000 rwxp bffeb000 00:00 ffffe000-fffff000 r-xp 00000000 00:00 r = read inode 197199 197199 0 75746549 757470 75746174 757471 757473 757469 757465 757462 757464 757468 130356 757480 65801 757477 757504 0 0 w = write x = execute s = shared p = private (copy on write) path /usr/sbin/sshd /usr/sbin/sshd [heap] /dev/zero (deleted) /lib/libnss_dns-2.7.so /dev/zero (deleted) /lib/libnss_files-2.7.so /lib/libnss_nis-2.7.so /lib/libnss_compat-2.7.so /lib/libdl-2.7.so /lib/libc-2.7.so /lib/libcrypt-2.7.so /lib/libnsl-2.7.so /usr/lib/libz.so.1.2.3 /lib/libutil-2.7.so /usr/lib/libcrypto.so.0.9.8 /lib/libresolv-2.7.so /lib/ld-2.7.so [stack] [vdso] UXP – lato 2015, Grzegorz Blinowski pageout • Gdy wielkość dostępnej wolnej pamięci RAM (wolnych ramek) spada poniżej ustalonego progu system uruchamia mechanizm odzyskiwania pamięci • Wzrost zajętości pamięci (typowe powody): – Tworzenie nowych procesów – Rozszerzenie segmentów istniejących procesów (sterta, stos, ...) • Proces nigdy nie zabiera innemu pamięci bezpośrednio (było by to sprzeczne z zasadą izolacji procesów) • System decyduje o zmniejszeniu zbioru rezydentnego stron (RSS) procesów w celu zwiększenia rozmiaru puli wolnych ramek UXP – lato 2015, Grzegorz Blinowski pageout • Algorytmy selekcji stron: – OPT – należy odzyskać tę stronę, która będzie najpóźniej potrzebna procesowi w przyszłości (w praktyce niemożliwe w realizacji) – LRU – Least Recenty Used, NRU – Not Recently Used – Clock – algorytm zegarowy • Algorytm odzyskiwania stron: – Wybrana strona jest “podkradana” - umieszczana na liście intrans (in transit), pozostaje przypisana do procesu i jeśli proces będzie chciał ją wykorzystać może być ponownie mu przydzielona – Z listy intrans strona może być przydzielona innemu procesowi – (strony zwolnione (koniec procesu, zwolniony vnode, itp.) trafiają na listę free) – Jeśli strona była oznaczona jako zmodyfikowana (dirty) w stosunku do ostatniego zapisu na dysk to musi być ponownie zapisana – Tylko strony danych potencjalnie wymagają zapisu aktualizacyjnego (strony text nie) UXP – lato 2015, Grzegorz Blinowski Proces systemowy pageout • Pageout - proces systemowy (widoczny na liście procesów), lub mechanizm kernela • Budzony okresowo w zależności od zapotrzebowania na pamięć • Przegląda pamięć szukając stron do odzyskania • Odzyskane strony umieszczane są na liście wolnych i stają się dostępne do przydziału UXP – lato 2015, Grzegorz Blinowski Algorytm clock procesu pageout ramki pamięci • Wskazówki pageout poruszają się z taką samą prędkością przemiatając cyklicznie całą pamięć (cały zbiór ramek) • Częstość i prędkość skanowania (liczba stron skanowanych w jednym cyklu) zależy od zapotrzebowania na pamięć • Fronthand - kasuje sprzętowy bit odwołania (reference bit) • Backend sprawdza rb • Jeśli rb nieustawiony to strona ma status “NRU” i trafia na listę intransit backhand han ead r p ds fronthand UXP – lato 2015, Grzegorz Blinowski Algorytm clock procesu pageout Parametry i zmienne pageout lotsfree minfree GPSLO Skanowanie z szybkością <slowscan, fastscan> desfree Stronicowanie 4 / sek Stronicowanie przy clock • lotsfree – gdy tyle lub więcej pamięci pageout nie jest uruchamiany • desfree – tyle wolnej pamięci system powinien mieć • minfree – system nie powinien mieć mniej wolnej pamięci • GPSLO – poziom wolnej pamięci poniżej ktorego uruchamianyjest sched (usuwane całe procesy) • desscan – tyle stron przejrzeć w jednym cyklu zawiera się w przedziale <slowscan, fastscan> • nscan- liczba stron z czyszczonym rb • handspread – odległość wskazówek • maxpgio – maksymalnie tyle zapisów na swap UXP – lato 2015, Grzegorz Blinowski Linux OOM killer • Linux Out of Memory Killer usuwa procesy gdy brakuje pamięci • Rozwiązanie z pogranicza zarządzania procesami i pamięcią • Procesom przyznawana jest liczba punktów - “badness” – Proces o największym b jest zabijany – Kryteria: odzyskujemy maks pamięci, mimimalizujemy szkody (krótki czas pracy), usuwamy proces, który użytkownik sam by usunął – Miary: pamięć, nice, nie root, dużo wywołań fork() UXP – lato 2015, Grzegorz Blinowski Posix threads (pthreads -wątki) UXP – lato 2015, Grzegorz Blinowski pthreads • Standardy: – POSIX 1003.1c 1995 r – ISO/IEC 9945-1 1996 r – 1003.4a - DCE – ANSI / IEEE 1003.1 • Dostępne w większości systemów Unix (Linux) • Często implementowane na poziomie kernela • Bardziej przenośne niż inne implementacje wątków (np. Boost) • Silnie “zintegrowane” ze środowiskiem Unix – głównie w zakresie obsługi sygnałów UXP – lato 2015, Grzegorz Blinowski dlaczego wątki? • Współbieżne procesy: – fork() jest kosztowny – komunikacja jest trudna – powielanie kodu – zajętość pamięci operacyjnej – wyczerpywanie zasobów jądra – spowalnianie – kłopoty z synchronizacją – migotanie zawartości cache procesora UXP – lato 2015, Grzegorz Blinowski wątki • Wątek – odrębny tok wykonania w pocesie • Wątki współdzielą większość danych procesu, z wyjątkiem: – identyfikatora wątku, priorytetu – stosu i rejestrów – kodu powrotu z funkcji systemowej; errno – maski obsługi sygnałów • Dostęp do wspólnych danych wymaga synchronizacji, dane wspólne: – dane globalne – sterta (malloc(), calloc(), także niejawnie przydzielane) – Koncepcja: “MT-safe”, “MT-level”: • niektóre funkcje systemowe posiadają zmienne typu stattic: srand(), strtok() -> strtok_r() • niektóre funkcje systemowe nie są MT-safe, np. resolver (gethostbyname(), itd.) UXP – lato 2015, Grzegorz Blinowski implementacja wątków w systemie • Rodzaje implementacji: – biblioteka kliencka – wątki w trybie użytkownika (ULT), wątki w trybie jądra (KT) • jak mapowane są ULT-KT? – tylko jeden KT dla procesu – mapowanie LWP (lightweight process) – inne mapowania • Linux NPTL (Native Posix Thread Library), od kernel 2.6: clone(): tworzy nowy proces współdzielący zasoby z rodzicem – kwestia mapowania wątków na CPU sprzętowe UXP – lato 2015, Grzegorz Blinowski podstawowe funkcje wątków #include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg); • Tworzy nowy wątek • Zwraca 0 lub kod błędu: – [EAGAIN] brak zasobów lub przekroczenie limitu liczby wątków PTHREAD_THREADS_MAX – [EINVAL] niepoprawny argument • – [EPERM] brak uprawnień (dot. szeregowania) Zakończenie wątku: – void pthread_exit(void *value_ptr); • Porównanie deskryptorów wątków: – int pthread_equal(pthread_t t1, pthread_t t2); UXP – lato 2015, Grzegorz Blinowski tworzenie wątków #define _REENTRANT #include <pthread.h> #include <stdio.h> void *print_message_function(void *ptr); int main(int argc, char**argv) { pthread_t thread1, thread2; char *message1 = "Hello"; char *message2 = "World"; pthread_create(&thread2, NULL, (void *) &print_message_function, (void *) message2); pthread_create(&thread1, NULL, (void *) &print_message_function, (void *) message1); return 0; } void *print_message_function( void *ptr ) { char *message; message=(char *) ptr; printf("%s ", message); pthread_exit(NULL); } UXP – lato 2015, Grzegorz Blinowski synchronizacja wątków int main( int argc, char**argv ) { char *message1 = "Hello"; char *message2 = "World"; pthread_create(&thread2, NULL, (void *) &print_message_function, (void *) message2); pthread_create(&thread1, NULL, (void *) &print_message_function, (void *) message1); pthread_join(thread2, NULL); return 0; } void *print_message_function(void *ptr) { char *message; message=(char *) ptr; if( pthread_equal(pthread_self(),thread1)==0 ) } pthread_join( thread1, NULL ); printf("%s ", message); pthread_exit(NULL); UXP – lato 2015, Grzegorz Blinowski pthread_join() • int pthread_join(pthread_t thread, void **value_ptr); • Zawiesza wątek do momentu zakończenia wskazanego wątku • value_ptr odbiera kod powrotu wątku zakończonego • Zwraca 0 jeśli poprawne zakończenie lub kod błędu UXP – lato 2015, Grzegorz Blinowski mutex • Mutex – odpowiednik semafora binarnego (lub wielo wartościowego), podstawowy obiekt synchronizacyjny dla wątków • obiekt atrybutów mutexu – pthread_mutexattr_init(pthread_mutex_t *); – pthread_mutexattr_destroy(pthread_mutex_t *); • inicjowanie – pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER – pthread_mutex_init(pthread_mutex_t *); • likwidowanie pthread_mutex_destroy() • blokowanie – pthread_mutex_lock(pthread_mutex_t *); – pthread_mutex_trylock(pthread_mutex_t *); – pthread_mutex_unlock(pthread_mutex_t *); UXP – lato 2015, Grzegorz Blinowski mutex • Mutex domyślnie jest binarny • Problem - co się stanie gdy ten sam wątek kilkukrotnie będzie zajmować mutex? • Zachowanie mutaxa można zmienić: – PTHREAD_MUTEX_NORMAL - ponowne zajęcie powoduje deadlock – PTHREAD_MUTEX_ERRORCHECK - mutex binarny, ponowne zajęcie powoduje błąd – PTHREAD_MUTEX_RECURSIVE - mutex wielowartościowy – PTHREAD_MUTEX_DEFAULT - zachowanie przy ponownym zajęciu nieokreślone • ustalenie typu: – pthread_mutexattr_settype(pthread_mutex_t *, int type); UXP – lato 2015, Grzegorz Blinowski mutex #define _REENTRANT #include <pthread.h> #include <stdlib.h> #include <stdio.h> int main(int argc, char **argv) { pthread_t reader, writer; pthread_create(&reader, NULL, (void *) &reader_f, NULL); pthread_create(&writer, NULL, (void *) &writer_f, NULL); pthread_join(writer, NULL); return 0; } void *writer_f(void *ptr) { char ch; ch='a'; while(1) { pthread_mutex_lock( &mutex ); if(buffer_has_item == 0) { buffer = ch; buffer_has_item = 1; } pthread_mutex_unlock( &mutex ); UXP – lato 2015, Grzegorz Blinowski mutex } ch++; if(ch>'z') ch='a'; usleep(1); } pthread_exit(NULL); void *reader_f(void *ptr) { while(1) { pthread_mutex_lock( &mutex ); if(buffer_has_item == 1) printf("%c ", buffer); fflush(stdout); buffer_has_item = 0; } { pthread_mutex_unlock( &mutex ); usleep(1); } pthread_exit(NULL); } UXP – lato 2015, Grzegorz Blinowski zmienne warunkowe - cond • Mutex nie jest wystarczający jeśli oczekujemy na jakieś zdarzenie np: czekanie na akcje/zakończenie jakiegoś wątku – aktywne oczekiwanie jest (prawie) zawsze złym rozwiązaniem • cond zawsze używany w parze z mutexem • cond_wait: usypia wątek w oczekiwaniu na signal i zwalnia mutex • cond_signal: wysyła sygnał • typowa sekwencja: – T1: L(m); n++; S(c); U(m); – T2: L(m); while (n) W(c,m); UXP – lato 2015, Grzegorz Blinowski cond • obiekt atrybutów zmiennych warunkowych – pthread_condattr_init() – pthread_condattr_destroy() • inicjowanie zmiennych warunkowych – pthread_cond_t cond = PTHREAD_COND_INITIALIZER; – pthread_cond_init() • likwidowanie zmiennych warunkowych – pthread_cond_destroy() • blokowanie na zmiennych warunkowych – pthread_cond_wait(&cond, &mutex) – pthread_cond_timedwait(&cond, &mutex) – int pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clock_id); – pthread_cond_signal(&cond) – pthread_cond_broadcast(&cond) UXP – lato 2015, Grzegorz Blinowski cond char buffer; int buffer_has_item = 0; pthread_mutex_t mutex_w = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t mutex_r = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond_reader =PTHREAD_COND_INITIALIZER; pthread_cond_t cond_writer =PTHREAD_COND_INITIALIZER; void *writer_f(void *ptr) { int i; char ch = 'a'; while(1) { pthread_mutex_lock(&mutex_w); while(buffer_has_item != 0) pthread_cond_wait(&cond_writer, &mutex_w); pthread_mutex_unlock(&mutex_w); buffer = ch++; if(ch>'z') ch='a'; buffer_has_item = 1; pthread_mutex_lock(&mutex_r); pthread_cond_signal(&cond_reader); pthread_mutex_unlock(&mutex_r); } pthread_exit(NULL); } UXP – lato 2015, Grzegorz Blinowski cond void *reader_f(void *ptr) { int i; while(1) { pthread_mutex_lock( &mutex_r ); while(buffer_has_item == 0) pthread_cond_wait(&cond_reader, &mutex_r); pthread_mutex_unlock( &mutex_r); printf("%c ", buffer); fflush(stdout); buffer_has_item = 0; pthread_mutex_lock(&mutex_w); pthread_cond_signal(&cond_writer); pthread_mutex_unlock(&mutex_w); } pthread_exit(NULL); } UXP – lato 2015, Grzegorz Blinowski rd/wr lock • int pthread_rwlock_wrlock(pthread_rwlock *rwlock); • int pthread_rwlock_rdlock(pthread_rwlock *rwlock); • int pthread_rwlock_unlock(pthread_rwlock *rwlock); • Blokada typu czytelnicy / pisarz • pthread_rwlock inicjalizowany podobnie jak mutexy • Dostępne też funkcje: pthread_rwlock_trywrlock, pthread_rwlock_init, pthread_rwlockattr_init UXP – lato 2015, Grzegorz Blinowski Kończenie wątków • Żądanie zakończenia pthread_cancel(thread) – dla wątku określone: “cancel state” i “cancel type” – cancel state – czy można “zdalnie” zakończyć wątek – cancel type – jak wątek jest kończony: natychmiast, przy określonych operacjach – int pthread_setcanceltype(int type, int *oldtype); – int pthread_setcancelstate(int state, int *oldstate); state: PTHREAD_CANCEL_ENABLE, PTHREAD_CANCEL_DISABLE – int pthread_setcanceltype(int type, int *oldtype); state: PTHREAD_CANCEL_DEFERRED, PTHREAD_CANCEL_ASYNCHRONOUS • punkty zakończenia: – pthread_join() – pthread_cond_wait() – pthread_cond_timedwait() – pthread_testcancel() – sem_wait() – sig_wait() – read() – write() UXP – lato 2015, Grzegorz Blinowski Atrybuty wątków int pthread_attr_init(pthread_attr_t *attr); int pthread_attr_destroy(pthread_attr_t *attr); • Funkcja inicjalizuje/niszczy obiekt atrybutów wątku • Poszczególne atrybuty można ustawiać indywidualnie • Zniszczenie obiektu nie ma wpływu na watki nim zainicjowane • Typowe domyślne atrybuty wątku: = PTHREAD_CREATE_JOINABLE – Detach state – Scope – Inherit scheduler = PTHREAD_SCOPE_SYSTEM = PTHREAD_INHERIT_SCHED = SCHED_OTHER – Scheduling policy – Scheduling priority = 0 – Guard size – Stack address = 4096 bytes – Stack size = 0x..... = 0x..... UXP – lato 2015, Grzegorz Blinowski Atrybuty wątków c.d. int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); • Pozwala/zakazuje operacji join: PTHREAD_CREATE_DETACHED, PTHREAD_CREATE_JOINABLE int int pthread_attr_setscope(pthread_attr_t *attr, int scope); • Określa “pulę” wątków schedulera: PTHREAD_SCOPE_SYSTEM – watek należy do systemowej puli, PTHREAD_SCOPE_PROCESS – wątek należy do puli procesu • Linux obsługuje tylko pulę systemową! int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize); • Ustanawia dodatkowy obszar na końcu stosu wątku, nadpisanie tego obszaru generuje SIGSEGV • Rozmiar powinien być > rozm. strony UXP – lato 2015, Grzegorz Blinowski Atrybuty wątków c.d. int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize); int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr); int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); • Ustala adres i rozmiary stosu dla wątku • stos moze być dowolnym obszrem pamięci wcześniej przydzielonym UXP – lato 2015, Grzegorz Blinowski sygnały i wątki • Wątki mają odrębne maski sygnałów • Możliwe jest wybiórcze dostarczanie sygnałów do wątków poprzez blokowanie (wstrzymywanie) sygnałów w tych wątkach, które nie powinny otrzymywać sygnałów • Uwaga: stosowanie wątków nie zmienia koncepcji obsługi sygnałów przyjętej w modelu jednowątkowym procesu • “Ogólno - procesowe” skutki sygnału skutkują dla całego procesu (a nie wątku), np: SIGKILL, SIGSTOP zabija/wstrzymuje cały proces, a nie odbierający watek • Wątki w zakresie działań na masce sygnałów zwykle nie powinny wywoływać “procesowych” funkcji obsługi sygnałów np. sigprocmask(), a jedynie funkcje przeznaczone dla wątków: pthread_sig*() • int pthread_kill(pthread_t thread, int sig); <- sygnał może być dostarczony do wszystkich wątków • typowa obsługa sygnałów: wątki maja zablokowaną obsługę sygnałów, jeden wyróżniony wątek odpowiada za ich obsługę UXP – lato 2015, Grzegorz Blinowski Przypisanie wątku do obsługi sygnału pthread_mutex_t stats_lock=PTHREAD_MUTEX_INITIALIZER; int samples; /* wątek obsługi sygnału SIGUSR1 */ void *report_stats(void *p) { int caught; sigset_t sigs_to_catch; /* Odziedziczyliśmy maskę z blokowaniem sygnału SIGUSR1. Ponieważ wszystkie inne wątki również blokują ten sygnał i tylko my oczekujemy na niego za pomocą sigwait() mamy pewność, że każde wystąpienie SIGUSR1 zostanie dostarczone do nas. */ sigemptyset(&sigs_to_catch); sigaddset(&sigs_to_catch, SIGUSR1); for (;;) { // obsługa przybycia sygnału sigwait(&sigs_to_catch, &caught); // otrzymaliśmy sygnał SIGUSR1 pthread_mutex_lock(&stats_lock); printf("report_stats(): samples = %d\n", samples); pthread_mutex_unlock(&stats_lock); } return NULL; UXP – lato 2015, Grzegorz Blinowski Przypisanie wątku do obsługi sygnału void *worker_thread(void *p) { /* wątek - robotnik */ for (;;) { sleep(5); pthread_mutex_lock(&stats_lock); samples++; pthread_mutex_unlock(&stats_lock); } return NULL; } int main(int argc, char **argv) { int i; pthread_t threads[MAX_NUM_THREADS]; int num_threads = 0; sigset_t sigs_to_block; // Ustawiamy blokowanie sygnału SIGUSR1. Pozostałe wątki odziedziczą tą maskę. sigemptyset(&sigs_to_block); sigaddset(&sigs_to_block, SIGUSR1); pthread_sigmask(SIG_BLOCK, &sigs_to_block, NULL); UXP – lato 2015, Grzegorz Blinowski Przypisanie wątku do obsługi sygnału /* tworzymy wątek obsługujący SIGUSR1 */ pthread_create(&threads[num_threads++], NULL, report_stats, NULL); /* pozostałe wątki - robotnicy */ for (i=num_threads; i<MAX_NUM_THREADS; i++) { pthread_create(&threads[num_threads++], NULL, worker_thread, NULL); } for (i = 0; i < num_threads; i++) pthread_join(threads[i], NULL); return 0; } UXP – lato 2015, Grzegorz Blinowski Porównanie • Pthreads – rozwiązanie bazowe (inne zwykle budowane na nim) • OpenMP – C, C++, Fortran, zastosowania numeryczne/naukowe • Intel Cilk Plus – “niszowe” rozwiązanie Intela • Intel TBB – C++ template lib, obsługa multicore CPU • C++11 Threads – część standardu C++ wersji 11 UXP – lato 2015, Grzegorz Blinowski Porównanie • Dla testowego programu z opcją -O3: – Pthreads • linii kodu asm: 1 500 – OpenMP • linii kodu asm: 1 500 – Intel Cilk Plus • linii kodu asm: 4 000 – Intel TBB • linii kodu asm: 25 000 – C++11 Threads • linii kodu asm: 25 000 UXP – lato 2015, Grzegorz Blinowski Porównanie OpenMP Intel Cilk Plus Intel TBB C++11 Threads PThreads + Options + Performance + Possibilities + Flexibility + Flexibility + Portable + Scaling + Management + Type-Safety + Low-Level + Languages + Variables + Maintenance + Possibilities + Compatibility - Performance - Fortran - Language - Fortran - Efforts - Memory - Control - OOP - Compiler - Type-Safety - Unreliable - Possibilities - Control - Scaling - Management źródło: https://software.intel.com/en-us/articles/choosing-the-right-threading-framework UXP – lato 2015, Grzegorz Blinowski API XTI (TLI) STREAMS Pseudoterminale UXP – lato 2015, Grzegorz Blinowski STREAMS • Mechanizm kernela, który ujednolica komunikację pomiędzy procesami a sterownikami • Typowo używany w zastosowaniach sieciowych • 1984 – Dennis Ritchie: pseudo terminale • Dostępne: linia komercyjna (Solaris, AIX), MacOs X, BSD, Linux (jako dodatek oraz tryb kompatybilności) • POSIX: opcjonalne • Od strony procesu reprezentowany przez deskryptor • W jądrze strumień zbudowany na sterowniku urządzenia • Możliwość dodawania modułów strumienia bez uprawnień roota: – ioctl(fd, I_POP, (char*) 0 ); – ioctl(fd, I_PUSH, "tirdwr"); – ldterm - moduł obslugi pty UXP – lato 2015, Grzegorz Blinowski STREAMS c.d. • Paradygmat: data-driven (w kontraście do demand-driven) • Dane przekazywane jako wskaźniki do komunikatów (messages) • Zalety: – jednolity mechanizm kernela od przestrzeni użytkownika do sterowników sprzętowych – architektura wykorzystująca komunikaty pozwala na stosunkowo proste wprowadzanie nowych protokołów – obsługa strumieni danych oraz dtaagramów – multiplexowanie i demultiplexowanie komunikatów – obsługa architektur multi-CPU • Wady: – zaimplementowane w linii komercyjnej (SYS V) UXP – lato 2015, Grzegorz Blinowski STREAMS c.d. • W STREAMS zaimplementowano: – obsługę sieci TCP/IP – obsługę inny protokołów sieciowych – terminale / pseudoterminale – potoki i FIFO • efekt uboczny: potok/FIFO bazujący na STREAMS jest dwukierunkowy (ale bazowanie na tym fakcie jest nieprzenośne!) • potok nienazwany może być jedna widoczny poprzez vfs (nieprzenośne) • potok/FIFO możemieć rozszerzoną funkcjonalność poprzez moduły STREAMS UXP – lato 2015, Grzegorz Blinowski STREAMS Przestrzeń użytkownika Pojęcia: Głowy strumieni udp tcp x3 / x28 / x29 ip Moduły ixe x25 slip Async 802.3 llc2 Ethernet lapb Driver’y hdlc Sprzęt • • • • • • komunikat moduł kolejki sterowanie przepływem multiplexor stream scheduler UXP – lato 2015, Grzegorz Blinowski STREAMS• Każdy moduł posiada 2 kolejki – kolejka pisania - dane przemieszczaja sie "w dół" high water low water kolejka pisana put() service() Komunikat 1 mblk_t b_next b_prev b_data_p. b_cont – kolejka czytania - dane przemieszczaja się "w górę" put() service() Moduł kolejka czytana Komunikat 2 dblk_t db_base db_lim • Jednym z głównych zadań modułu jest obsługa sterowania przepływem w kolejkach • Funkcja put() "przepycha" komunikat w dół (lub w górę) strumienia, jeśli brak miejsca (sprawdzane przez canput() ) to kom. zostaje odłożony do kolejki • service() - wywoływane przez scheduler kolejki • Komunikaty są wieloczęściowe • Typy komunikatów : < QPCTL - zwykłe b_next b_prev b_data_p. b_cont data buffer b_next b_prev b_data_p. b_cont UXP – lato 2015, Grzegorz Blinowski STREAMS • Komunikaty mblk_t: pierwszy blok - informacje sterujące, w następnych dane Komunikaty zwykłe: • • • • • • • M_DATA M_PROTO M_BREAK M_CTL M_DELAY M_IOCTL M_SIG dane użytkownika dane protokołu driver wysyła sygnał BREAK w medium dane sterujące przekazywane miedzy modułami dla wyjścia driver’a dane ioctl(), patrz streamio(2) sygnał dla użytkownika Komunikaty priorytetowe - sterowanie przepływem w strumieniu: • • • • • • • • • • • M_COPYIN M_COPYOUT M_ERROR M_FLUSH M_HANGUP M_IOCACK M_IOCNAK M_IOCDATA M_PCSIG M_STOP M_START copyin() copyout() driver głowa priorytetowy sygnał zatrzymanie wyjścia driver’a aktywacja wyjścia driver’a UXP – lato 2015, Grzegorz Blinowski STREAMS • poprzez cdevsw urządzenie znakowe jest związane ze strumieniem • operacje na strumieniach: – open(), read(), write(), close(), ioctl(), pipe(), isastreem(), getmsg(), putmsg(), poll() • Rozkazy ioctl: – I_PUSH wstaw moduł do strumienia – I_POP wyjmij moduł ze strumienia – I_SETSIG odbierz SIGPOLL – I_FDINSERT daj informacje o strumieniu – I_SENDFD wyślij deskryptor pliku – I_LINK utwórz multiplexor – I_LOOK daj nazwę modułu pod głową – I_FLUSH usuń komunikaty ze strumienia – I_GETSIG daj zdarzenie generujące SIGPOLL – I_FIND czy dany moduł jest w strumieniu – I_PEEK czytaj komunikat bez pobierania go – ... UXP – lato 2015, Grzegorz Blinowski XTI (d. TLI) • API sieciowe zbudowane na modułach STREAMS • Interfejs alternatywny w stosunku do BSD sockets • W Solaris/AIX – generyczny interfejs sieciowy (gniazda są emulowane poprzez bibliotekę) • W wielu innych systemach XTI jest emulowane • Główne cechy (porównanie z gniazdami): – niezależne od protokołu (w większym stopniu niż gniazda) – bazuje na sterownikach protokołów i modułach STREAMS – ujednolicone zarządzanie strukturami dynamicznie alokowanymi – elastyczniejsze niż gniazda – wada: bardziej skomplikowany od gniazd UXP – lato 2015, Grzegorz Blinowski XTI Głowy strumieni timod timod sockmod udp sockmod Moduły tcp ip Transport service interface • brak komunikacji w dziedzinie Unix-a • brak read() i write() - ale zapewnia to moduł STREAMS tirdwr • komunikaty M_PROTO i M_PCPROTO przenoszą TSDU (transport service data unit) • z każdą TSDU związana jest struktura C sys/tihdr.h UXP – lato 2015, Grzegorz Blinowski XTI - API • • Struktura netbuf używana wszędzie tam gdzie dane zmiennej wielkości (adresy, bufory danych, itp) Alokowane automatycznie w zależności od zapotrzebowania przez t_alloc() struct netbuf { unsigned int maxlen; unsigned int len; char *buf; } • Przykład: struct t_call { struct netbuf addr; struct netbuf opt; struct netbuf udata; ... }; Obsługa błędów: extern int t_errno; extern char *t_errlist[]; void t_error( char *errmesg ); <- komunikat na stderr UXP – lato 2015, Grzegorz Blinowski XTI - API char * t_alloc(int fd, int structtype, int fields); int t_free(char *ptr, int structtype); • • • • Pomocniczna funkcja t_alloc() przydziela struktury danych, zawierające bufory typu netbuf t_free() zwalnia struktury Typy struktur: – T_BIND (adresy) (połączenie) – T_CALL (rozłączenie) – T_DIS (informacje) – T_INFO – T_OPTMGMT (opcje prot.) – T_UNITDATA (datagramy) – T_UDERROR (błędy) Pola do alokacji: – T_ALL (wszystkie) – T_ADDR (aresy) – T_OPT (opcje) – T_UDATA (datagramy) UXP – lato 2015, Grzegorz Blinowski t_open() t_bind() serwer t_alloc() t_open() socket() klient bind() t_rcvudata() t_bind() blokuje aż do otrzymania danych żądanie t_sndudata() socket() recvfrom() t_alloc() bind () żądanie t_sndudata() sendto() odpowiedź odpowiedź t_rcvdata() XTI sendto () recvfrom () gniazda BSD UXP – lato 2015, Grzegorz Blinowski t_open() t_bind() serwer t_alloc t_open() t_listen() t_bind() blokuje aż do połączenia t_alloc() socket() bind() listen() klient accept() socket() połączenie t_accept() żądanie t_snd() t_rcv() t_snd() ustanowienie połączenia t_connect() żądanie write() read() write() odpowiedź odpowiedź read() t_rcv() XTI connect() gniazda BSD UXP – lato 2015, Grzegorz Blinowski XTI - API int t_open( const char *name, int oflag, struct t_info *info); • • • Otwiera punkt końcowy transportu Transport reprezentowany jest przez sterownik, np.: “/dev/tcp”, “/dev/ip”, “/dev/udp” (tworzony jest strumień) Wypełnia strukturę t_info zawierającą informacje o transporcie Struct t_info { t_scalar_t addr; t_scalar_t options; t_scalar_t tsdu; t_scalar_t etsdu; t_scalar_t connect; t_scalar_t discon; t_scalar_t servtype; t_scalar_t flags; • /* /* /* /* /* /* /* /* /* /* /* /* /* /* max size of the transport protocol address max number of bytes of protocol-specific options max size of a transport service data unit (TSDU) max size of an expedited transport service data unit (ETSDU) max amount of data allowed on connection establishment functions max amount of data allowed on t_snddis() and t_rcvdis() functions service type supported by the transport provider other info about the transport provider */ */ */ */ */ */ */ */ */ */ */ */ */ */ } Flagi: T_COTS, T_COTS_ORD, T_CLTS - connection-mode / connectionless service UXP – lato 2015, Grzegorz Blinowski XTI - API int t_bind(int fd, struct t_bind *request, struct t_bind *return); • • request == NULL adres wybiera dostawca request != NULL && request->addr.len==0 • adres wybiera dostawca i wołający określa liczbę połączeń kolejkowanych qlen struct t_bind { struct netbuf addr; unsigned int qlen; }; • t_bind nie zależy od protokołu UXP – lato 2015, Grzegorz Blinowski XTI – nawiązanie połączenia int main(int argc, char *argv[]) { int fd; int nbytes; int flags = 0; char buf[1024]; struct t_call *sndcall; if ((fd = t_open("/dev/ticots",O_RDWR,NULL)) < 0) { t_error("t_open failed"); exit(1); } if (t_bind(fd, NULL, NULL) < 0) { t_error("t_bind failed"); exit(2); } if ((sndcall = (struct t_call *) t_alloc(fd, T_CALL, T_ADDR)) == NULL) { t_error("t_alloc failed"); exit(3); } sndcall->addr.len = sizeof(int); // fill sndcall->addr.buf UXP – lato 2015, Grzegorz Blinowski XTI - API int t_connect(int fd, struct t_call *sendcall, struct t_call *recvcall); • • sendcall->opt == 0 domyślne opcje recvcall == 0 ignorowanie zwrotnych danych • connect() w BSD • nie pozwala na przekaz danych w T_CONNECT • potrzebne: setsockopt() i getpeername() struct t_call { struct netbuf struct netbuf struct netbuf int sequence; }; addr; opt; udata; /* identyfikator połączenia */ UXP – lato 2015, Grzegorz Blinowski XTI - API int t_listen(int fd, struct t_call *call); call -> identyfikator żądanego połączenia int t_accept (int fd, /*źródło żądania*/ int connfd, /*punkt połączenia*/ struct t_call *call); • accept() w BSD t_listen()+ t_open() + t_accept() • Oddzielona jest faza akceptacji na poziomie sieciowym i akceptacji na poziomie API Można odrzucić połączenie zanim zostanie wynegocjowane w TCP • UXP – lato 2015, Grzegorz Blinowski XTI - API int t_snd(int fd, char *buff, unsigned int nbytes, int flags); Wysyłanie danych Flagi:T_EXPEDITED; T_MORE • • int t_rcv(int fd, char *buff, unsigned int nbytes, int *flags); • Datagramy (unit data): int t_sndudata(int fd, struct t_unitdata *unitdata); int t_rcvudata(int fd, struct t_unitdata *unitdata); struct t_unitdata { struct netbuf addr; struct netbuf opt; struct netbuf udata; }; UXP – lato 2015, Grzegorz Blinowski XTI – nawiązanie połączenia i przesłanie danych if (t_connect(fd, sndcall, NULL) < 0) { t_error("t_connect failed for fd"); exit(4); } while ((nbytes = t_rcv(fd, buf, 1024, &flags)) != -1) { if (fwrite(buf, 1, nbytes, stdout) < 0) { fprintf(stderr, "fwrite failed\n"); exit(5); } } UXP – lato 2015, Grzegorz Blinowski XTI - API int • • t_close(int fd); Zamknięcie deskryptora Nie musi zamykać połączenia, nie musi negocjować zamknięcia w protokole int t_snddis (int fd, struct t_call *call); int t_rcvdis (int fd, struct t_discon *discon ); • • Zamknięcie połączenia “abortive release” (w TCP – wysłanie, odebranie RST) Używane np. do odrzucenia inicjowanego połączenia po t_listen() int t_rcvrel(int fd); int t_sndrel(int fd); • • • Zamknięcie połączenia “orderly release” (pełna negocjacja połączenia) Sekwencja: A:t_sndrel, B:t_rcvrel, B:t_sndrel, A:rcvrel, A:t_close, B:t_close Transport musi zapewniać T_COTS_ORD UXP – lato 2015, Grzegorz Blinowski XTI - API struct t_discon { struct netbuf int reason; int sequence; udata; } • • • udata – dane przesłane razem z zamknięciem reason – kod / przyczyna zamnięcia (zależna od protokołu) sequence – identyfikator (przydatny gdy wiele połaczeń) UXP – lato 2015, Grzegorz Blinowski Przykład zamknięcia połączenia while ((nbytes = t_rcv(fd, buf, NB, &flags)) != -1) { fwrite(buf, 1, nbytes, stdout); } if ((t_errno == TLOOK) && (t_look(fd) == T_ORDREL)) { if (t_rcvrel(fd) < 0) { t_error("t_rcvrel failed"); exit( 1 ); } if (t_sndrel(fd) < 0) { t_error("t_sndrel failed"); exit( 1 ); } exit(0); } t_error("t_rcv failed"); UXP – lato 2015, Grzegorz Blinowski XTI - API int t_getinfo(int fd, struct t_info *info); • Informacja o transporcie int t_optmgmt(int fd, struct t_optmgmt *request, struct t_optmgmt *return); struct t_optmgmt { struct netbuf opt; / * struct t_opthdr, opcje XTI */ t_scalar_t flags; } • • Ustawianie / pobieranie opcji Flagi (wybrane dla XTI): XTI_LINGER, XTI_RCVBUF, XTI_RCVLOWAT, XTI_SNDBUF, XTI_SNDLOWAT UXP – lato 2015, Grzegorz Blinowski XTI - API int t_look( int fd ); • • • Podaje zdarzenie bieżące związane z punktem końcowym jeśli t_errno == TLOOK wywołujemy t_look aby określić stan Zwraca: – T_CONNECT - odebrano potwierdzenie połączenia – T_DATA - odebrano dane zwykłe – T_DISCONNECT - odebrano żądanie rozłączenia – T_ERROR – T_EXDATA - odebrano dane ekspresowe – T_LISTEN - połączenie wchodzące – T_ORDREL - odebrano rozłączenie Przykład - serwer sterowany zdarzeniami UXP – lato 2015, Grzegorz Blinowski // serwer sterowany zdarzeniami // moze dzialac w calosci w trybie NOBLOCK // na podstawie http://docs.oracle.com/cd/E19120-01/open.solaris/817-4415/tli-5455 #include #include #include #include #include #include <tiuser.h> <fcntl.h> <stdio.h> <poll.h> <stropts.h> <signal.h> #define NUM_FDS 1 #define MAX_CONN_IND 4 #define SRV_ADDR // ... int conn_fd; /* server connection here */ extern int t_errno; struct t_call *calls[NUM_FDS][MAX_CONN_IND]; // calls[indeks-z-tabl-poll][numer-deskryptora] Przykład - serwer sterowany zdarzeniami UXP – lato 2015, Grzegorz Blinowski main(int argc, char **argv) { struct pollfd pollfds[NUM_FDS]; struct t_bind *bind; int i; if ((pollfds[0].fd = t_open(“/dev/tivc”, O_RDWR, (struct t_info *) NULL)) == -1) { .... } if ((bind = (struct t_bind *) t_alloc(pollfds[0].fd, T_BIND, T_ALL)) == (struct t_bind *) NULL) {... } bind->addr.len = sizeof(int); // SRV_ADDR; if (t_bind(pollfds[0].fd, bind, bind) == -1) { . . . } Przykład - serwer sterowany zdarzeniami UXP – lato 2015, Grzegorz Blinowski pollfds[0].events = POLLIN; while (TRUE) { if (poll(pollfds, NUM_FDS, -1) == -1) { perror(“poll failed”); exit(5); } for (i = 0; i < NUM_FDS; i++) { switch (pollfds[i].revents) { default: perror(“poll returned error event”); exit(6); case 0: continue; case POLLIN: do_event(i, pollfds[i].fd); service_conn_ind(i, pollfds[i].fd); } } Przykład - serwer sterowany zdarzeniami UXP – lato 2015, Grzegorz Blinowski int do_event( int slot, int fd) { struct t_discon *discon; int i; switch (t_look(fd)) { case -1: t_error("t_look failed"); exit(9); case T_ERROR: fprintf(stderr, "t_look returned T_ERROR event\n"); exit(8); case T_LISTEN: /* find free element in calls array */ for (i = 0; i < MAX_CONN_IND; i++) { if (calls[slot][i] == (struct t_call *) NULL) break; } if ((calls[slot][i]=(struct t_call *) t_alloc( fd, T_CALL, T_ALL)) == (struct t_call *) NULL) { t_error("t_alloc of t_call structure failed"); Przykład - serwer sterowany zdarzeniami UXP – lato 2015, Grzegorz Blinowski if ((calls[slot][i]=(struct t_call *) t_alloc( fd, T_CALL, T_ALL)) == (struct t_call *) NULL) { t_error("t_alloc of t_call structure failed"); exit(11); } if (t_listen(fd, calls[slot][i] ) == -1) { t_error("t_listen failed"); exit(12); } break; case T_DISCONNECT: discon= (struct t_discon*)t_alloc(fd, T_DIS, T_ALL); if (discon == (struct t_discon *) NULL) { t_error("t_alloc of t_discon structure failed"); exit(13) } if(t_rcvdis( fd, discon) == -1) { t_error("t_rcvdis failed"); exit(14); } Przykład - serwer sterowany zdarzeniami UXP – lato 2015, Grzegorz Blinowski if(t_rcvdis( fd, discon) == -1) { t_error("t_rcvdis failed"); exit(14); } /* find call ind in array and delete it */ for (i = 0; i < MAX_CONN_IND; i++) { if (discon->sequence == calls[slot][i]->sequence) { t_free(calls[slot][i], T_CALL); calls[slot][i] = (struct t_call *) NULL; } } t_free(discon, T_DIS); break; } } Przykład - serwer sterowany zdarzeniami UXP – lato 2015, Grzegorz Blinowski service_conn_ind(int slot, int fd) { int i; for (i = 0; i < MAX_CONN_IND; i++) { if (calls[slot][i] == (struct t_call *) NULL) continue; if((conn_fd = t_open( “/dev/tivc”, O_RDWR, (struct t_info *) NULL)) == -1) { t_error("open failed"); exit(15); } if (t_accept(fd, conn_fd, calls[slot][i]) == -1) { if (t_errno == TLOOK) { t_close(conn_fd); return; } t_error("t_accept failed"); exit(17); Przykład - serwer sterowany zdarzeniami UXP – lato 2015, Grzegorz Blinowski if (t_accept(fd, conn_fd, calls[slot][i]) == -1) { if (t_errno == TLOOK) { t_close(conn_fd); return; } t_error("t_accept failed"); exit(17); } t_free(calls[slot][i], T_CALL); calls[slot][i] = (struct t_call *) NULL; run_server(fd); } } UXP – lato 2015, Grzegorz Blinowski Pseudoterminale • Pseudoterminal (“pty”) to para wirtualnych urządzeń znakowych zapewniających dwukierunkową komunikację między procesami • Komponenty: – slave (/dev/ttyXY) – zachowuje się jak “zwykłe” urządzenie terminalowe, może odbierać sygnały – master (/dev/ptyXY) – część sterująca (przykład: zapis ctrl-C generuje SIGINT w slave) • Interfejsy SYSV, BSD (Linux używa obydwu API) • Wykorzystanie: rlogin, ssh, telnet, screen, expect, xterm UXP – lato 2015, Grzegorz Blinowski Pseudoterminale c.d. Serwer Telnet Klient telnet Terminal driver TCP/IP Użytkownik przy terminalu Schemat sesji klient-serwer dla prot. telnet TCP/IP Login shell Pseudo-terminal driver Sesja TCP Pseudo terminal: Pozwala na realizację funkcji terminalowych dla danych niepochodzących z "fizycznych" terminali - obsługa sygnałów, grupy terminalowe procesów praca w tle (SIGINT SIGQUIT, SIGTSTP, SIGTTIN, SIGTTOU) UXP – lato 2015, Grzegorz Blinowski Pseudoterminale c.d. • Interfejs Unix 98: – /dev/ptmx - “master clone device” - generuje pty – /dev/pts/* (slave devices) – wolny terminal master uzyskujemy otwierając /dev/ptmx: • otwarcie ptmx zwraca fd ptm oraz generuje terminal slave (w kat. /dev/pts/), nazwę pts uzyskuje się wywołując ptsname() • następnie przez użyciem pts trzeba jeszcze wywołać: grantpt() i unlockpt() • BSD: – dostępne w systemie pary urządzeń znakowych: • /dev/pty[p-za-e][0-9a-f] (BSD master devices) • /dev/tty[p-za-e][0-9a-f] (BSD slave devices) – wolnego terminala master szukamy wywołując open() do skutku UXP – lato 2015, Grzegorz Blinowski Pseudoterminale c.d. char *ptsname(int fd); int ptsname_r(int fd, char *buf, size_t buflen); – fd – deskrytor ptm – zwraca: scieżkę do pts int grantpt(int fd); int unlockpt(int fd); – fd – deskrytor ptm – zmienia prawa dostepu i właściciela pts – userid pts <- RUID wywołujacego – groupid pts < “tty” (lub podobne) – mode <- 0620 UXP – lato 2015, Grzegorz Blinowski RPC UXP – lato 2015, Grzegorz Blinowski ONC (SUN) RPC • RPC – Remote Procedure Call – używane np. W NFS oraz NIS/NIS+ • RPC działa w warstwie zastosowań, wraz z XDR, (warstwa prezentacji) – “nad” protokołami UDP (historycznie) oraz TCP • RPC skonstruowano w sposób pozwalający na maksymalne “ukrycie” szczegółów komunikacji sieciowej przed programistą UXP – lato 2015, Grzegorz Blinowski ONC (SUN) RPC • Cechy: – Przenośne – Wysokopoziomowe – Interfejs C – Wydajne – mały narzut własny • Zastosowania: – NFS, NIS, NIS+, wiele innych • Zalety – Dostosowanie programu do RPC (“rozproszenie”) jest stosunkowo proste – wymaga zazwyczaj tylko zmiany sposobu przekazywania argumentów – Mniej czynności przygotowawczych i szczegółów związanych z komunikacją niż w BSD sockets – Rozgłaszanie: przydatna metoda “odkrywania” zasobów UXP – lato 2015, Grzegorz Blinowski RPC - rpcgen • Program rpcgen generuje: – Serwer – rejestracja procedury w portmap, kod serwisu i sieciowy – tzw “client stub” (“pieniek”) - szkieletowe funkcje przykrywające właściwą komunikacje sieciową po stronie klienta – Plik nagłówkowy i moduł konwersji danych – na podstawie pliku .x w języku IDL (Interface Definition language) UXP – lato 2015, Grzegorz Blinowski RPC - rpcgen • rpcgen -C prog.x generuje: – prog.h – nagłówek dla części serwerowej i klienckiej, zawiera struktury danych i stałe oraz prototypy funkcji stub. – prog_svc.c – serwer, zawiera funkcję main(), rejestrujacą serwis, wykonuje fork() tworząc demona obsługująceg serwis (zachowanie to można łatwo zmienić redefiniują stałą). – prog_clnt.c – zawiera client stub, który pakuje argumenty i wywołuje zdalną funkcję. – prog_xdr.c – zawiera funkcje realizujące konwersję argumentów, dla prostych argumentów _xdr.c może nie być obecna. UXP – lato 2015, Grzegorz Blinowski RPC – kompletny system (przykład) • Przy plikach źródłowych – proc.x – definicje IDL – proc.c – klient korzystający z wywołań RPC (będzie typowo zawierał main()) – proc_proc.c - moduł zawierający właściwą wywoływaną funkcję (po stronie serwera) • Generujemy poprzez rpcgen: – proc.h, proc_clnt.c, proc_svc.c proc_xdr.c • Program kliencki “proc” tworzą moduły: – proc.c -> proc.o proc_clnt.c -> proc_clnt.o proc_xdr.c -> proc_xdr.o proc: proc.o proc_clnt.o proc_xdr.o • Serwer “procsrv” tworzą moduły: – proc_xdr.c -> proc_xdr.o proc_proc.c -> proc_proc.o proc_svc.c -> proc_svc.o procsrv: proc_xdr.o proc_svc.o proc_proc.o UXP – lato 2015, Grzegorz Blinowski RPC – jak wprowadzić ? • W programie jak niżej chcemy zdalnie wywolać funkcję myintsqr(): int myintsqr(int a) { return a*a; } int main(void) { int res; int arg=2; res = myintsqr( arg ); printf(“ the result is %d\n”, res ); } • Program przyjmuje postać: #include <rpc/rpc.h> #include “myintsqr.h” int main(void) { CLIENT *clnt; int *res_1; int arg=2; clnt = clnt_create(“host”, SQR_PROG, SQR_VERS, "udp"); if (clnt==NULL) exit(1); res_1 = myintsqr_1(&arg, clnt); // should check res_1 for NULL! printf(“ the result is %d\n”, *res_1 ); clnt_destroy( clnt ); } UXP – lato 2015, Grzegorz Blinowski RPC – jak wprowadzić ? • Moduł serwera z funkcją wywoływaną: #include "myintsqr.h" int * myintsqr_1_svc(int *argp, struct svc_req *rqstp) { static int result; result = argp->arg * argp->arg; return &result; } UXP – lato 2015, Grzegorz Blinowski RPC - IDL • Program – pakiet funkcji • Specyfikacja IDL: program PROG { version PROG1 { void proca(int) = 1; Numer funkcji Numery programów: Numer wersji • a0x00000000Numer programu 0x1fffffff: defined by sun } = 1; } = 0x3a3afeeb; • Funkcje w IDL mogą mieć tylko jeden argument, większa liczba musi być opakowana w strukturę • Struktury danych IDL przypominają definicje C, są jednak pewne ograniczenia: np. nie można przekazywać wskaźników (ale można dane zmiennej długości – np. napisy) • 0x200000000x3fffffff defined by the user • 0x400000000x5fffffff transient processes •0x600000000x7fffffff reserved UXP – lato 2015, Grzegorz Blinowski RPC IDL • Warstwa XDR dokonuje konwersji argumentów po stronie nadawcy i odbiorcy, tak aby zachować kompatybilność typów (marshaling), przykłady: – long Data[100] – tablica stałej długości – long Data<50> - tablica zmiennej długości (do 50 elementów long) – char string<> - napis nieograniczonej długości – wskaźnik: bool (NULL, !NULL) – opaque some_bytes[128] – dane bez typu (bez konwersji) – enum state { BUSY=1, IDLE=2, TRANSIT=3 }; – Inaczej niż w C opisywane są też unie UXP – lato 2015, Grzegorz Blinowski RPC IDL • Enumeracje: – enum colortype { RED=0, GREEN=1, BLUE=2 }; • Tablica zmiennej długości: – int height<10> int width<> - tablica nieograniczonej długości – wygenerowana zostanie struktura C: – struct { u_int height_len; int *height_val} ; – podobnie zostanie obsłużony typ string UXP – lato 2015, Grzegorz Blinowski RPC IDL • Unie mają większe znaczenie niż w C z uwagi na “jednoargumentowość” RPC: – union read_result switch {int errno) { case 0: opaque data[1024]; case 1: string strdata<1024>; default: void; }; • unie generują struktury C zawierające selektor na pierwszej pozycji: struct read_result { int errno; union { char data[1024]; ... } } UXP – lato 2015, Grzegorz Blinowski RPC – funkcje podstawowe • Utworzenie uchwytu klienta: CLIENT *clnt_create(char *host, u_long prognum, u_long versnum, char *protocol); • Parametry: – host - nazwa/IP serwera – prognum, versnum - numer programu, wersji pakietu RPC (jak zdef. w .x) – protocol - "tcp" lub "udp", uwaga - dla UDP dane mogą mieć maks. 8 KB • zwracany - uchwyt do wykorzystania w wywołaniach RPC lub NULL jesli błąd UXP – lato 2015, Grzegorz Blinowski RPC – funkcje podstawowe • Wywołanie zdalnej procedury po stronie klienta • W przypadku korzystania z rpcgen użytkownik nie musi wywoływać tej funkcji (robi to stub): enum clnt_stat clnt_call(CLIENT *clnt, unsigned long procnum, xdrproc_t inproc, char *in, xdrproc_t outproc, char *out, struct timeval tout); • Parametry: – clnt - uchwyt klienta – procnum - numer procedury – inproc - funkcja kodująca argumenty – outproc - funkcja dekodująca wyniki – in, out – argument, wynik – tout - timeout • zwracany - status wywołania UXP – lato 2015, Grzegorz Blinowski RPC – podstawowe funkcje serwera • Rejestracja usługi przez serwer: int registerrpc(unsigned long prognum, unsigned long versnum, unsigned long procnum, char *(*procname) (char *), xdrproc_t inproc, xdrproc_t outproc); • Rejestruje procedurę w portmapper, wiąże wskaźnik na funkcję z numerem programu, wersji, procedury • Parametry: – prognum, versnum, procnum - numer programu, wersji, procedury (jak zdef. w .x) – procname - rejestrowana funkcja – inproc - funkcja dekodująca argumenty – outproc - funkcja kodująca wyniki • zwracany - 0 - OK, -1 jeśli błąd UXP – lato 2015, Grzegorz Blinowski RPC – funkcje serwera • Utworzenie “transportu”: SVCXPRT *svcudp_create(int sock); SVCXPRT *svcudp_bufcreate(int sock, unsigned int sendsize, unsigned int recosize); • SVCXPRT *svctcp_create(int sock, unsigned int send_buf_size, unsigned int recv_buf_size); • Tworzy deskryptor transportu RPC dla serwera • Parametry: – sock - gniazdo lub RPC_ANYSOCK - w tym przypadku będzie utworzony port efemeryczny; jeśli gniazdo niezasocjowane - zostanie zasocjowane – size - rozmiary bufora transportowego • zwracany (xprt) - uchwyt lub NULL jeśli błąd – xprt->xp_sock - utworzone gniazdo – xprt->xp_port - przydzielony port UXP – lato 2015, Grzegorz Blinowski RPC – funkcje serwera • wywołanie serwera: void svc_run(void); • Ostatni krok uruchomienia serwera • Wywoływane na koniec w server stub • Funkcja nie wraca, oczekuje w pętli na nadchodzące wywołania sieciowe • Wewnętrznie – funkcja zawiesza się na select() lub poll() UXP – lato 2015, Grzegorz Blinowski RPC – niskopoziomowe funkcje serwera • Rejestracja przez serwer: bool_t svc_register(SVCXPRT *xprt, unsigned long prognum, unsigned long versnum, void (*dispatch) (svc_req *, SVCXPRT *), unsigned long protocol); • Rejestracja dispatchera, disp. sam musi zidentyfikować i wywołać odpowiednie lokalne funkcje RPC • Parametry: – xprt - uchwyt do serwisu (zwracany przez svc_udp{tcp}_create() – prognum, versnum - numer programu, numer wersji – dispatch - funkcja dispatchera – protocol - IPPROTO_UDP, IPPROTO_TCP, jeśli 0 nie rejestruje w portmap • zwracany - 0 - błąd, 1 OK UXP – lato 2015, Grzegorz Blinowski RPC – portmaper - rpcbind • Demon portmapper / rpcbind informuje o mapowaniu { program, wersja, protokół} -> port • Dzięki temu wiązanie do portów RPC może być dynamiczne • Program rpcinfo odpytuje rpcbind na wskazanym hoście: • Rpcbind zawsze działa na porcie 111 bash-3.00$ rpcinfo -p fox program vers proto port 100000 2 tcp 111 portmapper 100000 2 udp 111 portmapper 100007 2 udp 649 ypbind 100007 1 udp 649 ypbind 100007 2 tcp 652 ypbind 100007 1 tcp 652 ypbind UXP – lato 2015, Grzegorz Blinowski RPC – obsługa portmapera • Rejestracja przez serwer: bool_t pmap_set(unsigned long prognum, unsigned long versnum, unsigned int protocol, unsigned short port); bool_t pmap_unset(unsigned long prognum, unsigned long versnum); • Rejestracja / derejestracja usługi w portmap • svc_register() pmap_unset() • Parametry: wywołuje pmap_set(), wskazane wcześniejsze – prognum, versnum - numer programu, wersji – protocol - IPPROTO_UDP, IPPROTO_TCP, jeśli 0 nie rejestruje w portmap – port – port sieciowy • zwracany - 0 - błąd, 1 OK UXP – lato 2015, Grzegorz Blinowski RPC – XDR • XDR odpowiada za konwersje argumentów, w przypadku korzystania z rpcgen odbywa się to transparentnie dla użytkownika • makra: svc_getargs(SVCXPRT *xprt, xdrproc_t inproc, char *in); svc_freeargs(SVCXPRT *xprt, xdrproc_t inproc, char *in); • dekoduje argumenty wywołania danego przez uchwyt xprt • Parametry: – in – adres bufora na argumenty – inproc – funkcja konwersji • Makro freeargs zwalnia struktury danych argumentów UXP – lato 2015, Grzegorz Blinowski RPC – XDR • XDR bazuje na strumieniu danych typu XDR* • Te same funkcje są używane do serializacji i deserializacji danych różnych typów • Uwaga: strumień XDR jest sformatowany, tj. przechowuje informację o rekordach i separatory rekordów bool_t bool_t bool_t bool_t bool_t bool_t bool_t bool_t xdr_bool(XDR *xdrs, bool_t *bp); xdr_char(XDR *xdrs, char *cp); xdr_double(XDR *xdrs, double *dp); xdr_float(XDR *xdrs, float *fp); xdr_enum(XDR *xdrs, enum_t *ep); xdr_int(XDR *xdrs, int *ip); xdr_long(XDR *xdrs, long *lp); xdr_opaque(XDR *xdrs, char *cp, unsigned int cnt); bool_t xdr_string(XDR *xdrs, char **sp, unsigned int maxsize); bool_t xdr_array(XDR *xdrs, char **arrp, unsigned int *sizep, unsigned int maxsize, unsigned int elsize, xdrproc_t elproc); • konwersja tablicy o rozmiarze wskazanym przez sizep, maks. rozm. maxsize, rozmiar elementu elsize, element konwertuje funkcja elproc UXP – lato 2015, Grzegorz Blinowski RPC – XDR • Typ void też musi być jawnie obsłużony bool_t xdr_void(void); • Typ opaque używany jest do uchwytów interpretowanych tylko po stronie serwera: bool_t xdr_opaque(XDR *xdrs, char *cp, unsigned int cnt); • reference i pointer - konwersja typów wskazywanych przez wskaźniki wewn. struktur, pointer dodatkowo obsługuje NULL – przydatne w drzewach i listach bool_t xdr_pointer(XDR *xdrs, char **objpp, unsigned int objsize, xdrproc_t xdrobj); bool_t xdr_reference(XDR *xdrs, char **pp, unsigned int size, xdrproc_t proc); • inne funkcje służące do konwersji typów złożonych: bool_t xdr_union(XDR *xdrs, int *dscmp, char *unp, struct xdr_discrim *choices, xdrproc_t defaultarm); bool_t xdr_vector(XDR *xdrs, char *arrp, unsigned int size, unsigned int elsize, xdrproc_t elproc); UXP – lato 2015, Grzegorz Blinowski RPC – XDR • Konwersja danych w XDR nie zależy od kierunku • Kierunek konwersji określony jest w strumieniu i ustalany automatycznie (np. poprzez niskopoziomowe funkcje biblioteki RPC) • jak to działa: void xdrrec_create(XDR *xdrs, unsigned int sendsize, unsigned int recvsize, char *handle, int (*readit) (char *, char *, int), int (*writeit) (char *, char *, int)); • strumień posiada dwie funkcje: readit (czytająca) i writeit (pisząca) podobne do read i write • strumień może być związany z obszarem pamięci lub plikiem • strumień posiada też aktualnie określony kierunek konwersji: xdrs->x_op (XDR_ENCODE | XDR_DECODE | XDR_FREE) UXP – lato 2015, Grzegorz Blinowski RPC – funkcje pomocnicze • Ustawianie parametrów komunikacji RPC: bool_t clnt_control(CLIENT *cl, int req, char *info); • Parametry: – cl - uchwyt – req, info - stała określająca parametr, dodatkowe dane • CLSET_TIMEOUT struct timeval • CLGET_TIMEOUT struct timeval • CLSET_RETRY_TIMEOUT struct timeval - tylko UDP • CLGET_RETRY_TIMEOUT struct timeval - tylko UDP UXP – lato 2015, Grzegorz Blinowski RPC – funkcje zaawansowane • Rozgłaszanie RPC: enum clnt_stat clnt_broadcast(unsigned long prognum, unsigned long versnum, unsigned long procnum, xdrproc_t inproc, char *in, xdrproc_t outproc, char *out, resultproc_t eachresult); • Wywoluje procedurę poprzez rozgłoszenie (UDP), odpowiedzi może być więcej niz 1. Maks. rozmiar ~1500B! • stosowane przy lokalizacji zasobów • Parametry: – jak clnt_call – z każdym otrzymanym wynikiem wywolywana jest funkcja eachresult(), jesli zwróci ona <>0 nie czekamy juz na więcej odpowiedzi: int eachresult(char *out, struct sockaddr_in *addr); UXP – lato 2015, Grzegorz Blinowski RPC – przykład, IDL const MAX = 1024; typedef char FileName<50>; typedef int Offset; typedef char Data[MAX]; • fread.x • Nieudolny klient "NFS" • jedna operacja - odczyt, • za każdym razem przekazujemy nazwę pliku, pozycję i liczbę bajtów • Argumenty musimy zawrzeć w strukturze • Funkcja zwraca bufor z danymi struct readargs { FileName fn; Offset offs; int nbytes; }; program FREAD { version VERSION { Data myread(readargs)=1; }=2; } = 0x3a3afeeb; UXP – lato 2015, Grzegorz Blinowski RPC – wygenerowany fread.h #ifndef _FREAD_H_RPCGEN typedef struct readargs readargs; #define _FREAD_H_RPCGEN #include <rpc/rpc.h> #define FREAD 0x3a3afeeb #define MAX 1024 #define VERSION 2 typedef struct { #define myread 1 u_int FileName_len; char *FileName_val; } FileName; typedef int Offset; typedef char Data[MAX]; struct readargs { FileName fn; Offset offs; int nbytes; }; extern char * myread_2(readargs *, CLIENT *); extern char * myread_2_svc(readargs *, struct svc_req *); extern int fread_2_freeresult (SVCXPRT *, xdrproc_t, caddr_t); UXP – lato 2015, Grzegorz Blinowski RPC – wygenerowany fread_clnt.c /* * Please do not edit this file. * It was generated using rpcgen. */ #include <memory.h> #include "fread.h" /* Default timeout can be changed using clnt_control() */ static struct timeval TIMEOUT = { 25, 0 }; char * myread_2(readargs *argp, CLIENT *clnt) { static Data clnt_res; memset((char *)clnt_res, 0, sizeof(clnt_res)); if (clnt_call (clnt, myread, (xdrproc_t) xdr_readargs, (caddr_t) argp, (xdrproc_t) xdr_Data, (caddr_t) clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (clnt_res); } UXP – lato 2015, Grzegorz Blinowski RPC – wygenerowany fread_svc.c /* * Please do not edit this file. * It was generated using rpcgen. */ #include "fread.h" #include <stdio.h> #include <stdlib.h> #include <rpc/pmap_clnt.h> #include <string.h> #include <memory.h> #include <sys/socket.h> #include <netinet/in.h> static void fread_2(struct svc_req *rqstp, SVCXPRT *transp) { union { readargs myread_2_arg; } argument; char *result; xdrproc_t _xdr_argument, _xdr_result; char *(*local)(char *, struct svc_req *); UXP – lato 2015, Grzegorz Blinowski RPC – wygenerowany fread_svc.c switch (rqstp->rq_proc) { case NULLPROC: svc_sendreply (transp, (xdrproc_t) xdr_void, NULL); return; case myread: _xdr_argument = (xdrproc_t) xdr_readargs; _xdr_result = (xdrproc_t) xdr_Data; local = (char *(*)(char *, struct svc_req *)) myread_2_svc; break; default: svcerr_noproc (transp); return; } memset ((char *)&argument, 0, sizeof (argument)); if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { svcerr_decode (transp); return; } UXP – lato 2015, Grzegorz Blinowski RPC – wygenerowany fread_svc.c result = (*local)((char *)&argument, rqstp); if (result != NULL && !svc_sendreply(transp, (xdrproc_t) _xdr_result, result)) { svcerr_systemerr (transp); } if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { fprintf (stderr, "%s", "unable to free arguments"); exit (1); } return; } UXP – lato 2015, Grzegorz Blinowski RPC – wygenerowany fread_svc.c int main (int argc, char **argv) { SVCXPRT *transp; pmap_unset (FREAD, VERSION); // portmapper transp = svcudp_create(RPC_ANYSOCK); if (transp == NULL) { fprintf (stderr, "%s", "cannot create svcudp."); exit(1); } if (!svc_register(transp, FREAD, VERSION, fread_2, IPPROTO_UDP)) { fprintf (stderr, "%s", "unable to register (FREAD, VERSION, udp)."); exit(1); } transp = svctcp_create(RPC_ANYSOCK, 0, 0); if (transp == NULL) { fprintf (stderr, "%s", "cannot create svctcp."); exit(1); UXP – lato 2015, Grzegorz Blinowski RPC – wygenerowany fread_svc.c if (!svc_register(transp, FREAD, VERSION, fread_2, IPPROTO_TCP)) { fprintf (stderr, "%s", "unable to register (FREAD, VERSION, tcp)."); exit(1); } svc_run (); fprintf (stderr, "%s", "svc_run returned"); exit (1); /* NOTREACHED */ } UXP – lato 2015, Grzegorz Blinowski RPC – wygenerowany fread_xdr.c /* * Please do not edit this file. * It was generated using rpcgen. */ #include "fread.h" bool_t xdr_FileName (XDR *xdrs, FileName *objp) { register int32_t *buf; } if (!xdr_array (xdrs, (char **)&objp->FileName_val, (u_int *) &objp->FileName_len, 50, sizeof (char), (xdrproc_t) xdr_char)) return FALSE; return TRUE; bool_t xdr_Offset (XDR *xdrs, Offset *objp) { register int32_t *buf; if (!xdr_int (xdrs, objp)) return FALSE; return TRUE; } UXP – lato 2015, Grzegorz Blinowski RPC – wygenerowany fread_xdrc.c bool_t xdr_Data (XDR *xdrs, Data objp) { register int32_t *buf; } if (!xdr_vector (xdrs, (char *)objp, MAX, sizeof (char), (xdrproc_t) xdr_char)) return FALSE; return TRUE; bool_t xdr_readargs (XDR *xdrs, readargs *objp) { register int32_t *buf; } if (!xdr_FileName (xdrs, &objp->fn)) return FALSE; if (!xdr_Offset (xdrs, &objp->offs)) return FALSE; if (!xdr_int (xdrs, &objp->nbutes)) return FALSE; return TRUE; UXP – lato 2015, Grzegorz Blinowski RPC - autoryzacja • RPC może funkcjonować bez jakiejkolwiek informacji autoryzacyjnej (autoryzacja na poziomie aplikacji?): clnt = clntudp_create( ...); clnt->cl_auth = authnone_create(); • Bazowa autoryzacja w RPC przekazuje informacje o UID, GID i nie jest kryptograficznie chroniona: AUTH *authunix_create(char *host, int uid, int gid, int len, int *aup_gids); clnt->cl_auth = authunix_create_default(); • Po stronie serwera w strukturze svc_req jest przekazywane pole rq_cred zawierające informacje autoryzacyjne przekazane prez klienta, które serwer musi samodzielnie zinterpretować: struct opaque_auth { enum_t sa_flavor; // AUTH_NULL, AUTH_UNIX, ... caddr_t sa_base; u_int sa_length; }; UXP – lato 2015, Grzegorz Blinowski RPC - autortyzacja • Biblioteka rpc_secure pozwala na szyfrowanie komunikatów kluczem serwera i kluczem sesji, używany jest alg. symetryczny DES(!) AUTH *authdes_create(char *name, unsigned window, struct sockaddr *addr, des_block *ckey); – Użyta przez klienta zwraca uchwyt autoryzacyjny – Parametry: name – nazwa serwera lub użytkownika, window – czas ważnosci klucza, adres – synchronizacja czasu, ckey<-NULL lub podany – Serwis keyserv generuje / dystrybuje klucze sesji bazujace na informacji przekazanej przez klienta • RFC 2695 rozszerza RPC o możliwość autoryzacji przy pomocy alg. DH oraz Kerberos, stosowane są klucze publiczne i prywatne, wykorzystane w NIS+ oraz secure NFS