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 – POLLMSG jest M_SIG lub M_PCSIG z sygnałem SIGPOLL 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 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 */