Copia di https://etherpad.wikimedia.org/p/so.cs.unibo.it (25 febbraio 2019)
Jump to navigation
Jump to search
Sistemi Operativi 2018/19
Esercizi?
Shell Scripting?
---------------------------------
Lezione di venerdì 14 Dicembre 2018
#define MAXLEN ....
bbsend(dst, msg)
static len=0
asend(getpid(), TAG)
while ((msgx = arecv(NULL)) != TAG)
if (msgx == ACK) len--;
while (len >= MAXLEN):
msgx = arecv(NULL);
if (msgx == ACK) len--;
len++;
asend(dst, msg)
bbsend(dst, msg)
static len=0
while (len >= MAXLEN):
arecv(dst);
len--;
len++;
asend(dst, msg)
bbrecv(sender)
msg=arecv(sender)
asend(sender, ACK)
return msg
philo: process[i, i=0,...,4]
asend(stick[i], (i, GET))
arecv(stick[i])
asend(stick[(i+1) % 5, (i,GET))
arecv(stick[i+1])
eat
asend(stick[i], (i, PUT))
asend(stick[(i+1) % 5, (i, PUT))
stick: process[i, i=0,...,4]
status == IDLE
queue = [] //coda dei processi in attesa di una risorsa
while True:
index, msg = recv(NULL)
if (msg == PUT)
status = IDLE
else
queue.enqueue(index)
if (status == IDLE and queue.len > 0)
status = BUSY
index = queue.dequeue()
asend(index, ACK)
//crea deadlock
---------------------------------
Lezione di giovedì 13 Dicembre 2018
Nel modello a memoria privata i vari processi vivono in due mondi separati e comunicano tra di loro inviandosi messaggi.
Primitive:
- send, permette di inviare dati ad un determinato destinatario;
- receive, permette di ricevere dati da un determinato mittente, oppure da tutti i mittenti possibili.
Modalità di message passing:
- sincrona, entrambe le primitive solo bloccanti (es. chiamata telefonica);
void ssend(pid_t dst, msg_t msg);
msg_t sreceive(pid_t sender);
- asincrona, la send non è bloccante, la receive si, quindi la send manda quando vuole mentre la receive attende finchè non ci sono dati (es. posta elettronica);
void asend(pid_t dst, msg_t msg);
msg_t arecv(pid_t sender);
- completamente asincrona, entrambe le primitive sono non bloccanti, la send manda quando vuole, la receive se non ci sono messaggi non rimane in attesa.
void nbsend(pid_t dst, msg_t msg);
msg_t nbrecv(pid_t sender);
Da asincrono a sincrono:
- problema: ho le primitive asend ed arecv, devo realizzare ssend e srecv;
- soluzione: implemento un sistema di acknowledgment, nel quale il destinatario avverte della avvenuta ricezione del pacchetto e
il mittente continua ad inviare fintanto che gli arrivano messaggi ACK.
- implementazione:
ssend(dst, msg):
asend(dst, (getpid(), msg)) //getpid() indica il processo mittente
arecv(dst)
srecv(sender):
(real_sender, msg) = arecv(sender)
asend(real_sender, 'ACK')
return msg
- side effects: se entrambi i processi invocano la ssend contemporaneamente si genera deadlock.
Da sincrono ad asincrono:
- problema: ho le primitive ssend ed srecv, devo realizzare asend e arecv;
- soluzione: aggiungo un daemon (demone in UNIX, ovvero un processo che attenda e risponda a determinati eventi) lato server che mi faccia da tramite e
che memorizzi in un buffer i dati che dal mittente devono arrivare al destinatario.
- implementazione:
asend(dst, msg):
ssend(server, (getpid(), MSG, dst, msg)) //il tag MSG indica al server che questo è un messaggio contenente dati
arecv(sender):
ssend(server, (getpid(), OK2RECV, sender, NULL)) //il tag OK2RECV indica al server che il destinatario è pronto a ricevere
msg=srecv(server)
return msg
daemon():
waiting4[] = {NONE,....,NONE}
while True:
(s, tag, proc, msg) = srecv(NULL)
switch tag:
case MSG: buf[proc].add(msg)
wproc = waiting4[proc]
if wproc != NONE && (msg=buf[proc].get(wproc))!=NULL
ssend(proc, msg)
waiting4[proc] = NONE
case OK2RECV:
if ((msg=buf[s].get(proc))!=NULL)
ssend(s, msg)
else
waiting4[s] = proc
- side effects: non hanno lo stesso potere espressivo in quanto devo aggiungere un processo e non una libreria.
---------------------------------
Lezione di martedì 11 Dicembre 2018
// http://www.cs.unibo.it/~renzo/so/compiti/2013.09.12.tot.pdf
process utente[i] {
while true {
/* sporca biancheria */
lavatrice.enter()
/* lava biancheria */
lavatrice.exit()
}
}
monitor lavatrice:
condition ok2wash
int waitcount
int washing
entry enter():
if (waitcount < 2 || washing > 0)
waitcount++;
ok2wash.wait()
waitcount--;
washing++;
if (washing < 5)
ok2wash.signal()
entry exit():
washing--;
if (washing == 0 && waitcount >= 3)
ok2wash.signal()
//filosofi
philo[i]: process i=0,...,4
think
dp.starteating(i)
eat
dp.endeating(i)
dp: monitor
bool stick[5] // true se usata
condition cphilo[5] // !stick[i] && !stick[(I+1) %5]
entry starteating(i)
if(stick[i] || stick[(i+1) %5])
cphilo[i].wait()
stick[i]=stick[(i+1) %5]=True
entry endeating(i):
stick[i]=stick[(i+1) %5]=False
if (stick[(i+4) % 5) == False
cphilo[(i+4) % 5].signal()
if (stick[(i+2) % 5) == False
cphilo[(i+1) % 5].signal()
s0 p0 s1 p1 s2 p2 s3 p3 s4 p4 s0
//no deadlock (tutte e due o nessuna)
//congiura dei filosofi
dp: monitor
bool stick[5] // true se usata
cond busy[5] // stick[i] == false
entry starteating(i)
if (stick[i]) busy[i].wait()
stick[i] = True;
if (stick[(i+1) %5]) busy[(i+1) %5]).wait()
stick[(i+1) %5] = True
entry endeating(i):
stick[i]=stick[(i+1) %5] = False
busy[i].signal()
abusy[(i+1) %5].signal()
//non crea deadlock per la mutua esclusione dei monitor
semaphore mutex //implementiamo i monitor con i semafori
stack urgent
queue condq[i]
enter():
mutex.P()
private __exit():
if ((s = urgent.pop()) != NULL)
s.V()
else
mutex.V()
exit():
__exit()
wait(i):
s = new semaphore(0)
condq[i].enqueue(s)
__exit()
s.P()
signal(i):
if ((s = condq[i].dequeue()) != NULL)
us = new semaphore(0)
urgent.push(us)
s.V();
us.P();
x=1 y=1 z=1
sem sp(0), sq(0),sr(0),ss(1);
Process P:
while(1):
sp.P()
x = x+1;
sq.V()
Process Q:
while(1):
sq.P()
x = x+1;
sr.V();
Process R:
while(1):
sr.P()
y = y+x;
ss.V()
Process S:
while(1):
ss.P()
if (z==y):
print(z)
z=0;
sp.V()
else:
z=z+1;
ss.V()
---------------------------------
Lezione di giovedì 6 Dicembre 2018
monitor semaphore: //implementiamo i semafori con un monitor
int value
condition C // value > 0
entry P():
if (value == 0) C.wait()
value--
entry V():
value++
C.signal()
semaphore(X)
value=X
monitor RW: //lettori e scrittori
int nr;
int nw;
int ww; //waiting writers
condition ok2read // nw == 0
condition ok2write // nr == 0 && nw == 0
entry beginr:
if (nw != 0 || ww > 0) ok2read.wait()
nr++
ok2read.signal()
entry endr:
nr--
if (nr == 0) ok2write.signal()
entry beginw:
if (nr != 0 || nw != 0) {ww++; ok2write.wait(); ww--}
nw++
entry endw:
nw--
ok2read.signal()
if (nr == 0) ok2write.signal()
----- versione precedente
monitor RW: //lettori e scrittori
int nr;
int nw;
condition ok2read // nw == 0
condition ok2write // nr == 0 && nw == 0
entry beginr:
if (nw != 0) ok2read.wait()
nr++
ok2read.signal() //attivazione in cascata dei lettori
entry endr:
nr--
if (nr == 0) ok2write.signal()
entry beginw:
if (nr != 0 || nw != 0) { ok2write.wait() }
nw++
entry endw:
nw--
ok2read.signal()
if (nr == 0) ok2write.signal()
//questo ^ ha starvation ^
buffer limitato (BB => Bounded Buffer)
process producer()
while True:
x = produce
bb.put(x)
process consumer()
while True:
x = bb.get()
consume(x)
Monitor bb:
condition ok2put // len(buffer) < SIZE
condition ok2get // len(buffer) > 0
queue buffer;
entry put(int x):
if (! (len(buffer) < SIZE)) ok2put.wait()
buffer.enqueue(x)
if (len(buffer) < SIZE) ok2put.signal()
if (len(buffer) > 0) ok2get.signal()
entry get():
if (! len(buffer) > 0) ok2get.wait()
rv = buffer.dequeue()
if (len(buffer) < SIZE) ok2put.signal()
if (len(buffer) > 0) ok2get.signal()
return rv
Ottimizzato:
Monitor bb:
condition ok2put // len(buffer) < SIZE
condition ok2get // len(buffer) > 0
queue buffer;
entry put(int x):
if (! (len(buffer) < SIZE)) ok2put.wait()
buffer.enqueue(x)
ok2get.signal()
entry get():
if (! len(buffer) > 0) ok2get.wait()
rv = buffer.dequeue()
ok2put.signal()
return rv
class Monitor
{
//Constructor
//Properties
int SIZE;
Queue SharedBuffer; //zona di memoria condivisa
Stack UrgentStack;
//Procedure entries (funzioni visibili all'esterno)
entry F1(...);
//Condition variables (espressione booleana sullo stato del sistema)
class Condition
{
Wait(); //blocca l'esecuzione di un processo, mettendo il processo nella coda della condizione
Signal(); //risveglia l'esecuzione del primo processo bloccato, mettendo il processo corrente nello urgentStack e togliendo dalla coda della condizione il primo processo in attesa
}
Condition C1;
Condition C2;
...
}
beginr():
mutex.P()
if (!nw == 0 || !wq.empty())
//non entra nemmeno se uno scrittore è in attesa, prima o poi il numero di lettori diventerà nullo
s = new(sem(0))
rq.enqueue(s)
mutex.V()
s.P()
free(s)
}
nr++
if (!rq.empty())
s = dequeue(rq)
s.V()
else
mutex.V()
}
endr():
mutex.P()
nr--
if (nr==0 && !wq.empty())
s = dequeue(wq)
s.V()
else
mutex.V()
beginw():
mutex.P()
if (!(nr == 0 && nw == 0))
s = new(sem(0))
wq.enqueue(s)
mutex.V()
s.P()
free(s)
}
wlast = true
nw++
mutex.V()
endw():
mutex.P()
nw--
if (!rq.empty())
s = dequeue(rq)
s.V()
else if (!wq.empty())
s = dequeue(wq)
s.V()
else
mutex.V()
----prev
beginr():
mutex.P()
if (!nw == 0)
s = new(sem(0))
rq.enqueue(s)
mutex.V()
s.P()
free(s)
}
nr++
nuovostato()
}
endr():
mutex.P()
nr--
nuovostato()
beginw():
mutex.P()
if (!(nr == 0 && nw == 0))
s = new(sem(0))
wq.enqueue(s)
mutex.V()
s.P()
free(s)
}
wlast = true
nw++
nuovostato()
endw():
mutex.P()
nw--
nuovostato()
nuovostato():
if (nw == 0 && !rq.empty())
s = dequeue(rq)
s.V()
else if (nw==0 && nr==0 && !wq.empty())
s = dequeue(wq)
s.V()
else
mutex.V()
---------------------------------
Lezione di giovedì 29 Novembre 2018
beginr():
mutex.P()
if (!nw == 0) {
s = new(sem(0))
rq.enqueue(s)
mutex.V()
s.P()
free(s)
}
nr++
nuovostato()
}
endr():
mutex.P()
nr--
nuovostato()
beginw():
mutex.P()
if (!(nr == 0 && nw == 0))
s = new(sem(0))
wq.enqueue(s)
mutex.V()
s.P()
free(s)
}
nw++
nuovostato()
endw():
mutex.P()
nw--
nuovostato()
nuovostato():
if (nw == 0 && ! rq.empty())
s = dequeue(rq)
s.V()
else if (nw==0 && nr==0 && !wq.empty())
s = dequeue(wq)
s.V()
else
mutex.V()
(Semafori, prod/cons, Buffer Limitato, Sem Binari).
(Cena Filosofi, RW)
---------------------------------
Lezione di martedì 27 Novembre 2018
nP: numero di operazioni P completate
nV: numero di operazioni V completate
nP <= init + nV
init + nV - nP >= 0
init + nV - nP valore del semaforo
Il valore del semaforo è interno quindi non esiste una primitiva per leggerlo
class semaphore:
int value;
queue_of_proc Q
semaphore(x): valure=x
P(): csenter()
if valuee >0:
value--;
else{
this.process=ready.dequeue( );
self.Q.enqueue(thisprocess);
}
csexit()
V():
csenter()
if (len(Q) > 0){
p = self.Q.dequeue()
ready.enqueue(p)
}else{
value++;
}
csexit()
Problema Produttore-Consumatore con i semafori
buffer buf
semaphore empty(BUFSIZE)
semaphore full(0)
semaphore mutex(1) // serve per mutua esclusione
producer: process
int x
while(1) {
x=produce();
empty.P();
mutex.P();
buf.enqueue(x);
mutex.V();
full.V();
}
consumer: process
while(1){
full.P();
x=buf.dequeue();
consume(x);
empty.V()
consume(x);
}
Cena filosofi
Soluzione problema cena dei filosofi con semaforo
semaphore chopstick[5]=(1,1,1,1,1)
philo: process(i) i=0...4
think()
eat(i)
eat(i) {
chopstick[MIN(i,(i+1)%5)].P()
chopstick[MAX(i,(i+1)%5)].P()
...eat...
chopstick[MAX(i,(i+1)%5)].V()
chopstick[MIN(i,(i+1)%5)].V()
}
---------------------------------
Lezione di giovedì 22 Novembre 2018
Paradigma:
- Definizione di strutture, costrutti, elementi del linguaggio ai quali associamo un significato
- Necessario dimostrare necessità
- Come implementare in sistemi reali
- Semafori
- Concetto inventato da Djikstra 62/63
- Permette la gestione di risorse condivise tra processi
- Un semaforo è un intero protetto e condiviso tra i processi che facilita e limita l'accesso a risorse condivise in un ambiente con più processi attivi
- Numero contenuto nel semaforo rappresenta il numero di risorse di un certo tipo disponibili ai processi.
- Caso particolare, semaforo binario. Unici valori possibili 0/1 (Mutex)
- Operazioni sui semafori:
- V, verhogen(aumentare):
Semaforo incrementato. Se ci sono processi in coda, uno dei task in coda viene tolto dalla coda e posto in stato di ready
- P, proberen (verificare):
Semaforo decrementato. Se il semaforo ha un valore negativo, il processo viene sospeso e accodato, in attesa di essere riattivato
Invariante del semaforo:
init <- valore iniziale
nP <- numero di operazioni di P completate
nV <- numero di operazioni V completate
Problema produttore-consumatore
producer e consumer condividono un buffer comune. Compito del produttore è generare dati e depositarli nel buffer in continuo. Contemporaneamente, il consumatore utilizzerà i dati prodotti, rimuovendoli di volta in volta dal buffer.
Dobbiamo assicurarci che il produttore non elabori nuovi dati, se il buffer è pieno, e che il consumatore non cerchi dati, se il buffer è vuoto.
semaphore full(0);
semaphore empty(1);
share int global;
process producer:
int i;
while true:
x = produce()
empty.P();
global = x;
full.V();
process consumer;
int x;
while true:
full.P()
x = global;
empty.V();
consume(x);
-----------------------------------
Lezione di martedì 20 Novembre 2018
Python:
#formattazione dell'output
nome = "Renzo"
eta = 25
- .format(): "{0} è un giovanotto di {1} anni.".format(nome, eta) (disponibile dalla versione >= 2.6);
- f-string: f"{nome} è un giovanotto di {eta} anni." (disponibile dalla versione >= 3.6).
C:
- _Atomic: keyword che impone al compilatore di trattare l'oggetto denotato (e le operazioni su di esso) in maniera atomica;
- volatile: keyword che impone al compilatore di trattare l'oggetto denotato come una vera a propria cella di memoria, senza nessuna "ottimizzazone" (es. uso di registri ausiliari) su di essa.
concorrenza: parallelismo apparente o reale (è indifferente per la trattazione teorica)
starting balance = 100
A: ld balance
A: add 10
B: ld balance
B: sub 10
B: st balance
A: st balance
Durante la sua vita un processo può essere in uno dei seguenti stati:
- running se in esecuzione;
- waiting/blocked se in attesa di un I/O o risorsa;
- ready se ci sono tutte le condizioni per eseguirlo, ma in attesa di essere eseguito.
Evoluzione della vita di un processo
->running
- running->ready se "perde il turno" (preemption)
- ready->running se scelto dallo scheduler
- running->blocked se fa operazione bloccante
- blocked->ready se la condizione attesa si avvera....
Esempio di programmazione concorrente in pseudolinguaggio
Process prova[i]: i=0..2
print('ciao %d', i)
Process caio:
codice caio
Process sempronio:
codice sempronio
cobegin
.... codice processo 1
//
.... codice processo 2
coend
..dopo
Modelli della memoria:
- privata: se più attori utilizzano aree di memoria diverse e per scambiarsi informazioni utilizzano dei "messaggi" -> message passing (processi);
- condivisa: se più attori utilizzano la stessa memoria in maniera concorrente -> sincronizzazione (threading).
Casi d'uso della programmazione concorrente:
- un singolo programma strutturato in più processi, ognuno con un proprio task;
- programmi distinti collaborano tramite strutture dati (file condivisi)
- programmi distinti condividono lo stesso ambiente di lavoro.
Proprietà di un programma sequenziale/imperativo:
- liveness: arrivare;
- safety: al risultato.
Proprietà di un programma concorrente/sincrono:
- liveness: something eventually happens;
- safety: nothing bad happens.
Safety:
- Se un processo decide, decide uno dei valori proposti
- Se due processi decidono, decidono lo stesso valore
Liveness:
- ogni processo corretto prima o poi decide
Nella programmazione concorrente:
- Safety:
- NO RACE CONDITION
- NO DEADLOCK
- Liveness:
- Terminazione
- NO STARVATION
P: get(A) get(B) leave(A) leave(B)
Q: get(B) get(A) leave(A) leave(B)
H1: while(1) {get(A) leave(A)} // hi priority
H2: while(1) {get(A) leave(A)} // hi priority
L: while(1) {get(A) leave(A)} // low priority
Transazione atomica:
una istruzione indivisibile di cui l'hardware può sempre garantirci l'esecuzione.
Singola operazione di macchina. L'atomicità di una operazione dipende dal tipo di hardware
es.Macchina RISC,tutte le istruzione hanno la stessa lunghezza
-x=0 o x=100 operazioni atomiche (corrispondo allo store di 0 e 100)
-x=40000000 è un'operazione troppo lunga per una singola istruzione e va eseguita in più parti, perdendo così di atomicità
Necessità di creare atomicità dove non c'è, definizione di sezione critica
PER NOI:
assegnamento di costante: ATOMICO
aggiornamento di variabile (incremento, valutazione di espressioni) NO.
Sezioni critiche:
metodo per rendere atomiche cose che non lo sono pendendo un blocco di comandi e rendendolo atomico
Nella nostra sintassi <...> rendono il blocco atomico.
Aggiunta al conto:
< load balance, sum 10, store balance >
csenter() ... csexit()
Rimozione dal conto:
< load balance, sub 10, store balance >
csenter() ... csexit()
process P: i=1...N
while True:
csenter()
codice critico
csexit()
codice non critico
A* mutua esclusione:
- 1 solo processo alla volta nel codice critico
B* assenza di deadlock:
- Non si bloccano definitivamente in csenter
C* assenza di delay non necessari:
- Un processo che vuole eseguire codice critico non deve essere ritardato da uno che non vuole eseguire codice critico
D* assenza di starvation:
- Quando un processo entra nella csenter prima o poi ne esce
DEKKER1 garantisce A, B, D, ma non C
DEKKER2 garantisce B, C, D, ma non A
DEKKER3 garantisce A, C, D, ma non B
DEKKER4 garantisce A, B, C, ma non D
Implementazione software delle sezioni critiche (Dekker, Peterson).
Implementazione hardware: spinlock.
Semafori...
-----------------------------------
Lezione di giovedì 15 Novembre 2018
Python:
-Basi linguaggio
-Shell interattiva
-Interpretato (semicompilato automaticamente)
- // divisione intera Python3
- # commento
- # !/usr/bin/env python3, per rendere eseguibili file.py
- eval(...) valuta come espressione il parametro passato, poco sicuro
- doppio underscore rende attributi privati (__attribute)
-----------------------------------
Lezione di martedì 13 Novembre 2018
Segnale:
- è una comunicazione dal kernel al processo che qualcosa è successo;
- man 7 signal, comando per visualizzare la tabella completa dei segnali;
- segnale : software = interrupt : hardware.
Segnali affidabili e non.
Segnali e fork/exec
funzioni re-entrant
Se durante l'esecuzione di un system-call lenta (es. lettura da terminale) viene invocato un segnale:
- storicamente, il processo terminava restituendo uno speciale errore;
- attualmente, il processo esce, gestisce il segnale e rientra.
signal set
sigaction:
- (funzione) sceglie cosa fare quando viene comunicato un segnale, salvando lo stato attuale;
- (struttura) continene le informazioni relative ad un' azione (handler, flags, mask, ...).
sigprocmask: permette di mascherare i segnali.
sigsuspend: al momento del raise del segnale, sospende l'esecuzione e applica la maschera scelta.
sigpending: ritorna una struttura contenente tutte i signali in attesa di essere gestiti.
pselect: attende per uno o più file descriptor di diventare pronti per operazioni di I/O.
poll: attende per un qualsiasi tipo di file descriptor o segnale.
new fd syscall: signalfd, eventfd, timerfd_create
wait3/wait4 and profiling
-----------------------------------
Lezione di giovedì 08 Novembre 2018
(fcntl)
file col buco...
mknod, pipe, mkfifo/named pipe.
select/poll
kill, signal
-----------------------------------
Lezione di martedì 06 Novembre 2018
uMPS2.
approfondimento su azioni atomiche dup2, APPEND mode, pread/pwrite
System call file system prefissi: L/F
System call file system:
-int stat (const char *file_name, struct stat *buf) restituisce informazioni sul file
-int access(const char *pathname, int mode) controllo se il chiamante può accedere al pathname
-mode_t umask (mode_t mask) imposta la maschera dei permessi dei file del processo del chiamante
-mode_t chmod(const char *pathname, mode_t mode) change a files mode bits
-int chown(const char *pathname, uid_t owner, gid_t group) cambia proprietario e gruppo del file
-int truncate(const char *path, off_t length) tronca un file ad una certa dimensione
-int link(const char *oldpath, const char *newpath) crea un nuovo hard link ad un file
-int symlink(const char *path1, const char *path2) crea un nuovo symlink ad un file
-int unlink(const char *pathname) elimina un nome del file
-int rename(const char *oldpath, const char *newpath),
-int mkdir(const char *path, mode_t mode),
-int rmdir(const char *path),
chdir/fchdir (...at system call)
navigazione directory: getdents. (Lib: opendir/readdir/rewinddir/closedir)
-----------------------------------
Lezione di martedì 30 Ottobre 2018
Componenti di un sistema operativo
Gestione dei processi
Gestione della memoria principale
Gestione della memoria secondaria
Gestione file system
Gestione dei dispositivi di I/O
Protezione
Networking
(Interprete dei comandi)
System call gestione processi:
-fork(...) duplica il processo in esecuzione. Ritorna 1 al padre e 0 al figlio (-1 in caso di errore)
-wait(..) sospende il processo corrente finché un figlio termina o finché il processo corrente non riceve un segnale di terminazione
-waitpid(...) attente la terminazione del processo con pid passato alla funzione
-nice(..) cambia valore di nice di un processo
(A process' nice value is a non-negative number for which a more positive value shall result in less favorable scheduling)
System call gestione memoria
-execvp(const char *file, char *const argv[]) esegue il programma puntato da file
Relative funz. di libreria
-brk(void *addr), cambia la dimensione di un segmento di dati. Alloco/Dealloca la memoria di un processo
System call gestione file
-open(...) dato un pathname per un file ritorna un file descriptor
file descriptor=numero intero che rappresenta un file, una pipe o un socket aperto da un processo per effettuare operazioni
-close(...) chiude un file descriptor
-read(int fd, void *buf, size_t count) legge da un file descriptor count bytes dentro il buffer buff
-write(...) scrive su un file descriptor
-pread(...) legge da un file descriptor con un certo offset
-pwrite(...) scrive su un file descriptor con un certo offset
-dup(...) duplica un file descriptor. La copia rimane sincronizzata con l’originale
Ridirezione
System call gestione I/O (ioctl)
-ioctl(...) device specific input/output operations
-fcntl(...) manipolazione del file descriptor. Può eseguire diverse operazioni a seconda del parametro comunque passato
-----------------------------------
Lezione di giovedì 25 Ottobre 2018
SO: facilita' di uso, indipendenza dall'hardware, programmabilita',
affidabilita'/solidita'.
Servizi (system call):
gestione processi
accesso I/O
gestione errori
interoperabilita' (IPC/networking)
accounting
protezione
Generazione 0: Babbage/Lady Ada/Menabrea
Generazione 1: valvole 45-55
operatore = utente = costruttore = programmatore
Generazione 2: transistor 55-65
primi linguaggi ad alto livello (Fortran, Lisp)
Schede perforate.
Batch.
Definizione di Job
Operatore != (programmatore = utente)
Monitor (embrione di un Sistema Operativo)
Generazione 3: circuiti integrati 65 - 80
Multiprogrammazione (avvicendamento per I/O)
(interrupt per evitare polling)
(gestione memoria)
poi Time Sharing (quanto di tempo, necessita' dell'Interval Timer)
-> sistemi interattivi.
(gestione memoria, scheduling, protezione)
esempi multics poi UNIX 1970.
utenti != programmatori
Generazione 4: personal computer >80
microprocessori -> sistemi personali
facilita' d'uso
(problemi di sicurezza).
Sistemi Paralleli SIMD/MIMD
per i MIMD: tightly coupled/loosely coupled
per i sistemi operativi: SMP, non SMP
Sistemi Real Time
Hard/soft
Architettura.
Macchina di von Neumann:
-dati programma e istruzioni da eseguire sono nello stesso spazio di memoria
- bus complesso di fili paralleli ai quali sono collegate le unità
Ciclo di CPU : fetch/decode/load operandi/execute/store (+controllo interrupt)
-2 tipi di archihtetture
-RISC, tutte le istruzioni hanno la stessa lunghezza
-CISC, istruzione di lunghezza diversa, passate al livello microcodice
RISC più efficiente rispetto a CISC, ma per fare le stesse operazioni ha bisogno di più istruzioni
I modi del processore:
- modo kernel: accesso completo a memoria e hardware
- modo user: accesso solo alla sua memoria e a operazioni base
modalità controllata da un bit sulla CPU
nell'architettura intel i livelli di privilegi sono chiamati ring
Comunicazione coi controllori dei device
Interrupt
- il ciclo di una istruzione ha una fase in più di controllo per gli interrupt ( Non viene mai interrotto il ciclo di una istruzione)
- più fili del bus per alzare tensione e comunicarlo al processore per eseguire delle operazioni specifiche (variano da processore a processore)
- interrupt fa saltare a riga specifica, necessario salvare i dati dell'istruzione corrente per riprendere dopo fine dell'interrupt
Interrupt hardware vs Interrupt software (detti Trap)
Gestione dell'Interrupt
parte del processore: interrupt, alla fine instruzione correntesalva stato,
cerca handler, salta handler
parte del S.O. salva stato, gestione, (scheduler?) ripristino stato, ritorno controllo
Una system call è un interrupt software generato dal processore per il codice in esecuzione, che salta così al gestore dell'interrupt che lo valuta
System call sono gestite come errori
interrupt multipli e nidificati
- interrupt con priorità diverse
- interrupt di priorità maggiore vengono eseguiti prima
mascheramento degli interrupt (mono e multiprocessore)
-cpu ha registro che consente di mascherarsi dagli interrupt
Senza interrupt = Polling
- polling = azione ripetuta
esempio dalla vita: quello che fanno i bambini in macchina: siamo arrivati? siamo arrivati?
esempio prima degli interrupt: la cpu chiede al processo: hai finito? hai finito? prima o poi risponderà sì
DMA (Direct Memory Access).
- Operazioni di buffer fatte dal controller
- Migliora l'efficienza e semplifica la scrittura di SO
Gerarchia di memoria
Tipi di memoria:
- primaria
(codice e dati delle elaborazioni correnti);
(nanosecondo);
- secondaria
(attivazione esecuzioni e programmi);
(millisecondo);
- terziaria
(lente e umane, esempio CD, DVD, chiavette USB);
concetto di Cache: località (se faccio un accesso è probabile che dopo poco accedo al memoria subito vicina)
Concetto generale di Cache (hw o sw)
Protezione di memoria => MMU (Memory Management Unit)
- alcuni registri accessibili e altri no
- limita l'accesso a determinati registri e nel caso scaturisce una Trap call
- fa in modo che in usermode siano visibili solo alcuni registri
Casi semplici base/limite, cenni di funzionamento MMU moderne
Multiprocessore/muticore
System Call.
La gestione dei tipi nelle librerie.
-------------------------------------
Lezione di martedì 23 Ottobre 2018
Il catalogo delle system call di UNIX...
File I/O
Device I/O
Memoria
Process mgmt
Networking
IPC
Ritorno alla teoria...
Breve storia dei sistemi anche operativi.
Richiami di architettura: von newmann, interrupt/trap DMA, gerarchia di memoria, cache...
-------------------------------------
Lezione di giovedì 18 Ottobre 2018
* utente
* programmatore (prog. utente)
* programmatore (sistema)
* amministratore
* sviluppatore kernel
Storia dei S.O.
librerie e system call...
librerie: statiche dinamiche (nm, ldd,
libreria standard C
--------------------------------------
Lezione di martedì 16 Ottobre 2018
Ulteriori dettagli di C:
static extern auto register
preprocessore compilazione condizionale....
inizializzazioni
const
... collegamento con shell:
parametri a linea comando (e _start).
(environment)
librerie statiche e dinamiche...
execs-s2argv
link fisici e simbolici
ps/pstree kill processi foreground/background
> >> quoting...
-------------------------------------
Lezione di giovedì 11 Ottobre 2018
installazione di GNU-Linux
gruppi lab...
UNIX: struttura standard del file system (ma cosa e' il file system?)
user/group
tipi di file...
strace
strcase
man
user/group
tipi di file...
-------------------------------------
Lezione di martedì 9 Ottobre 2018
gruppi (wiki&42, !!, mascheramento
struct vs union (+ passate per valore).
l'ampiezza di un unione è pari all'ampiezza del campo più grande
l'ampiezza di una struct é pari alla somma delle ampiezze dei campi
Git/autotools/cmake
-------------------------------------
Lezione di martedì 2 Ottobre 2018
Linguaggio C: prin_C_ples
Compiler startup
Esperimento: http://www.cs.unibo.it/~renzo/so/misc/portability3.tgz
Analisi esercizi di Lettura
-----------------------------------
Lezione di giovedì 27 Settembre 2018
prima definizione di sistema operativo
(astrazione-unificazione-gestione-controllo)
algoritmo - programma - processo
livelli - linguaggi - servizi - astrazioni
livello n+1
linguaggio n+1
livello n
linguaggio n
programmi utente
---
librerie
---system call + ISA
Sistema Operativo (kernel)
---
HW
linguaggio = (alfabeto, lessico, sintassi, semantica)
UNIX & C
everything is a file
(unix v3)
(CLI)
C-toolchain
data types (+void)
operators (+bit wise operators)
control statements
functions
(quine)