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)