close

Transkrypt

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

Podobne dokumenty