UXP1A Unix Programowanie i Architektura lato 2018 Grzegorz Blinowski Instytut Informatyki Politechniki Warszawskiej
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ń
Regulamin itp. Inne Wolne czwartki: 3/04; 31.05 Kolokiwum I po omówieniu mechanizmów IPC (~ 5.04) Kolokiwum II po omówieniu całości materiału (~ 7.06) Oceny, wpisy, poprawa 14 lub 15.06 Projekt: start prawdopodobnie 5.04, zaliczenie do 07.06 Wymagania Systemy operacyjne Dobra znajomość języka C Materiały dostępne na stronie http://www.ii.pw.edu.pl/~gjb Kontakt: g.blinowski@ii.pw.edu.pl, tel PW: 222347184; konsultacje wt. 10:00-12:00
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
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
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
PDP-7, PDP-11 Architektura: 18 bit RAM: 4 Kw - słów 18b (9KB) RAM maks: 64 Kw (144 KB) Cykl zegara 1.75 us (0,571 MHz) Peryferia: klawiatura/drukarka, taśma papierowa, taśma magentyczna Koszt: ok 72 K USD
Historia c.d.
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)
(Wikimedia Commons)
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
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
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
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
(Wikimedia Commons)
Cechy Przenośność - źródła C + znikomy % 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, 48b adresu w modelu 64b) Wirtualny i rozproszony system plików Wirtualny - w jednym drzewie wiele typów systemów plików Rozproszony - obejmuje wiele maszyn w sieci
System procesów
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.
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()
Diagram stanów procesu (stary)
Diagram stanów procesu - Linux TASK_RUNNING w trakcie wykonania lub gotowy INTERRUPTIBLE lub UNINTERRUPTIBLE odp. SSLEEP TASK_STOPPED (nie pokazany SIGTSTP i inne)
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)
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()
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
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
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
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)
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
API #include <sys/types.h> #include <unistd.h> pid_t getpid(void); pid_t getppid(void);
Mapa pamięci procesu
BSS a Data int a; int b_data=1; int c_bss=0; int foo(void) { static foo_a_static; static foo_c_static=1; int foo_dynamic; } return 0; $ nm bss_data.o 00000004 00000000 00000000 00000000 00000004 00000004 C D B T b d a b_data c_bss foo foo_a_static.0 foo_c_static.2 D/d data (global/local) zainicjowane B/b BSS (global/local) niezainicjowane, bez jawnej alokacji T- Text (kod) C Common trafi do BSS, różnica tylko na poziomie plików.o (common wiele wystąpień tej samej niezainicjowanej zmiennej w plikach.o tj. przed konsolidacją (kwestie kompatybliności wstecznej))
Mapa pamięci procesu 2^32 lub 2^40-44 (1-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
Adresowanie 32/64b Model ILP : Int(eger) Long Pointer Klasyczny model: ILP32, 4 GB wirtualnie Przejście na 64b związane nie tylko z systemem ale i z systemem typów w C: LP64: 4/8/8 ILP64: 8/8/8 LLP64: 4/4/8 ILP64 problem z kompatybilnością wstecz LP64: najlepszy kompromis, standard LLP64: bez problemów z kompatybilnością Zob: http://www.unix.org/whitepapers/64bit.html
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 */
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:
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 */ };
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
/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
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);
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
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
fork(), wait(), exec()
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)
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
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
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
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
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
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
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!
Ś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:~$
Ś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
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
Sygnały Przyczyny wystąpienia sygnałów: Sam proces, wyjątek 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
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
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)
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
Funkcja obsługi sygnału int (*signal(int sig, void (*func)(int)))(int); lub: void (*signal(int sig, void(*function)(int)))(int); Albo: typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); 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ą) Posix mówi używać tylko do ustawienia: SIG_IGN i SIG_DFL
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
Maski sygnałów - 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
Oczekiwanie na sygnał int pause(void); int sigsuspend(const sigset_t *mask); int sigpending( sigset_t *set ); pause() - bezterminowe oczekiwanie na (jakiś) sygnał sigsuspend() - atomowo zawiesza się i zmienia maskę; maska ulega zmianie tylko do momentu wystąpienia i obsłużenia sygnału sigpending() - Zwraca aktualnie wstrzymane sygnały
Sygnały niezawodne - przykład sig_handler() {flag=1;}... Sig_handler() {flag=1;}... for (;;) { for (;;) { sigprocmask( ); while (flag == 0) while (flag == 0)sygnał pause(); }... sigsuspend(mask); }
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, SIGBUS, etc. będą miały wyższy priorytet
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()
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
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
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
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
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
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...
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).
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
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() }
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.
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ł)
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
start systemu, init
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
Runlevel Zmienna kernela określająca rodzaj pracy systemu Wprowadzona w SYSV, dostępna 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)
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)
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
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
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
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
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
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);
System plików
System plików - podstawy Drzewo plików Korzeń drzewa: File system root, katalog aktualny current dir Korzeń systemowy, korzeń dla procesu (zwykle takie same) int chroot(const char *path); Typy plików: zwykłe, katalogi (d), sterowniki znakowe (c) i blokowe (b), FIFO (p), sym-link (l), socket (s), inne Nazwa (nazwa nie jest atrybutem): znaki dozwolone wszystkie prócz \0 i / Niezalecane znaki wzorce shella, spacja Atrybuty plików: właściciel, grupa Typ / prawa dostępu: owner (user), group, other; setuid, setgid, sticky Daty-czasy: dostępu, modyfikacji, modyfikacji metryczki
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
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
Prawa dostępu pliki/katalogi 3 triady: właściciel, grupa, pozostali; znaczenie bitów rwx: r: plik - czytanie; katalog - dostęp do nazw plików w: plik pisanie; katalog tworzenie, usuwanie, zmiana nazwy pliku x: plik wykonanie (pod warunkiem, że właściwy format dla exec()); katalog dostęp do danych i meta-danych pliku (ale nie nazwy!), także dozwolona operacja cd Uwaga: prawa nie są dziedziczone katalog katalog, ani katalog plik (zob. jednak g+x); brak danego prawa oznacza odmowę dostępu; dla katalogu rzadko nie łączymy rx
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
Prawa do katalogów gjb@skinner:~/test$ ls -lr total 4 drwxr-xr-x 3 gjb users 4096 2015-11-05 13:18 a/./a: total 8 drwxr-xr-x 2 gjb users 4096 2015-11-05 13:18 b/ -rw-r--r-- 1 gjb users 1 2015-11-05 13:18 p.txt./a/b: total 0 gjb@skinner:~/test$ chmod ogu-x a gjb@skinner:~/test$ ls -lr total 4 drw-r--r-- 3 gjb users 4096 2015-11-05 13:18 a/ /bin/ls: cannot access./a/b: Permission denied /bin/ls: cannot access./a/p.txt: Permission denied./a: total 0 d?????????????? b/ -?????????????? p.txt /bin/ls: cannot open directory./a/b: Permission denied
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
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
API systemu plików chmod, chown, close, creat, dup, dup2, fcntl, link, lseek, open, read, stat, umask, write, unlink, stat(5),
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ługość 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)
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 */
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 */
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)
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)
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)
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
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*/
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
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
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...
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);
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)
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
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...
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...
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)
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 aktualną pozycję, przewija do wskazanej pozycji
Prosty ls #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; }
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