KERNEL
Quattro categorie:
- Kernel monolitici
	Insieme completo e unico di procedure mutuamente correlate e coordinate (efficienza)
	Svantaggio -> aggiungere dispositivo significa aggiungere modulo (ricompilare)
- Microkernel
	Si porta fuori tutto ciò che può essere portato fuori dal kernel
	Solo due system call: send e receive -> comunicazione basata su message passing
	Semplici da realizzare; più espandibili (aggiungere un servizio significa aggiungere un processo a livello utente)
	e per modificare un servizio basta riscrivere solo il codice del servizio stesso.
	più stabili.
- Kernel ibridi
	Mantengono una parte di codice in kernel space per ragioni di efficienza 
	Adottano il message passing tra i moduli in user space.
- Exokernel
	Mai usciti dal livello di ricerca
	Farebbe in modo che i processi abbiano la visione diretta dell’hardware.
	Quindi si occuperebbe di definire solo a quali settori di memoria può accedere un processo.

MACCHINE VIRTUALI
Sfruttano hypervisor per condividere e gestire l'hardware
Vantaggi:
	Consentono di avere sistemi operativi differenti che coesistono, 
	Possono far funzionare sistemi monotask su sistemi multitask,
	Permettono di emulare architetture hardware diverse. 
Svantaggi: 
	Varie inefficienze, a cui si tenta di dare soluzioni tramite istruzioni hardware,
	Difficoltà a condividere le risorse, che richiederebbero una rete per comunicare

VM di processo -> supporta un solo processo e ha lo stesso lifespan del processo
	Fornisce un ambiente di programmazione indipendente dalla piattaforma

PROCESSI
Un processo è rappresentato da:
1. Il codice da eseguire (segmento codice).
2. dati su cui opera (segmento dati).
3. Lo stack di lavoro per la gestione di chiamate di funzione, passaggio di parametri e variabili locali.
4. Un insieme di attributi contenenti tutte le informazioni necessarie per la gestione del processo (PCB)

Nei PCB ci sono tre informazioni:
	identificazione di processo, informazioni di stato del processo, informazioni di controllo del processo.

	- Per identificare un processo si utilizza il PID, per evitare omonimie si usa un indice di reincarnazione
		 Quando un processo termina il suo ID è riassegnato il contatore aumenta.

	- Lo stato del processo contenuto nel PCB è quello aggiornato all'ultima sospensione (es. se arriva interrupt)

	- Le informazioni di controllo sono varie:
		 - Di scheduling (es. stato)
		 - Di gestione della memoria (es. per MMU)
		 - Di accounting (es. tempo di esecuzione)
		 - Relative alle risorse (es. file aperti)
		 - Per IPC (es. semafori)

MULTITHREADING
Un thread è l’unità base di utilizzazione della CPU.
In un processo multithreaded esistono molte “linee di controllo”.
Esiste una tabella dei thread che tiene traccia di ogni thread a cui un processo "punta". 
	Quindi in un SO che supporta multithread a livello kernel, lo scheduler gestisce i singoli thread, non i processi
I thread condividono lo spazio di memoria e le risorse allocate degli altri thread dello stesso processo.

SCHEDULER
Schedule = sequenza temporale di assegnazioni delle risorse da gestire ai richiedenti. 
Scheduling = l'azione di calcolare uno schedule. 
Scheduler = componente software che calcola lo schedule.

Un processo potrebbe essere:
Running -> correntemente in esecuzione
Waiting -> ad esempio se avviene un interrupt
Ready -> nella coda di processi ready

Se accade che la coda dei processi ready è vuota, potrebbe essere perché tutti i processi sono in waiting.
Lo scheduler quindi potrebbe fare busy waiting (consuma energia) oppure il processore potrebbe fare nulla passando allo stato idle.

Tutte le volte che avviene un interrupt il processore è soggetto ad un mode switching (user / kernel).
	Al termine della routine di gestione viene chiamato lo scheduler che potrebbe far partire un altro processo (context switch).

Gli eventi che possono causare context switching sono:
1. Quando un processo passa da uno stato running a uno stato waiting in seguito ad una system call bloccante. 
	Si sceglie per forza un altro processo.
2. Quando un processo un processo passa dallo stato running allo stato ready, ad esempio in seguito ad un interrupt 
	È possibile scegliere il processo corrente.
3. Quando un processo passa dallo stato waiting allo stato ready. 
	È possibile scegliere il processo corrente.
4. Quando un processo termina. 
	Lo scheduler dovrà per forza scegliere un altro processo.

Esistono due tipi di scheduler: preemptive e non-preemptive.
	In uno scheduler non-preemptive il controllo della risorsa viene trasferito solo se l'assegnatario attuale lo cede volontariamente (casi 1 e 4)
		Il principale vantaggio è l'assenza di un interval timer.
	Negli scheduler preemptive invece gli switch possono avvenire in ogni condizione.
		È quindi possibile utilizzare meglio le risorse.

Durante l'esecuzione di un processo si alternano attività svolte dalla CPU (CPU burst) a periodi di I/O (I/O burst).
	Esistono processi che raramente fanno I/O, con CPU burst lunghi, detti CPU bound (es. database management system)
	Invece processi caratterizzati da frequenti I/O burst si dicono I/O bound, ad esempio un programma di ray tracing.

ALGORITMI DI SCHEDULING
- FCFS First come, first served
	Implementato con coda FIFO
	Elevati tempi di attesa
	Svantaggia processi I/O bound
	Se un processo lento si mette davanti a processi più veloci fa bottleneck (convoy effect)

- SJF Shortest job first
	Prima il processo con CPU burst più breve
	Ottimale rispetto a tempo di attesa
	Soggetto a starvation
	NON IMPLEMENTABILE (teorico) -> non si sa cosa farà un processo in futuro
		Si usano approssimazioni guardando comportamento passato
			T_n+1 = at_n + (1-a)T_n, ovvero la previsione è uguale al tempo * per a + (1-a) * la precedente previsione
				Il parametro a è compreso tra 0 e 1, quindi più a è grande più si adatta ai cambiamenti, più a è basso meno cambiamenti avvengono.

In Shortest-Remaining-Time First un processo può essere messo in pausa se ne arriva un altro con CPU burst più breve

- ROUND ROBIN
	Time slice (hardware dedicato per interval timer)

La durata del quanto di tempo è un parametro critico del sistema, quindi va tarato. 
Se è breve, il sistema è meno efficiente perché deve cambiare il processo attivo più spesso (il context switch costa).
Se è lungo, in presenza di numerosi processi pronti ci sono lunghi periodi di inattività di ogni singolo processo.

Round robin è troppo democratico
	I processi non sono tutti uguali: 
		Ad esempio usando round-robin puro la visualizzazione di un video MPEG potrebbe essere ritardata 
		da un processo che sta smistando la posta. Ma la lettera può aspettare mentre il frame video no.

- SCHEDULING A PRIORITÀ
	Ogni processo ha una sua priorità
		Può essere statica (possibile starvation) o dinamica (aging)
			Aging: la priorità aumenta in base al tempo di attesa per poi ripristinare la priorità base una volta eseguito.

- SCHEDULING A CLASSI DI PRIORITÀ
	Processi simili sono divisi in classi di priorità.
	La ready queue è divisa in tante sotto-code
	Il processo da eseguire è scelto tra quelli ready con classe di priorità massima
	
	- SCHEDULING MULTILIVELLO
		Le classi di priorità hanno a loro volta una propria politica di scheduling.

- SCHEDULING REAL-TIME
	Processi periodici (riattivati con cadenza regolare) e aperiodici
	Due stati: critico e non critico
	Programmi scritti con linguaggi ad hoc (es. no cicli infiniti)

	- SCHEDULER RATE MONOTONIC
		Ogni processo ha una priorità statica
		Scheduling in base a frequenza (periodo più corto)
	- SCHEDULER EARLIEST DEADLINE FIRST
		Priorità dinamica
		Viene scelto chi ha la deadline più prossima


RISORSE
- Tipi:
	Fungibili: qualunque istanza va bene
	Infungibili: serve una determinata risorsa

	Singole: una sola risorsa di classe definita
	Multiple: più risorse di più classi

	Bloccanti: richiedente si sospende finché non ottiene l'assegnazione
	
	Condivisibili: utilizzabili da più processi contemporaneamente

- Assegnazione:
	Statica: risorsa assegnata a processo per tutta la sua vita (es. descrittore processo)
	Dinamica: risorse allocate e deallocate durante l'esecuzione

	Una risorsa è Pre-rilasciabile se la funzione di gestione può sottrarla ad un processo prima che questo l'abbia effettivamente rilasciata. 
		La risorsa verrà restituita in un secondo momento.
		Il suo stato non si modifica durante l'utilizzo o il suo stato può essere facilmente salvato e ripristinato (es. processore durante interrupt)

Diciamo che un processo è in stato di starvation quando esso è pronto per l'esecuzione 
ma è impossibilitato perpetuamente ad accedere alla risorsa di cui ha bisogno per l’esecuzione.

Si dice che due o più processi sono in deadlock quando ognuno di essi attende che venga eseguita una certa azione da un altro processo.	

DEADLOCK
Il deadlock è una condizione patologica del sistema che impedisce ai processi di terminare correttamente. 
Le risorse bloccate in deadlock non possono essere utilizzate da altri processi.
Le condizioni per avere deadlock, dette di Coffman, sono:
	- Le risorse devono essere non condivisibili.
	- Assenza di prerilascio: le risorse coinvolte non possono essere prerilasciate volontariamente dai processi che le controllano.
	- Le richieste devono essere bloccanti (hold and wait) e un processo che ha già ottenuto risorse può chiederne ancora.
	- Attesa circolare: esiste una sequenza di processi tali per cui ognuno vuole una risorsa che possiede il successivo e l’ultimo vuole quella del primo.

Per rappresentare l'allocazione delle risorse si usano i grafi di Holt che tengono traccia di assegnamenti e richieste

DEADLOCK DETECTION
Teorema
SE ogni classe contiene una sola istanza di ogni risorsa,
Se le risorse sono ad accesso mutualmente esclusivo, non condivisibili e non prerilasciabili lo stato è di deadlock sse il Grafo di Holt contiene un ciclo.
Se il Grafo di Holt contiene un ciclo, questo alternerà nodi processo a nodi risorsa. 
Quindi questo ciclo significa che un processo ha qualcosa che sta aspettando qualcun’altro e quest’ultimo ha qualcosa che sta aspettando il primo.

Teorema
Se le risorse sono ad accesso mutualmente esclusivo, non condivisibili e non prerilasciabili NON c'è di deadlock sse il grafo di Holt è completamente riducibile
	Ovvero esiste una sequenza di passi di riduzione che elimina tutti gli archi del grafo.

	KNOT
	Dato un certo nodo n, possiamo costruire un insieme di raggiungibilità chiamato R(n) che rappresenta l'insieme di tutti i nodi raggiungibili da n.
	Un knot del grafo G è il massimo sottoinsieme (non banale, non composto da un solo nodo) di nodi M tale che per ogni n in M, R(n)= M, 
	ovvero partendo da un qualunque nodo di M posso raggiungere tutti i nodi di M e nessun nodo all’infuori di esso.

Teorema
Dato un grafo di Holt con una sola richiesta sospesa per processo, se le risorse sono ad accesso mutualmente esclusivo, non condivisibili e non prerilasciabili, 
allora il grafo rappresenta uno stato di deadlock se e solo se esiste un knot.

DEADLOCK RECOVERY
Una volta individuata la deadlock, la soluzione può essere:
	Manuale: l'operatore viene informato ed eseguirà alcune azioni che permettano al sistema di proseguire, 
	Automatica: il sistema operativo è dotato di meccanismi che permettono di risolvere in modo automatico la situazione, in base ad alcune politiche. 

In entrambi i casi non se ne esce se non in maniera traumatica, perdendo esecuzioni e terminando processi.
I meccanismi di terminazione sono:
	Terminazione totale: tutti i processi coinvolti vengono eliminati.
	Terminazione parziale: viene eliminato un processo alla volta finché il deadlock non sparisce.

Un'altra soluzione è il checkpoint/rollback: 
	Lo stato dei processi viene periodicamente salvato su disco (checkpoint) e in caso di deadlock, si ripristinano (rollback) uno o più processi.

DEADLOCK PREVENTION
Si potrebbe pensare di:
Permettere la condivisione di risorse.
	Problema: richieste bloccanti
	Soluzione: allocazione totale:
		È possibile richiedere che un processo richieda tutte le risorse all'inizio della computazione. 
		Facendo così il processo prima di partire sa già se potrà terminare. 
		Costo elevato -> un processo tiene la risorsa per tutto il tempo anche se gli servirà per poco.

Prevenire la condizione di pre-rilascio (non sempre possibile).

Risolvere il problema dell'attesa circolare:
	Allocazione gerarchica: 
		Alle classi di risorse vengono associati valori di priorità e ogni processo può allocare solamente risorse di priorità superiore a quelle che già possiede. 
		Se un processo vuole allocare una risorsa a priorità inferiore, deve prima rilasciare tutte le risorse con priorità uguale o superiore a quella desiderata.

Sia allocazione gerarchica che allocazione totale sono altamente inefficienti, infatti:
	- Nella prima l'indisponibilità di una risorsa ad alta priorità ritarda processi che già detengono risorse ad alta priorità
	- Nella seconda anche se un processo ha necessità di risorse per poco tempo deve allocarle per tutta la propria esistenza.

DEADLOCK AVOIDANCE
ALGORITMO DEL BANCHIERE (vedi slides)

OSTRICH ALGORITHM
Algoritmo dello struzzo -> ignorare il problema del tutto lasciarlo risolvere all’amministratore di sistema quando se ne accorge. 
	Usato perché dal punto di vista ingegneristico, il costo di evitare i deadlock può essere troppo elevato.


GESTIONE DELLA MEMORIA
Effettuata da memory manager-> software che tiene traccia della memoria libera e occupata e alloca/dealloca la memoria per i processi.

Distinguiamo due spazi di indirizzamento: logici e fisici. 
Ogni processo è associato ad uno spazio di indirizzamento logico e gli indirizzi usati in un processo sono indirizzi logici relativi a questo spazio.
La CPU genera quindi indirizzi logici.
In base a come è stata istruita la MMU, ad ogni indirizzo logico verrà fatto corrispondere un indirizzo fisico. 
La funzione di trasformazione può essere parziale, in modo da non permettere di accedere a determinati indirizzi fisici garantendo un livello di protezione.

ALLOCAZIONE
Può essere contigua -> lo spazio fornito è costituito da celle di memoria consecutive o non contigua -> celle separate.
	In quest’ultimo caso la MMU deve gestire un processo di traduzione più complesso.

Parliamo di allocazione statica quando viene assegnata un’area di memoria ad un processo e questa non cambia più 
Parliamo di allocazione dinamica quando durante l'esecuzione un processo può essere spostato all’interno della memoria.

Nei sistemi embedded e a real-time viene usata una tecnica contigua e statica detta a partizioni fisse 
	(ogni processo viene caricato in una partizione sufficientemente grande)
		Semplice ma limita il parallelismo ad un processo per partizione.

FRAMMENTAZIONE
La presenza di spazio inutilizzato all'interno di un'unità di allocazione è un fenomeno detto frammentazione interna.

La frammentazione interna dipende dall'uso di unità di allocazione di dimensione diversa da quella richiesta 
La frammentazione esterna, invece, deriva dal susseguirsi di allocazioni e deallocazioni.
Per ovviare alla frammentazione esterna esiste la compattazione che fa uso del binding dinamico: 
	Si fermano i processi e si copiano le aree di memoria per non lasciare buchi.
		Molto costosa e non utilizzabile in processi interattivi.

ALLOCAZIONE DINAMICA
Strutture dati per indicare zone libere e zone occupate:
	- MAPPE DI BIT
		Ad ogni unità di allocazione viene assegnato un bit (0 se libera o 1 se occupata)
		I processori CISC hanno un'istruzione per trovare sequenze di bit uguali
		La mappa deve avere una dimensione consona
			Potrebbe essere calcolabile a priori
		Scorrerla tutta costa O(m) dove m è il numero di unità di allocazione

	- LISTE DI PUNTATORI
		Si mantiene una lista dei blocchi allocati e liberi di memoria
		Ogni elemento della lista specifica se si tratta di un processo (P) o di un blocco libero (hole, H) e specifica la dimensione (inizio/fine) del segmento.
		Le operazioni avvengono in tempo costante

Quando il Memory Manager deve decidere quale area tra quelle libere utilizzare lo fa attraverso due tecniche: 
First fit: primo blocco libero di ampiezza sufficiente
Next fit: come first fit ma riparte dall'ultimo punto in cui si era fermato

Esistono anche best fit (miglior spazio libero, tecnica molto lenta) e worst fit (blocco più grande tra quelli presenti).

PAGINAZIONE
Riduce frammentazione interna ed esterna, anche se non le elimina
Richiede hardware dedicato

Ogni processo ha uno spazio di indirizzamento logico, che viene suddiviso in un insieme di blocchi di dimensione fissa detti pagine di ampiezza una potenza di 2. 
Dividiamo tutta la memoria fisica dei processi in frame della stessa dimensione delle pagine.
	Grandezze standard delle pagine potrebbero essere 1, 2, 4 KB
Quando dobbiamo allocare un processo in memoria cerchiamo i frame disponibili e li assegniamo alle pagine. 
	Questi frame possono essere ovunque in memoria.

Ogni processo utilizza la tabella delle pagine per memorizzare la mappatura tra indirizzi virtuali e fisici.

TLB
Il TLB è una cache della tabella delle pagine, cioè solamente un sottoinsieme del suo contenuto viene memorizzato.

Ogni registro del TLB è diviso in chiave (indirizzo virtuale) e valore (indirizzo reale)
Se la chiave è presente (TLB hit) si ritorna il valore corrispondente, altrimenti (TLB miss) si usa la tabella in memoria
	In questo caso si copia il valore nel TLB.

Grazie a questo sistema la frammentazione interna è al massimo di una pagina.

SEGMENTAZIONE
Serve per dividere la memoria di un processo in segmenti, ovvero aree distinte che vengono gestite diversamente.

In un sistema basato su segmentazione, un segmento è un'area di memoria (logicamente continua) contenente elementi tra loro affini.
Ogni segmento è caratterizzato da un nome (indice) e da una lunghezza.
La divisione in segmenti la fa in automatico il compilatore, o secondo le esigenze, la può fare il programmatore.

es:
Se abbiamo due processi che eseguono lo stesso codice (ad esempio un editor condiviso), 
il segmento codice è comune e viene mappato in memoria allo stesso indirizzo, mentre i dati e lo stack sono separati.

La segmentazione NON risolve la frammentazione.

MEMORIA VIRTUALE
Si usa una parte della memoria secondaria, in modo che le pagine siano recuperabili quando servono.

Ogni processo ha accesso ad uno spazio di indirizzamento virtuale che può essere più grande di quello fisico. 
Gli indirizzi virtuali (particolari indirizzi logici) possono essere mappati su indirizzi fisici della memoria primaria o della memoria secondaria.
Quando il programma tenta di accedere ad un indirizzo in memoria secondaria i dati associati vengono trasferiti in memoria principale.
Se la memoria è piena, si spostano in memoria secondaria i dati contenuti in memoria principale che sono considerati meno utili.

Demand paging -> tecnica di paginazione che ammette la presenza di pagine in memoria secondaria
	Si utilizza un bit valid che se a 1 indica che la pagina è in memoria centrale
	Se un processo tenta di accedere ad una pagina in memoria secondaria avviene un page fault e il pager carica la pagina mancante in RAM

ALGORITMI DI RIMPIAZZAMENTO
ANOMALIA DI BELADY
Ci si aspetta che con un algoritmo di rimpiazzamento il numero di page fault decresca in funzione del numero di frame, ma non è sempre così

	- FIFO
		Viene scelto il frame più vecchio in memoria
		Non richiede hardware
		Causa anomalia di Belady

	- MIN
		Viene scelta la pagina a cui si accederà più tardi nel futuro
		È solo teorico -> non si possono conoscere i riferimenti futuri

	- LFU (least frequently used)
		Viene scelta la pagina meno acceduta
		Teorico -> Mantiene un contatore di accessi e si basa sul rapporto accessi / permanenza in memoria
		Poco affidabile, in quanto un processo potrebbe essere usato per tanto tempo frequentemente e poi non essere più utilizzato.

ALGORITMO A STACK
	Un algoritmo si definisce a stack se per ogni stringa di riferimento e per ogni istante di tempo,
	l'insieme delle pagine con memoria m sono un sottoinsieme dell'insieme delle pagine con m + 1 frame di memoria.

Gli algoritmi a stack non generano anomalia di Belady
Dimostrazione:
Poniamoci ad un certo tempo t:
Se p1 ... pn sono le pagine in memoria, aggiungendo un frame le pagine saranno p1 ... pn, p_n+1
Per avere l'anomalia di Belady dovrebbe avvenire un page fault nel caso della memoria con più frame.
	(All'aumentare dei frame dovrebbero aumentare i page fault)
Ora ci sono tre casi:
	- La pagina si trova in p1 ... pn -> non c'è page fault in nessun caso
	- La pagina è in p_n+1 -> c'è page fault solo con memoria m
	- Nessuno dei precedenti -> page fault in entrambi i casi
QED.


	- LRU (least recently used)
		Viene scelta la pagina utilizzata meno recentemente nel passato
		Algoritmo a stack (non causa anomalia di Belady)
		Potrebbe essere implementato con uno stack

Dimostrazione: LRU è a stack
Nello stato iniziale, quando in memoria ci sono un numero di pagine minore o uguale a m, allora gli stati della memoria con m ed m + 1 frame sono identici.
Quando si aggiunge il frame m + 1-esimo, nella memoria con più frame questo verrà normalmente aggiunto, in quella con m frame uno dovrà essere scelto come vittima.
Per ipotesi induttiva poniamo che LRU rispetti la definizione di algoritmo a stack.
Vogliamo dimostrare che resta a stack anche se confrontiamo i tempi t e t+1, m e m+1 frame.
Denotiamo con s[t+1] la pagina che entra al tempo t+1, abbiamo tre casi:
	- s[t+1] appartiene agli m frame al tempo t -> no page fault
	- s[t+1] appartiene agli m+1 frame al tempo t -> s[t+1] è l'elemento in più quindi un frame di m viene sostituito
	- s[t+1] non appartiene agli m+1 frame al tempo t -> page fault, due casi:
		- Sostituire l'elemento in più: in questo caso dagli m+1 frame viene eliminato l'elemento che non è in m frame
			mentre dagli m frame viene eliminato un elemento scelto dall'algoritmo (resta sottoinsieme)
		- Sostituire un elemento nell'intersezione -> dipende dall'algoritmo:
			Dobbiamo mostrare che la scelta fra m pagine dipende solo dalla stringa di riferimenti, ovvero scelgano entrambi la stessa pagina. 
			LRU dato il tempo, sceglie deterministicamente la pagina usata meno di recente, mentre FIFO è influenzato anche dall’ampiezza della memoria.
QED.
	
LRU è molto costoso e supportato da poche MMU
	Si utilizza un reference bit che viene posto a 1 quando una pagina è acceduta
		Se si salvano periodicamente gli ultimi n reference bit si può avere una storia degli accessi di n bit 
			Si shiftano i bit di una posizione e si guarda il valore degli n bit di storia, la pagina vittima è quella con valore minore

Second-chance algorithm (algoritmo dell'orologio)
	Passa in rassegna le pagine mettendo a 0 il reference bit se è a 1 o scegliendo come vittima la prima pagina con il bit a 0
	In caso di pagine tutte a 0 degenera nell'algoritmo FIFO.
	Facile da implementare, non richiede grandi sforzi da parte della MMU.


ALLOCAZIONE
Locale: ogni processo ha un insieme proprio di frame
	Poco flessibile e statica (un processo potrebbe avere bisogno di più frame in un determinato momento e non in un altro)

Globale: tutti i processi possono allocare tutti i frame presenti nel sistema, quindi avviene competizione.
	Possibile TRASHING
		Quando un sistema spende più tempo per la paginazione che per l'esecuzione.
			Avviene se si caricano troppi processi in memoria virtuale con pochi frame di memoria
				Bisogna sospendere uno dei processi, in quanto è meglio sospendere un processo che mandare il sistema in trashing

WORKING SET
(indipendente dagli algoritmi di rimpiazzamento)
Modo per stimare, data una stringa di riferimento, di quante pagine avrà bisogno il processo. 
Si definisce working set di finestra X l'insieme delle pagine accedute nei più recenti X riferimenti.
	Se una pagina non compare in X riferimenti successivi in memoria, allora esce dal working set: non è più una pagina su cui si lavora attivamente
La somma dell'ampiezza di tutti i working set dei processi attivi deve essere sempre minore del numero di frame disponibili, altrimenti il sistema è in trashing.
X deve essere adeguato, altrimenti si potrebbero avere falsi positivi o negativi di trashing se, rispettivamente, troppo grande o troppo piccolo


GESTIONE MEMORIA SECONDARIA
I driver sono procedure software che permettono al SO di utilizzare l'hardware senza sapere come esso funzioni, ma dialogandoci attraverso un'interfaccia standard.

I sistemi di I/O possono essere caratterizzati in diversi aspetti:
- Modalità di trasferimento
	A caratteri: quando l'unità di scambio è il singolo carattere come nel caso del terminale.
	A blocchi: quando si inviano i dati a blocchi di byte, come i dischi.
		Dati letti e scritti a blocchi di 512 - 1024 Bytes
- Modalità di accesso:
	Sequenziali: un insieme di elementi sono acceduti in una sequenza ordinata predeterminata (es. musicassetta).
	Random: può accedere ad un elemento arbitrario di una sequenza in tempo costante e indipendente dalla dimensione della sequenza stessa (es. CD)
- Trasferimento
	Sincrono: si invia un flusso continuo di dati al ricevitore utilizzando segnali di temporizzazione regolari che garantiscono
			  che sia il trasmettitore che il ricevitore siano sincronizzati tra loro. Un esempio sono i nastri.
	Asincrono: invia i dati dal trasmettitore al ricevitore con bit di parità (bit di inizio e fine) in intervalli irregolari, come ad esempio il mouse.
- Condivisione
	Condivisibili
	Dedicati
- Velocità
	Pochi vs tanti Gb/s.
- Direzione di I/O
	Sola lettura.
	Sola scrittura.
	Entrambe.

Per la gestione dei SO esistono varie tecniche:
- Buffering: serve per disaccoppiare la produzione e il consumo dei dati.
	Se un dispositivo è più lento si usa un buffer intermedio.
- Caching: mantenere nella memoria più veloce il contenuto utile delle Memorie più lente.
- Spooling: serve per rendere condivisibili delle unità non condivisibili per loro natura.
	È un buffer che mantiene output per un dispositivo che non può accettare flussi dati distinti (es. stampante)

MEMORIA SECONDARIA
SSD
	Non hanno fragilità meccaniche.
	Consumano meno energia dei dischi rotazionali.
	Hanno un numero massimo di cicli di scrittura, questo implica scelte di file system più adatte di altre.
	Sono più veloci a leggere che a scrivere.
	Si legge a blocchi, si scrive a “banchi” (molti blocchi insieme)
	Accesso uniforme su tutto lo spazio di memoria (impiega sempre lo stesso tempo indipendentemente dalla posizione).

DISCHI ROTAZIONALI
	Un disco è composto da un insieme di piatti, suddivisi in tracce, le quali sono suddivise in settori. 
	I dischi sono caratterizzati da tre parametri fondamentali:
		r: la velocità di rotazione, espressa in rpm (revolutions per minute).
		Ts: il tempodi seek, ovvero il tempo medio necessario affinché la testina si sposti sulla traccia desiderata.
		Vr: la velocità di trasferimento, espressa in byte al secondo.
	Il ritardo rotazionale è il tempo medio necessario affinché un settore arrivi sotto la testina ed è uguale alla metà del tempo che impiega a fare un giro
	Il tempo di accesso è il tempo necessario per leggere un settore del disco, composto da tempo di seek, ritardo rotazionale e tempo di trasferimento. 
	

DISK SCHEDULING
	- FCFS First come, first served
		FIFO
		Non genera starvation

	- SSTF Shortest seek time first
		Seleziona la richiesta che richiede lo spostamento minimo della testina
			In caso di parità sceglie a caso
		È solo teorico perché può generare starvation

	- LOOK (algoritmo dell'ascensore)
		Ad ogni istante la testina è associata ad una direzione.
		La testina si sposta di richiesta in richiesta, seguendo la direzione scelta.
		Quando si raggiunge l'ultima richiesta nella direzione scelta, la direzione viene invertita e si eseguono le richieste nella direzione opposta.
		Efficiente, no starvation

	- C-LOOK
		Come LOOK ma al termine delle richieste in una direzione si sposta alla richiesta più lontana nell'altra direzione

RAID
Serve per aumentare la velocità di accesso ai dischi grazie al parallelismo.
L'idea è quella di utilizzare un array di dischi indipendenti, che possano gestire più richieste di I/O in parallelo,
assicurandoci però di garantire che i dati letti in parallelo risiedano su dischi indipendenti.

Striping: il sistema RAID viene visto come un disco logico. 
I dati nel disco logico vengono suddivisi in strip (settori, blocchi o altri multipli). 
Strip consecutivi sono distribuiti su dischi diversi, aumentando le performance di lettura dei dati sequenziali.

	- RAID 0
		Non ha ridondanza
		Velocità senza affidabilità
		Dati distribuiti su più dischi
		es: disco 1 -> Strip 0, 2, 4, 6; disco 2 -> Strip 1, 3, 5 , 7 
	
	- RAID 1
		Usa mirroring
		es: disco 1 -> Strip 0, 1, 2, 3; disco 2 -> Strip 0, 1, 2, 3 
	
	- RAID 5
		I dischi memorizzano i dati come se avessero un disco in meno.
		Per ogni “riga di strip” nei vari dischi viene memorizzato uno strip di parità che contiene l’or esclusivo di tutti gli strip corrispondenti.
		es: con 5 dischi avremo gli strip da 0 a 3 nei primi 4 e lo strip di parità (0-3) nel quinto, ogni disco contiene uno strip di parità
		Grazie alle propietà dello xor è facile aggiornare dati e strip

	- RAID 4
		Come RAID 5 ma gli strip di parità sono tutti in un solo disco

	- RAID 6
		Come RAID 5, ma si utilizzano due strip di parità, aumentando l'affidabilità (è necessario il guasto di tre dischi affinché i dati non siano utilizzabili).


FILE SYSTEM
Il compito del file system è quello di astrarre la complessità di utilizzo dei diversi media
	Propone un'interfaccia comune, efficiente e conveniente da usare per i sistemi di memorizzazione. 
	Dal punto di vista dell'utente, il file system è composto da:
		- File: unità logica di memorizzazione.
		- Directory: servono per organizzare e fornire informazioni sui file che compongono un file system.

	File
		Il file è l’entità atomica del file system.
		Gli attributi di un file sono: Nome Tipo, Locazione e dimensione, Data e ora, proprietà, Attributi di protezione, Flag, informazioni di locking, ecc.
		I tipi di file possiamo catalogarli a seconda della struttura (formato) o del contenuto (ASCII, Binario, Sorgente, Oggetto, Eseguibile)
		Per identificare i tipi dei file esistono tre tecniche:
			- Meccanismi delle estensioni: sono delle convenzioni (.pdf, .png, .exe).
			- Attributi nelle directory: quando le directory elencano i file, questi avranno delle informazioni di corredo, tra i quali il tipo.
			- Magic number: in punti specifici dei file vengono inseriti dei numeri che identificano il tipo (es: #! per gli script nei sistemi Unix e Unix-like).
		
		Struttura dei file
			I file possono essere strutturati in molti modi:
				- Sequenze di byte,
				- Sequenze di record logici,
				- File indicizzati (struttura ad albero).
			Il metodo 1 è il più semplice.
			I metodi 2 e 3 vengono usati per grossi archivi di formato standard o per database.
			
			I sistemi operativi possono attuare diverse scelte nella gestione della struttura dei file:
				- Scelta minimale: i file sono considerati semplici stringhe di byte, a parte i file eseguibili il cui formato è dettato dal SO.
					Qualsiasi programma / applicativo deve contenere il proprio codice per interpretare in modo appropriato la struttura di un file.
				- Parte strutturata / parte a scelta dell'utente: ad esempio i file Macintosh hanno due parti: resource fork e data fork.
					La prima contiene le informazioni che interessano all’utente, mentre la seconda il codice e i dati del programma.
				- Diversi tipi di file predefiniti.
					Lo scopo è quello di rendere più leggero il kernel quindi la gestione viene sempre più delegata ai programmi applicativi.
				
				- Più formati : implicano un codice di sistema molto più complesso e la necessità di dover accedere a file di formato diverso 
					Potrebbe causare incompatibilità tra i programmi, a fronte di una maggiore efficienza per la gestione dei formati speciali (Kernel based).
				- Meno formati: implicano invece un codice più snello (Application based).

		Metodi di accesso	
			I file memorizzano informazioni; 
				Al momento dell'uso è necessario accedere a queste informazioni e trasferirle in memoria.
			
			Esistono molti metodi per accedere alle informazioni dei file:
			- Sequenziale: le informazioni del file si elaborano ordinatamente, le operazioni di lettura e scrittura fanno avanzare il puntatore del file. 
				È il metodo più comune, usato da compilatori e editor.
			- Accesso diretto: Il file si considera come una sequenza numerata di blocchi o record che si possono leggere o scrivere in modo arbitrario.
				Le operazioni di lettura e scrittura diventano parametrizzate al blocco da leggere e scrivere
					Molto utili quando è necessario accedere immediatamente a grandi quantità di informazioni.
			- Accesso indicizzato: Read e write avvengono mediante una chiave che dà accesso all'elemento
				La chiave diventa l’identificativo del record all’interno del file.
				Esiste una tabella di corrispondenza chiave-posizione (memorizzabile in memoria o paginabile su disco)

		Operazioni sui file
			Le operazioni fondamentali sui file sono:
				- Creazione;
				- Apertura/chiusura;
				- Lettura/scrittura;
				- Posizionamento;
				- Cancellazione;
				- Troncamento;
				- Lettura/scrittura attributi.
			L'API (interfaccia per la programmazione) relativa alle operazioni su file è basata sulle operazioni open/close.
				I file devono essere aperti prima di effettuare operazioni e chiusi al termine.
				Questo perché la maggior parte delle operazioni sopra citate richiede una ricerca dell'elemento associato al file specificato nella directory.
			
			Per evitare questa ricerca, quindi, molti sistemi richiedono l’impiego di una chiamata di sistema open() la prima volta che si adopera un file.
			Il sistema operativo mantiene una piccola tabella contenente informazioni riguardanti tutti i file aperti (tabella dei file aperti).
			Quando si richiede un'operazione su un file, questo viene individuato tramite un indice in tale tabella, in questo modo si evita qualsiasi ricerca.
			Quando il file non è più attivamente usato viene chiuso dal processo e il sistema operativo rimuove l'elemento a esso associato dalla tabella.
			L’astrazione relativa all’apertura/chiusura dei file è utile per 
				- Mantenere le strutture dati di accesso al file, 
				- Controllare le modalità di accesso,
				- Gestire gli accessi concorrenti,
				- Definire un descrittore per le operazioni di accesso ai dati.

	Directory
		Il file system si basa sul concetto di directory, la quale fornisce un'astrazione per un insieme di file.
		Le operazioni su directory sono:
			- Creazione;
			- Cancellazione;
			- Apertura/chiusura di una directory;
			- Lettura di una directory;
			- Rinominazione;
			- Link/unlink;
		Le directory possono avere diverse strutture: livello singolo, due livelli, albero, grafo aciclico (DAG) e grafo.
		
		Nella directory a livello singolo tutti i file sono contenuti nella stessa directory facilitando il supporto e la comprensione.
		Tuttavia, una directory a livello singolo è limitata quando il numero di file aumenta o quando il sistema ha più di un utente
			Ad esempio non possiamo avere più file con lo stesso nome
		
		Nella struttura a due livelli, ogni utente ha la sua directory separata, in ciascuna sono elencati solo i file del proprietario.
		Ogni volta che comincia una nuova elaborazione si fa una ricerca nella directory principale (master file directory, MFD).

		Directory a grafo aciclico (DAG)
			Nella struttura ad albero ogni file è contenuto in una directory univocamente.
			Quindi in quest'ottica, i file possono essere contenuti in due o più directory, anche se il file è unico
				Le modifiche sono visibili in tutte le directory.
		
		Semantica della coerenza
			Le scritture in un file aperto da parte di un utente sono immediatamente visibili ad altri utenti che hanno aperto contemporaneamente lo stesso file.
			Esistono due tipi di condivisione del file in questo caso:
				- Una con puntatore alla posizione corrente
				- Una con condivisione di distinti puntatori alla posizione corrente
			Usata in UNIX
			
		Semantica delle sessioni 	
			Fa in modo che le scritture su un file aperto non vengano viste immediatamente da altri utenti.
			Una volta chiuso il file le modifiche apportate sono visibili.
			In questo modo un file può essere associato ad immagini diverse, così da rendere l’accesso ai file più veloce.
			Usata in AFS.


	IMPLEMENTAZIONE FILE SYSTEM
		- Organizzazione di un disco
			Un disco può essere diviso in una o più partizioni, Le partizioni sono ottenute prendendo i blocchi fisici e dandone una visione logica.
			Le informazioni su come è strutturato il disco e sul partizionamento sono memorizzate nel master boot record (MBR).
				Contiene la partition table e l'indicazione della partizione attiva. Al boot il MBR viene letto ed eseguito. 
				GPT è un partizionamento più moderno e versatile di MBR.
				
			Il motivo della partizione è voler tenere file system o SO diversi in partizioni diverse
				Oppure per non tenere il file system (aree di swap gestite dal pager).
			Le partizioni hanno una struttura standard:
				- Il boot block è l'inizio di ogni partizione e viene caricato dal MBR che lo attiva ed esegue permettendogli di caricare il SO e lo esegue.
				- Il superblock contiene l'indice del file system, informazioni sul tipo file system e sui parametri fondamentali della sua organizzazione.
				- Le tabelle per la gestione dello spazio libero contengono la struttura dati contenente le informazioni sui blocchi liberi.
				- Le tabelle per la gestione dello spazio occupato contengono le informazioni sui file presenti nel sistema
					Non è presente in tutti i file system.
				- La root dir è la radice del file system.
				- File e directory grazie alla root dir.

		- Allocazione dello spazio in blocchi
			Una volta fornito dall'hardware e dal driver l'accesso al disco, bisogna occuparsi del posizionamento dei file nei blocchi attraverso l'allocazione.
			Ci sono diversi tipi di allocazione:
				- Allocazione contigua
					I file sono memorizzati in sequenze contigue di blocchi di dischi.
						Vantaggi: non si utilizzano strutture per collegare i blocchi, 
								  L'accesso sequenziale risulta efficiente in quanto blocchi contigui non necessitano di seek e l’accesso diretto è efficiente.
						Svantaggi: Se volessimo ampliare un file dovremmo riscriverlo da un’altra parte nel disco, 
								   L'allocazione contigua può portare alla frammentazione esterna.
						Soluzione: Per prevenire la frammentazione esterna si copia un intero file system su un altro disco o nastro.
						Svantaggio soluzione: Alcuni sistemi richiedono l'esecuzione di questa funzionalità con il file system non montato.
											Durante questo “periodo morto” (down time) il normale funzionamento del sistema non è possibile.
						Utilizzo: Utile per file system di dispositivi come i CD ROM in cui si accede in sola lettura.
						
				- Allocazione concatenata
					Ogni file è costituito da una lista concatenata di blocchi: 
						ogni blocco contiene un puntatore al blocco successivo
						il descrittore del file contiene i puntatori al primo e all'ultimo elemento della lista,
							così da poter appendere velocemente i dati in fondo alla lista di blocchi senza scandire tutta la sequenza.
						Vantaggi: In questo modo risolviamo frammentazione esterna e possiamo allargare i file, 
								  L'accesso sequenziale in “append mode” è efficiente.
						Svantaggi: L’accesso diretto è inefficiente (in quanto bisogna seguire l’intera lista di puntatori), 
								   Progressivamente l’efficienza globale del file system degrada (aumenta il numero di seek),
								   Inoltre la dimensione utile di un blocco non è una potenza di due (overhead alto).
							Soluzione: Per minimizzare l’overhead dei puntatori i blocchi vengono riuniti in cluster, e vengono allocati in modo indivisibile. 
									   In questo modo la percentuale di spazio utilizzata dai puntatori diminuisce.
						Svantaggio soluzione: Aumenta lo spazio sprecato per la frammentazione interna.
						
				- Allocazione basata su FAT
					Si crea una tabella unica con un elemento per blocco (o per cluster).
						Vantaggi: In questo modo i blocchi dati sono interamente dedicati ai dati, risolvendo il problema di overhead,
								  Ottimizzazione del tempo d'accesso diretto
									La testina del disco può trovare la locazione di ogni blocco leggendo le informazioni contenute nella FAT,
								  È possibile fare caching in memoria dei blocchi FAT così che l'accesso diretto diventa più efficiente 
									(in quanto la lista di puntatori può essere seguita in memoria).
						Svantaggi: L’accesso sequenziale richiede comunque la lettura della FAT, quindi aumenta il numero di accessi al disco.
						Utilizzo: Questo metodo è usato da DOS, macchine fotografiche e chiavette.
						
				- Allocazione indicizzata
					L'elenco dei blocchi che compongono un file viene memorizzato in un blocco indice.
					Per accedere ad un file, si carica in memoria la sua area indice e si utilizzano i puntatori contenuti nel blocco indice.
						Vantaggi: Risolve il problema della frammentazione esterna.
						Svantaggi: Come sempre, bisogna scegliere bene l'ampiezza dei blocchi per non sprecare memoria.

					CONCATENAZIONE BLOCCHI INDICE
					L’ultimo elemento del blocco indice non punta al blocco dati ma al blocco indice successivo.
						Rimane il problema per l'accesso diretto

					INDICE MULTILIVELLO
					Si utilizza un blocco indice dei blocchi indice. 
					Degradano le prestazioni, in quanto richiede un maggior numero di accessi. 
					Se non bastano gli indici basta aggiungere un nuovo livello di indici.

				In UNIX si utilizza l'allocazione indicizzata. 
				Ogni file ha associato un inode, ovvero un nodo indice. 
				Un inode è una struttura dati contenente gli attributi del file, e un indice di blocchi diretti e indiretti, secondo uno schema misto:
					Puntatori diretti: vanno direttamente alle aree dati
					Puntatori indiretti: vanno verso uno o più livelli di indici
						es: Doppio indiretto -> Indice -> Indice -> Data
													 |--> Indice -> ...

				Lo schema UNIX garantisce buone performance nel caso di accesso sequenziale
					Si possono ottenere miglioramenti grazie al pre-caricamento delle pagine

		- Gestione dello spazio libero
			Si usa lista dello spazio libero
				Creare un file = togliere spazio dalla lista, cancellarlo = aggiungere spazio alla lista

			GRANDEZZA DEI BLOCCHI
			Blocchi piccoli = ridotte prestazioni di seek
			Blocchi grandi = spreco di memoria (frammentazione interna) 

			MAPPA DI BIT
				Ad ogni blocco corrisponde un bit in una bitmap.
					I blocchi liberi sono associati ad un bit di valore 0, i blocchi occupati sono associati ad un bit di valore 1.
					Metodo semplice ed efficiente per trovare n blocchi liberi
					Svantaggio: occupano molto spazio
			
			LISTA CONCATENATA
			Collegare gli spazi liberi, tenere un puntatore al primo di questi in una speciale locazione del disco e caricarlo in memoria.
				Vantaggi: Richiede poco spazio in memoria centrale
				Svantaggi: L'allocazione di un’area di ampie dimensioni è costosa
						   L'allocazione di aree libere contigue è molto difficoltosa.

			LISTA CONCATENATA DI BLOCCHI
			È costituita da una lista concatenata di blocchi contenenti puntatori a blocchi liberi
				Vantaggi: Non è necessaria una struttura dati a parte, ma basta mantenere in memoria un blocco contenente elementi liberi.
				Svantaggi: Ha gli stessi svantaggi della lista concatenata

		- Implementazione delle directory
			Una directory è un file speciale contenente informazioni sui file contenuti nella directory ed è suddivisa in un certo numero di directory entry. 
			Ogni entry deve permettere di accedere a tutte le informazioni necessarie per gestire il file (in UNIX le entry contengono indici Inode).
			Gli attributi delle directory possono essere contenuti nelle directory entry (MS-DOS) o negli Inode (in UNIX tutte le info tranne il nome).

			NOMI DELLE DIRECTORY
				L'approccio più semplice è quello con nomi di lunghezza fissa, tuttavia la lunghezza massima deve essere ben calcolata:
					Nomi corti -> problema di espressività
					Nomi lunghi -> spreco di memoria
				
				Per implementare i nomi a lunghezza variabile si potrebbe porre come lunghezza massima 255 caratteri, anche se spreca molto spazio

				Un'altra idea sarebbe avere entry di lunghezza diversa, che oltre agli attributi contengano la propria lunghezza
					I nomi dei file sono terminati dal carattere 0

				Un quarto approccio è mantenere la lunghezza fissa delle entry e inserire i nomi di lunghezza variabili in un heap alla fine della directory

			SCANSIONE DELLE DIRECTORY
			La scansione lineare delle directory risulta inefficiente in presenza di cartelle molto grandi
				Si può utilizzare una tabella hash, più efficiente ma con possibili collisioni

			LINKING
			Hard link: i blocchi dei file non vengono listati nelle directory, ma in una struttura dati associata
				Quando si crea un hard link, viene incrementato il valore nell’Inode del file, in modo che questo sappia da quante directory entries è puntato. 
				Se viene rimosso un link, l’Inode rimane attivo finché il suo link counter non scende a 0.

			Symbolic link: creare un nuovo file che contenga il path del file a cui vuole essere linkato. 
				Un link simbolico è un file speciale che contiene un riferimento sotto forma di cammino assoluto al file in questione
				Quando si ricerca un file, lo si cerca nella directory e se si scopre che si tratta di un link questo viene risolto tramite il suo contenuto.

		- Tecniche per ottimizzare le prestazioni e garantire la coerenza
			Le cache permettono di accelerare l’accesso a dati in memoria secondarie. 
			
			Ma se le copie in memoria primaria vengono modificate, queste modifiche devono ripercuotersi anche sulle
			copie in memoria secondaria, altrimenti si causa incoerenza nel file system.

		FILE SYSTEM CHECKER
		Confrontano i dati delle directory con quelli contenuti nei blocchi dei dischi, tentando di correggere ogni incoerenza. 
		Questi programmi non garantiscono di recuperare tutti i dati che dovevano essere scritti su disco. 
		Tutti i file system checker verificano ogni file system (partizione del disco) indipendentemente dagli altri.

			FUNZIONAMENTO DI FSCK
			1. Scandisce la tabella degli Inode, controllando le incoerenze.
			2. Controlla le directory, che devono puntare ad Inode legali. 
			3. Scandisce l'albero da root per vedere se tutti i file sono raggiungibili. 
				Se trova Inode validi che non sono collegati li mette nella cartella Lost + Found. 
			4. Verifica il numero di riferimenti ad ogni file, controllando che sia corretto. 
				I blocchi dati devono essere usati da 0 (liberi) o 1 file.
				L'Inode deve comparire in uno o più directory, tante quante sono nel suo counter.
			5. Verifica Inode e blocchi liberi-occupati.
			6. Aggiorna le tabelle per salvare i cambiamenti

		SISTEMI BASATI SU JOURNALING
		Tutte le modifiche si annotano in modo sequenziale in un file di registrazione, detto log. 
		Ogni insieme di operazioni che esegue uno specifico compito si chiama transazione.
			Una transazione è un'operazione che viene eseguita in modo atomico: o tutto, o niente.
			Una transazione si considera completata (committed) quando è stata memorizzata nel log

		Periodicamente, le transazioni nel log vengono effettuate nel file system. 
			Quando un'intera transazione è stata completata, se ne rimuovono le annotazioni dal log.
			In caso di crollo del sistema nel log ci potranno essere zero o più transazioni.
				Le transazioni presenti non sono mai state ultimate nel file system e devono essere ripetute.


SICUREZZA
- Nozioni base
	Non esistono sistemi sicuri al 100%.
	Il livello massimo di sicurezza è uguale al suo anello più debole.
	Un sistema sicuro e composto da: hardware, software e HUMANWARE.
	Un sistema "sicuro" oggi non è detto sia sicuro un domani.
	Il livello di sicurezza di un sistema si misura in "trust".

- proprietà garanti di un sistema sicuro
	Data confidentiality: Se viene meno abbiamo disclosure.
	Data integrity: Se viene meno abbiamo alteration. 
	System availability: Se viene meno abbiamo denial of service.

- Policy
	Le componenti che descrivono la policy sono completamente estranee ai meccanismi di implementazione delle stesse rendendo i meccanismi
	che ne garantiscono l'integrità	capaci di cambiare senza impattare le policy lo stesso ragionamento vale al contrario.
	
- Crittografia
	Processo mediante il quale si rende un messaggio plaintext -> ciphertext in modo da garantirne la segretezza per tutti tranne il destinatario del messaggio.
		Implementazione
			Le implementazioni avvengono tramite one-way function "OWF"
				Dato x, f(x) è relativamente semplice da calcolare, ma dato f(x), calcolare x è “computazionalmente difficile" o “impossibile”.
			Solitamente "mescolano" i bit in modo complesso, spesso tramite diverse iterazioni sullo stesso insieme di bit
				Operazioni di bit swapping, inversioni etc.
			Un’alternativa è la fattorizzazione di numeri primi molto grandi.
				Due tipi di implementazione:
					Crittografia a chiave segreta
						Algoritmo DES	
					Crittografia a chiave pubblica
						Algoritmo RSA
						
- Tipologie di attacco
	Attivi
		Facile da individuare (dos)
	Passivi
		Difficili da scoprire (Spyware)
	Esterni
		Sistema solitamente compromesso attraverso la rete (dos)
	Interni
		Sistema compromesso dall'interno (Malware)
	Elementi sensibili: Hardware, Sistema operativo, Librerie e tool di sistema, Applicazioni, Utenti (Punto più critico).

		Esempio di attacco
			Step 1:	Individuare il sistema da attaccare.
			Step 2:	Ottenere informazioni sul sistema.
			Step 3:	Ottenere un nome utente
			Step 4:	Ricercare la password di quell'utente (Brute Force o attacco dizionario se la password è banale)
			Step 5:	Cercare di acquisire privilegi da superuser.
			Step 6:	Si installa un trojan horse.

	Buffer Overflow
		Si verifica una vulnerabilità di overflow del buffer quando si forniscono a un programma troppi dati.
		I dati in eccesso danneggiano lo spazio vicino permettendo all'attaccante di fare una injection di codice.
		In particolare, il buffer viene riempito con codice malevolo e si fa in modo che il codice in eccesso imposti come indirizzo di ritorno l'inizio del buffer.
		I linguaggi più suscettibili sono C e C++ data la loro natura a basso livello
			Fornisce totale libertà allo sviluppatore riguardo l'allocazione di memoria.

		Contromisure:
			Per difendersi dal buffer overflow bisogna:
				Scrivere codice sicuro, evitando lo “sloppy programming” (codice poco versatile)
				Non usare funzioni come gets(), strcpy() e sprintf().
			Soluzione 1: Aggiungere controlli a tempo di compilazione, che permettono di verificare i limiti del buffer.
				Svantaggio: In questo modo diminuiscono le prestazioni.
			Soluzione 2: Aggiungere patch per compilatori che controllano i valori di ritorno, copiandoli in posizione sicura. 
				Svantaggio: Richiede ri-compilazione delle applicazioni.
			Soluzione 3: È possibile effettuare controlli a run-time che ad esempio verificano le dimensioni degli stack frame
						 Se questi presentano qualche errore terminano il programma.

	Time-of-check to Time-of-use
		TOCTTOU è causato da modifiche in un sistema tra il controllo di una condizione (come verifica delle credenziali) e l'uso dei risultati di tale controllo.
	
	Trojan horse
		I Trojan horse sono programmi che replicano le funzionalità di programmi di uso comune o programmi dall’apparenza innocua ma che contengono codice malevolo.
		Soluzione: Comportarsi da utente consapevole quindi non lanciando eseguibili non trusted.

	Bombe Logiche
		Una bomba logica è un pezzo di codice inserito in un sistema software che attiva una funzione dannosa quando vengono soddisfatte le condizioni specificate.
			Ad esempio, se un dipendente viene licenziato e il suo nome sparisce dall'elenco dei salari
		Soluzione: controllare il codice sorgente.
		
	Backdoor / Trapdoor
		Backdoor / Trapdoor sono fatte per creare accessi secondari nel sistema, che aggirano i sistemi di protezione ordinari.
		Soluzione: controllare il codice sorgente.

	Virus/Worm
		Un virus è un frammento di programma che viene inserito in programmi esistenti modificandoli
			Viene creata quindi la possibilità di trasmettersi come frammento di programma in altri programmi (riproduzione) per poi attivarsi una volta diffuso.
		Un worm invece è esso stesso un programma che diffonde copie di sé stesso in rete.
		Oggi la distinzione tra virus e worm non è molto chiara.

		Worm di Morris
			Il Worm di Morris fu il primo worm ad attirare l’attenzione mediatica.
			Il worm sfruttava diverse vulnerabilità per ottenere l’accesso ai sistemi:
				- Un buco nella debug mode del programma sendmail di UNIX
					Gli utenti potevano mettere all’interno di un file l'indicazione per decidere a chi inoltrare la posta.
						In questo file si poteva inserire una pipe per fare elaborare la posta da un processo.
				- Un buffer overflow presente nel servizio finger.
				- La possibilità di accedere alla rete tramite esecuzione remota con rsh senza password.
			Questo worm non faceva nulla di male, era solo programmato per autoreplicarsi, ma in breve tempo saturò la rete.


- Autenticazione
	L'autenticazione consiste nel verificare l'identità di un utente in modo da fornirgli i permessi a lui concessi.
	I meccanismi di autenticazione sono basati su:
		- Qualcosa che l'utente "conosce".
		- Qualcosa che l'utente "ha".
		- qualcosa che l'utente "è".

	L'autenticazione basata su password è il tipo di autenticazione più utilizzato.
	
	Tipi di attacco	
		Meccanismo di salt - attacco database
			Per velocizzare l'operazione di cracking si può applicare la funzione hash al vocabolario, e memorizzare i valori criptati in un database.
			In questo modo, con il tempo di ricerca in un database si confrontano gli hash della vittima con quelli già memorizzati nel database.
			Soluzione: Non si cripta solo la password ma vi si aggiungono dei byte casuali (sale), il sale viene memorizzato in chiaro nel file delle password.
					   Inoltre, è meglio che l'elenco degli utenti non sia pubblico in modo da complicare i metodi di attacco.

		Login spoofing
			Viene creata dall’attaccante una finta schermata di login. Si attende che la vittima inserisca i dati di autenticazione.
			Quando l'utente immette i dati mostra un messaggio di login fallito, a questo punto fa partire il vero programma di login
				Nel mentre ha rubato i dati immessi (che erano giusti).
				La vittima crede di aver digitato male la password, la reimmette ma questa volta entrando senza problemi nel sistema.
			Soluzione 1: Essere molto accorti come utenti.
			Soluzione 2: Autenticazione tramite oggetti fisici.

		Packet sniffing
			Un packet sniffer è un software che analizza il traffico di rete.
			Cerca di individuare pacchetti contenenti coppie login/password spediti "in chiaro" da meccanismi di comunicazione come telnet e ftp (oggi deprecato).
			Memorizza le coppie login/password per uso futuro.
			Soluzione 1: Questo attacco è aggirabile utilizzando protocolli di criptazione dei dati in rete come SSL.
			Soluzione 2: Password challange e response.
			Soluzione 3: Password one-shot.

	PAM
		Pluggable Authentication Module (PAM) è un servizio generale di autenticazione basato su file di configurazione.
		Generalmente, ogni servizio crea un contatto chiaro con PAM e si aspetta una risposta di tipo "sì" o "no".
	
	Autorizzazione
		L'autorizzazione rappresenta l'insieme di meccanismi e politiche con cui il SO decide se un soggetto (processi) ha il permesso di
		eseguire una determinata azione su un oggetto (risorse).
		
		È realizzata tramite meccanismi software:
			- Trusted Computing Base, Reference Monitor.
			- Matrice di Accesso (ACL, Capability).

		Principi fondamentali
			- Principio di Accesso mediato: tutti gli accessi ad un oggetto devono essere controllati.
				In genere si controllano i diritti solo al momento dell’apertura.
			- Principio di Separazione dei privilegi: un sistema non dovrebbe concedere permessi in base ad una singola condizione.
			- Principio di Failsafe default: nessun soggetto ha diritti per default.
			- Principio di Privilegio minimo: ogni soggetto ha, in ogni istante, i soli diritti necessari per quella fase dell’elaborazione.
				Le azioni in più creano un problema di sicurezza.
			 
	
	Definizioni
		Come detto, abbiamo un insieme di soggetti S che sono le entità attive (processi, thread).
		Un insieme di oggetti O che sono le entità su cui si vogliono eseguire le azioni (S è un sottoinsieme di O).
		Infine, abbiamo un insieme di diritti di accesso R che è l'insieme delle azioni che si possono eseguire (read, write ecc).
		Per semplicità conviene uniformare i processi a livello di proprietario.
		Un Dominio di protezione è un insieme di coppie <o, rs> dove o è un oggetto ed rs è un sottoinsieme di R.
		Informalmente, ogni coppia specifica un oggetto e un insieme di azioni che possono essere eseguite su tale oggetto.
		Normalmente ogni utente (identificato da UID e GID) opera all’interno di un dominio di protezione che determina cosa può fare e cosa no.
		
	Matrice di accesso
		Una matrice di accesso è una matrice domini/oggetti.
		L'elemento A(i, j) contiene i diritti che il dominio Dj prevede per l'oggetto Oj.
		Quando si crea un nuovo oggetto viene aggiunta una colonna e il contenuto di tale colonna è deciso al momento della creazione dell’oggetto.
		L'associazione di un soggetto ad un dominio può essere statica o dinamica. Esempi di associazione dinamica sono SETUID di UNIX e run as di Windows.		
		
		Implementazione
			- Access control list
				La ACL associa ad ogni oggetto una lista di elementi <dominio, diritti di accesso>.
					Ad esempio user::rw, group::r. L'ampiezza della lista può essere ridotta associando i diritti a insiemi di domini o usando diritti standard.
				Ad esempio UNIX usa tre liste: owning user, owning group, others.
			- Capability
				Ad ogni dominio viene associata una lista di capability, ovvero coppie <oggetti, diritti di accesso>.
				Sono una sorta di chiave che apre la serratura che protegge l'oggetto.
				I Processi mantengono le capability e le presentano quali “credenziali” per accedere all'oggetto.
				Per funzionare le capability non devono essere coniate.
				Esistono quindi due possibili approcci:
					- Capability mantenute nello spazio kernel associato al processo, protette dai meccanismi di protezione del kernel.
					- Capability mantenute nello spazio utente: protette da meccanismi crittografici, memorizzate dai processi ma non modificabili 
						(approccio usato nei sistemi distribuiti).
			- Revoca
				La revoca dei diritti di accesso può essere:
					- Immediata o ritardata (subito o si può attendere).
					- Selettiva o generale (per alcuni i domini o per tutti).
					- Parziale o totale (tutti i diritti o solo alcuni).
					- Temporanea o permanente.
				Nei sistemi ACL è sufficiente aggiornare in modo corrispondente le strutture dati dei diritti di accesso (si rimuove un dominio dall'oggetto).
				Tuttavia, nei sistemi basati su capability l'informazione è memorizzata nei processi. In questo caso abbiamo più opzioni da cui scegliere:
					- Capability a validità temporale limitata: Una capability “scade” dopo un prefissato periodo di tempo.
					- Doppia memorizzazione: ogni capability viene controllata prima di essere utilizzata (si perdono alcuni benefici delle capability).
					- Capability indirette: si assegnano i diritti non agli oggetti ma ad elementi di una tabella globale che puntano agli oggetti.
					 È possibile revocare diritti cancellando elementi dalla tabella intermedia.
					- Cambio identità dell'oggetto: I processi devono chiedere nuovamente l'autorizzazione di accesso.