<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://so.v2.cs.unibo.it/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=FedericoB</id>
	<title>Sistemi Operativi - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://so.v2.cs.unibo.it/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=FedericoB"/>
	<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php/Special:Contributions/FedericoB"/>
	<updated>2026-04-29T16:04:55Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.35.5</generator>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaTeorica_2014.02.22&amp;diff=1961</id>
		<title>ProvaTeorica 2014.02.22</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaTeorica_2014.02.22&amp;diff=1961"/>
		<updated>2017-09-10T12:49:48Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: /* Soluzione di FedericoB */ Corretto nome di variabile nella spiegazione di una soluzione.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Testo: [http://www.cs.unibo.it/~renzo/so/compiti/2014.02.21.tot.pdf]&lt;br /&gt;
&lt;br /&gt;
==Esercizio c.1==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Coci===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
monitor bbwl {&lt;br /&gt;
	condition oktowrite;&lt;br /&gt;
	condition oktolog;&lt;br /&gt;
	condition oktoread;&lt;br /&gt;
	int logging = 0;&lt;br /&gt;
	queue q;&lt;br /&gt;
	&lt;br /&gt;
	procedure entry write (eltype elem) {&lt;br /&gt;
	if (q.length() == MAXELEM || logging)&lt;br /&gt;
			oktowrite.wait();&lt;br /&gt;
	q.enqueue (elem);&lt;br /&gt;
	logging = 1;&lt;br /&gt;
	oktolog.signal();&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure entry eltype log() {&lt;br /&gt;
	if (q.length == 0)&lt;br /&gt;
		oktolog.wait();&lt;br /&gt;
	eltype tmp = q.top();&lt;br /&gt;
	logging = 0;&lt;br /&gt;
	oktoread.signal();&lt;br /&gt;
	return tmp;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	procedure entry eltype read() {&lt;br /&gt;
	if (q.length == 0 || logging)&lt;br /&gt;
		oktoread.wait();&lt;br /&gt;
	eltype tmp = q.dequeue();&lt;br /&gt;
	oktowrite.signal();&lt;br /&gt;
	return tmp;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Save===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
condition oktowrite, oktoread, oktolog ;&lt;br /&gt;
queue buffer = MAXELEM ;&lt;br /&gt;
queue Llogger ;&lt;br /&gt;
&lt;br /&gt;
monitor bbwl&lt;br /&gt;
{&lt;br /&gt;
 procedure entry write (altype elem)&lt;br /&gt;
 {&lt;br /&gt;
	if (buffer.length &amp;gt;= MAXELEM)&lt;br /&gt;
		oktorite.wait() ;&lt;br /&gt;
	 buffer.enqueue(elem);&lt;br /&gt;
     oktolog.signal () ;	&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
procedur entry logger()                 # logger deve tenere traccia di tutti i pacchetti transitati mettendoli in una sua memoria.&lt;br /&gt;
 {&lt;br /&gt;
	if (buffer.length == 0)&lt;br /&gt;
		oktolog.wait();&lt;br /&gt;
	 Llogger.enqueue(buffer.front())    # restituisci il valore in testa di buffer e inseriscilo in coda a Llogger&lt;br /&gt;
	 ok toread.signal() ;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 procedure entry read() &lt;br /&gt;
 {&lt;br /&gt;
	if (buffer.length == 0)&lt;br /&gt;
		oktoread.wait();&lt;br /&gt;
	 buffer.dequeue()                   # restituisci il valore in testa di buffer e rimuovilo.&lt;br /&gt;
	 oktowrite.signal() ;&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
save&lt;br /&gt;
&lt;br /&gt;
==Esercizio c.2==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Coci===&lt;br /&gt;
&lt;br /&gt;
alpha(x,y): &amp;lt;x=4, y=sqrt(x)&amp;gt;&lt;br /&gt;
&lt;br /&gt;
il valore che viene salvato in y è sempre 2, quindi non va bene.&lt;br /&gt;
&lt;br /&gt;
bravo(x,y): &amp;lt;y=sqrt(x), x=4&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
xi yi xf yf&lt;br /&gt;
0  0  4  0&lt;br /&gt;
0  1  4  1&lt;br /&gt;
1  0  4  1&lt;br /&gt;
1  1  4  1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
il valore iniziale di x viene salvato in y, quindi va bene.&lt;br /&gt;
&lt;br /&gt;
charlie(x,y): &amp;lt;y=sqrt(x), x=4*y&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anche questo va bene, per le stesse ragioni di bravo.&lt;br /&gt;
&lt;br /&gt;
delta(z,t): &amp;lt;z=z xor t, t=z xor t&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
zi ti zf tf&lt;br /&gt;
0  0  0  0&lt;br /&gt;
0  1  1  0&lt;br /&gt;
1  0  1  1&lt;br /&gt;
1  1  0  1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il valore iniziale di z viene salvato in t, quindi va bene.&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Diego.tului===&lt;br /&gt;
&lt;br /&gt;
   alpha: non funziona perché y non andrà mai a 0, quindi non entrerà mai in CS.&lt;br /&gt;
&lt;br /&gt;
   bravo: funziona perché quando y va a 0 x rimane 4, e perciò finché x non ritorna 0 la CS è occupata.&lt;br /&gt;
&lt;br /&gt;
   charlie: dipende dal valore iniziale di x, perché se all'inizio x = 0 (y = 0, x = 4 * 0 = 0) quindi non ci&lt;br /&gt;
   sarà mutua esclusione.&lt;br /&gt;
&lt;br /&gt;
   delta: dipende dal valore iniziale di t, perché se all'inizio è 0, 0 xor 0 resta sempre 0, ergo come sopra.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Diego&lt;br /&gt;
&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
alpha assegna valori costanti quindi non è possibile utilizzarla per la test and set in quanto il suo risultato non può essere influenzato da parametri esterni.&lt;br /&gt;
&lt;br /&gt;
bravo(lock,vp) assegna a lock 4 e a vp la radice del vecchio lock&lt;br /&gt;
Un possibile codice che utilizza bravo è il seguente:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lock=0&lt;br /&gt;
process p&lt;br /&gt;
	int vp;&lt;br /&gt;
	while(1) {&lt;br /&gt;
		do {&lt;br /&gt;
			bravo(lock,vp)&lt;br /&gt;
		} while (vp==2)&lt;br /&gt;
		//sezione critica&lt;br /&gt;
                vp=0&lt;br /&gt;
		bravo(vp,lock) //assegno a lock 0&lt;br /&gt;
		}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
charlie(lock,vp) assegna a lock 4 moltiplicato per la radice del vecchio lock e a vp la radice del vecchio lock &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lock=1&lt;br /&gt;
process p&lt;br /&gt;
	int vp;&lt;br /&gt;
	while(1) {&lt;br /&gt;
		do {&lt;br /&gt;
			charlie(lock,vp)&lt;br /&gt;
		} while (vp!=1)&lt;br /&gt;
		//sezione critica&lt;br /&gt;
		vp=1&lt;br /&gt;
                charlie(vp,lock) //assegno a lock 1&lt;br /&gt;
		}&lt;br /&gt;
&amp;lt;/pre&amp;gt;		&lt;br /&gt;
Ho cambiato il valore iniziale del lock per evitare l'effetto assorbente dello zero.&lt;br /&gt;
Inoltre ora quando il lock è impostato i processi in attesa avranno un vp completamente differente tra loro quindi ho dovuto utilizzare una diseguaglianza.&lt;br /&gt;
&lt;br /&gt;
delta(z,t) &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     zi ti zf tf&lt;br /&gt;
comb1 0  0  0  0&lt;br /&gt;
comb2 0  1  1  0&lt;br /&gt;
comb3 1  0  1  1&lt;br /&gt;
comb4 1  1  0  1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Impossibile perchè nessuna delle combinazioni di input e utilizzabile.&lt;br /&gt;
La combinazioni 1 e 3 non cambiano il lock e quindi non ci sarebbe mutua esclusione.&lt;br /&gt;
Utilizzando le combinazioni 2 e 3 (quindi con vp inizializzato a 1) non ci sarebbe comunque mutua esclusione perchè&lt;br /&gt;
se inizializziamo lock a 0 viene messo dalla combinazione 2 a 1 e vp=0, il primo processo ottiene la sezione critica.&lt;br /&gt;
Un altro processo si troverebbe il lock impostato a 1 e vp=1 per inizializzazione locale.&lt;br /&gt;
Verrebbe quindi utilizzata la combinazione 4 che però riporterebbe il lock a 0 e quindi un terzo processo potrebbe entrare in sezione critica.&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.1==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Coci===&lt;br /&gt;
&lt;br /&gt;
Sia n=3 il numero di pagine mantenute in memoria&lt;br /&gt;
&lt;br /&gt;
a) 123456789123456789...&lt;br /&gt;
&lt;br /&gt;
MIN = MINNUM&lt;br /&gt;
 11144777&lt;br /&gt;
  2225588&lt;br /&gt;
    333669&lt;br /&gt;
&lt;br /&gt;
b) 145231231231231...&lt;br /&gt;
&lt;br /&gt;
MINNUM&lt;br /&gt;
&lt;br /&gt;
 111232123&lt;br /&gt;
   44444444&lt;br /&gt;
     5555555&lt;br /&gt;
&lt;br /&gt;
MIN&lt;br /&gt;
 111111111&lt;br /&gt;
   44222222&lt;br /&gt;
     5333333&lt;br /&gt;
&lt;br /&gt;
Alessandro&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Diego.tului===&lt;br /&gt;
&lt;br /&gt;
Sia n=3 il numero di pagine mantenute in memoria&lt;br /&gt;
&lt;br /&gt;
la soluzione di Alessandro secondo noi presenta dei problemi, la integriamo:&lt;br /&gt;
&lt;br /&gt;
a) 1234431432431432431&lt;br /&gt;
&lt;br /&gt;
   111444444444444444&lt;br /&gt;
    222221112221112221&lt;br /&gt;
     3333333333333333&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaTeorica_2014.02.22&amp;diff=1959</id>
		<title>ProvaTeorica 2014.02.22</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaTeorica_2014.02.22&amp;diff=1959"/>
		<updated>2017-07-16T18:30:09Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: /* Soluzione di FedericoB */ Corretta soluzione a seguito di una segnalazione fatta da un collega&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Testo: [http://www.cs.unibo.it/~renzo/so/compiti/2014.02.21.tot.pdf]&lt;br /&gt;
&lt;br /&gt;
==Esercizio c.1==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Coci===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
monitor bbwl {&lt;br /&gt;
	condition oktowrite;&lt;br /&gt;
	condition oktolog;&lt;br /&gt;
	condition oktoread;&lt;br /&gt;
	int logging = 0;&lt;br /&gt;
	queue q;&lt;br /&gt;
	&lt;br /&gt;
	procedure entry write (eltype elem) {&lt;br /&gt;
	if (q.length() == MAXELEM || logging)&lt;br /&gt;
			oktowrite.wait();&lt;br /&gt;
	q.enqueue (elem);&lt;br /&gt;
	logging = 1;&lt;br /&gt;
	oktolog.signal();&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure entry eltype log() {&lt;br /&gt;
	if (q.length == 0)&lt;br /&gt;
		oktolog.wait();&lt;br /&gt;
	eltype tmp = q.top();&lt;br /&gt;
	logging = 0;&lt;br /&gt;
	oktoread.signal();&lt;br /&gt;
	return tmp;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	procedure entry eltype read() {&lt;br /&gt;
	if (q.length == 0 || logging)&lt;br /&gt;
		oktoread.wait();&lt;br /&gt;
	eltype tmp = q.dequeue();&lt;br /&gt;
	oktowrite.signal();&lt;br /&gt;
	return tmp;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Save===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
condition oktowrite, oktoread, oktolog ;&lt;br /&gt;
queue buffer = MAXELEM ;&lt;br /&gt;
queue Llogger ;&lt;br /&gt;
&lt;br /&gt;
monitor bbwl&lt;br /&gt;
{&lt;br /&gt;
 procedure entry write (altype elem)&lt;br /&gt;
 {&lt;br /&gt;
	if (buffer.length &amp;gt;= MAXELEM)&lt;br /&gt;
		oktorite.wait() ;&lt;br /&gt;
	 buffer.enqueue(elem);&lt;br /&gt;
     oktolog.signal () ;	&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
procedur entry logger()                 # logger deve tenere traccia di tutti i pacchetti transitati mettendoli in una sua memoria.&lt;br /&gt;
 {&lt;br /&gt;
	if (buffer.length == 0)&lt;br /&gt;
		oktolog.wait();&lt;br /&gt;
	 Llogger.enqueue(buffer.front())    # restituisci il valore in testa di buffer e inseriscilo in coda a Llogger&lt;br /&gt;
	 ok toread.signal() ;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 procedure entry read() &lt;br /&gt;
 {&lt;br /&gt;
	if (buffer.length == 0)&lt;br /&gt;
		oktoread.wait();&lt;br /&gt;
	 buffer.dequeue()                   # restituisci il valore in testa di buffer e rimuovilo.&lt;br /&gt;
	 oktowrite.signal() ;&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
save&lt;br /&gt;
&lt;br /&gt;
==Esercizio c.2==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Coci===&lt;br /&gt;
&lt;br /&gt;
alpha(x,y): &amp;lt;x=4, y=sqrt(x)&amp;gt;&lt;br /&gt;
&lt;br /&gt;
il valore che viene salvato in y è sempre 2, quindi non va bene.&lt;br /&gt;
&lt;br /&gt;
bravo(x,y): &amp;lt;y=sqrt(x), x=4&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
xi yi xf yf&lt;br /&gt;
0  0  4  0&lt;br /&gt;
0  1  4  1&lt;br /&gt;
1  0  4  1&lt;br /&gt;
1  1  4  1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
il valore iniziale di x viene salvato in y, quindi va bene.&lt;br /&gt;
&lt;br /&gt;
charlie(x,y): &amp;lt;y=sqrt(x), x=4*y&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anche questo va bene, per le stesse ragioni di bravo.&lt;br /&gt;
&lt;br /&gt;
delta(z,t): &amp;lt;z=z xor t, t=z xor t&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
zi ti zf tf&lt;br /&gt;
0  0  0  0&lt;br /&gt;
0  1  1  0&lt;br /&gt;
1  0  1  1&lt;br /&gt;
1  1  0  1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il valore iniziale di z viene salvato in t, quindi va bene.&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Diego.tului===&lt;br /&gt;
&lt;br /&gt;
   alpha: non funziona perché y non andrà mai a 0, quindi non entrerà mai in CS.&lt;br /&gt;
&lt;br /&gt;
   bravo: funziona perché quando y va a 0 x rimane 4, e perciò finché x non ritorna 0 la CS è occupata.&lt;br /&gt;
&lt;br /&gt;
   charlie: dipende dal valore iniziale di x, perché se all'inizio x = 0 (y = 0, x = 4 * 0 = 0) quindi non ci&lt;br /&gt;
   sarà mutua esclusione.&lt;br /&gt;
&lt;br /&gt;
   delta: dipende dal valore iniziale di t, perché se all'inizio è 0, 0 xor 0 resta sempre 0, ergo come sopra.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Diego&lt;br /&gt;
&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
alpha assegna valori costanti quindi non è possibile utilizzarla per la test and set in quanto il suo risultato non può essere influenzato da parametri esterni.&lt;br /&gt;
&lt;br /&gt;
bravo(lock,vp) assegna a lock 4 e a y la radice del vecchio lock&lt;br /&gt;
Un possibile codice che utilizza bravo è il seguente:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lock=0&lt;br /&gt;
process p&lt;br /&gt;
	int vp;&lt;br /&gt;
	while(1) {&lt;br /&gt;
		do {&lt;br /&gt;
			bravo(lock,vp)&lt;br /&gt;
		} while (vp==2)&lt;br /&gt;
		//sezione critica&lt;br /&gt;
                vp=0&lt;br /&gt;
		bravo(vp,lock) //assegno a lock 0&lt;br /&gt;
		}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
charlie(lock,vp) assegna a lock 4 moltiplicato per la radice del vecchio lock e a vp la radice del vecchio lock &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lock=1&lt;br /&gt;
process p&lt;br /&gt;
	int vp;&lt;br /&gt;
	while(1) {&lt;br /&gt;
		do {&lt;br /&gt;
			charlie(lock,vp)&lt;br /&gt;
		} while (vp!=1)&lt;br /&gt;
		//sezione critica&lt;br /&gt;
		vp=1&lt;br /&gt;
                charlie(vp,lock) //assegno a lock 1&lt;br /&gt;
		}&lt;br /&gt;
&amp;lt;/pre&amp;gt;		&lt;br /&gt;
Ho cambiato il valore iniziale del lock per evitare l'effetto assorbente dello zero.&lt;br /&gt;
Inoltre ora quando il lock è impostato i processi in attesa avranno un vp completamente differente tra loro quindi ho dovuto utilizzare una diseguaglianza.&lt;br /&gt;
&lt;br /&gt;
delta(z,t) &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     zi ti zf tf&lt;br /&gt;
comb1 0  0  0  0&lt;br /&gt;
comb2 0  1  1  0&lt;br /&gt;
comb3 1  0  1  1&lt;br /&gt;
comb4 1  1  0  1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Impossibile perchè nessuna delle combinazioni di input e utilizzabile.&lt;br /&gt;
La combinazioni 1 e 3 non cambiano il lock e quindi non ci sarebbe mutua esclusione.&lt;br /&gt;
Utilizzando le combinazioni 2 e 3 (quindi con vp inizializzato a 1) non ci sarebbe comunque mutua esclusione perchè&lt;br /&gt;
se inizializziamo lock a 0 viene messo dalla combinazione 2 a 1 e vp=0, il primo processo ottiene la sezione critica.&lt;br /&gt;
Un altro processo si troverebbe il lock impostato a 1 e vp=1 per inizializzazione locale.&lt;br /&gt;
Verrebbe quindi utilizzata la combinazione 4 che però riporterebbe il lock a 0 e quindi un terzo processo potrebbe entrare in sezione critica.&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.1==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Coci===&lt;br /&gt;
&lt;br /&gt;
Sia n=3 il numero di pagine mantenute in memoria&lt;br /&gt;
&lt;br /&gt;
a) 123456789123456789...&lt;br /&gt;
&lt;br /&gt;
MIN = MINNUM&lt;br /&gt;
 11144777&lt;br /&gt;
  2225588&lt;br /&gt;
    333669&lt;br /&gt;
&lt;br /&gt;
b) 145231231231231...&lt;br /&gt;
&lt;br /&gt;
MINNUM&lt;br /&gt;
&lt;br /&gt;
 111232123&lt;br /&gt;
   44444444&lt;br /&gt;
     5555555&lt;br /&gt;
&lt;br /&gt;
MIN&lt;br /&gt;
 111111111&lt;br /&gt;
   44222222&lt;br /&gt;
     5333333&lt;br /&gt;
&lt;br /&gt;
Alessandro&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Diego.tului===&lt;br /&gt;
&lt;br /&gt;
Sia n=3 il numero di pagine mantenute in memoria&lt;br /&gt;
&lt;br /&gt;
la soluzione di Alessandro secondo noi presenta dei problemi, la integriamo:&lt;br /&gt;
&lt;br /&gt;
a) 1234431432431432431&lt;br /&gt;
&lt;br /&gt;
   111444444444444444&lt;br /&gt;
    222221112221112221&lt;br /&gt;
     3333333333333333&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2017.01.18&amp;diff=1944</id>
		<title>Prova teorica 2017.01.18</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2017.01.18&amp;diff=1944"/>
		<updated>2017-06-18T20:17:19Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: /* Esercizio g.2 */ aggiunta risposta esercizio 2&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/compiti/2017.01.18.ris.pdf link al compito]&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.2==&lt;br /&gt;
&lt;br /&gt;
# '''Perché un algoritmo di rimpiazzamento a stack non soffre dell’anomalia di Belady?''' &amp;lt;br&amp;gt; L'anomalia di Belady consiste nel aumento del page fault al aumentare dei frame in memoria. Se un algoritmo è a stack vuol dire che fissato una stringa di riferimento, in qualsiasi instante t l'insieme delle pagine con n frame è un sottoinsieme del insieme della pagine con n+1 frame. Quindi i page fault non possono aumentare perchè comunque è presente il set di pagine che era presente anche con n frame più un addizionale pagina.&lt;br /&gt;
# '''Perché si può usare un meccanismo di crittografia simmetrica nella gestione delle capability di accesso alle risorse?''' &amp;lt;br&amp;gt; Possiamo usare la crittografia simmetrica per la gestione delle capability se queste sono salvate nella parte di memoria utente del processo. Non dovendo trasmettere la chiave attraverso la rete possiamo utilizzare crittografia simmetrica.&lt;br /&gt;
# '''Quali operazioni svolge il processore per la gestione di un interrupt e quali invece sono a carico del kernel del sistema operativo?''' &amp;lt;br&amp;gt; Il processore si occupa, all'attivazione della linea elettronica dell interrupt di recuperare dalla memoria in locazioni fisse l'indirizzo delle istruzioni di gestione degli interrupt. In seguito carica questo indirizzo nel program counter e quindi &amp;quot;salta&amp;quot; a queste istruzioni attivando la modalità di esecuzione kernel mode. &amp;lt;br&amp;gt; Le routine di gestione degli interrupt fanno parte del codice kernel e si occupano di gestire opportunamente le operazioni necessarie in base al tipo di interrupt. Per esempio operazioni come eseguire lo scheduler o recuperare l'output di un device.&lt;br /&gt;
# '''Perche’ il mascheramento degli interrupt non si può utilizzare per creare sezioni critiche in sistemi multicore o multiprocessore?''' &amp;lt;br&amp;gt; Il mascheramento degli interrupt previene l'interruzione del esecuzione del processo corrente e il context switch a un altro processo e di conseguenza la non atomicità delle istruzioni che stava eseguendo il processo. &amp;lt;br&amp;gt; Anche disabilitando gli interrupt (è uguale se su un solo processore o su tutti) c'è comunque la possibilità che altro codice eseguito in ''parallelismo reale'' vada in concorrenza con quello dove si sta cercando di ottenere una sezione critica.&lt;br /&gt;
# '''Per quali tipi di processo l’algoritmo di schedulazione round-robin è efficiente e per quali no?''' &amp;lt;br&amp;gt; L'algoritmo di schedulazione round-robin è efficente per i processi I/O bound in quanto è ''fair'', cioè permette a tutti i processi il diritto di esecuzione entro un certo lasso di tempo. Può non essere efficiente per i processi CPU bound che hanno tempi di esecuzione lunghi e il round robin li costringe a ritardare il loro completamento. Inoltre un processo CPU bound potrebbe essere interrotto quando manca davvero poco al suo termine ma il time slice è terminato oppure necessita di I/O.Il processo viene messo in coda e bisognerà aspettare l'esecuzione di tutti gli altri processi prima di poter finire l'esecuzione.Uno scheduler shortest remaining time first avrebbe invece rimesso subito in esecuzione il processo CPU bound se il tempo di esecuzione previsto rimanente era davvero ridotto.&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2017.05.29&amp;diff=1943</id>
		<title>Prova teorica 2017.05.29</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2017.05.29&amp;diff=1943"/>
		<updated>2017-06-18T20:12:20Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: /* Esercizio g.2 */ aggiunte risposte domande 1, 3 e 4&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/compiti/2017.05.29.tot.pdf link al compito]&lt;br /&gt;
&lt;br /&gt;
==Esercizio c.1==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Save===&lt;br /&gt;
&lt;br /&gt;
se qualcuno può correggermi o confermare il rispetto delle regole concorrenziali, mi fa un grande piacere. &lt;br /&gt;
saluti&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
Monitor rubaBandiera () {&lt;br /&gt;
&lt;br /&gt;
  condition oktochiama, oktopunteggio, oktopronto; &lt;br /&gt;
&lt;br /&gt;
  listnum [] = [1,2,3,4]&lt;br /&gt;
  numeri[]&lt;br /&gt;
  numeriChiamati[0...MAX-1]&lt;br /&gt;
  studentiPronti = True; &lt;br /&gt;
&lt;br /&gt;
  punteggio [A] = 0;&lt;br /&gt;
  punteggio [B] = 0;&lt;br /&gt;
&lt;br /&gt;
 procedure entry gioco() {&lt;br /&gt;
   &lt;br /&gt;
  &lt;br /&gt;
      n=random(listnum);&lt;br /&gt;
      numeri(n)=random(numeriChiamati);&lt;br /&gt;
&lt;br /&gt;
      if( studentiPronti == false)&lt;br /&gt;
&lt;br /&gt;
             oktochiama(numeri).wait() ;&lt;br /&gt;
&lt;br /&gt;
      &lt;br /&gt;
      studentiPronti(numeri) = false ;&lt;br /&gt;
      oktopunteggio.signal();&lt;br /&gt;
}&lt;br /&gt;
 procedure entry studente () {&lt;br /&gt;
&lt;br /&gt;
    while(studentiPronti(numeri))&lt;br /&gt;
&lt;br /&gt;
         oktochiama(numeri).signal();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 procedure entry chiama () {&lt;br /&gt;
&lt;br /&gt;
      print(punteggio[A] &amp;quot;:&amp;quot; punteggio[B]);&lt;br /&gt;
      if (studentiPronti(numeri))&lt;br /&gt;
&lt;br /&gt;
         oktopunteggio.wait()&lt;br /&gt;
&lt;br /&gt;
      if (allabadiera(A))&lt;br /&gt;
         punteggio[A] ++;&lt;br /&gt;
      else punteggio [B]++;&lt;br /&gt;
      print(punteggio[A] &amp;quot;:&amp;quot; punteggio[B]);&lt;br /&gt;
      studentiPronti(numeri) = true&lt;br /&gt;
&lt;br /&gt;
      pronto();&lt;br /&gt;
&lt;br /&gt;
 procedure entry pronto() {&lt;br /&gt;
&lt;br /&gt;
      if(punteggio[A] || punteggio[B] &amp;lt; 10)&lt;br /&gt;
&lt;br /&gt;
        return 0;&lt;br /&gt;
      else return 1;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Secondo devi utilizzare il nome corretto per le procedure entry, cioè mancano nuovapartita e allabandiera, che non hai definito. Con i corretti parametri. --[[User:FedericoB|FedericoB]] ([[User talk:FedericoB|talk]]) 18:45, 15 June 2017 (CEST)&lt;br /&gt;
===Soluzione di Stefano Zaniboni===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
Monitor rb () {&lt;br /&gt;
&lt;br /&gt;
  condition ok2chiama, ok2run[MAX], ok2fine&lt;br /&gt;
  int npronti, punteggio[2], arrivati, c&lt;br /&gt;
&lt;br /&gt;
 procedure entry void nuovapartita() {&lt;br /&gt;
    c = npronti = arrived punteggio[0] = punteggio[1] = 0&lt;br /&gt;
}&lt;br /&gt;
 procedure entry int chiama(int *chiamati) {&lt;br /&gt;
     c = chiamati.length&lt;br /&gt;
     if(npronti &amp;lt; 2 * MAX)&lt;br /&gt;
           ok2chiama.wait()&lt;br /&gt;
     for(int i = 0; i &amp;lt; c; i++){&lt;br /&gt;
           ok2run[i].signal()&lt;br /&gt;
           ok2run[i].signal()&lt;br /&gt;
     }&lt;br /&gt;
     ok2fine.wait()&lt;br /&gt;
     if(max(punteggio) == 10)&lt;br /&gt;
           for(int i = 0; i in range(MAX); i++){&lt;br /&gt;
                ok2run[i].signal()&lt;br /&gt;
                ok2run[i].signal()&lt;br /&gt;
          }  &lt;br /&gt;
      return punteggio;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
 procedure entry int pronto(int squadra, int numero) {&lt;br /&gt;
      if(max(punteggio) == 10)&lt;br /&gt;
            return -1&lt;br /&gt;
      npronti++ &lt;br /&gt;
      if (npronti &amp;gt; 2 * MAX)&lt;br /&gt;
            ok2chiama.signal()&lt;br /&gt;
            if(n not in chiamata)&lt;br /&gt;
                   ok2run[n].wait()&lt;br /&gt;
       npronti--&lt;br /&gt;
       if(max(punteggio) == 10)&lt;br /&gt;
                return -1&lt;br /&gt;
       else return 0&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
 procedure entry void allabandiera(int squadra, int numero) {&lt;br /&gt;
      arrived++ &lt;br /&gt;
      if(arrived = 2 * c)&lt;br /&gt;
                 punteggio[1 - squadra]++&lt;br /&gt;
                 ok2fine.signal()&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Esercizio c.2==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Stefano Zaniboni===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
sia dato un servizio di message passing asincrono distratto. Questo servizio si comporta come un servizio di message passing asincrono ma talvolta dimentica la destinazione.&lt;br /&gt;
E’ però possibile indicare un processo come “ufficio messaggi smarriti” al quale verranno recapitati tutti i messaggi per i quali il servizio distratto ha dimenticato la destinazione.&lt;br /&gt;
&lt;br /&gt;
Void dmsgsend(pid_t dest, msg_t msg); //si comporta come amsgsend ma può dimenticare la desticazione&lt;br /&gt;
msg_t dmsgrecv() //si comporta come amsgrecv(*) &lt;br /&gt;
void dset_lost_n_found(pid_t pid); //indica il processo per i messaggi smarriti.&lt;br /&gt;
&lt;br /&gt;
Usando il servizio “distratto” e un processo “ufficio messaggi smarriti”, &lt;br /&gt;
implementare un servizio di message passing standard (senza la selezione del mittente in ricezione, la amsgrecv riceve da qualsiasi mittente).&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void asend(pid_t dest, msg_t msg){&lt;br /&gt;
        dmsgsend(dest,{getpid(), dest, msg})&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
msg_t arecv(pid_t sender){&lt;br /&gt;
        {snd, dst, m} = dmsgrecv()&lt;br /&gt;
        return m&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
process l+f{&lt;br /&gt;
        dset_lost_n_found(getpid())&lt;br /&gt;
        while(true){&lt;br /&gt;
               {snd, dst, m} = dmsgrecv()&lt;br /&gt;
               if dst != getpid(){&lt;br /&gt;
                        asend(dst, {snd, dst, m})&lt;br /&gt;
               }&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.1==&lt;br /&gt;
===Soluzione di GiovanniF===&lt;br /&gt;
&lt;br /&gt;
min = lifo 1234123124123124...&lt;br /&gt;
&lt;br /&gt;
lifo = lru 123124123124123124...&lt;br /&gt;
&lt;br /&gt;
--[[User:FedericoB|FedericoB]] ([[User talk:FedericoB|talk]]) 10:17, 16 June 2017 (CEST) &amp;lt;br&amp;gt;&lt;br /&gt;
Ho verificato la soluzione:&amp;lt;br&amp;gt;&lt;br /&gt;
Il ragionamento da fare qui è: &amp;lt;br&amp;gt;&lt;br /&gt;
MIN = LIFO &amp;lt;br&amp;gt;&lt;br /&gt;
MIN sceglie la pagina che più tardi nel futuro verrà richiesta &amp;lt;br&amp;gt;&lt;br /&gt;
LIFO sceglie l'ultima pagina caricata &amp;lt;br&amp;gt;&lt;br /&gt;
Quindi ad ogni page fault l'ultima pagina caricata deve essere quella che più tardi nel futuro verrà richiesta&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MIN&lt;br /&gt;
1	2	3	4	1	2	3	1	2	4	1	2	3	1	2	4&lt;br /&gt;
1	1	1	1	1	1	1	1	1	1	1	1	1	1	1	1&lt;br /&gt;
	2	2	2	2	2	2	2	2	2	2	2	2	2	2	2&lt;br /&gt;
		3	4	4	4	3	3	3	4	4	4	3	3	3	4&lt;br /&gt;
			#			#			#			#			#&lt;br /&gt;
&lt;br /&gt;
LIFO&lt;br /&gt;
1	2	3	4	1	2	3	1	2	4	1	2	3	1	2	4&lt;br /&gt;
1	1	1	1	1	1	1	1	1	1	1	1	1	1	1	1&lt;br /&gt;
	2	2	2	2	2	2	2	2	2	1	2	2	2	2	2&lt;br /&gt;
		3	4	4	4	3	3	3	4	4	4	3	3	3	4&lt;br /&gt;
			#			#			#			#			#&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
LIFO = LRU &amp;lt;br&amp;gt;&lt;br /&gt;
LRU sceglie la pagina che più precedentemente nel passato è stata richiesta &amp;lt;br&amp;gt;&lt;br /&gt;
Quindi ad ogni page fault l'ultima pagina caricata deve essere quella che più precedentemente nel passato è stata richiesta&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LRU&lt;br /&gt;
1	2	3	1	2	4	1	2	3	1	2	4	1	2	3&lt;br /&gt;
1	1	1	1	1	1	1	1	1	1	1	1	1	1	1&lt;br /&gt;
	2	2	2	2	2	2	2	2	2	2	2	2	2	2&lt;br /&gt;
		3	3	3	4	4	4	3	3	3	4	4	4	4&lt;br /&gt;
					#			#			#			#&lt;br /&gt;
&lt;br /&gt;
LIFO&lt;br /&gt;
1	2	3	1	2	4	1	2	3	1	2	4	1	2	3&lt;br /&gt;
1	1	1	1	1	1	1	1	1	1	1	1	1	1	1&lt;br /&gt;
	2	2	2	2	2	2	2	2	2	2	2	2	2	2&lt;br /&gt;
		3	3	3	4	4	4	3	3	3	4	4	4	4&lt;br /&gt;
					#			#			#			#&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.2==&lt;br /&gt;
#'''Un i-node di un file system tipo ext2 per un errore viene riportato un numero di link maggiore del reale. Cosa può succedere se si continua ad usare il file system? (perchè?) E se il numero di link errato fosse il contrario inferiore al reale cosa potrebbe succedere? (perchè?) ''' &amp;lt;br&amp;gt; Che quando i reali link al i-node verrano eliminati e il numero decrementato questo non raggiungerà mai zero e lo spazio non verrà rilasciato. Se invece il numero di link è inferiore al reale può succedere che con solo l'eliminazione di pochi link al inode l'area del file venga segnata come libera perchè il sistema operativo pensa che l'inode non sia più utilizzato.&lt;br /&gt;
#'''Perchè è necessario usare spinlock in sistemi multiprocessore per implementare kernel di tipo simmetrico (SMP)?''' &amp;lt;br&amp;gt;Perchè la semplice disabilitazione degli interrupt non sarebbe sufficiente in caso di parallelismo reale a prevenire l'accesso a memoria condivisa in concorrenza. Bisogna perciò utilizzare strumenti come gli spinlock che grazie a istruzioni atomiche, per esempio la test-and-set, bloccano l'esecuzione di un processo fino al verificarsi di una condizione. Per evitare il busy wait si possono usare gli spinlock solo per rendere atomiche le istruzioni P e V di un semaforo in modo che la maggior parte del tempo di attesa per entrare in una sezione critica un processo lo passi in stato di wait invece che consumare inutilmente cicli di CPU.&lt;br /&gt;
#'''Perchè nei sistemi reali l'algoritmo di rimpiazzamento second chance (orologio) viene preferito a LRU sebbene il primo non sia a stack e il secondo sì?''' &amp;lt;br&amp;gt; perchè la gestione dei timestamp richiesti da LRU è costosa nella MMU.&lt;br /&gt;
#'''Perchè revocare un'autorizzazione espressa come capability è più difficile che revocare lo stesso diritto quando espresso come access control list?''' &amp;lt;br&amp;gt; Perchè le capability sono salvate al interno dello spazio di memoria di ciascun processo mentre le access control list sono legate al file.&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2017.05.29&amp;diff=1937</id>
		<title>Prova teorica 2017.05.29</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2017.05.29&amp;diff=1937"/>
		<updated>2017-06-16T08:17:10Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: /* Esercizio g.1 */ Aggiunta convalida soluzione es g.2&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/compiti/2017.05.29.tot.pdf link al compito]&lt;br /&gt;
&lt;br /&gt;
==Esercizio c.1==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Save===&lt;br /&gt;
&lt;br /&gt;
se qualcuno può correggermi o confermare il rispetto delle regole concorrenziali, mi fa un grande piacere. &lt;br /&gt;
saluti&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
Monitor rubaBandiera () {&lt;br /&gt;
&lt;br /&gt;
  condition oktochiama, oktopunteggio, oktopronto; &lt;br /&gt;
&lt;br /&gt;
  listnum [] = [1,2,3,4]&lt;br /&gt;
  numeri[]&lt;br /&gt;
  numeriChiamati[0...MAX-1]&lt;br /&gt;
  studentiPronti = True; &lt;br /&gt;
&lt;br /&gt;
  punteggio [A] = 0;&lt;br /&gt;
  punteggio [B] = 0;&lt;br /&gt;
&lt;br /&gt;
 procedure entry gioco() {&lt;br /&gt;
   &lt;br /&gt;
  &lt;br /&gt;
      n=random(listnum);&lt;br /&gt;
      numeri(n)=random(numeriChiamati);&lt;br /&gt;
&lt;br /&gt;
      if( studentiPronti == false)&lt;br /&gt;
&lt;br /&gt;
             oktochiama(numeri).wait() ;&lt;br /&gt;
&lt;br /&gt;
      &lt;br /&gt;
      studentiPronti(numeri) = false ;&lt;br /&gt;
      oktopunteggio.signal();&lt;br /&gt;
}&lt;br /&gt;
 procedure entry studente () {&lt;br /&gt;
&lt;br /&gt;
    while(studentiPronti(numeri))&lt;br /&gt;
&lt;br /&gt;
         oktochiama(numeri).signal();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 procedure entry chiama () {&lt;br /&gt;
&lt;br /&gt;
      print(punteggio[A] &amp;quot;:&amp;quot; punteggio[B]);&lt;br /&gt;
      if (studentiPronti(numeri))&lt;br /&gt;
&lt;br /&gt;
         oktopunteggio.wait()&lt;br /&gt;
&lt;br /&gt;
      if (allabadiera(A))&lt;br /&gt;
         punteggio[A] ++;&lt;br /&gt;
      else punteggio [B]++;&lt;br /&gt;
      print(punteggio[A] &amp;quot;:&amp;quot; punteggio[B]);&lt;br /&gt;
      studentiPronti(numeri) = true&lt;br /&gt;
&lt;br /&gt;
      pronto();&lt;br /&gt;
&lt;br /&gt;
 procedure entry pronto() {&lt;br /&gt;
&lt;br /&gt;
      if(punteggio[A] || punteggio[B] &amp;lt; 10)&lt;br /&gt;
&lt;br /&gt;
        return 0;&lt;br /&gt;
      else return 1;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Secondo devi utilizzare il nome corretto per le procedure entry, cioè mancano nuovapartita e allabandiera, che non hai definito. Con i corretti parametri. --[[User:FedericoB|FedericoB]] ([[User talk:FedericoB|talk]]) 18:45, 15 June 2017 (CEST)&lt;br /&gt;
&lt;br /&gt;
==Esercizio c.2==&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.1==&lt;br /&gt;
===Soluzione di GiovanniF===&lt;br /&gt;
&lt;br /&gt;
min = lifo 1234123124123124...&lt;br /&gt;
&lt;br /&gt;
lifo = lru 123124123124123124...&lt;br /&gt;
&lt;br /&gt;
--[[User:FedericoB|FedericoB]] ([[User talk:FedericoB|talk]]) 10:17, 16 June 2017 (CEST) &amp;lt;br&amp;gt;&lt;br /&gt;
Ho verificato la soluzione:&amp;lt;br&amp;gt;&lt;br /&gt;
Il ragionamento da fare qui è: &amp;lt;br&amp;gt;&lt;br /&gt;
MIN = LIFO &amp;lt;br&amp;gt;&lt;br /&gt;
MIN sceglie la pagina che più tardi nel futuro verrà richiesta &amp;lt;br&amp;gt;&lt;br /&gt;
LIFO sceglie l'ultima pagina caricata &amp;lt;br&amp;gt;&lt;br /&gt;
Quindi ad ogni page fault l'ultima pagina caricata deve essere quella che più tardi nel futuro verrà richiesta&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MIN&lt;br /&gt;
1	2	3	4	1	2	3	1	2	4	1	2	3	1	2	4&lt;br /&gt;
1	1	1	1	1	1	1	1	1	1	1	1	1	1	1	1&lt;br /&gt;
	2	2	2	2	2	2	2	2	2	2	2	2	2	2	2&lt;br /&gt;
		3	4	4	4	3	3	3	4	4	4	3	3	3	4&lt;br /&gt;
			#			#			#			#			#&lt;br /&gt;
&lt;br /&gt;
LIFO&lt;br /&gt;
1	2	3	4	1	2	3	1	2	4	1	2	3	1	2	4&lt;br /&gt;
1	1	1	1	1	1	1	1	1	1	1	1	1	1	1	1&lt;br /&gt;
	2	2	2	2	2	2	2	2	2	1	2	2	2	2	2&lt;br /&gt;
		3	4	4	4	3	3	3	4	4	4	3	3	3	4&lt;br /&gt;
			#			#			#			#			#&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
LIFO = LRU &amp;lt;br&amp;gt;&lt;br /&gt;
LRU sceglie la pagina che più precedentemente nel passato è stata richiesta &amp;lt;br&amp;gt;&lt;br /&gt;
Quindi ad ogni page fault l'ultima pagina caricata deve essere quella che più precedentemente nel passato è stata richiesta&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LRU&lt;br /&gt;
1	2	3	1	2	4	1	2	3	1	2	4	1	2	3&lt;br /&gt;
1	1	1	1	1	1	1	1	1	1	1	1	1	1	1&lt;br /&gt;
	2	2	2	2	2	2	2	2	2	2	2	2	2	2&lt;br /&gt;
		3	3	3	4	4	4	3	3	3	4	4	4	4&lt;br /&gt;
					#			#			#			#&lt;br /&gt;
&lt;br /&gt;
LIFO&lt;br /&gt;
1	2	3	1	2	4	1	2	3	1	2	4	1	2	3&lt;br /&gt;
1	1	1	1	1	1	1	1	1	1	1	1	1	1	1&lt;br /&gt;
	2	2	2	2	2	2	2	2	2	2	2	2	2	2&lt;br /&gt;
		3	3	3	4	4	4	3	3	3	4	4	4	4&lt;br /&gt;
					#			#			#			#&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.2==&lt;br /&gt;
#'''Un i-node di un file system tipo ext2 per un errore viene riportato un numero di link maggiore del reale. Cosa può succedere se si continua ad usare il file system? (perchè?) E se il numero di link errato fosse il contrario inferiore al reale cosa potrebbe succedere? (perchè?)&lt;br /&gt;
#'''Perchè è necessario usare spinlock in sistemi multiprocessore per implementare kernel di tipo simmetrico (SMP)?''' &amp;lt;br&amp;gt;Perchè la semplice disabilitazione degli interrupt non sarebbe sufficiente in caso di parallelismo reale a prevenire l'accesso a memoria condivisa in concorrenza. Bisogna perciò utilizzare strumenti come gli spinlock che grazie a istruzioni atomiche, per esempio la test-and-set, bloccano l'esecuzione di un processo fino al verificarsi di una condizione. Per evitare il busy wait si possono usare gli spinlock solo per rendere atomiche le istruzioni P e V di un semaforo in modo che la maggior parte del tempo di attesa per entrare in una sezione critica un processo lo passi in stato di wait invece che consumare inutilmente cicli di CPU.&lt;br /&gt;
#'''Perchè nei sistemi reali l'algoritmo di rimpiazzamento second chance (orologio) viene preferito a LRU sebbene il primo non sia a stack e il secondo sì?&lt;br /&gt;
#'''Perchè revocare un'autorizzazione espressa come capability è più difficile che revocare lo stesso diritto quando espresso come access control list?'''&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2016.05.30&amp;diff=1936</id>
		<title>Prova teorica 2016.05.30</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2016.05.30&amp;diff=1936"/>
		<updated>2017-06-16T07:32:24Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Creata pagina con soluzione esercizio g.1 e risposta a 2 domande esercizio g.2&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/compiti/2016.05.30.ris.pdf link al compito]&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.1==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
a) Sia data una memoria di 3 frame. Trovate una stringa di riferimenti di lunghezza infinita tale che se confrontiamo FIFO e MIN, da&lt;br /&gt;
un certo istante in poi il numero di page fault per FIFO sia almeno doppio di quello di MIN. &lt;br /&gt;
Dimostrate che la proprieta’ sia verificata.&lt;br /&gt;
b) Trovate una stringa di riferimenti di lunghezza infinita tale che se confrontiamo FIFO e MIN, &lt;br /&gt;
da un certo istante in poi il numero di page fault per FIFO sia almeno triplo di quello di MIN. &lt;br /&gt;
Dimostrate che la proprietà sia verificata.&lt;br /&gt;
c) (extra/hint) E’ possibile soddisfare b con una memoria di 3 frame? Perché?&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
a) la stringa di riferimento 12341234123412341234...&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
FIFO&lt;br /&gt;
1	2	3	4	1	2	3	4	1	2	3	4	1	2	3	4&lt;br /&gt;
1	1	1	4	4	4	3	3	3	2	2	2	1	1	1	4&lt;br /&gt;
	2	2	2	1	1	1	4	4	4	3	3	3	2	2	2&lt;br /&gt;
		3	3	3	2	2	2	1	1	1	4	4	4	3	3&lt;br /&gt;
			#	#	#	#	#	#	#	#	#	#	#	#	#&lt;br /&gt;
			&lt;br /&gt;
MIN&lt;br /&gt;
1	2	3	4	1	2	3	4	1	2	3	4	1	2	3	4&lt;br /&gt;
1	1	1	1	1	1	1	1	1	2	2	2	2	2	2	2&lt;br /&gt;
	2	2	2	2	2	3	3	3	3	3	3	3	3	3	4&lt;br /&gt;
		3	4	4	4	4	4	4	4	4	4	1	1	1	1&lt;br /&gt;
			#			#			#			#			#&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
b) la stringa di riferimento per il punto (a) genera il triplo dei page fault su FIFO rispetto a MIN.&lt;br /&gt;
&lt;br /&gt;
c) Sì è possibile ma non è possibile farne il quadruplo.Questo perchè MIN è l'algoritmo teorico ottimale che genera meno page fault possibili. MIN sostituisce la pagina che più tardi nel futuro verrà richiesta. Data una stringa di riferimento che genera page fault ad ogni richiesta con FIFO, quindi in cui non ci sono mai richieste uguali consecutive, con MIN se dobbiamo ottenere un terzo dei page fault vuol dire avere un page fault minimo ogni 3 richieste. Con una memoria di 3 frame se una frame viene occupato dalla nuova pagina vuol dire che rimangono 2 frame in cui rimarranno pagine che verrano utilizzate nell'immediato futuro, quindi potremo avere 2 richieste che non genereranno page fault, ma sicuramente alla terza richiesta verrà generato.&lt;br /&gt;
Se avessimo una memoria con 4 frame potremmo avere un quarto dei page fault rispetto a FIFO.&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.2==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
'''1. A cosa serve il processo IDLE (che mette il processore in wait state se possibile o esegue un loop infinito)? Quando viene messo in esecuzione? Come si gestisce l’attivazione di IDLE negli scheduler a priorità e nel round-robin?''' &lt;br /&gt;
&lt;br /&gt;
Il processore normalmente ad ogni istruzione incrementa il program counter di una posizione oppure esegue un salto. Quindi non è tecnicamente possibile non fargli eseguire nulla. Il processo IDLE serve proprio nelle situazioni in cui non sono presenti processi da eseguire. Lo stato di wait riduce il consumo energetico del processore, ne abbassa la frequenza. Negli scheduler a priorità il processo IDLE può essere un processo con priorità minima. Nel round robin invece conviene inserire il processo IDLE solo se si rileva che la coda ready è vuota. Altrimenti se fosse sempre presente nella coda ready saremmo costretti ad eseguire il processo IDLE ad ogni turn-around della coda circolare e questo decrementerebbe le prestazioni.&lt;br /&gt;
&lt;br /&gt;
'''2. Lo stato unsafe è condizione necessaria ma non sufficiente perché possa verificarsi deadlock. Quali sono I passi logici necessari per dimostrare questa affermazione?''' &lt;br /&gt;
&lt;br /&gt;
Bisogna dimostrare che se si verifica deadlock allora si è sicuramente verificato anche uno stato unsafe. Se si verifica deadlock vuol dire che ci sono processi in attesa di risorse. Uno stato unsafe è uno stato in cui il sistema non ha potuto soddisfare nessuna delle richieste perchè non ha risorse da assegnare, quindi i processi devono rimanere in attesa. Se non si fosse in uno stato di unsafe allora il sistema potrebbe garantire una risorsa a uno dei processi in deadlock e questo sbloccherebbe anche tutti gli altri. &lt;br /&gt;
&lt;br /&gt;
'''3. Quali metodi di allocazione per file system ammettono la creazione di file di grandezza arbitrario?''' &lt;br /&gt;
&lt;br /&gt;
'''4. Perché per utilizzare le capability come strumento di autorizzazione è sufficiente usare un metodo crittografico a singola&lt;br /&gt;
chiave?'''&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2017.05.29&amp;diff=1935</id>
		<title>Prova teorica 2017.05.29</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2017.05.29&amp;diff=1935"/>
		<updated>2017-06-15T20:26:38Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Aggiunto link al compito ora che il prof ha caricato il pdf&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/compiti/2017.05.29.tot.pdf link al compito]&lt;br /&gt;
&lt;br /&gt;
==Esercizio c.1==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Save===&lt;br /&gt;
&lt;br /&gt;
se qualcuno può correggermi o confermare il rispetto delle regole concorrenziali, mi fa un grande piacere. &lt;br /&gt;
saluti&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
Monitor rubaBandiera () {&lt;br /&gt;
&lt;br /&gt;
  condition oktochiama, oktopunteggio, oktopronto; &lt;br /&gt;
&lt;br /&gt;
  listnum [] = [1,2,3,4]&lt;br /&gt;
  numeri[]&lt;br /&gt;
  numeriChiamati[0...MAX-1]&lt;br /&gt;
  studentiPronti = True; &lt;br /&gt;
&lt;br /&gt;
  punteggio [A] = 0;&lt;br /&gt;
  punteggio [B] = 0;&lt;br /&gt;
&lt;br /&gt;
 procedure entry gioco() {&lt;br /&gt;
   &lt;br /&gt;
  &lt;br /&gt;
      n=random(listnum);&lt;br /&gt;
      numeri(n)=random(numeriChiamati);&lt;br /&gt;
&lt;br /&gt;
      if( studentiPronti == false)&lt;br /&gt;
&lt;br /&gt;
             oktochiama(numeri).wait() ;&lt;br /&gt;
&lt;br /&gt;
      &lt;br /&gt;
      studentiPronti(numeri) = false ;&lt;br /&gt;
      oktopunteggio.signal();&lt;br /&gt;
}&lt;br /&gt;
 procedure entry studente () {&lt;br /&gt;
&lt;br /&gt;
    while(studentiPronti(numeri))&lt;br /&gt;
&lt;br /&gt;
         oktochiama(numeri).signal();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 procedure entry chiama () {&lt;br /&gt;
&lt;br /&gt;
      print(punteggio[A] &amp;quot;:&amp;quot; punteggio[B]);&lt;br /&gt;
      if (studentiPronti(numeri))&lt;br /&gt;
&lt;br /&gt;
         oktopunteggio.wait()&lt;br /&gt;
&lt;br /&gt;
      if (allabadiera(A))&lt;br /&gt;
         punteggio[A] ++;&lt;br /&gt;
      else punteggio [B]++;&lt;br /&gt;
      print(punteggio[A] &amp;quot;:&amp;quot; punteggio[B]);&lt;br /&gt;
      studentiPronti(numeri) = true&lt;br /&gt;
&lt;br /&gt;
      pronto();&lt;br /&gt;
&lt;br /&gt;
 procedure entry pronto() {&lt;br /&gt;
&lt;br /&gt;
      if(punteggio[A] || punteggio[B] &amp;lt; 10)&lt;br /&gt;
&lt;br /&gt;
        return 0;&lt;br /&gt;
      else return 1;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Secondo devi utilizzare il nome corretto per le procedure entry, cioè mancano nuovapartita e allabandiera, che non hai definito. Con i corretti parametri. --[[User:FedericoB|FedericoB]] ([[User talk:FedericoB|talk]]) 18:45, 15 June 2017 (CEST)&lt;br /&gt;
&lt;br /&gt;
==Esercizio c.2==&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.1==&lt;br /&gt;
#'''Soluzione di GiovanniF&lt;br /&gt;
&lt;br /&gt;
min = lifo 1234123124123124...&lt;br /&gt;
&lt;br /&gt;
lifo = lru 123124123124123124...&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.2==&lt;br /&gt;
#'''Un i-node di un file system tipo ext2 per un errore viene riportato un numero di link maggiore del reale. Cosa può succedere se si continua ad usare il file system? (perchè?) E se il numero di link errato fosse il contrario inferiore al reale cosa potrebbe succedere? (perchè?)&lt;br /&gt;
#'''Perchè è necessario usare spinlock in sistemi multiprocessore per implementare kernel di tipo simmetrico (SMP)?''' &amp;lt;br&amp;gt;Perchè la semplice disabilitazione degli interrupt non sarebbe sufficiente in caso di parallelismo reale a prevenire l'accesso a memoria condivisa in concorrenza. Bisogna perciò utilizzare strumenti come gli spinlock che grazie a istruzioni atomiche, per esempio la test-and-set, bloccano l'esecuzione di un processo fino al verificarsi di una condizione. Per evitare il busy wait si possono usare gli spinlock solo per rendere atomiche le istruzioni P e V di un semaforo in modo che la maggior parte del tempo di attesa per entrare in una sezione critica un processo lo passi in stato di wait invece che consumare inutilmente cicli di CPU.&lt;br /&gt;
#'''Perchè nei sistemi reali l'algoritmo di rimpiazzamento second chance (orologio) viene preferito a LRU sebbene il primo non sia a stack e il secondo sì?&lt;br /&gt;
#'''Perchè revocare un'autorizzazione espressa come capability è più difficile che revocare lo stesso diritto quando espresso come access control list?'''&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2017.01.18&amp;diff=1934</id>
		<title>Prova teorica 2017.01.18</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2017.01.18&amp;diff=1934"/>
		<updated>2017-06-15T20:22:15Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: /* Esercizio g.2 */ aggiunta risposta domanda 1&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/compiti/2017.01.18.ris.pdf link al compito]&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.2==&lt;br /&gt;
&lt;br /&gt;
# '''Perché un algoritmo di rimpiazzamento a stack non soffre dell’anomalia di Belady?''' &amp;lt;br&amp;gt; L'anomalia di Belady consiste nel aumento del page fault al aumentare dei frame in memoria. Se un algoritmo è a stack vuol dire che fissato una stringa di riferimento, in qualsiasi instante t l'insieme delle pagine con n frame è un sottoinsieme del insieme della pagine con n+1 frame. Quindi i page fault non possono aumentare perchè comunque è presente il set di pagine che era presente anche con n frame più un addizionale pagina.&lt;br /&gt;
# '''Perché si può usare un meccanismo di crittografia simmetrica nella gestione delle capability di accesso alle risorse?'''&lt;br /&gt;
# '''Quali operazioni svolge il processore per la gestione di un interrupt e quali invece sono a carico del kernel del sistema operativo?''' &amp;lt;br&amp;gt; Il processore si occupa, all'attivazione della linea elettronica dell interrupt di recuperare dalla memoria in locazioni fisse l'indirizzo delle istruzioni di gestione degli interrupt. In seguito carica questo indirizzo nel program counter e quindi &amp;quot;salta&amp;quot; a queste istruzioni attivando la modalità di esecuzione kernel mode. &amp;lt;br&amp;gt; Le routine di gestione degli interrupt fanno parte del codice kernel e si occupano di gestire opportunamente le operazioni necessarie in base al tipo di interrupt. Per esempio operazioni come eseguire lo scheduler o recuperare l'output di un device.&lt;br /&gt;
# '''Perche’ il mascheramento degli interrupt non si può utilizzare per creare sezioni critiche in sistemi multicore o multiprocessore?''' &amp;lt;br&amp;gt; Il mascheramento degli interrupt previene l'interruzione del esecuzione del processo corrente e il context switch a un altro processo e di conseguenza la non atomicità delle istruzioni che stava eseguendo il processo. &amp;lt;br&amp;gt; Anche disabilitando gli interrupt (è uguale se su un solo processore o su tutti) c'è comunque la possibilità che altro codice eseguito in ''parallelismo reale'' vada in concorrenza con quello dove si sta cercando di ottenere una sezione critica.&lt;br /&gt;
# '''Per quali tipi di processo l’algoritmo di schedulazione round-robin è efficiente e per quali no?''' &amp;lt;br&amp;gt; L'algoritmo di schedulazione round-robin è efficente per i processi I/O bound in quanto è ''fair'', cioè permette a tutti i processi il diritto di esecuzione entro un certo lasso di tempo. Può non essere efficiente per i processi CPU bound che hanno tempi di esecuzione lunghi e il round robin li costringe a ritardare il loro completamento. Inoltre un processo CPU bound potrebbe essere interrotto quando manca davvero poco al suo termine ma il time slice è terminato oppure necessita di I/O.Il processo viene messo in coda e bisognerà aspettare l'esecuzione di tutti gli altri processi prima di poter finire l'esecuzione.Uno scheduler shortest remaining time first avrebbe invece rimesso subito in esecuzione il processo CPU bound se il tempo di esecuzione previsto rimanente era davvero ridotto.&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2017.05.29&amp;diff=1933</id>
		<title>Prova teorica 2017.05.29</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2017.05.29&amp;diff=1933"/>
		<updated>2017-06-15T16:45:54Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: /* Esercizio c.1 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Media:20170529.jpg | link al compito]]&lt;br /&gt;
&lt;br /&gt;
Ho caricato questa foto del compito intanto che non è ancora disponibile il pdf. Quando sarà disponibile sostituire con il link al pdf. --[[User:FedericoB|FedericoB]] ([[User talk:FedericoB|talk]]) 11:41, 15 June 2017 (CEST)&lt;br /&gt;
&lt;br /&gt;
==Esercizio c.1==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Save===&lt;br /&gt;
&lt;br /&gt;
se qualcuno può correggermi o confermare il rispetto delle regole concorrenziali, mi fa un grande piacere. &lt;br /&gt;
saluti&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
Monitor rubaBandiera () {&lt;br /&gt;
&lt;br /&gt;
  condition oktochiama, oktopunteggio, oktopronto; &lt;br /&gt;
&lt;br /&gt;
  listnum [] = [1,2,3,4]&lt;br /&gt;
  numeri[]&lt;br /&gt;
  numeriChiamati[0...MAX-1]&lt;br /&gt;
  studentiPronti = True; &lt;br /&gt;
&lt;br /&gt;
  punteggio [A] = 0;&lt;br /&gt;
  punteggio [B] = 0;&lt;br /&gt;
&lt;br /&gt;
 procedure entry gioco() {&lt;br /&gt;
   &lt;br /&gt;
  &lt;br /&gt;
      n=random(listnum);&lt;br /&gt;
      numeri(n)=random(numeriChiamati);&lt;br /&gt;
&lt;br /&gt;
      if( studentiPronti == false)&lt;br /&gt;
&lt;br /&gt;
             oktochiama(numeri).wait() ;&lt;br /&gt;
&lt;br /&gt;
      &lt;br /&gt;
      studentiPronti(numeri) = false ;&lt;br /&gt;
      oktopunteggio.signal();&lt;br /&gt;
}&lt;br /&gt;
 procedure entry studente () {&lt;br /&gt;
&lt;br /&gt;
    while(studentiPronti(numeri))&lt;br /&gt;
&lt;br /&gt;
         oktochiama(numeri).signal();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 procedure entry chiama () {&lt;br /&gt;
&lt;br /&gt;
      print(punteggio[A] &amp;quot;:&amp;quot; punteggio[B]);&lt;br /&gt;
      if (studentiPronti(numeri))&lt;br /&gt;
&lt;br /&gt;
         oktopunteggio.wait()&lt;br /&gt;
&lt;br /&gt;
      if (allabadiera(A))&lt;br /&gt;
         punteggio[A] ++;&lt;br /&gt;
      else punteggio [B]++;&lt;br /&gt;
      print(punteggio[A] &amp;quot;:&amp;quot; punteggio[B]);&lt;br /&gt;
      studentiPronti(numeri) = true&lt;br /&gt;
&lt;br /&gt;
      pronto();&lt;br /&gt;
&lt;br /&gt;
 procedure entry pronto() {&lt;br /&gt;
&lt;br /&gt;
      if(punteggio[A] || punteggio[B] &amp;lt; 10)&lt;br /&gt;
&lt;br /&gt;
        return 0;&lt;br /&gt;
      else return 1;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Secondo devi utilizzare il nome corretto per le procedure entry, cioè mancano nuovapartita e allabandiera, che non hai definito. Con i corretti parametri. --[[User:FedericoB|FedericoB]] ([[User talk:FedericoB|talk]]) 18:45, 15 June 2017 (CEST)&lt;br /&gt;
&lt;br /&gt;
==Esercizio c.2==&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.1==&lt;br /&gt;
#'''Soluzione di GiovanniF&lt;br /&gt;
&lt;br /&gt;
min = lifo 1234123124123124...&lt;br /&gt;
&lt;br /&gt;
lifo = lru 123124123124123124...&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.2==&lt;br /&gt;
#'''Un i-node di un file system tipo ext2 per un errore viene riportato un numero di link maggiore del reale. Cosa può succedere se si continua ad usare il file system? (perchè?) E se il numero di link errato fosse il contrario inferiore al reale cosa potrebbe succedere? (perchè?)&lt;br /&gt;
#'''Perchè è necessario usare spinlock in sistemi multiprocessore per implementare kernel di tipo simmetrico (SMP)?''' &amp;lt;br&amp;gt;Perchè la semplice disabilitazione degli interrupt non sarebbe sufficiente in caso di parallelismo reale a prevenire l'accesso a memoria condivisa in concorrenza. Bisogna perciò utilizzare strumenti come gli spinlock che grazie a istruzioni atomiche, per esempio la test-and-set, bloccano l'esecuzione di un processo fino al verificarsi di una condizione. Per evitare il busy wait si possono usare gli spinlock solo per rendere atomiche le istruzioni P e V di un semaforo in modo che la maggior parte del tempo di attesa per entrare in una sezione critica un processo lo passi in stato di wait invece che consumare inutilmente cicli di CPU.&lt;br /&gt;
#'''Perchè nei sistemi reali l'algoritmo di rimpiazzamento second chance (orologio) viene preferito a LRU sebbene il primo non sia a stack e il secondo sì?&lt;br /&gt;
#'''Perchè revocare un'autorizzazione espressa come capability è più difficile che revocare lo stesso diritto quando espresso come access control list?'''&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2015.09.14&amp;diff=1931</id>
		<title>Prova teorica 2015.09.14</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2015.09.14&amp;diff=1931"/>
		<updated>2017-06-15T14:45:42Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Creata pagina e aggiunta risposta a domanda 2 e 3 dell'esercizio g.2&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/compiti/2015.09.14.ris.pdf link al compito]&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.2==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
&lt;br /&gt;
# '''In quali casi si usano ancora oggi I file system con metodo di allocazione contigua?'''&lt;br /&gt;
# '''Come viene usato l'algoritmo della media esponenziale nello scheduler shortest job first? La media esponenziale dipende da un parametro (normalmente indicato con alpha, di valore fra zero e uno). A cosa serve questo parametro?''' &amp;lt;br&amp;gt; Serve a fare una previsione della durata del prossimo intervallo di esecuzione di un processo. Lo scheduler shortest job first sceglie il processo che ha tempo previsto di esecuzione minore. La media esponenziale viene calcolata basandosi sulla durata del precendente intervallo di esecuzione passati e sulla precedente media esponenziale calcolata per lo scorso intervallo. Il parametro alpha assume valori nell'intervallo [0,1]. Più il valore di alpha è vicino a 1 più ci si basa su recenti misurazioni mentre più alpha è uguale a 0 più ci si basa su previsioni che si portano dietro misurazioni del passato.&lt;br /&gt;
# '''Lo stato unsafe e' condizione necessaria ma non sufficiente perche' possa verificarsi deadlock. Quali sono I passi logici necessari per dimostrare questa affermazione?''' &amp;lt;br&amp;gt; Bisogna dimostrare che se si verifica deadlock allora si è sicuramente verificato anche uno stato unsafe. Se si verifica deadlock vuol dire che ci sono processi in attesa di risorse. Uno stato unsafe è uno stato in cui il sistema non ha potuto soddisfare nessuna delle richieste perchè non ha risorse da assegnare, quindi i processi devono rimanere in attesa. Se non si fosse in uno stato di unsafe allora il sistema potrebbe garantire una risorsa a uno dei processi in deadlock e questo sbloccherebbe anche tutti gli altri.&lt;br /&gt;
# '''Due sistemi gestiscono file system di grandi dimensioni (per es. 10 Terabyte di dati). Il primo usa file system di tipo ext2, il secondo di tipo ext3. Per un guasto al gruppo di continuita' entrambi i sistemi vengono spenti senza un appropriato shutdown. Cosa cambia alla riaccensione? Perche'?'''&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2016.07.27&amp;diff=1930</id>
		<title>Prova teorica 2016.07.27</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2016.07.27&amp;diff=1930"/>
		<updated>2017-06-15T13:57:35Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Creata pagina e aggiunta risposta a due domande del esercizio g.2&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/compiti/2016.07.27.ris.pdf link al compito]&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.2==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
&lt;br /&gt;
'''Le seguenti affermazioni sono vere o false? (spiegarne il motivo).'''&lt;br /&gt;
# '''Usando un file system con journaling anche se un sistema viene spento senza fare la procedura di shutdown non si perdono dati.'''&lt;br /&gt;
# '''La crittografia a chiave pubblica (doppia chiave) puo’ essere usata al posto di quella a singola chiave e quindi quest’ultima sta cadendo in disuso.'''&lt;br /&gt;
# '''Gli scheduler a priorita’ sono particolarmente indicati per i programmi interattivi.''' &amp;lt;br&amp;gt; Vero, in quanto si può assegnare un elevata priorità a un processo I/O Bound e una priorità inferiore a un processo CPU Bound in modo da evitare l'effetto convoglio. Il processo CPU Bound non rischierebbe la starvation perchè nel caso un processo I/O Bound abbia tempi di esecuzione complessivamente lunghi, gli verrebbe ridotta la priorità tramite aging. Per esempio il processo X di Linux ha elevata priorità in quanto deve gestire l'aggiornamento dell'ambiente grafico.&lt;br /&gt;
# '''Deadlock prevention e avoidance sono la stessa cosa.''' &amp;lt;br&amp;gt; Falso, la prevention elimina il deadlock ''strutturalmente'' rendendo falsa una o più delle condizioni necessarie al deadlock cioè risorse non condivisibili, bloccanti, non prerilasciabili e l'attesa circolare (di solito si agisce su questa trovando i Knot e uccidendo i processi). &amp;lt;br&amp;gt; L'avoidance invece opera ritardando un operazione per cui si è visto che può causare deadlock. (algoritmo del banchiere, ritardare l'attribuzione di una risorsa).&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2015.06.17&amp;diff=1929</id>
		<title>Prova teorica 2015.06.17</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2015.06.17&amp;diff=1929"/>
		<updated>2017-06-15T13:34:50Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Creata pagina con soluzione a esercizio g.1&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[http://www.cs.unibo.it/~renzo/so/compiti/2015.06.17.ris.pdf link al compito]]&lt;br /&gt;
&lt;br /&gt;
==Esercizio 1===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Sia dato il seguente stato dell'algoritmo del Banchiere Multivaluta&lt;br /&gt;
Valuta A: COH=x Valuta B: COH=y&lt;br /&gt;
p1: credito=6 assegnati=4 credito=10 assegnati=5&lt;br /&gt;
p2: credito=6 assegnati=3 credito=5 assegnati=3&lt;br /&gt;
p3: credito=6 assegnati=2 credito=10 assegnati=4&lt;br /&gt;
Denotare l'insieme di tutti I valori (x, y) per I quali lo stato sopra indicato risulta safe&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Assumo che per credito si intenda il credito residuo e non il limite di credito del cliente.&lt;br /&gt;
Lo stato è safe se almeno uno dei clienti è soddisfacibile.&lt;br /&gt;
Determino un valore di COHx e COHy per cui una sequenza è soddisfacibile e COHx-1 e COHy-1 non è più soddisfacibile.&lt;br /&gt;
Inizio ponendo COHx e COHy pari al minimo del credito&lt;br /&gt;
Verifico per la valuta x&lt;br /&gt;
avail[1] = 6 6&amp;gt;=6? yes&lt;br /&gt;
avail[2] = 6+2 = 8 &amp;gt;= 6? yes&lt;br /&gt;
avail[3] = 8+3 = 11 &amp;gt;= 6? yes&lt;br /&gt;
avail[4] = 11+4 = 16 &amp;gt;= 6? yes&lt;br /&gt;
se COHx fosse uguale a 5 nessun cliente sarebbe soddisfacibile.&lt;br /&gt;
Per y invese se COHy=5&lt;br /&gt;
avail[1] = 5 5&amp;gt;=5? yes&lt;br /&gt;
avail[2] = 5+3 = 8 &amp;gt;= 10? No stato unsave&lt;br /&gt;
Porto COHY=7&lt;br /&gt;
avail[1] = 7 7&amp;gt;=5? yes&lt;br /&gt;
avail[2] = 7+3 = 10&amp;gt;=10? yes&lt;br /&gt;
avail[3] = 10+4 = 14&amp;gt;=10? yes&lt;br /&gt;
avail[4] = 14+5 = 19&amp;gt;=10? yes&lt;br /&gt;
&lt;br /&gt;
Quindi lo stato risulta safe per ogni (x,y) tale che x&amp;gt;5 e y&amp;gt;6&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1927</id>
		<title>Lezioni Anno Accademico 2016/17</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1927"/>
		<updated>2017-06-15T12:15:55Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: /* Lezione del 7 marzo 2017 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;''scrivete qui idee, riassunti dei concetti espressi, commenti approfondimenti sulle lezioni.''&lt;br /&gt;
&lt;br /&gt;
== Lezione del 23 settembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
'''YY'''&lt;br /&gt;
&lt;br /&gt;
* Presentazione del corso.&lt;br /&gt;
* Strumenti del corso: web, wiki.mailing list&lt;br /&gt;
* Indice dei contenuti&lt;br /&gt;
* Prove di esame (Scritto, Prova Pratica, Progetto).&lt;br /&gt;
&lt;br /&gt;
* Hardware Software&lt;br /&gt;
* Informatica&lt;br /&gt;
* Informazione/Dato&lt;br /&gt;
* Rivoluzione digitale (secondo rinascimento, Terza rivoluzione Industriale).&lt;br /&gt;
* Software Libero&lt;br /&gt;
&lt;br /&gt;
== Lezione del 27 settembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
'''OS:Y'''&lt;br /&gt;
&lt;br /&gt;
* '''Organizzazione (martedi' vs. venerdi')'''&lt;br /&gt;
In genere la lezione del martedì sarà più teorica mentre quella del venerdì più orientata agli aspetti pratici di laboratorio. Questa suddivisione potrebbe cambiare in quanto qualche martedì verrà saltato. &lt;br /&gt;
* '''Universit&amp;amp;agrave;'''&lt;br /&gt;
Università = studenti + docenti &lt;br /&gt;
* '''Digitale/Analogico'''&lt;br /&gt;
Digit significa cifra. Nell'informatica è fondamentale il passaggio dal continuo al discreto.&lt;br /&gt;
* '''Binario/Decimale'''&lt;br /&gt;
Nei calcolatori l'informazione è memorizzata utilizzando il sistema binario ...&lt;br /&gt;
(''in quanto in passato i relè consentivano di avere due stati: &amp;quot; 1 e 0 &amp;quot;'': &amp;amp;egrave; impreciso, qualcuno sa dare una definizione migliore? [[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 12:00, 29 September 2016 (CEST)) &amp;lt;br&amp;gt;&lt;br /&gt;
In quanto è molto semplice implementare le operazioni aritmetiche con dei circuiti elettronici utilizzando la codifica binaria. Per esempio è possibile creare un [https://it.wikipedia.org/wiki/Half-adder circuito] che somma due bit con riporto con solo due porte logiche. [[User:FedericoB|FedericoB]] ([[User talk:FedericoB|talk]]) 17:05, 29 September 2016 (CEST)&lt;br /&gt;
* '''I linguaggi e i problemi dell'Informatica'''&lt;br /&gt;
**Processing dell'informazione (Linguaggi di programmazione)&lt;br /&gt;
**Comunicazione (Il problema che i dati che mi servono sono in un altro luogo) (Linguaggi dei protocolli di rete, che però sono dei linguaggi temporizzati)&lt;br /&gt;
**Memorizzazione (Il problema che i dati mi serviranno in futuro) (Linguaggio del formato dei dati)&lt;br /&gt;
* '''Conoscenze a lungo, medio e breve termine nell'Informatica.'''&lt;br /&gt;
**BREVE: tecnologia, determinati programmi di sviluppo.&lt;br /&gt;
**MEDIO: singoli linguaggi, singoli OS, protocolli di comunicazione.&lt;br /&gt;
**LUNGO: algoritmi, informatica teorica, fondamenti costruttivi di reti e di OS.&lt;br /&gt;
&lt;br /&gt;
* '''Cos'è una distribuzione'''&lt;br /&gt;
E' un antologia di applicazioni e librerie in modo che siano compatibili tra loro.&lt;br /&gt;
&lt;br /&gt;
* '''Sistema Operativo: perch&amp;amp;eacute;'''&lt;br /&gt;
Un sistema operativo è un programma che controlla l’esecuzione di programmi applicativi e agisce come interfaccia tra le applicazioni e l’hardware del calcolatore. &amp;lt;br&amp;gt;&lt;br /&gt;
E' uno strumento software complesso ed implementabile in diversi modi (monolitico, microkernel, etc.). I microcontroller non hanno OS. &lt;br /&gt;
Permette: contabilità delle risorse (consente a sua volta attività di benchmarking); continuità dei servizi; controllo errori&lt;br /&gt;
(programmi, dispositivi); multiuser (gli utenti hanno diversi diritti e proprietà sulle risorse); protezione delle attività dei                     &lt;br /&gt;
processi; astrazione file system.&lt;br /&gt;
* '''Struttura a livelli (layer)'''&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
  ---------------&lt;br /&gt;
  ---------------&lt;br /&gt;
        OS&lt;br /&gt;
  ---------------L'&lt;br /&gt;
        hw&lt;br /&gt;
  ---------------L&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
Consente di: separare la complessità, di avere portabilità. &lt;br /&gt;
Ogni livello rappresenta un cambio di linguaggio. &lt;br /&gt;
Più aggiungiamo livelli più aumenta il livello di astrazione.&lt;br /&gt;
* '''Programma/processo'''&lt;br /&gt;
Algoritmo: sequenza finita di istruzioni non ambigue, atta a risolvere un problema.&lt;br /&gt;
&lt;br /&gt;
Programma: traduzione degli algoritmi in un linguaggio di programmazione.&lt;br /&gt;
&lt;br /&gt;
Processo: istanza esecutiva di un programma. Il programma è testo statico, quando viene eseguito diviene processo. Possiamo avere&lt;br /&gt;
uno stesso programma in esecuzione su più processi (e.g apertura di più terminali che eseguono un editor di testo).&lt;br /&gt;
&lt;br /&gt;
System call: richieste di processi al OS. e.g. printf (visto nel codice compilato in assembly tramite gcc -s), invoca write().&lt;br /&gt;
vi è una sequenza di azioni del tipo: richiesta di stampa al kernel --&amp;gt; attesa --&amp;gt; risposta.&lt;br /&gt;
* '''Storia dell'Informatica prima dei transistor'''&lt;br /&gt;
&lt;br /&gt;
La storia dei calcolatori non ha un inizio preciso. Nel 1900 venne trovato sul relitto di una nave greca risalente al II secolo a.C un meccanismo in grado di calcolare i movimenti degli astri, si ipotizza con una sorprendente meccanica. Oggi questo sistema viene conosciuto come Macchina di Anticitera. Negli ultimi anni di ricerche archeologiche e storiche è venuta sempre più in superficie una vena pionieristica nel mondo classico per quanto riguarda lo studio sul calcolo automatizzato e addirittura di automi (si veda Erone di Alessandria). Ma facciamo un salto in avanti di qualche secolo... &amp;lt;br&amp;gt;&lt;br /&gt;
Già nel XVII secolo Pascal teorizza e mette in pratica uno strumento in grado di addizionare e sottrarre, e nel 1672 G.W. von Leibniz lo perfeziona con l'aggiunta della moltiplicazione e della divisione. Ma è solamente nei primi anni del 1800 che entra in scena la macchina analitica di Babbage. Estremamente complessa, la macchina era dotata di un sistema di input e di elaborazione dati e di un sistema di output. Ed è con Babbage che nascono le schede perforate. La macchina analitica venne presentata per la prima volta in Italia, durante un convegno torinese del 1840. Tra i vari collaboratori scientifici alla macchina è da annotare la presenza di Luigi Federico Menabrea, che tra le altre cose fu Ministro della Marina Militare, ingegnere e Presidente del Consiglio dei Ministri del Regno d'Italia (1867-1869). Il primo vero programmatore della storia, collaboratrice di Babbage, è Ada Lovelace (figlia del poeta Lord Byron!): progettò un algoritmo in grado di elaborare i numeri di Bernulli. &amp;lt;br&amp;gt;&lt;br /&gt;
Nel 1896 viene fondata la Tabulating Machine Company (futura IBM) e agli albori del '900, vari scienziati (trai quali Thomas Edison e Guglielmo Marconi) portarono avanti gli studi sulla valvola termoionica, elemento indispensabile dei primi calcolatori elettronici. La valvola incrementò vertiginosamente i tempi di calcolo ma l'estrema fragilità della stessa causò una serie di problematiche che si risolsero solamente con l'entrata in gioco del transistor.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 30 settembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
'''YC'''&lt;br /&gt;
&lt;br /&gt;
(Storia dopo l'avvento del transistor)&lt;br /&gt;
&lt;br /&gt;
Tre innovazioni indipendenti:&lt;br /&gt;
* Multitasking: Inizialmente ideato per mantenere la CPU occupata con altri processi mentre si faceva Input/Output. Permette di dare l'idea al utente di poter eseguire più processi contemporaneamente anche se questi in realtà sono eseguiti dalla cpu in modo intervallato. Ci sono certi computer che hanno più esecutori e quindi possono realmente eseguire multipli processi contemporaneamente. &lt;br /&gt;
* Supporto Multiuser&lt;br /&gt;
* Interattivit&amp;amp;agrave;:l'interazione con l'utente cambia il flusso del programma.&lt;br /&gt;
&lt;br /&gt;
Queste tre proprietà sono ortogonali quindi possono esistere nei sistemi varie combinazioni di queste.&lt;br /&gt;
&lt;br /&gt;
Iniziano anche a separarsi i ruoli tra programmatori e utenti.Prima i computer venivano utilizzati solo da chi creava programmi ora anche da chi li utilizza senza avere le conoscenze per crearli.&lt;br /&gt;
&lt;br /&gt;
Il Time Sharing.&lt;br /&gt;
&lt;br /&gt;
Sistemi Paralleli (SIMD/MIMD)&lt;br /&gt;
Sistemi Real Time&lt;br /&gt;
&lt;br /&gt;
Dal 1969: UNIX.&lt;br /&gt;
&lt;br /&gt;
Il linguaggio C e la portabilit&amp;amp;agrave; dei sistemi operativi.&lt;br /&gt;
Caratteristiche del linguaggio C:&lt;br /&gt;
* grande controllo sul codice generato&lt;br /&gt;
* costrutti per l'elaborazione a basso livello&lt;br /&gt;
* supporto per la programmazione strutturata&lt;br /&gt;
* linguaggio &amp;quot;leggero&amp;quot;: poche parole chiave, I/O gestito tramite librerie&lt;br /&gt;
* Il compilatore C &amp;amp;egrave; scritto in C.&lt;br /&gt;
&lt;br /&gt;
Portabilit&amp;amp;agrave; di un compilatore. I cross-compiler.&lt;br /&gt;
&lt;br /&gt;
Il comando UNIX cc (o gcc): un comando unico per attivare l'intera toolchain per generare un eseguibile.&lt;br /&gt;
&lt;br /&gt;
Il punto e virgola trasforma espressioni in istruzioni (terminatore).&lt;br /&gt;
&lt;br /&gt;
1975? l'avvento dei personal computer. (in realt&amp;amp;agrave; il primo PC forse e' la Perrottina Olivetti del 1964).&lt;br /&gt;
 &lt;br /&gt;
Compiti a casa:&lt;br /&gt;
Portare esperimenti in C ritenuti &amp;quot;difficili&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 10:41, 1 October 2016 (CEST)&lt;br /&gt;
&lt;br /&gt;
== Lezione del 7 ottobre 2016 ==&lt;br /&gt;
Audio Lezione: [https://drive.google.com/open?id=0B8G3NNuJ7MA7TzJXUTJZM3RBUlE 7\10\16]&lt;br /&gt;
* '''Esempi di programmi in linguaggio C'''&lt;br /&gt;
Abbiamo commentato i programmi dal 1.1 al 1.7 che si possono trovare in questa [[2016-17_Programmi_C|pagina]].&lt;br /&gt;
* '''Economia Push/Economia Pull'''&lt;br /&gt;
Nell'economia push si basa la produzione sulla previsione di domanda e la si cerca di aumentare tramite la pubblicità. Questa è stata la principale metodologia utilizzata nei secoli scorsi.&lt;br /&gt;
Al giorno d'oggi si sta facendo avanti un differente approccio chiamato &amp;quot;economia pull&amp;quot; dove la produzione è basata sulla domanda effettiva. Per esempio per richiedere un finanziamento per l'economia push l'approccio comune è tramite una banca a cui si chiede un prestito in base alle previsioni di ricavo. Invece nell'economia pull è esemplare il fenomeno del crowdfounding dove si riceve un finanziamento dalla &amp;quot;folla&amp;quot; cioè da un gruppo di persone che dona una piccola parte di denaro ciascuna in base al reale interesse per il prodotto/servizio.&lt;br /&gt;
* '''Sistemi Operativi e parallelismo'''&lt;br /&gt;
Più processi si dicono in esecuzione concorrente quando accedono a risorse condivise.&lt;br /&gt;
Un programma è multithread quando tutti i thread condividono la stessa memoria. Ogni thread deve avere un suo stack.&lt;br /&gt;
La concorrenza si studia in particolare in sistemi operativi in quanto un sistema operativo multitasking è un sistema concorrente.&lt;br /&gt;
Concorrenza di processi in architetture SMP (Symmetric multiprocessor system) e non. (RD: non mi pare di aver spiegato cosa sia l'SMP) &amp;lt;br&amp;gt;&lt;br /&gt;
Simulazione del fenomeno della '''race condition'''. Si ha '''race condition''' quando un programma concorrente pu&amp;amp;ograve; puo' produrre diversi risultati se eseguito piu' volte per colpa della diversa evoluzione temporale delle elaborazioni.&amp;lt;br&amp;gt;&lt;br /&gt;
Per risolvere questo dovremmo raggruppare le istruzioni in ''sezioni critiche'' (sequenze inscindibili di istruzioni che devono essere eseguite come se fossero atomiche, tutta la sequenza o niente). &lt;br /&gt;
* '''Architettura a livelli e virtualizzazione'''&lt;br /&gt;
Dato un A e un A’ e riusciamo a usare A’ come A, allora A’ lo possiamo chiamare A virtuale.&lt;br /&gt;
L'idea che alla base delle macchine virtuali è di creare un interprete di un ISA differente o uguale a quello del sistema in modo da virtualizzare una reale macchina hardware e quindi poterla usare per gli stessi scopi di una macchina reale. E' quindi possibile, per esempio, installare sistemi operativi su hardware virtuale.&amp;lt;br&amp;gt;&lt;br /&gt;
L'insieme dei componenti hardware virtuali e il software installato su di questi prende il nome di ''macchina virtuale''.&lt;br /&gt;
Uno dei principali vantaggi, oggi molto utilizzato in ambito cloud, è quello di poter suddividere facilmente risorse hardware fisiche, per esempio spazio su disco e potenza di elaborazione, tra macchine virtuali differenti. &lt;br /&gt;
* '''Laboratorio di cross-compiling'''&lt;br /&gt;
Nel ultima parte della lezione abbiamo fatto un esperimento sulla portabilità dei compilatori. &amp;lt;br&amp;gt;&lt;br /&gt;
Abbiamo a disposizione un linguaggio ad alto livello che chiameremo L, due interpreti hw0 e hw1 per linguaggi a basso livello che chiameremo lhw0 e lhw1 e infine due compilatori L-&amp;gt;lhw0 uno scritto in python e l'altro in L. &amp;lt;br&amp;gt;&lt;br /&gt;
Se compiliamo con il compilatore scritto in python (abbiamo un interprete python installato sulla macchina che stiamo utilizzando) il compilatore scritto in L otteniamo un compilatore scritto in lhw0 L-&amp;gt;lhw0. &amp;lt;br&amp;gt;&lt;br /&gt;
Se ora modifichiamo il compilatore scritto in L L-&amp;gt;lhw0 in modo che crei codice per hw1 (basta modificare gli indici numerici delle istruzioni) otteniamo un compilatore in L L-&amp;gt;lhw1. Se lo compiliamo con il compilatore L-&amp;gt;lhw0 otteniamo un ''cross-compiler'' scritto in lhw0 L-&amp;gt;lhw1, cioè un compilatore che crea codice eseguibile su una macchina diversa da quella attuale. &amp;lt;br&amp;gt;&lt;br /&gt;
Se ora utilizziamo il cross compiler L-&amp;gt;lhw1 per compilare il compilatore scritto in L L-&amp;gt;hw1 otteniamo invece un compilatore che può essere eseguito su hw1. &amp;lt;br&amp;gt;&lt;br /&gt;
A questo punto su hw1 potremmo utilizzare l'ultimo compilatore creato per compilare sempre il compilatore scritto in L L-&amp;gt;hw1 ma vedremo che otterremo sempre lo stesso file perché avremo raggiunto il limite.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Il materiale per poter replicare l'esperimento sulla portabilit&amp;amp;agrave; dei compilatori &amp;amp;egrave; [http://www.cs.unibo.it/~renzo/so/tools/portability3.tgz qui]. &lt;br /&gt;
[[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 13:42, 8 October 2016 (CEST)&lt;br /&gt;
&lt;br /&gt;
== Lezione del 11 ottobre 2016 ==&lt;br /&gt;
*'''C'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
  #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  void dump (void* f, size_t len){ //size_t is the sizeof() return type&lt;br /&gt;
        unsigned char *s = f;&lt;br /&gt;
        size_t i;&lt;br /&gt;
        for (i = 0; i &amp;lt; len; i++)&lt;br /&gt;
                printf(&amp;quot;%02x &amp;quot;,s[i]); //%02x means print at least 2 digits and add 0's if there are less&lt;br /&gt;
        printf(&amp;quot;\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  int main(){&lt;br /&gt;
&lt;br /&gt;
  int v[] = {1, 2, 3, 4}; //v is (int*) 0x32ef...&lt;br /&gt;
  v[2] = 42; //*((int*) 0x32ef) + 2 = 42, moves the address by 2 &amp;quot;positions&amp;quot;, aka 4+4 bytes&lt;br /&gt;
  v[2] = 0x33;//(int*) 0x32ef.. = 0x33 (i.e.) 4 = 42&lt;br /&gt;
&lt;br /&gt;
  char s[] = &amp;quot;ciao&amp;quot;;&lt;br /&gt;
  int t[] = {0x63, 0x69, 0x61, 0x6f, 0x00}; //ciao in ASCII hex. if int OUT:'c'. if char OUT:&amp;quot;ciao&amp;quot;&lt;br /&gt;
  char u[] = {99, 105,97, 111, 0 }; //ciao in ASCII dec. OUT:&amp;quot;ciao&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  char *q = &amp;quot;ciao&amp;quot;; &lt;br /&gt;
  char *w = &amp;quot;mare&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  printf(&amp;quot;%s\n&amp;quot;, s);&lt;br /&gt;
  printf(&amp;quot;%s\n&amp;quot;, t);&lt;br /&gt;
  printf(&amp;quot;%s\n&amp;quot;, u);&lt;br /&gt;
&lt;br /&gt;
  dump(s, sizeof(s));&lt;br /&gt;
  dump(t, sizeof(t));&lt;br /&gt;
  dump(q, sizeof(*q)); //*q size is 1B (it points to 1 char). OUT: 63('c')&lt;br /&gt;
  dump(q, sizeof(q)); //q size is 8B. OUT: 5B of &amp;quot;ciao&amp;quot; + 3B of contiguous string &amp;quot;mare&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OUTPUT:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  ciao&lt;br /&gt;
  c  &lt;br /&gt;
  ciao&lt;br /&gt;
  63 69 61 6f 00&lt;br /&gt;
  63 00 00 00  69 00 00 00  61 00 00  00 6f 00 00  00 00 00 00 00 //int are 4B, char 1B, so we have empty bytes. &lt;br /&gt;
  Little endian pattern is evident. %s allow to print untill the first null, so printf prints just 'c' with int declaration of t[]&lt;br /&gt;
  63 &lt;br /&gt;
  63 69 61 6f  00 6d 61 72 // 6d = m, 61 = a, 72 = r.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''Assioma di finite progress.'''&lt;br /&gt;
&amp;quot;Tutto è vero purchè ogni statement abbia durata sconosciuta, ma finita&amp;quot;&lt;br /&gt;
* '''Modalità di concorrenza.'''&lt;br /&gt;
1- Inconsapevolmente concorrenti: ogni processo elabora ciò per cui è stato creato. Il kernel si occupa di coordinazione e comunicazione.&lt;br /&gt;
&lt;br /&gt;
2- Indirettamente concorrenti: processi che accedono a file condivisi.&lt;br /&gt;
&lt;br /&gt;
3- Consapevolmente concorrenti: il programma ha diversi processi in esecuzione.&lt;br /&gt;
*''' Convenzioni usate nel corso (e negli esami) relative alle istruzioni atomiche'''&lt;br /&gt;
Le istruzioni atomiche vengono compiute in modo indivisibile, garantendo che l'azione non interferisca con altri processi durante la sua esecuzione.&lt;br /&gt;
Vengono rappresentate nel seguente modo:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;istruzione atomica&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Anche gli statement sono istruzioni atomiche(escluso il caso long long).&lt;br /&gt;
*''' Proprieta' di Safety e Liveness'''&lt;br /&gt;
-Safety: se il programma evolve, evolve bene(S) &lt;br /&gt;
&lt;br /&gt;
&amp;quot;''Something good'' happens&amp;quot;&lt;br /&gt;
&lt;br /&gt;
-Liveness: il programma va avanti(L)&lt;br /&gt;
&lt;br /&gt;
&amp;quot;''Something eventually'' happens&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* '''Problema del consenso.'''&lt;br /&gt;
Il problema è così definito: ci sono n processi, ognuno propone un valore. Al termine tutti i processi devono accordarsi su di 1 dei valori proposti.Il suddetto problema è risolvibile  se e solo se vengono rispettate le seguenti proprietà (2 di Safety + 1 di Liveness)&lt;br /&gt;
&lt;br /&gt;
Proprietà:&lt;br /&gt;
&lt;br /&gt;
S(1)- Tutti i processi devono ritornare lo stesso valore&lt;br /&gt;
&lt;br /&gt;
S(2)- Ogni processo corretto decide uno tra i valori proposti&lt;br /&gt;
&lt;br /&gt;
L(1)- Ogni processo corretto deve dare una risposta&lt;br /&gt;
&lt;br /&gt;
w/o S(1): f(x) = x&lt;br /&gt;
&lt;br /&gt;
w/o S(2): f(x) = 42&lt;br /&gt;
&lt;br /&gt;
w/o L(1): f(x) = while(1)&lt;br /&gt;
&lt;br /&gt;
*w/o = without&lt;br /&gt;
&lt;br /&gt;
* '''Problemi delle elaborazioni concorrenti. Prima definizione (non formale) di Deadlock e Starvation'''&lt;br /&gt;
Introduzione alla notazione per la rappresentazione dei processi concorrenti&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1.0&lt;br /&gt;
&lt;br /&gt;
  process p {&lt;br /&gt;
    ...&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  process q {&lt;br /&gt;
    ...&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Due processi eseguiti in concorrenza.&lt;br /&gt;
  &lt;br /&gt;
  ...&lt;br /&gt;
  cobegin&lt;br /&gt;
  ...&lt;br /&gt;
  //&lt;br /&gt;
  ...&lt;br /&gt;
  //&lt;br /&gt;
  ...&lt;br /&gt;
  coend&lt;br /&gt;
&lt;br /&gt;
cobegin e coend indicano la sezione di codice con le istruzioni (rappresentate da &amp;quot;...&amp;quot; e separate da &amp;quot;//&amp;quot;)che verranno eseguite in concorrenza.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1.1 - Merge sort&lt;br /&gt;
  cobegin &lt;br /&gt;
    sort(v[1 : N])&lt;br /&gt;
  //&lt;br /&gt;
    sort(v[N : N])&lt;br /&gt;
  //&lt;br /&gt;
    merge(v[1 : N]), v[N : M])&lt;br /&gt;
  coend&lt;br /&gt;
&lt;br /&gt;
Se isoliamo le istruzioni senza considerare cobegin e coend, lo pseudocodice è corretto. &lt;br /&gt;
Se consideriamo anche cobegin e coend il programma esegue la funzione di merge() in concorrenza con le due chiamate a sort(), utilizzando risorse in comune e pertanto cercando di ordinare  elementi non ancora ordinati nelle due metà dell'array.&lt;br /&gt;
Se spostiamo l'invocazione di merge al di fuori della sezione cobegin ... coend, lo pseudocodice (vedi in basso) è corretto.&lt;br /&gt;
&lt;br /&gt;
  cobegin &lt;br /&gt;
    sort(v[1 : N])&lt;br /&gt;
  //&lt;br /&gt;
    sort(v[N : N])&lt;br /&gt;
  coend&lt;br /&gt;
    merge(v[1 : N]), v[N : M])&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In particolare nella programmazione concorrente si devono affrontare due problemi che riguardano la liveness, parliamo di deadlock e starvation.&lt;br /&gt;
&lt;br /&gt;
'''DEADLOCK''': uno o più processi non possono continuare l'esecuzione perchè ognuno di essi è in attesa che gli altri facciano qualcosa&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1.2&lt;br /&gt;
&lt;br /&gt;
  process p {&lt;br /&gt;
  while 1:&lt;br /&gt;
    prendi a&lt;br /&gt;
    prendi b&lt;br /&gt;
    calcola f(a,b)&lt;br /&gt;
    lascia a&lt;br /&gt;
    lascia b&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  process q {&lt;br /&gt;
  while 1:&lt;br /&gt;
    prendi a&lt;br /&gt;
    prendi b&lt;br /&gt;
    calcola f(a,b)&lt;br /&gt;
    lascia a&lt;br /&gt;
    lascia b&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Supponiamo di invertire l'ordine di prendi a e prendi b nel processo p. Dopo che entrambi i processi eseguono la prima istruzione lo scenario che si sviluppa è il seguente: al processo p serve la risorsa a, presa dal processo q, al quale servirebbe la risorsa b, presa dal processo p. I processi aspettano a vicenda che uno rilasci la risorsa che possiede l'altro. Risorsa necessaria ad entrambi i processi per eseguire l'istruzione (f(a,b)) che le richiederebbe entrambe. I processi sono deadlocked.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''STARVATION''': quando un processo attende una risorsa indefinitamente, e questa non gli viene mai allocata&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1.3&lt;br /&gt;
  process p {&lt;br /&gt;
  while 1:&lt;br /&gt;
    prendi a&lt;br /&gt;
    calcola f(a)&lt;br /&gt;
    lascia a&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  process q {&lt;br /&gt;
  while 1:&lt;br /&gt;
    prendi a&lt;br /&gt;
    calcola f(a)&lt;br /&gt;
    lascia a&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Supponiamo che i processi per realizzare &amp;quot;prendi a&amp;quot; verifichino la disponibilit&amp;amp;agrave; di a tramite polling (provando ripetutamente se a &amp;amp;egrave; libero). Il processo q pu&amp;amp;ograve; essere sfortunato e non riuscire mai ad avere accesso ad a (tutte le volte che cerca di &amp;quot;prenderlo&amp;quot; lo trover&amp;amp;agrave; impegnato da p). Il processo q è in starvation.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1.4&lt;br /&gt;
&amp;quot;Supponiamo di avere tre processi (P1, P2, P3) i quali richiedono periodicamente accesso alla risorsa R. Consideriamo la situazione nella quale P1 è in possesso della risorsa, e sia P2 che P3 attendono per quella risorsa. Quando P1 esce dalla sua sezione critica, a P2 o P3 dovrebbe essere permesso l'accesso a R. Assumiamo che il SO garantisca l'accesso a P3 e che P1  richieda nuovamente la risorsa prima che P3 termini la sua sezione critica. Se il SO dovesse garantire l'accesso a P1 dopo che P3 abbia finito, e successivamente dovesse garantire alternativamente a P1 e P3 l'accesso alla risorsa, allora P2 potrebbe vedersi indefinitamente negato l'accesso alla risorsa, sebbene non vi sia una situazione di deadlock&amp;quot; (Stallings W.(2015). ''Operating System: internals and design principles''. (8th ed). Pearson)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Problema: posso creare sezioni critiche, tramite la programmazione, senza aiuti hardware?'''&lt;br /&gt;
&lt;br /&gt;
ESEMPIO&lt;br /&gt;
&lt;br /&gt;
  process p&lt;br /&gt;
    /*enter_cs*/&lt;br /&gt;
    A = A + 1&lt;br /&gt;
    /*exit_cs*/&lt;br /&gt;
&lt;br /&gt;
  process q&lt;br /&gt;
    /*enter_cs*/&lt;br /&gt;
    A = A - 1&lt;br /&gt;
    /*exit_cs*/&lt;br /&gt;
&lt;br /&gt;
'''Le 4 proprietà della critical section(CS)'''&lt;br /&gt;
&lt;br /&gt;
SAFETY(1): una operazione all'interno della CS &lt;br /&gt;
&lt;br /&gt;
ESEMPIO&lt;br /&gt;
  process p&lt;br /&gt;
  while 1:&lt;br /&gt;
    /*enter_cs*/&lt;br /&gt;
    A = A + 1&lt;br /&gt;
    /*exit_cs*/&lt;br /&gt;
    ...  &amp;lt;--- process q deve poter &amp;quot;entrare&amp;quot; qui&lt;br /&gt;
&lt;br /&gt;
LIVENESS(1): no deadlock&lt;br /&gt;
&lt;br /&gt;
LIVENESS(2): no starvation&lt;br /&gt;
&lt;br /&gt;
LIVENESS(3): no useless wait&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Dekker(1st attempt) //KEY: turno''&lt;br /&gt;
&lt;br /&gt;
  turn = p //var globale condivisa&lt;br /&gt;
&lt;br /&gt;
  process p{&lt;br /&gt;
    while (turn != p)&lt;br /&gt;
      ;&lt;br /&gt;
    /*A = A + 1*/&lt;br /&gt;
    turn = q&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  process q{&lt;br /&gt;
    while(turn != q)&lt;br /&gt;
      ;&lt;br /&gt;
    /*A = A - 1*/&lt;br /&gt;
    turn = p&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Non rispetta la proprietà di liveness(3), infatti uno dei due processi può dover attendere a lungo il suo turno eseguendo ciclicamente l'empty statement all'interno&lt;br /&gt;
del corpo del while. Questa situazione può verificarsi se l'altro processo fallisce dentro o fuori la sua CS (in questo caso l'attesa è infinita). Inoltre visto che i processi si alternano strettamente nell'utilizzo della loro critical section, il ritmo di esecuzione è dettato dal processo più lento dei due.&lt;br /&gt;
&lt;br /&gt;
''Dekker(2nd attempt) //KEY: flag, ma senza turno''&lt;br /&gt;
&lt;br /&gt;
  inp = false&lt;br /&gt;
&lt;br /&gt;
  inq = false&lt;br /&gt;
&lt;br /&gt;
  process p{&lt;br /&gt;
    while 1:&lt;br /&gt;
      while(inq)&lt;br /&gt;
         ;&lt;br /&gt;
      inp = true&lt;br /&gt;
      /*A = A + 1*/&lt;br /&gt;
      inp = false&lt;br /&gt;
      ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  process q{&lt;br /&gt;
    while 1:&lt;br /&gt;
      while(inp)&lt;br /&gt;
         ;&lt;br /&gt;
      inq = true&lt;br /&gt;
      /*A = A - 1*/&lt;br /&gt;
      inq = false&lt;br /&gt;
      ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In questo caso se uno dei processi fallisce fuori dalla CS (dopo aver settato il flag a false) l'altro processo non viene bloccato. Se invece un processo fallisce nella sua CS oppure dopo aver settato il flag a true allora l'altro processo resta bloccato indefinitamente. Non è garantita inoltre la mutua esclusione in quanto entrambi potrebbero essere al contempo nelle loro rispettive CS, ad esempio:&lt;br /&gt;
&lt;br /&gt;
p valuta la condizione del while e trova inq a false&lt;br /&gt;
 &lt;br /&gt;
q valuta la condizione del while e trova inp a false&lt;br /&gt;
&lt;br /&gt;
p assegna true a inp ed entra nella sua CS&lt;br /&gt;
&lt;br /&gt;
q assegna true a inq ed entra nella sua CS &lt;br /&gt;
&lt;br /&gt;
....entrambi sono nella rispettiva CS --&amp;gt; no liveness(1)&lt;br /&gt;
&lt;br /&gt;
''Dekker(3nd attempt) //KEY: come 2nd ma con due istruzioni scambiate di ordine''&lt;br /&gt;
&lt;br /&gt;
  process p{&lt;br /&gt;
    while 1:&lt;br /&gt;
      inp = true&lt;br /&gt;
      while(inq)&lt;br /&gt;
         ;    &lt;br /&gt;
      /*A = A + 1*/&lt;br /&gt;
      inp = false&lt;br /&gt;
      ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  process q{&lt;br /&gt;
    while 1:&lt;br /&gt;
      inq = true&lt;br /&gt;
      while(inp)&lt;br /&gt;
         ;&lt;br /&gt;
      /*A = A - 1*/&lt;br /&gt;
      inq = false&lt;br /&gt;
      ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Scambiando l'ordine di due istruzioni vi è la mutua esclusione, tuttavia se entrambi i processi assegnano true al loro rispettivo flag, prima di valutare la condizione&lt;br /&gt;
del while, entrambi resteranno all'interno del while in attesa che l'altro processo &amp;quot;esca dalla CS&amp;quot; (non vi è mai entrato) --&amp;gt; deadlock&lt;br /&gt;
&lt;br /&gt;
''Dekker(4nd attempt) //KEY: flag + delay''&lt;br /&gt;
&lt;br /&gt;
  process p{&lt;br /&gt;
    while 1:&lt;br /&gt;
      inp = true&lt;br /&gt;
      while(inq){&lt;br /&gt;
        inp = false; &amp;lt; delay &amp;gt;; inp = true;&lt;br /&gt;
      }          &lt;br /&gt;
      /*A = A + 1*/&lt;br /&gt;
      inp = false&lt;br /&gt;
      ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  process q{&lt;br /&gt;
    while 1:&lt;br /&gt;
      inq = true&lt;br /&gt;
      while(inp){&lt;br /&gt;
        inq = false; &amp;lt; delay &amp;gt;; inq = true;&lt;br /&gt;
      }&lt;br /&gt;
      /*A = A - 1*/&lt;br /&gt;
      inq = false&lt;br /&gt;
      ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In questo caso il settare il proprio flag a true è indice del desiderio di entrare nella propria CS. Vi è ancora mutua esclusione. Se però dovesse verificarsi una sequenza del tipo:&lt;br /&gt;
&lt;br /&gt;
p imposta flag inp a true&lt;br /&gt;
&lt;br /&gt;
q imposta falg inq a true&lt;br /&gt;
&lt;br /&gt;
p valuta la condizione del while &lt;br /&gt;
&lt;br /&gt;
q valuta la condizione del while&lt;br /&gt;
&lt;br /&gt;
p imposta flag inp a false&lt;br /&gt;
&lt;br /&gt;
q imposta flag inq a false&lt;br /&gt;
&lt;br /&gt;
p imposta flag inp a true&lt;br /&gt;
&lt;br /&gt;
q imposta flag inq a true&lt;br /&gt;
&lt;br /&gt;
allora c'è la possibilità che si ripeta all'infinito (entrambi i processi potrebbero rimanere nel while). Essendo però una condizione che dipende dalla velocità relativa dei due processi, esiseranno delle situazioni nelle quali uno dei due processi potrà entrare, così come vi saranno delle situazioni nelle quali nessun processo avrà accesso alla propria CS --&amp;gt; livelock&lt;br /&gt;
&lt;br /&gt;
'''Dekker...finally //KEY: flag + turni (&amp;quot;cambio delay con turno)'''&lt;br /&gt;
&lt;br /&gt;
  needp = false &lt;br /&gt;
&lt;br /&gt;
  needq = false&lt;br /&gt;
&lt;br /&gt;
  turn = p&lt;br /&gt;
&lt;br /&gt;
  process p{&lt;br /&gt;
    while 1:&lt;br /&gt;
      needp = true&lt;br /&gt;
      while (needq) { &lt;br /&gt;
        needp = false &lt;br /&gt;
        while(turn == q)&lt;br /&gt;
         ; &lt;br /&gt;
        needp = true&lt;br /&gt;
      }&lt;br /&gt;
     /*A = A + 1*/&lt;br /&gt;
     needp = false&lt;br /&gt;
     turn = q&lt;br /&gt;
     ...&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
  process q{&lt;br /&gt;
   while 1:&lt;br /&gt;
     needq = true&lt;br /&gt;
     while(needp) {&lt;br /&gt;
       needq = false; &lt;br /&gt;
       while(turn == p)&lt;br /&gt;
        ; &lt;br /&gt;
       needq = true&lt;br /&gt;
    }&lt;br /&gt;
    /*A = A - 1*/&lt;br /&gt;
    needq = false&lt;br /&gt;
    turn = p&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In questo caso il processo che vuole entrare nella sua CS ne manifesta il bisogno settando need a true. Se entrambi i processi entrano nel corpo del while il tie-break è espletato dal while la cui condizione è un turn che sarà uguale soltanto ad uno dei due processi (in questo caso turn è inizializzata a p), e dunque non si verificherànno situazioni di deadlock o livelock.&lt;br /&gt;
*'''Proprietà della sezione critica'''&lt;br /&gt;
Mutua esclusione &amp;lt;br&amp;gt;&lt;br /&gt;
Assenza di deadlock &amp;lt;br&amp;gt;&lt;br /&gt;
Assenza di starvation &amp;lt;br&amp;gt;&lt;br /&gt;
Assenza di delay non necessari&lt;br /&gt;
&lt;br /&gt;
*'''La test&amp;amp;set'''&lt;br /&gt;
E' un istruzione atomica per impostare il valore di una variabile a 1 (lock) ma ritornare il vecchio valore. Questa istruzione può essere utilizzata per gestire sezioni critiche. &amp;lt;br&amp;gt; &lt;br /&gt;
Il valore ritornato dalla test and set viene comparato a 1, se è uguale si aspetta altrimenti si procede.&amp;lt;br&amp;gt;&lt;br /&gt;
In questo modo il primo processo che cercherà di entrare nella sezione critica ci riuscirà e imposterà il lock a 1, gli altri processi si troveranno il lock a 1 e dovranno aspettare. Quando il primo processo avrà finito ripristinerà il vecchio valore di lock e permetterà a uno degli altri processi di eseguire la test and set e di entrare nella sezione critica.&lt;br /&gt;
----&lt;br /&gt;
Altri argomenti trattati che non sono stati qui riportati in forma di appunti:&lt;br /&gt;
Gestione algoritmica della critical section.&lt;br /&gt;
Algoritmo di Dekker.&lt;br /&gt;
Gestione architetturale della critical section.&lt;br /&gt;
&lt;br /&gt;
[[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 10:35, 17 October 2016 (CEST)&lt;br /&gt;
&lt;br /&gt;
== Lezione del 14 ottobre 2016 ==&lt;br /&gt;
Audio Lezione: [https://drive.google.com/open?id=0B8G3NNuJ7MA7UlJadTRVbzhWRFE 14\10\16]&lt;br /&gt;
* Sistemi Operativi per elaboratori multiprocessore SMP/non SMP&lt;br /&gt;
* Macchine di von Neumann (e di Harvard).&lt;br /&gt;
* Il bus.&lt;br /&gt;
* Interrupt e Trap&lt;br /&gt;
* Gestione degli interrupt&lt;br /&gt;
* Mascheramento degli interrupt&lt;br /&gt;
* Interrupt Nidificati&lt;br /&gt;
* DMA&lt;br /&gt;
* Device a carattere e device a blocchi&lt;br /&gt;
* System call come trap.&lt;br /&gt;
* La gerarchia delle memorie&lt;br /&gt;
* cache&lt;br /&gt;
&lt;br /&gt;
== Lezione del 18 ottobre 2016 ==&lt;br /&gt;
Audio Lezione: [https://drive.google.com/open?id=0B8G3NNuJ7MA7eVlFdWR0cUkxd2M 18/10/16]&lt;br /&gt;
&lt;br /&gt;
Argomenti trattati:&lt;br /&gt;
*'''Implementazione in C della Test&amp;amp;Set, degli Algoritmi di Dekker e Peterson.'''&lt;br /&gt;
Codice sorgente disponibile [[Dekker,_Peterson_e_Test%26Set_in_C|qui]].&lt;br /&gt;
*'''Definizione di Semaforo.'''&lt;br /&gt;
Un semaforo è una variabile intera che può essere modificata solo da 2 operazioni atomiche: P(Proberen) e V(Verhogen).&amp;lt;br&amp;gt;&lt;br /&gt;
P aspetta che il valore sia minore o uguale a zero. Quando lo diventa decrementa il valore.&amp;lt;br&amp;gt;&lt;br /&gt;
S incrementa il valore. &amp;lt;br&amp;gt;&lt;br /&gt;
*'''Invariante del Semaforo.'''&lt;br /&gt;
Il numero di Proberen deve essere minore o uguale al numero di Verhogen più il valore d'inizializzazione del semaforo. &amp;lt;br&amp;gt;&lt;br /&gt;
In questo modo si può gestire per esempio l'allocazione di risorse. Il semaforo viene inizializzato al numero di risorse e ogni volta che un processo ha bisogno della risorsa esegue un proberen, quindi decrementa il valore se la risorsa è disponibile. Quando un processo rilascia la risorsa esegue un verhogen.&lt;br /&gt;
*'''Esempio: implementazione di sezione critica e di semplice sincronizzazione con semafori.'''&lt;br /&gt;
&lt;br /&gt;
== Lezione del 21 ottobre 2016 ==&lt;br /&gt;
*'''Make'''&lt;br /&gt;
Make è un sistema per la compilazione intelligente dei programmi. &amp;lt;br&amp;gt;&lt;br /&gt;
Permette invece di compilare tutto il programma tutte le volte di compilarne solo una parte, solo quella che è stata modificata.&lt;br /&gt;
Per capirne il funzionamento è utile un parallelismo con le ricette di cucina. Si specificano dei target che sono gli obbiettivi della ricetta.Per fare una ricetta servono degli ingredienti e magari quegli ingredienti hanno a loro volta delle ricette. Make allo stesso modo definisce dei target che possono avere come dipendenze altri target.  &amp;lt;br&amp;gt;&lt;br /&gt;
Nel installazione del programma il programma ./configure cerca nel sistema se le librerie da cui è dipendente il programma sono presenti. &amp;lt;br&amp;gt;&lt;br /&gt;
*'''Git'''&lt;br /&gt;
Git è un sistema di versioning, serve a tener traccia delle modifiche apportante a un progetto nel tempo. E' utile per collaborare a un progetto condiviso. &amp;lt;br&amp;gt;&lt;br /&gt;
Possibilità per ogni membro del repository di lavorare in maniera indipendente e poi aggiornare il progetto comune. &amp;lt;br&amp;gt;&lt;br /&gt;
Ce ne sono stati tanti di sistemi di versioning tipo SVN, Mercurial ma attualmente Git è il più utilizzato.&amp;lt;br&amp;gt;&lt;br /&gt;
Git non serve solo per scrivere programmi ma per esempio anche libri, in modo collaborativo.&amp;lt;br&amp;gt;&lt;br /&gt;
Git è completamente distribuito, non esiste in un repository una copia principale e delle copie secondarie ma ogni copia può essere principale. &amp;lt;br&amp;gt;&lt;br /&gt;
Per iniziare un repository git si utilizza il comando ''git init''.&amp;lt;br&amp;gt;&lt;br /&gt;
Per aggiungere un file al repository si utilizza il comando ''git add''.&amp;lt;br&amp;gt;&lt;br /&gt;
Ma ad ogni singola modifica git non aggiorna automaticamente il repository ma è l'utente che tramite il comando git commit indica che una certa versione del progetto deve essere salvata.&amp;lt;br&amp;gt;&lt;br /&gt;
Il comando ''git log'' serve a visualizzare la storia del repository.&amp;lt;br&amp;gt;&lt;br /&gt;
Il comando ''git diff'' serve a vedere la differenza tra ciò che è nel repository e le modifiche di cui non si è ancora fatto il commit.&amp;lt;br&amp;gt;&lt;br /&gt;
Dopo aver dato il comando add si dice che i file sono 'on stage' prima del commit.&amp;lt;br&amp;gt;&lt;br /&gt;
Il comando ''git checkout'' serve a spostarsi nella storia del repository.&amp;lt;br&amp;gt;&lt;br /&gt;
Il comando ''git clone'' serve a copiare un repository remoto.&amp;lt;br&amp;gt;&lt;br /&gt;
Il comando ''git push'' serve a inviare le modifiche al repository remoto mentre il comando ''git pull'' serve a ricevere le modifiche più aggiornate dal repository remoto.&amp;lt;br&amp;gt;&lt;br /&gt;
Il ''merge'' è il processo in cui uno degli utenti che crea un conflitto deve &amp;quot;a mano&amp;quot; scegliere quali parti dei due commit in conflitto vuole tenere.&amp;lt;br&amp;gt;&lt;br /&gt;
Git è utile per trovare i bug che non erano presenti in versioni precedenti del progetto. Si può tornare alla versione precedente stabile e avanzare nella storia fino a trovare una versione instabile. A questo punto è probabile che l'origine del bug sia nelle modifiche che hanno creato questa versione instabile.&amp;lt;br&amp;gt;&lt;br /&gt;
Github è una piattaforma di repository dove possono essere mantenuti dei progetti gratuitamente se sono con licenza libera.&amp;lt;br&amp;gt;&lt;br /&gt;
In una directory in cui è presente un repository git e sempre la presente la cartella ''.git'' dove il sistema di versioning mantiene il database della storia. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lezione del 25 ottobre 2016 ==&lt;br /&gt;
Audio Lezione: [https://drive.google.com/open?id=0B8G3NNuJ7MA7WjZJMm11cjhuQXc 25/10/16]&lt;br /&gt;
&lt;br /&gt;
Argomenti trattati:&lt;br /&gt;
* '''Semafori fair'''&lt;br /&gt;
Un semaforo è &amp;quot;fair&amp;quot; quando tende a non sbloccare dallo stato di attesa sempre lo stesso processo ma cerca di distribuire la possibilità di esecuzione a più processi.&lt;br /&gt;
* '''Problemi classici della concorrenza risolti con semafori'''&lt;br /&gt;
** '''Produttore/consumatore'''&lt;br /&gt;
::Due processi: produttore e consumatore. Il produttore genera continuamente dei valori e il consumatore continuamente li legge. Il consumatore deve leggere i valori nello stesso ordine in cui il produttore li genera. &amp;lt;br&amp;gt;&lt;br /&gt;
::Un meccanismo di sincronizzazione e richiesto perchè sorgono immediatamente due problemi:&lt;br /&gt;
::* Produttore troppo veloce: il consumatore non fa in tempo a consumare il valore e c'è la possibilità che i valori non ancora letti vengano sovrascritti.&lt;br /&gt;
::* Consumatore troppo veloce: possibilità che il consumatore legga un valore che ha già letto perchè il produttore non ha fatto in tempo a produrne uno nuovo.&lt;br /&gt;
::Per risolvere il problema utilizziamo due semafori: empty e full. &amp;lt;br&amp;gt;&lt;br /&gt;
::Il produttore aspetta che il segnale del semaforo empty (Proberen), scrive il valore e dà segnale sul semaforo full (Verhogen).&amp;lt;br&amp;gt;&lt;br /&gt;
::Il consumatore aspetta  il segnale del semaforo full (Proberen), legge il valore e dà segnale sul semaforo empty (Verhogen).&amp;lt;br&amp;gt;&lt;br /&gt;
:* '''Buffer-limitato'''&lt;br /&gt;
::Simile al problema produttore/consumatore ma qui lo scambio avviene tramite un buffer limitato. &amp;lt;br&amp;gt;&lt;br /&gt;
::In questo modo nel caso di rallentamento temporaneo del consumer il producer non deve aspettare ma può produrre più unità e viceversa nel caso di rallentamento del producer, se il buffer ha più di un elemento, il consumer può utilizzare il tempo in attesa del producer per consumare più dati. &amp;lt;br&amp;gt;&lt;br /&gt;
::Nel caso il buffer sia completamente pieno/vuoto rimangono comunque i problemi di sincronizzazione tipici del problema: il producer non deve sovrascrivere dei valori che il consumer non ha ancora letto e il consumer non deve leggere più volte lo stesso valore se il producer non è stato abbastanza veloce da cambiarlo. &amp;lt;br&amp;gt;&lt;br /&gt;
::Come soluzione al problema si può utilizzare un coda circolare. Il produttore esegue una enqueue e il consumatore una dequeue. &amp;lt;br&amp;gt;&lt;br /&gt;
::Il problema può essere generalizzato facilmente a produttori/consumatori multipli ma in questo caso dobbiamo creare un semaforo per rendere atomiche la enqueue e la dequeue altrimenti incorriamo in una race condition nel accesso alla coda.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 28 ottobre 2016 ==&lt;br /&gt;
&lt;br /&gt;
'''Implementazione dei semafori''' &amp;lt;br&amp;gt;&lt;br /&gt;
Un processo che esegue una Proberen in teoria dovrebbe aspettare continuando a controllare se può entrare nella sezione critica. Questo meccanismo si chiama &amp;quot;busy waiting&amp;quot; e consuma molte istruzioni della CPU. &amp;lt;br&amp;gt;&lt;br /&gt;
Il busy waiting è diverso dal polling in quando il polling prevede di eseguire altre azioni mentre si aspetta tra un tentativo e l'altro. &amp;lt;br&amp;gt;&lt;br /&gt;
Di solito si utilizza il busy waiting solo quando il tempo di lock di un processo è molto breve. Questo perchè anche se esoso di risorse del processore il busy waiting non comporta un context switch, cioè salvare tutto lo stato del processo in memoria, e quindi permette di risparmiare memoria. &lt;br /&gt;
Se invece il tempo di lock è più lungo allora è preferibile utilizzare un context-switch dove il processo si blocca da solo e chiama un processo di blocking che lo mette in una coda di attesa associata al semaforo che sta aspettando. Il controllo passa poi allo scheduler della CPU che seleziona un altro processo da eseguire.&lt;br /&gt;
Le primitive suspend and wakeup indicano rispettivamente lo spostamento di un processo nella/dalla coda dei processi ready.&lt;br /&gt;
* nei kernel monoprocessore&lt;br /&gt;
: Implementazione dei semafori tramite mutex che possono essere a loro volta implementate con un sistema di mascheramento degli interrupt.&lt;br /&gt;
* nei kernel multiprocessore&lt;br /&gt;
: Utilizzo di soluzioni hardware come test and set. Sono soluzioni che utilizzano il busy waiting ma in questo caso solo per rendere atomico le operazioni del semaforo, che sono poche istruzioni.&lt;br /&gt;
Certe implementazioni dei semafori potrebbero permettere di far andare il valore in negativo. Questo serve per misurare il numero dei processi in attesa di quel semaforo. &amp;lt;br&amp;gt;&lt;br /&gt;
'''Il problema della cena dei filosofi''': &amp;lt;br&amp;gt;&lt;br /&gt;
5 filosofi passano la loro vita pensando e mangiando. Condividono un tavolo circolare con 5 sedie e ogni sedia appartiene a un solo filosofo. Alla sinistra e alla destra di ogni piatto c'è una bacchetta per un totale di 5 bacchette. La bacchetta destra che un filosofo usa per mangiare viene quindi utilizzata come bacchetta sinistra dal filosofo alla sua destra e simmetricamente è uguale per il filosofo che siede a sinistra. &amp;lt;br&amp;gt;&lt;br /&gt;
Ogni tanto un filosofo smette di pensare e cerca di prendere le rispettive bacchette, se ci riesce può mangiare e poi rilasciare le bacchette. &amp;lt;br&amp;gt;&lt;br /&gt;
Questo problema è un modello di problema ad accesso a ''insiemi'' di risorse. Mentre nel produttore/consumatore e buffer limitato gli attori in gioco avevano la necessità di avere l'accesso esclusivo a una sola risorsa in questo problema è richiesto l'accesso a insiemi di risorse (le bacchette) le cui parti possono essere desiderate da altri richiedenti.&lt;br /&gt;
* soluzione con contesa sulle posate e casi di deadlock&lt;br /&gt;
:Poniamo un semaforo per ogni bacchetta. Può succedere però, se nel implementazione i filosofi &amp;quot;prendono in mano&amp;quot; prima la bacchetta destra e poi quella sinistra, che tutti prendono in mano contemporaneamente la bacchetta destra e rimangono in un'attesa infinita della bacchetta sinistra che non gli verrà mai rilasciata perché anche il rispettivo filosofo alla propria sinistra è in attesa. &amp;lt;br&amp;gt; Questo è un caso di deadlock. &amp;lt;br&amp;gt;&lt;br /&gt;
:Una soluzione può essere quella di imporre che un filosofo sia &amp;quot;mancino&amp;quot; cioè prenda prima la bacchetta a sinistra e poi la destra. &amp;lt;br&amp;gt;&lt;br /&gt;
* soluzione con acquisizione atomica di entrambe le posate, casi di starvation&lt;br /&gt;
:Un'altra soluzione è quella di rendere atomico l'acquisizione di entrambe le posate. &amp;lt;br&amp;gt;&lt;br /&gt;
:Può succede che 4 filosofi compiano una &amp;quot;congiura&amp;quot;, cioè siano d'accordo a non lasciar risorse a un quinto filosofo. Questo è un caso di starvation e non è irreale in quando i 4 filosofi potrebbero essere processi malevoli che siano stati programmati di proposito per questo.&lt;br /&gt;
'''System Call''':&lt;br /&gt;
* POSIX.&lt;br /&gt;
:Il POSIX è uno standard (IEEE 1003) che definisce le signature delle system call. Un sistema operativo deve rispettare il POSIX per essere definito &amp;quot;Unix-like&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
:Di solito i parametri delle system call sono pensati per essere contenuti in un registro. Quasi sempre le system call ritornano un intero: 0 se la procedura è stata eseguita correttamente, -1 se c'è un errore. Quando una system call ritorna -1 la variabile &amp;quot;globale&amp;quot; errno (error number) contiene il codice di errore. La variabile errno non è realmente globale perché ne esiste una per ogni processo. &amp;lt;br&amp;gt;&lt;br /&gt;
:Unix è un sistema operativo file-system centrico quindi quando facciamo input/output anche da periferiche come stampanti e lettori CD usiamo le system call per la scrittura/lettura dei file. &amp;lt;br&amp;gt;&lt;br /&gt;
:Un catalogo delle system call principali può essere trovato [[Il ''catalogo'' delle System Call | qui]].&lt;br /&gt;
* fork/exec&lt;br /&gt;
:fork e exec sono system call importanti e servono per creare un nuovo processo: &amp;lt;br&amp;gt;&lt;br /&gt;
:fork: genera un processo clone di quello che ha chiamato fork. &amp;lt;br&amp;gt;&lt;br /&gt;
:exec: &amp;quot;cambia&amp;quot; il codice eseguito dal processo corrente. &amp;lt;br&amp;gt;&lt;br /&gt;
:Quindi per &amp;quot;aprire un nuovo programma&amp;quot; si utilizza una fork seguita da un exec.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 4 novembre 2016 ==&lt;br /&gt;
Audio Lezione: [https://drive.google.com/open?id=0B8G3NNuJ7MA7blhMT0tSNDRGSUU 4/11/16]&lt;br /&gt;
&lt;br /&gt;
* '''Problema della Cena dei filosofi'''. Soluzione con acquisizione atomica delle bacchette (con starvation)&lt;br /&gt;
* '''Tecnica del passaggio del testimone''' (Passing the Baton).&lt;br /&gt;
&lt;br /&gt;
: Non uscire dalla mutua esclusione se c'è qualcun altro che vuole entrarci e semplicemente fare in modo che lui l'acquisisca. La mutua esclusione termina solo se non c'è nessuno che più la richiede.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO IMPLEMENAZIONE BATON&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
  void baton(int i) {&lt;br /&gt;
    int j;&lt;br /&gt;
    for (j=1; j&amp;lt;5; j++) {&lt;br /&gt;
       if(philo_status[(i+j)%5] == 'W' &amp;amp;&amp;amp; philo_status[(i+j+1)%5] != 'E' &amp;amp;&amp;amp; philo_status[(i+j+4)%5] != 'E') {&lt;br /&gt;
          semaphore_V(philowait[(i+j)%5]);&lt;br /&gt;
          return;&lt;br /&gt;
       }&lt;br /&gt;
    }&lt;br /&gt;
    semaphore_V(mutex);&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* '''Cena dei filosofi, acquisizione atomica delle bacchette con passaggio del testimone'''.&lt;br /&gt;
* '''Implementazione della congiura dei filosofi'''&lt;br /&gt;
&lt;br /&gt;
* '''Le system call di gestione file (open, read, write, close, lseek)'''&lt;br /&gt;
Quando lavoriamo con dei file dobbiamo sempre specificare se vogliamo solo leggerli oppure anche scrivere, magari in append mode. Ci sono quindi varie modalità con cui possiamo interagire con i file. &amp;lt;br&amp;gt;&lt;br /&gt;
Un utente potrebbe essere autorizzato a gestire il file in certe modalità oppure no. &amp;lt;br&amp;gt;&lt;br /&gt;
Il descrittore di un file è un intero. &amp;lt;br&amp;gt;&lt;br /&gt;
Le system call, a differenza delle chiamate di funzioni di libreria, non sono bufferizzate. &lt;br /&gt;
&lt;br /&gt;
ESEMPIO&lt;br /&gt;
&lt;br /&gt;
Se si decommenta printf stampa 2 volte hello world perchè la stringa resta nel buffer. &lt;br /&gt;
&lt;br /&gt;
Se si decommenta write stampa 1 volta hello world.&lt;br /&gt;
&lt;br /&gt;
Per vedere le chiamate di sistema effettuate in fase di esecuzione, compilare il codice sorgente e poi utilizzare strace -f &amp;quot;eseguibile&amp;quot;.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
   #include&amp;lt;stdio.h&amp;gt;&lt;br /&gt;
   #include&amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  int main(int argc, char* argv[]){&lt;br /&gt;
    //printf(&amp;quot;hello world&amp;quot;); //if you ends the strings with \n is like to &amp;quot;fflushing&amp;quot; the buffer &lt;br /&gt;
    //write(1, &amp;quot;hello world&amp;quot;, 11); // 1 and 11 are file descriptors&lt;br /&gt;
    switch(fork()) {&lt;br /&gt;
      case 0: //child&lt;br /&gt;
         break;&lt;br /&gt;
      default: //parent&lt;br /&gt;
         wait(NULL);&lt;br /&gt;
         break;&lt;br /&gt;
      case -1: //error&lt;br /&gt;
         break;&lt;br /&gt;
    }&lt;br /&gt;
    printf(&amp;quot;\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Copia il file passato come primo argomento (argv[1]) nel file passato come secondo argomento (argv[2]), se quest'ultimo non esiste, allora lo crea.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
  #include&amp;lt;stdio.h&amp;gt;&lt;br /&gt;
  #define BUFSIZE 2048&lt;br /&gt;
  char buffer[BUFSIZE];&lt;br /&gt;
  int main(int argc, char *argv[]){&lt;br /&gt;
     int fdin = open(argv[1], O_RDONLY);&lt;br /&gt;
     int fdout = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666); //0666 is the umask used to modify the permissions of new files&lt;br /&gt;
&lt;br /&gt;
     int n;&lt;br /&gt;
     while((n = read(fdin, buffer, BUFSIZE)) &amp;gt; 0)&lt;br /&gt;
        write(fdout, buffer, n);&lt;br /&gt;
    &lt;br /&gt;
     close(fdin);&lt;br /&gt;
     close(fdout);&lt;br /&gt;
     return 0;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''umask''' &amp;lt;br&amp;gt;&lt;br /&gt;
Con la open possiamo usare dei permessi al massimo per quelli garantiti dal umask. &amp;lt;br&amp;gt;&lt;br /&gt;
Un numero composto da quattro cifre ottali. Controlla la maschera di creazione dei file. I bit di permesso contenuti nella maschera NON vengono settati nel nuovo file creato. Corrisponde all'opereazione:&lt;br /&gt;
&lt;br /&gt;
  R:(D &amp;amp;(~M)) &lt;br /&gt;
&lt;br /&gt;
Può essere vista come una sottrazione, i bit accesi nella umask vengono spenti nel file creato. &lt;br /&gt;
&lt;br /&gt;
e.g. se i permessi di default corrispondono a 0666(D) e la maschera corrisponde a 0022(M), R è uguale a 0666-0022 = 0644. Infatti&lt;br /&gt;
&lt;br /&gt;
   0022 =        |000|000|010|010|&lt;br /&gt;
  ~0022 =        |111|111|101|101|(7755)&lt;br /&gt;
   0666 =        |000|110|110|110|&lt;br /&gt;
  ~0022 &amp;amp; 0666 = |000|110|100|100|(0644)&lt;br /&gt;
&lt;br /&gt;
Tabella significato cifre umask&lt;br /&gt;
&lt;br /&gt;
  OTTALE  BINARIO  SIGNIFICATO&lt;br /&gt;
   0       000      Nessun permesso&lt;br /&gt;
   1       001      Solo esecuzione&lt;br /&gt;
   2       010      Solo scrittura&lt;br /&gt;
   3       011      Scrittura ed esecuzione&lt;br /&gt;
   4       100      Solo lettura&lt;br /&gt;
   5       101      Lettura ed esecuzione&lt;br /&gt;
   6       110      Lettura e scrittura&lt;br /&gt;
   7       111      Lettura, scrittura ed esecuzione&lt;br /&gt;
&lt;br /&gt;
I permessi sono rappresentati in simboli come segue:&lt;br /&gt;
&lt;br /&gt;
  r: read&lt;br /&gt;
  w: write&lt;br /&gt;
  x: execute&lt;br /&gt;
&lt;br /&gt;
'''umask - S''' da shell per visualizzare la umask attuale con rappresentazione simbolica&lt;br /&gt;
&lt;br /&gt;
'''ls -l &amp;quot;nomefile&amp;quot;''' da shell per visualizzare i permessi del file in notazione simbolica: '-' seguito da 3 triple che indicano i permessi di accesso per '''ugo''',  ovvero, rispettivamente, per user,  per group, per other.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO&lt;br /&gt;
  -rw-r--r--&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''File aperti durante fork/exec'''https://www.kernel.org/&lt;br /&gt;
&lt;br /&gt;
I file aperti vengono ereditati dai processi figli.&lt;br /&gt;
Processo padre e processo figlio condividono lo stesso offset nei file comuni. In questo modo si evita di sovrascriversi a vicenda. Ogni volta che il padre o il figlio scrivono sul file aggiornano l'offset comune.In questo modo se l'altro processo vuole anche lui scrivere in quel file partirà dal offset aggiornato e non sovrascriverà i dati scritti dal altro processo. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''La system call dup e la ridirezione'''&lt;br /&gt;
E' possibile fare in modo che più file descriptor siano associati a un unico file (per la precisione a un unica file table, che serve per gestire un file). &amp;lt;br&amp;gt;&lt;br /&gt;
La system call dup permette di fare ciò. Ritorna un nuovo file descriptor che punta a una file table già esistente.&amp;lt;br&amp;gt;&lt;br /&gt;
Possiamo quindi &amp;quot;ridirezionare&amp;quot; l'output o l'input di un processo figlio modificando i file descriptor prima di creare il processo figlio. Per esempio possiamo modificare un file descriptor che puntava a un normale file in modo che punti a un file di standard output e quindi anche se al processo figlio sembrerà di scrivere in un normale file starà scrivendo nel terminale.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
  #include&amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
  #include&amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
  #include&amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
  #include&amp;lt;stdio.h&amp;gt;&lt;br /&gt;
  #include&amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  int main(int argc, char * argv[]){&lt;br /&gt;
    int fdout;&lt;br /&gt;
    if(argc &amp;gt; 1);&lt;br /&gt;
    fdout = open (argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0666);&lt;br /&gt;
    switch(fork()) {&lt;br /&gt;
        case 0: //child&lt;br /&gt;
          dup2(fdout, 1); // duplica descrittore file, posso ridirezionare l'output. E' come fare '&amp;gt;' dalla shell &lt;br /&gt;
          execlp(”ls”, “-l”, 0); &lt;br /&gt;
          close(fdout);&lt;br /&gt;
          break;&lt;br /&gt;
        default: // parent&lt;br /&gt;
          wait(NULL);&lt;br /&gt;
          break;&lt;br /&gt;
        case -1: // error&lt;br /&gt;
          break;&lt;br /&gt;
    }&lt;br /&gt;
    printf(“\n”);&lt;br /&gt;
    return 0;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lezione del 8 novembre 2016 ==&lt;br /&gt;
'''Codice per la congiura dei filosofi'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;quot;semaphore.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
semaphore philowait[5];&lt;br /&gt;
semaphore mutex;&lt;br /&gt;
semaphore cospsem;&lt;br /&gt;
char philo_status[]=&amp;quot;TTTTT&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
void baton(int i){&lt;br /&gt;
    int j = 0;&lt;br /&gt;
    //iterate through remaining philosophers&lt;br /&gt;
    for(j = 1; j &amp;lt; 5;j++){&lt;br /&gt;
        //if there's any waiting philosopher, and no other philosophers(on his left and right side) are eating&lt;br /&gt;
        if(philo_status[(i+j)%5]=='W' &amp;amp;&amp;amp; philo_status[(i+j+1)%5]!= 'E' &amp;amp;&amp;amp; philo_status[(i+j+4)%5]!='E'){&lt;br /&gt;
            semaphore_V(philowait[(i+j)%5]);&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    semaphore_V(mutex);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ok2eat(int i){&lt;br /&gt;
    semaphore_P(mutex);&lt;br /&gt;
    philo_status[i]='W';&lt;br /&gt;
    if(philo_status[(i+1)%5] == 'E' || philo_status[(i+4)%5] == 'E'){&lt;br /&gt;
        semaphore_V(mutex);&lt;br /&gt;
        semaphore_P(philowait[i]); //wait for baton&lt;br /&gt;
    }&lt;br /&gt;
    philo_status[i] = 'E';&lt;br /&gt;
    printf(&amp;quot;philo eating:   %d |%s|\n&amp;quot;,i,philo_status);&lt;br /&gt;
    baton(i);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ok2think(int i){&lt;br /&gt;
    semaphore_P(mutex);&lt;br /&gt;
    philo_status[i] = 'T';&lt;br /&gt;
    printf(&amp;quot;philo thinking: %d |%s|\n&amp;quot;,i,philo_status);&lt;br /&gt;
    baton(i);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void conspiracy_in(){&lt;br /&gt;
    static int first_time = 1; //this variable's value is preserved between calls to this function&lt;br /&gt;
    semaphore_P(mutex);&lt;br /&gt;
    if(first_time){&lt;br /&gt;
        //first conspirator entering here will act like nothing happened&lt;br /&gt;
        first_time = 0;&lt;br /&gt;
    }&lt;br /&gt;
    else{&lt;br /&gt;
        //conspire&lt;br /&gt;
        semaphore_V(cospsem);&lt;br /&gt;
    }&lt;br /&gt;
    semaphore_V(mutex);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void conspiracy_out(){&lt;br /&gt;
    semaphore_P(cospsem);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void *philo(void *arg) {&lt;br /&gt;
    int i = (uintptr_t)arg;&lt;br /&gt;
    while (1) {&lt;br /&gt;
        //thinking/still thinking&lt;br /&gt;
        usleep(random() % 200000);&lt;br /&gt;
        ok2eat(i);&lt;br /&gt;
        //eating&lt;br /&gt;
&lt;br /&gt;
        //conspirators need to be synchronized to conspire&lt;br /&gt;
        if(i==1 || i==3) conspiracy_in();&lt;br /&gt;
        usleep(random() % 200000);&lt;br /&gt;
        if(i==1 || i==3) conspiracy_out();&lt;br /&gt;
        ok2think(i);&lt;br /&gt;
        //thinking again&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[]) {&lt;br /&gt;
    int i;&lt;br /&gt;
    pthread_t philo_t[5];&lt;br /&gt;
    srandom(time(NULL));&lt;br /&gt;
    mutex = semaphore_create(1);&lt;br /&gt;
    cospsem = semaphore_create(0);&lt;br /&gt;
    for (i=0; i&amp;lt;5; i++)&lt;br /&gt;
        philowait[i]=semaphore_create(0);&lt;br /&gt;
    for (i=0; i&amp;lt;5; i++)&lt;br /&gt;
        pthread_create(&amp;amp;philo_t[i], NULL, philo, (void *)(uintptr_t) i);&lt;br /&gt;
    for (i=0; i&amp;lt;5; i++)&lt;br /&gt;
        pthread_join(philo_t[i], NULL);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''Problema dei lettori e scrittori'''&lt;br /&gt;
:Spesso quando gestiamo la concorrenza all’accesso a strutture dati separiamo i ruoli di solo lettori con anche quello di scrittori (che per fare ciò possono dover anche leggere).&lt;br /&gt;
:Mentre che per i lettori non c’è un limite a quanti contemporaneamente possono avere accesso alle informazioni, gli scrittori devono avere accesso esclusivo alla struttura dati.&lt;br /&gt;
:Questo perché incorriamo in una race-condition solo quando modifichiamo la struttura dati.&lt;br /&gt;
:Dare precedenza di accesso a una categoria o all’altra porta starvation nella categoria con meno priorità.&lt;br /&gt;
:Una soluzione che abbiamo dato è quella di dare esclusività ai ruoli a turno:&lt;br /&gt;
:dopo che uno scrittore ha finito di scrivere garantisce l’accesso prima a tutti i lettori in attesa e se non sono presenti agli scrittori in attesa. Per i lettori è il contrario: dopo che l’ultimo lettore ha finito di leggere garantisce l’accesso a uno dei possibili scrittori in attesa e se non ce ne sono ai lettori.&lt;br /&gt;
:La soluzione in C al problema lettori e scrittori può essere trovata [[Zibaldone|qui]].&lt;br /&gt;
* '''Semafori Binari(Introduzione)'''&lt;br /&gt;
: Un semaforo binario è un semaforo ordinario che rispetta la seguente invariante: '''nP&amp;lt;=nV+Init&amp;lt;=nP+1''', ovvero è un semaforo che può assumere solo valori 0 o 1.&lt;br /&gt;
: Si può dimostrare che i semafori ordinari sono espressivi tanto quanto quelli binari(e viceversa) fornendo una implementazione di semafori binari tramite i semafori ordinari e viceversa una implementazione di semafori ordinari tramite semafori binari. (l'invariante dei semafori binari si pu&amp;amp;ograve; anche scrivere '''0 &amp;lt;= nV + Init - nP &amp;lt;= 1''' dove '''nV + Init - nP''' rappresenta proprio il valore del semaforo).&lt;br /&gt;
&lt;br /&gt;
== Lezione del 11 novembre 2016 ==&lt;br /&gt;
* '''Valore di ritorno di un processo'''&lt;br /&gt;
:Un processo ritorna un valore che è un numero intero. &amp;lt;br&amp;gt;&lt;br /&gt;
:Questo può essere deciso come valore di ritorno del main, oppure con la funzione ''exit''.&lt;br /&gt;
:La system call wait aspetta che lo stato di un processo figlio cambi e quando cambia scrive il nuovo in una variabile. &amp;lt;br&amp;gt;&lt;br /&gt;
:Quando un programma termina il suo valore di ritorno viene salvato nel descrittore del processo. Se il padre non esegue la wait il descrittore del processo figlio non verrà mai eliminato e il processo figlio rimarrà uno &amp;quot;zombie&amp;quot;.&lt;br /&gt;
:Un esempio del uso si queste system call si può trovare [[2016-17_Programmi_C#Print_child_exit_status|qui]].&lt;br /&gt;
* '''Pipe'''&lt;br /&gt;
:La system call pipe crea un canale monodirezionale che può essere usato per la comunicazione tra processi. Dimostrazione del uso di pipe si può trovare [[2016-17_Programmi_C#Demonstration_of_the_use_of_pipe_system_call|qui]].&lt;br /&gt;
* '''Lo zoo delle exec'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang='C'&amp;gt;&lt;br /&gt;
int execl(const char *path, const char *arg, ...); &lt;br /&gt;
int execlp(const char *file, const char *arg, ...);&lt;br /&gt;
int execle(const char *path, const char *arg, ..., char * const envp[]);&lt;br /&gt;
int execv(const char *path, char *const argv[]);&lt;br /&gt;
int execvp(const char *file, char *const argv[]);&lt;br /&gt;
int execvpe(const char *file, char *const argv[],char *const envp[]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Spiegazione dei postfissi:&lt;br /&gt;
* l = elenco dei parametri passato tramite parametri a cardinalità variabile (variadic function)&lt;br /&gt;
* v = elenco dei parametri passato come array&lt;br /&gt;
* p = bisogna specificare il path completo dell'eseguibile&lt;br /&gt;
* e = bisogna specificare anche l'enviroment con cui verrà eseguito il programma. L'enviroment è una serie di variabili globali, tra cui il path. Il path è l'elenco dei percorsi dei programmi più usati.&lt;br /&gt;
Tutte queste funzioni alla fine sono solo delle interfacce per execve.&lt;br /&gt;
* '''Programmazione ad eventi'''&lt;br /&gt;
:E' un paradigma di programmazione dove il flusso del programma è determinato dal avverarsi di eventi. Il programmatore specifica il codice (Handlers) che deve essere eseguito al verificarsi di un certo evento. &amp;lt;br&amp;gt;&lt;br /&gt;
:System call che sono utili per far questo sono select,pselect,poll,ppoll,epoll.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 15 novembre 2016 ==&lt;br /&gt;
* '''Differenza tra select,poll,pselect,ppoll e epoll'''&lt;br /&gt;
:La poll a differenza della select richiede un array di strutture pollfd invece che una bitmask. Permette di specificare precisi eventi per cui stare in ascolto. &amp;lt;br&amp;gt;&lt;br /&gt;
:Le versioni con prefisso &amp;quot;p&amp;quot; implementano anche la gestione dei segnali. &amp;lt;br&amp;gt;&lt;br /&gt;
:L'epoll è linux only. E' moderna (2002) e fornisce un file dove leggere direttamente quali file descriptor sono stati modificati in modo da non dover scorrere tutta la lista.&lt;br /&gt;
* '''Dimostrazione della equivalenza di potere espressivo fra semafori e semafori binari.'''&lt;br /&gt;
:I semafori implementati per dimostrare l'equivalenza possono essere trovati a [[Esperimenti_con_semafori_e_monitor_in_C | questa pagina]].&lt;br /&gt;
* '''Definizione di Monitor.'''&lt;br /&gt;
Un monitor è un costrutto ad alto livello per la sincronizzazione pensato ispirandosi alla programmazione ad oggetti. &amp;lt;br&amp;gt;&lt;br /&gt;
E' una classe che incapsula la risorsa condivisa, la rende privata e permette di accedere a questa solo tramite metodi. Questi metodi implementano i meccanismi di sincronizzazione. &amp;lt;br&amp;gt;&lt;br /&gt;
Uno solo processo può essere al interno del monitor e avere accesso ai dati protetti da mutua esclusione. &amp;lt;br&amp;gt;&lt;br /&gt;
E' presente una coda di attesa per l'accesso al monitor. &amp;lt;br&amp;gt;&lt;br /&gt;
Definiamo inoltre delle variabili ''condition''. &amp;lt;br&amp;gt;&lt;br /&gt;
Gli unici metodi che possono essere chiamati su una condition sono wait e signal. Ogni condition ha una propria coda di attesa &amp;lt;br&amp;gt;&lt;br /&gt;
L'operazione wait sospende il chiamante e lo sposta nella coda di attesa della condition.&lt;br /&gt;
L'operazione signal risveglia un processo in attesa. Se non c'è nessun processo in attesa all'interno del monitor la signal fa entrare un eventuale processo in attesa di entrare nel monitor. Il chiamante di signal dopo aver risvegliato l'altro possibile processo deve mettersi in attesa per evitare che ci siano due processi in concorrenza al interno del monitor, ma invece di andare nella coda di attesa della condition viene messo in un ''urgent stack'' che ha più priorità rispetto alla coda esterna dei processi in attesa di entrare nel monitor. &amp;lt;br&amp;gt;&lt;br /&gt;
Ricapitolando le strutture dati per un monitor sono una coda esterna per l'accesso al monitor, una coda interna (non accessibile da fuori ma non dentro la mutua esclusione) per ogni variabile condizionale e l'urgent stack. &amp;lt;br&amp;gt;&lt;br /&gt;
* '''Implementazione dei Semafori tramite monitor.'''&lt;br /&gt;
&lt;br /&gt;
== Lezione del 18 novembre 2016 ==&lt;br /&gt;
* '''File speciali a blocchi o a caratteri'''&lt;br /&gt;
: In Unix molte cose sono rappresentate come file. Per esempio i device come i terminali e le partizioni del disco sono file. Si chiamano ''file speciali'' ma non sono tanto differenti dai file comuni. Utilizzano anche gli stessi sistemi di protezione all'accesso. Si dividono i due categori:&lt;br /&gt;
:* File speciali a blocchi: utilizzati maggiormente dai sistemi di storage tipo hard-disk e CD-ROMs. Se per esempio il blocco è di 1024 bytes allora possiamo scrivere e leggere con tale granularità, cioè non potremo mai scrivere soltato 500 bytes ma dovremo scrivere, appunto a &amp;quot;blocchi&amp;quot;.&lt;br /&gt;
:* File speciali a caratteri: utilizzati maggiormente per dispositivi di input-output tipo mouse,tastiera,terminali ecc.. permettono di lavorare con una granularità di 1 byte ma hanno un accesso sequenziale invece che diretto come i file a blocchi.&lt;br /&gt;
:Inoltre nei file speciali l'ampiezza è sostituita da 2 numeri, uno che indica il driver del dispositivo e l'altro le possibili parti del dispositivo, per esempio le partizioni.&lt;br /&gt;
* '''System call ioctl'''&lt;br /&gt;
: Ioctl serve per controllare dei device. Per esempio permette di fermare la testina di lettura del disco oppure far partire il motore del lettore CD. E' necessaria in quando si è voluto uniformare la lettura e scrittura di stream per tutti i device tramite le system call read/write ma chiaramente ogni dispositivo ha anche le sue diverse necessità e queste vengono soddisfatte da ioctl.&lt;br /&gt;
* '''Syscall'''&lt;br /&gt;
: Syscall è una funzione di libreria C che serve a chiamare system calls che non hanno una loro interfaccia per il linguaggi C. Per esempio getdents.&lt;br /&gt;
* '''System call e funzioni di libreria per directory'''&lt;br /&gt;
:Le directories sono sempre file ma si può fare l'accesso solo in lettura, per esempio per ottenere la lista di tutti i file e directory all'interno di quella directory. &amp;lt;br&amp;gt;&lt;br /&gt;
:getdents legge un file directory ed è la system call principale, tutte le funzioni di libreria C tipo opendir,readdir,readdir_r(per multithread) chiamano getdents. &amp;lt;br&amp;gt;&lt;br /&gt;
:Tutte queste funzioni lavorano con una struct dirent che rappresenta una directory entry. &amp;lt;br&amp;gt;&lt;br /&gt;
:Si può usare anche scandir che è reetrant (nessun problema di race condition) e invece di leggere un elemento della directory per volta ritorna direttamente un array di tutti gli element allocato dinamicamente (occorre ricordarsi di deallocare con free tutti gli elementi e l'array stesso).&lt;br /&gt;
&lt;br /&gt;
== Lezione del 22 novembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
Problema dei filosofi a cena risolto con i monitor. Soluzione con le condizioni associate alle &amp;quot;bacchette&amp;quot; e con le condizioni associate ai filosofi.&lt;br /&gt;
discussione dei problemi di deadlock e starvation nell'implementazione tramite monitor.&lt;br /&gt;
&lt;br /&gt;
Problema dei lettori e scrittori tramite monitor. Soluzione con privilegio ai lettori, agli scrittori e modifiche necessarie per la soluzione priva di starvation.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 25 novembre 2016 ==&lt;br /&gt;
[[Coding Contest]]&lt;br /&gt;
&lt;br /&gt;
== Lezione del 29 novembre 2016 ==&lt;br /&gt;
* '''Implementazione dei monitor tramite semafori'''&lt;br /&gt;
:Un soluzione simile a quella vista a lezione può essere trovata [[Esperimenti_con_semafori_e_monitor_in_C#Monitor implementati con semafori | qui]].&lt;br /&gt;
* '''Introduzione al message passing'''&lt;br /&gt;
:E' un altro modo per fare programmazione concorrente alternativo alla memoria condivisa che abbiamo visto fin ora.&lt;br /&gt;
:Se i programmi non condividono memoria ma devono sincronizzarsi per lavorare assieme hanno bisogno di scambiarsi messaggi.&lt;br /&gt;
:Per l'indirizzamento serve un sistema di naming, come il pid dei processi.&lt;br /&gt;
:Due primitive: send e receive. Nella receive come indirizzo da chi ricevere si può mettere una wildcard in modo da riceve da tutti.&lt;br /&gt;
:Nella send invece occorre indicare l'identificativo del destinatario (se si ammettesse di poter inserire una wildcard si spedirebbero messaggi in broadcast/multicast. E' una problematica pi&amp;amp;ugrave; complessa che viene trattata nei corsi di sistemi distribuiti).&lt;br /&gt;
:Lo scambio di messaggi può essere:&lt;br /&gt;
:* Sincrono: send bloccante e receive bloccante, senza memoria di supporto.&lt;br /&gt;
:* Asincrono: send non bloccante e receive bloccante, con memoria di supporto. I messaggi spediti rimangono in una coda (di ampiezza massima illimitata nel modello).&lt;br /&gt;
:* Completamente asincrono: send non bloccante e receive non bloccante. Poco utilizzato, il destinatario deve fare un polling per controllare se il messaggio è arrivato o no.&lt;br /&gt;
:Per dimostrare la stessa espressività dei metodi dobbiamo sempre creare delle librerie e non dobbiamo aggiungere nessun processo.&lt;br /&gt;
:La metodologia asincrona è espressiva almeno quanto quella sincrona mentre non è vero il contrario.&lt;br /&gt;
:Per creare un metodo sincrono con chiamate asincrone basta bloccare il programma finché non si riceve un acknowledgement.&lt;br /&gt;
:Invece per creare un metodo asincrono con chiamate sincrone bisogna aggiungere un processo e quindi questo dimostra che non sono equivalentemente espressive.&lt;br /&gt;
* '''Soluzioni di esercizi d'esame'''&lt;br /&gt;
** Problema della lavatrice&lt;br /&gt;
** Problema del ponte a senso unico&lt;br /&gt;
&lt;br /&gt;
== Lezione del 2 dicembre 2016 ==&lt;br /&gt;
* '''Implementazione della libreria per message passing'''&lt;br /&gt;
:I sorgenti della implementazione possono essere trovati [[Esperimenti con message passing in C|qui]]&lt;br /&gt;
* '''I segnali'''&lt;br /&gt;
:I segnali sono un metodo di comunicazione tra processi utilizzato in UNIX.&lt;br /&gt;
:Ogni segnale è rappresentato da un numero. Per esempio &amp;quot;segmentation fault&amp;quot; è il numero 11. &lt;br /&gt;
:La system call kill() serve per inviare un segnale. signal() serve per definire l'handler, una funzione che viene eseguita quando il processo riceve uno specifico segnale.&lt;br /&gt;
:Ogni segnale ha il suo handler di default e tramite signal() noi sovrascriviamo il default handler. &lt;br /&gt;
* '''Permesso setuid'''&lt;br /&gt;
:Permette a dei programmi di eseguire operazioni che hanno bisogno dei permessi di root. L'attributo setUserId attivato vuol dire che il programma verrà eseguito con i privilegi del possessore del file piuttosto di quelli di chi lo esegue.&lt;br /&gt;
* '''Proposta di soluzione esercizio 4 del [[Coding Contest 25 novembre 2016]]'''&lt;br /&gt;
&lt;br /&gt;
== Lezione del 6 dicembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
* Implementazione di un servizio di Message Passing Asincrono dato uno sincrono (con processo server).&lt;br /&gt;
* Implementazione di un servizio di Message Passing completamente asincrono dato uno sincrono.&lt;br /&gt;
* Analisi del potere espressivo dei servizi di message passing.&lt;br /&gt;
&lt;br /&gt;
* kernel monolitici e microkernel&lt;br /&gt;
* macchine virtuali a livello di processo e a livello di sistema&lt;br /&gt;
&lt;br /&gt;
I sorgenti degli esperimenti con il message passing sono in [http://www.cs.unibo.it/~renzo/so/tools/mp.extra.tgz questo file].&lt;br /&gt;
&lt;br /&gt;
== Lezione del 9 dicembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
* Il problema della Cena dei filosofi con il message passing (asincrono)&lt;br /&gt;
&lt;br /&gt;
* Buffer Limitato con message passing (asincrono).&lt;br /&gt;
&lt;br /&gt;
== Lezione del 13 dicembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
La lezione TACE per malattia (influenza) del docente.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 16 dicembre 2016 ==&lt;br /&gt;
*'''Specifiche del progetto fase uno'''&lt;br /&gt;
Il documento delle specifiche può essere trovato [http://www.cs.unibo.it/~renzo/so/mikaboo/phase1.pdf qui].&lt;br /&gt;
:*List.h&lt;br /&gt;
:*Lista dei processi&lt;br /&gt;
:*Coda dei thread&lt;br /&gt;
:*Code dei messaggi&lt;br /&gt;
*'''Autotools'''&lt;br /&gt;
:Insieme di strumenti per la generazione automatica di Makefiles. &amp;lt;br&amp;gt;&lt;br /&gt;
:comando ''autoreconf'' per generale il file ''configure'' &amp;lt;br&amp;gt;&lt;br /&gt;
:''./configure'' per generare makefile.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 21 febbraio 2017 ==&lt;br /&gt;
'''Argomenti trattati:'''&lt;br /&gt;
* '''Visione a strati di un elaboratore'''&lt;br /&gt;
:Gli strati sono livelli di astrazione. Ogni strato deve &amp;quot;parlare&amp;quot; il linguaggio del livello sottostante e deve fornire un proprio linguaggio ai livelli superiori.&lt;br /&gt;
:Per esempio il sistema operativo parla l'ISA del livello hardware e fornisce alle applicazioni le system call. Il sistema operativo maschera il livello hardware in quanto fornisce la possibilità alle applicazioni di utilizzare istruzioni ISA ma non tutte, per ragioni di sicurezza.&lt;br /&gt;
* '''Componenti fondamentali di un sistema operativo'''&lt;br /&gt;
:A sua volta il sistema operativo può essere visto come un insieme di strati. Esistono sistemi operativi a singolo strato (come MS-DOS) e più strati (layered).&lt;br /&gt;
:Le parti principali in cui possiamo suddividere un sistema operativo sono:&lt;br /&gt;
:*&amp;lt;u&amp;gt;Lo scheduler&amp;lt;/u&amp;gt;&lt;br /&gt;
:*&amp;lt;u&amp;gt;Il memory manager&amp;lt;/u&amp;gt;&lt;br /&gt;
:*&amp;lt;u&amp;gt;Il file system&amp;lt;/u&amp;gt;&lt;br /&gt;
:*&amp;lt;u&amp;gt;L'I/O manager&amp;lt;/u&amp;gt;&lt;br /&gt;
:*&amp;lt;u&amp;gt;Il networking manager&amp;lt;/u&amp;gt;&lt;br /&gt;
:Di questa lista, gli elementi assolutamente fondamentali per considera un programma un sistema operativo sono lo scheduler e una parte essenziale del memory e I/O manager.&lt;br /&gt;
:Il networking e file manager si appoggiano al I/O manager per svolgere i loro compiti.&lt;br /&gt;
*'''Bootstrap'''&lt;br /&gt;
:Letteralmente &amp;quot;mettersi in piedi tirandosi dai lacci degli stivali&amp;quot;, nel senso di attivarsi senza aiuto esterno. All'accensione il computer legge da una ROM le prime istruzioni necessarie. Viene sempre letto dai primi settori della memoria di massa il bootloader. :Il bootloader (per esempio GRUB) conosce già molti filesystem, ma non abbastanza. Il bootloader carica un file system temporaneo apposito chiamato initrd per caricare i moduli necessari al kernel.&lt;br /&gt;
*'''Come rappresentiamo i processi'''&lt;br /&gt;
:Dobbiamo sia tener conto a che punto siamo arrivati nell'esecuzione del processo sia tener conto delle risorse di cui il processo è detentore.&lt;br /&gt;
:Utilizziamo la struttura ''Process Control Block'' (task_struct in linux) per i processi e &amp;quot;Thread Control Block&amp;quot; (task_struct in linux) per i thread.&lt;br /&gt;
:Process control block:&lt;br /&gt;
:*PID (Process ID) &lt;br /&gt;
:*PPID (Parent Process ID) per poter creare una gerarchia di processi&lt;br /&gt;
:*punti di accesso alla memoria (codice,dati,tabella di rilocazione,paginazione)&lt;br /&gt;
:*lista dei thread&lt;br /&gt;
:*owner (se il sistema è multiuser)&lt;br /&gt;
:*Risorse del processo&lt;br /&gt;
:*Coda dei messaggi per IPC (Inter-Process Communication)&lt;br /&gt;
:*accounting (tempo di esecuzione sia in modalità kernel sia in modalità user)&lt;br /&gt;
:*valore di ritorno del processo&lt;br /&gt;
:Thread control block:&lt;br /&gt;
:*TID (Thread ID)&lt;br /&gt;
:*stato del processore&lt;br /&gt;
:*call stack&lt;br /&gt;
:*ready/running/waiting&lt;br /&gt;
*'''Avvicendamento dei processi'''&lt;br /&gt;
:Definizione di avvicendamento: ''Successione prevista o regolata nel tempo, alternanza.''&lt;br /&gt;
:I context switch avvengono a seguito di trap/interrupt. &lt;br /&gt;
:Il ciclo di vita di un processo passa tra gli stati ''ready, running, waiting'' e infine ''term''.  &lt;br /&gt;
:Lo scheduling (processo decisionale) può venire applicato nelle seguenti 4 situazioni:&lt;br /&gt;
:# Quando un processo passa dallo stato di running a quello di waiting&lt;br /&gt;
:# Quando un processo passa dallo stato di running a quello di ready&lt;br /&gt;
:# Quando un processo passa dallo stato di waiting a quello di ready&lt;br /&gt;
:# Quando un processo passa dallo stato di running a quello di term&lt;br /&gt;
:Se lo scheduling avviene solo nella situazione 1 e 4 allora si utilizza un metodo non preemptive. Dal momento che la CPU è allocata a un processo questo la detiene finchè non termina o entra di sua spontanea volontà in stato di waiting. La CPU non può decidere di far aspettare un processo spostandolo temporaneamente nella coda dei processi ready. Questo sistema era utilizzato da vecchi sistemi operativi come Windows 3.x e dalle versione di Mac OS prima della versione X.&lt;br /&gt;
:Nel preemptive scheduling invece la CPU può decidere di sospendere un processo, questo può creare race condition ma anche un sistema più equo di distribuzione delle risorse. Le race condition vengono risolte con i metodi che abbiamo visto nel primo semestre.&lt;br /&gt;
'&lt;br /&gt;
&lt;br /&gt;
== Lezione del 23 febbraio 2017 ==&lt;br /&gt;
&lt;br /&gt;
'''System call'''&lt;br /&gt;
&lt;br /&gt;
Abbiamo analizzato le system call che erano già presenti nella [http://man.cat-v.org/unix-6th/2/ versione 6 di Unix] (1975). &amp;lt;br&amp;gt;&lt;br /&gt;
Le abbiamo poi confrontate con quelle attuali del [[Il_%27%27catalogo%27%27_delle_System_Call]].&lt;br /&gt;
&lt;br /&gt;
'''Shell Scripting'''&lt;br /&gt;
&lt;br /&gt;
Gli script vengono utilizzati per lanciare un demone, guardare i processi, etc.&lt;br /&gt;
Anche l'amministrazione di sistema avviene tramite scripting. Ad esempio, dopo il boot, il processo init avvia le funzionalità di sistema tramite script.&lt;br /&gt;
&lt;br /&gt;
Unix unifica l'idea di script con quella di shell.&lt;br /&gt;
La shell è un processo che interagisce con l'utente. In Unix la novità introdotta è rappresentata dalla presenza di un linguaggio che riguarda i comandi, utile per scrivere gli script per la shell.&lt;br /&gt;
&lt;br /&gt;
Come indichiamo quale shell usare? Scrivendo nella prima riga #! seguito dal path e dal nome della shell. Il kernel prende ciò che è scritto nella prima riga e lo esegue per interpretare lo script. &lt;br /&gt;
Nell'esempio sottostante Script è un commento perchè è preceduto da #.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO&lt;br /&gt;
&lt;br /&gt;
   #!/bin/bash&lt;br /&gt;
   #Script&lt;br /&gt;
&lt;br /&gt;
   echo &amp;quot;Hello World!&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Per scrivere script abbiamo bisogno di variabili. Nelle shell queste possono essere di 2 tipi:&lt;br /&gt;
&lt;br /&gt;
- Locali: viste solo dalla shell, non perturbano comportamento dei processi.&lt;br /&gt;
&lt;br /&gt;
- D'ambiente: recepita dai comandi come ambiente.&lt;br /&gt;
&lt;br /&gt;
'''ASSEGNAMENTO E VARIABILI'''&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1&lt;br /&gt;
&lt;br /&gt;
  a=2  /* Non devono esserci spazi!!! a = 2 doesn't work */&lt;br /&gt;
  echo $a  /* Stampa 2 */&lt;br /&gt;
  echo ${a}a  /* Stampa 2a */&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 2 - Nuova shell&lt;br /&gt;
&lt;br /&gt;
  bash  /* Apro nuova shell */&lt;br /&gt;
  echo $a /* Stamperà una riga vuota perchè la var. a non è locale della nuova shell */&lt;br /&gt;
  exit  /* chiudo nuova shell */&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 3 - Esportare variabile locale&lt;br /&gt;
&lt;br /&gt;
  export a /* La var. a diventa variabile d'ambiente */&lt;br /&gt;
  bash &lt;br /&gt;
  echo $a /* Ora possiamo chiamare la var. a perchè è diventata d'ambiente */&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 4 - csh &lt;br /&gt;
&lt;br /&gt;
csh in molti casi dovrebbe già essere installato di default. &lt;br /&gt;
Se non è così è possibile installarlo eseguendo&lt;br /&gt;
&lt;br /&gt;
  sudo apt-get install csh&lt;br /&gt;
&lt;br /&gt;
  csh /* Apro cshell, noterete il simbolo di percentuale % a inizio riga di comando */&lt;br /&gt;
  set a=42 /* assegnamento */&lt;br /&gt;
  csh /* apro nuova shell */&lt;br /&gt;
  echo $a /* non possiamo chiamare a(42) perchè locale alla prima shell, viene chiamata a d'ambiente se presente */&lt;br /&gt;
  exit&lt;br /&gt;
  setenv a 42 /* Per creare una variabile d'ambiente, da notare la mancanza di = */&lt;br /&gt;
  csh&lt;br /&gt;
  echo $a&lt;br /&gt;
&lt;br /&gt;
'''ARGOMENTI'''&lt;br /&gt;
&lt;br /&gt;
Negli script possiamo avere degli argomenti. &lt;br /&gt;
Vengono visti come $1, ..., $9, ${10}, etc.&lt;br /&gt;
$* indica tutti gli argomenti&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1&lt;br /&gt;
  #!/bin/bash&lt;br /&gt;
  echo $*  /* Richiama tutti gli argomenti */&lt;br /&gt;
  echo $1 $2 /* Richiama solo i primi due argomenti */&lt;br /&gt;
  shift /* Sposta la riga di comando degli argomenti di una posizione verso sx */&lt;br /&gt;
  echo $* &lt;br /&gt;
&lt;br /&gt;
Una volta salvato il file con estensione .sh, eseguire da terminale:&lt;br /&gt;
  chmod +x nomefile.sh&lt;br /&gt;
  ./nomefile.sh  arg1 arg2 ...&lt;br /&gt;
&lt;br /&gt;
Se passiamo come argomenti: uno due tre quattro,&lt;br /&gt;
l'output sarà: &lt;br /&gt;
  uno due tre quattro&lt;br /&gt;
  uno due&lt;br /&gt;
  due tre quattro&lt;br /&gt;
&lt;br /&gt;
A volte dobbiamo proteggere dei caratteri. Alcuni di essi possono essere delle wildcard. Ci sono 3 modi per proteggerli:&lt;br /&gt;
  \*&lt;br /&gt;
  '*'&lt;br /&gt;
  &amp;quot;*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Analizziamone le differenze tramite un esempio.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1 &lt;br /&gt;
&lt;br /&gt;
  echo \*  // OUTPUT: *&lt;br /&gt;
  echo '*$a' // OUTPUT: *$a&lt;br /&gt;
  echo &amp;quot;*$a&amp;quot; // OUTPUT: *2 &lt;br /&gt;
&lt;br /&gt;
Nel primo caso l'escape vale solo per *. Nel secondo caso vale per tutta la stringa. Nel terzo caso vale per * e richiama la variabile.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 2&lt;br /&gt;
  echo ab // OUTPUT: ab&lt;br /&gt;
  echo &amp;quot;ab&amp;quot; //OUTPUT: ab&lt;br /&gt;
  &lt;br /&gt;
  echo a b //OUTPUT: ab &amp;lt;-- non rileva gli spazi&lt;br /&gt;
  echo &amp;quot;a b&amp;quot; //OUTPUT: a b&lt;br /&gt;
&lt;br /&gt;
== Lezione del 28 febbraio 2017 ==&lt;br /&gt;
'''Argomenti trattati''':&lt;br /&gt;
* '''Ciclo di vita di un kernel'''&lt;br /&gt;
: Dopo l'inizializzazione (Creazione di code, rilevazione di device, ecc..) si crea a &amp;quot;mano&amp;quot; il primo processo. &lt;br /&gt;
: L'esecuzione dello scheduler si alterna a quella dei processi. Quando un processo utente viene eseguito il kernel non ha più il controllo del flusso e gli viene restituito solo quando avviene un interrupt/trap.&lt;br /&gt;
* '''Parametri di valutazione di un algoritmo di scheduling'''&lt;br /&gt;
** % Utilizzo CPU: Bisogna cercare di tener utilizzata il processore il più possibile riducendo i momenti di inutilizzo (idle).&lt;br /&gt;
** Produttività (Throughput) Numero di processi completati per unità di tempo&lt;br /&gt;
** Tempo totale (turnaround time) Tempo totale impiegato dall'esecuzione del processo al completamente. Tiene conto del tempo speso in attesa oltre al tempo in esecuzione.&lt;br /&gt;
** Tempo di risposta: Tempo prima che un primo output venga generato dal momento in cui il processo viene eseguito. In un sistema interattivo è importante per non dare all'utente l'idea di malfunzionamento.&lt;br /&gt;
** Tempo di attesa. Non aspettando l'I/O ma bensì tempo speso nella ready queue.&lt;br /&gt;
* '''Processi CPU Bound e I/O Bound'''&lt;br /&gt;
:: I processi si possono dividere in queste due categorie. I processi che utilizzano il processore per tempi più lunghi (per esempio quelli di calcolo scientifico) si chiamano CPU Bound mentre quelli che richiedono spesso interazioni e si fermano ad aspettare l'I/O si chiamano I/O Bound. Algoritmi di scheduling differenti possono favorire o sfavorire una delle due categorie. L'obbiettivo e trovare un algoritmo che sia ottimale per tutti i processi.&lt;br /&gt;
* '''Algoritmi di scheduling'''&lt;br /&gt;
** First come first served&lt;br /&gt;
:: Lo scheduler implementa una semplice coda dei processi che hanno richiesto l'esecuzione. E' semplice da capire e creare ma il tempo medio di attesa per un processo può essere lungo. Dipende molto dall'ordine in cui vengono avvicendati processi CPU bound e I/O Bound. Un processo CPU bound può creare un ''effetto convoglio'' e far dipendere il tempo di attesa di altri processi che sono I/O bound al suo tempo di esecuzione. Questo algoritmo non è quindi equo.&lt;br /&gt;
:* Shortest job first&lt;br /&gt;
:: Forse più correttamente nominato algoritmo ''shortest next CPU burst''. Ad ogni processo viene associato il suo tempo di esecuzione e viene scelto dalla ready queue sempre il processo con il tempo più basso. Per determinare il tempo di esecuzione a priori si utilizza una media esponenziale dei tempi di esecuzione dello stesso processo nel passato. [[File:Media_esponenziale.png]] ''t'' sono le misurazioni recenti mentre 'tau' sono la storia più ''remota''. Il parametro alfa determina quanto la media esponenziale sia basata sulla storia recente o su quella passata.&lt;br /&gt;
:* Shortest remaining time first&lt;br /&gt;
:: SJF può essere preemptive oppure no. Nel caso preemptive prende il nome di ''Shortest remaining time first''. Se un processo A è in esecuzione e arriva un'altro processo B con tempo di esecuzione minore allora A viene fermato e viene eseguito B. La priorità di A nella coda è ora il tempo stimato rimanente.&lt;br /&gt;
:* Round robin&lt;br /&gt;
:: E' definita un unità di tempo, di solito tra i 10 e i 100 millisecondi, e ogni processo ha un tempo massimo di esecuzione pari a questa unità. I processi vengono avvicendati a turno in una coda. Un processo termina l'esecuzione o per sua volontà (il tempo rimasto del quanto di tempo non è considerato) oppure perchè è scattato l'interval timer. Nell'ultimo caso il processo viene inserito in coda come se fosse un processo appena arrivato. La performance di questo algoritmo dipendono molto da che tipi di processi si gestiscono e dalla dimensione dell'unita di tempo. Se questa è larga allora l'algoritmo è più simile a FCFS mentre un'unita di tempo piccola porta a più context switch e quindi a più overhead. L'unità di tempo andrebbe decisa in proporzione al tempo di context switch.&lt;br /&gt;
:* Scheduling a priorità&lt;br /&gt;
:: Ad ogni processo è assegnata una priorità che si riflette nella priorità che ha il processo nella ready queue.&lt;br /&gt;
:: La priorità di un processo può essere influenzata da fattori interni (come i requisiti di memoria) o esterni al sistema operativo (come ''l'importanza'' di un processo). L'algoritmo può essere non preemptive o premtive, nel caso in cui interrompa il processo attuale se arriva un processo con più priorità.&lt;br /&gt;
:: L'SJF può essere visto come un caso particolare di scheduling a priorità, in cui la priorità e appunto inversamente proporzionale al tempo di esecuzione.&lt;br /&gt;
:: Un problema dello scheduling a priorità è la starvation, che può essere risolta con il meccanismo di ''aging'' in cui un processo aumenta di priorità proporzionalmente al tempo cui rimane nella ready queue.&lt;br /&gt;
:* Scheduler multilivello&lt;br /&gt;
:: Questo è lo scheduler più completo in quanto divide i processi in gruppi e utilizza differenti algoritmi per ogni gruppo.&lt;br /&gt;
:: Una tipica suddivisione è quella di processi foreground e background, rispettivamente I/O bound e CPU bound. Per ogni gruppo viene creata una coda apposita.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 2 marzo 2017 ==&lt;br /&gt;
&lt;br /&gt;
'''Shell Scripting'''&lt;br /&gt;
&lt;br /&gt;
'$' seguito da caratteri esegue una sostituzione, questa può essere:&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 1 (contenuto)'''&lt;br /&gt;
&lt;br /&gt;
  echo $SHELL&lt;br /&gt;
&lt;br /&gt;
  echo $USER&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 2 (comando)'''&lt;br /&gt;
&lt;br /&gt;
  echo $ls&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 2.1'''&lt;br /&gt;
&lt;br /&gt;
  du -s $(ls -d *es) //du -s prende come input l'output del comando compreso tra parentesi tonde&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 3 (wildcard)'''&lt;br /&gt;
&lt;br /&gt;
  echo *       // tutti file/dir&lt;br /&gt;
  echo *es     // file/dir che terminanon con es&lt;br /&gt;
  echo *[lg]es // file/dir che terminanon con les/ges&lt;br /&gt;
  echo ?????   // file/dir con 5 caratteri &lt;br /&gt;
&lt;br /&gt;
Queste wildcard servono per poter individuare insiemi di file in un colpo solo.&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 4 ($ come sintassi riconosciuta da grep)'''&lt;br /&gt;
&lt;br /&gt;
  grep '&amp;gt;$' *.c // trova tutte le linee che terminano con '&amp;gt;' all'interno dei file .c&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 5 (PID)'''&lt;br /&gt;
&lt;br /&gt;
  echo $$ // restituisce PID dello script nel quale viene eseguito&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 6a'''&lt;br /&gt;
&lt;br /&gt;
  echo $1 $2 $3 &amp;gt;/tmp/tmpscript //scrive i 3 argomenti in tmpscript&lt;br /&gt;
 &lt;br /&gt;
  cat /tmp/tmpscript  //lettura tmpscript&lt;br /&gt;
  rm -f /tmp/tmpscript  //rimozione tmpscript&lt;br /&gt;
&lt;br /&gt;
Se cerchiamo di lanciare quasi contemporaneamente lo script dell'esempio 6 in due shell differenti, lo script non funziona perchè lavorano sullo stesso file. Possiamo quindi ricorrere all'utilizzo di $$.&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 6b'''&lt;br /&gt;
&lt;br /&gt;
  echo $1 $2 $3 &amp;gt;/tmp/tmpscript$$&lt;br /&gt;
  &lt;br /&gt;
  sleep 3 //in modo da poter lanciare lo script in un'altra shell &lt;br /&gt;
  cat /tmp/tmpscript$$  &lt;br /&gt;
  rm -f /tmp/tmpscript$$&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 7'''&lt;br /&gt;
  grep bash myscript.sh &amp;gt;&amp;amp; dev/null //ridireziono l'output di grep sul &amp;quot;null device&amp;quot;&lt;br /&gt;
  echo $? //restituisce 0 se il comando precedente è stato eseguito correttamente, 1 altrimenti&lt;br /&gt;
&lt;br /&gt;
'''Gli operatori &amp;gt;&amp;gt; e &amp;lt;&amp;lt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;gt;&amp;gt; è simile a &amp;gt; ma scrive alla fine del file invece che all'inizio. &amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;&amp;lt; serve invece per inserire l'input di un comando direttamente nello script, come mostrato nel successivo esempio della mail.&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 8 (script per inviare mail)'''&lt;br /&gt;
  &lt;br /&gt;
  #!/bin/bash&lt;br /&gt;
   &lt;br /&gt;
  mail $1 &amp;lt;&amp;lt; ENDOFMESSAGE&lt;br /&gt;
  ciao $1 questa è una mail di prova&lt;br /&gt;
  ENDOFMESSAGE //etichetta usata per indicare il termine del messaggio&lt;br /&gt;
&lt;br /&gt;
Con&lt;br /&gt;
&lt;br /&gt;
  echo $1; echo $2; ls &lt;br /&gt;
&lt;br /&gt;
raggruppiamo una sequenza di comandi sulla stessa riga&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 9((|| &amp;amp;&amp;amp;)'''&lt;br /&gt;
&lt;br /&gt;
Gli operatori || e &amp;amp;&amp;amp; possono essere utilizzati per processare più comandi consecutivamente.&lt;br /&gt;
&lt;br /&gt;
  command || die&lt;br /&gt;
&lt;br /&gt;
  gcc -o hwx hw.c &amp;amp;&amp;amp; ./hwx&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 10 (sottoshell)'''&lt;br /&gt;
&lt;br /&gt;
Una lista di comandi all'interno di () viene eseguita in una sottoshell. Le variabili all'interno della sottoshell non sono visibili al di fuori del blocco di codice nella sottoshell. Sono variabili locali.&lt;br /&gt;
&lt;br /&gt;
  (cd subdir; ls) //eseguo un comando in un'altra directory&lt;br /&gt;
&lt;br /&gt;
'''Pushd e popd'''&lt;br /&gt;
&lt;br /&gt;
Simili al comando ''cd'' ma pushd inserisce i percorsi visitati in una pila e tramite popd e possibile ripercorrere a ritroso le directory visitate.&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 11 (background jobs)&lt;br /&gt;
  ping google.com &amp;amp; //esegue ping in background&lt;br /&gt;
  jobs //mostra la lista dei jobs in esecuzione&lt;br /&gt;
  //poniamo che l'output di jobs sia &amp;quot;[1]+  In esecuzione           ping google.com &amp;amp;&amp;quot;&lt;br /&gt;
  kill -9 %1 //termina il processo con identificatore 1&lt;br /&gt;
''bg'' repristina un processo fermato con CTRL+Z. &amp;lt;br&amp;gt;&lt;br /&gt;
''fg'' riporta in primo piano un processo mandato in background.&lt;br /&gt;
&lt;br /&gt;
'''test e type'''&lt;br /&gt;
&lt;br /&gt;
''test'' serve a comparare valori e controllare il tipo dei file. &amp;lt;br&amp;gt;&lt;br /&gt;
la sintassi ''[]'' è un alias di test.&lt;br /&gt;
  if [ $$ -eq '10853' ] ; then echo &amp;quot;il processo corrente ha pid 10853&amp;quot;; else echo &amp;quot;il processo corrente non ha pid 10853&amp;quot;; fi;&lt;br /&gt;
  //che è equivalente a &lt;br /&gt;
  if test $$ -eq '10853'; then echo &amp;quot;il processo corrente ha pid 10853&amp;quot;; else echo &amp;quot;il processo corrente non ha pid 10853&amp;quot;; fi;&lt;br /&gt;
''type'' invece mostra l'origine di un comando, se sono integrati nella shell oppure è un programma a parte.&lt;br /&gt;
&lt;br /&gt;
'''Switch'''&lt;br /&gt;
Sintassi degli switch in bash.  &lt;br /&gt;
  case expression in&lt;br /&gt;
    pattern1 )&lt;br /&gt;
        statements ;;&lt;br /&gt;
    pattern2 )&lt;br /&gt;
        statements ;;&lt;br /&gt;
    ...&lt;br /&gt;
    esac&lt;br /&gt;
'''Comando cron''' &amp;lt;br&amp;gt;&lt;br /&gt;
''cron'' serve per eseguire operazioni pianificate. Gli script in /etc/init.d vengono eseguiti automaticamente da cron.&lt;br /&gt;
&lt;br /&gt;
'''xarg''' &amp;lt;br&amp;gt;&lt;br /&gt;
xarg permette di eseguire un comando passandogli come parametri i valori letti da standard input. &lt;br /&gt;
&lt;br /&gt;
'''uniq''' &amp;lt;br&amp;gt;&lt;br /&gt;
uniq può essere usato per trovare e eliminare righe duplicate.&lt;br /&gt;
&lt;br /&gt;
'''wc''' &amp;lt;br&amp;gt;&lt;br /&gt;
''wc'' crea il ''word count'' di un file. Non conta solo le parole ma anche i caratteri e le righe.&lt;br /&gt;
&lt;br /&gt;
'''diff e patch''' &amp;lt;br&amp;gt;&lt;br /&gt;
Il comando ''diff'' può essere utilizzato per mostrare le differenze tra due file testuali. E' utilizzato internamente da git. &amp;lt;br&amp;gt;&lt;br /&gt;
Patch serve per applicare un ''diff file'' a un altro file in modo da aggiornarlo.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 7 marzo 2017 ==&lt;br /&gt;
Una risorsa è ogni elemento utile all'elaborazione.&lt;br /&gt;
* '''Classificazione di risorse'''&lt;br /&gt;
** '''Fisiche e logiche'''&lt;br /&gt;
::Esempi:&lt;br /&gt;
::CPU, memoria e dispositivi sono risorse ''fisiche''.&lt;br /&gt;
::file, strutture dati, critical section sono risorse ''logiche''.&lt;br /&gt;
:* '''Fungibili'''&lt;br /&gt;
::Una risorsa è fungibile se è uguale prendere un bene o un'altro se sono dello stesso tipo. Richiedere un area di memoria o un altra è indifferente se tutte e due soddisfano i requisiti del processo. Richiedere file o dispositivi differenti invece cambia.&lt;br /&gt;
:* '''Assegnazione statica o dinamica'''&lt;br /&gt;
::L'assegnazione statica prevede che una risorsa sia monopolio di un processo.&lt;br /&gt;
:* '''Accetta richieste multiple o solo singole'''&lt;br /&gt;
:: Se può ricevere richieste d'uso da più processi e memorizzare/servire contemporaneamente le richieste oppure no.&lt;br /&gt;
:* '''condivisibili e non condivisibili'''&lt;br /&gt;
:: Un file in lettura è una risorsa condivisibile, più processi possono accedere contemporaneamente. Un file in scrittura invece è una risorsa non condivisibile perché potrebbe capitare una race condition.&lt;br /&gt;
:* '''bloccante e non bloccante'''&lt;br /&gt;
::Se l'accesso alla risorsa implica dei tempi di attesa relativamente lunghi al processo, la risorsa è da considerarsi bloccante. &lt;br /&gt;
:* '''prelasciabile o non prelasciabile'''&lt;br /&gt;
::Il processo può rilasciare in qualsiasi momento la risorsa e permetterne l'uso ad un altro processo? Una stampante è un esempio di risorsa non prerilasciabile. &lt;br /&gt;
* '''Deadlock''' &lt;br /&gt;
:Un deadlock può avvenire in presenza di risorse bloccanti, non condivisibili, non prerilasciabili e con una situazione di attesa circolare. &lt;br /&gt;
:* '''detection: grafo di Holt ''' &lt;br /&gt;
::I grafi di allocazione delle risorse, introdotti da Holt nel 1972 sono un buon modo per descrivere lo stato di un sistema e prevedere possibili deadlock. Ogni nodo rappresenta una risorsa o un processo. Di solito le risorse sono rappresentate da quadrati e i processi da cerchi. Un arco da un processo a una risorsa significa che quel processo ha richiesto un'istanza della risorsa. Un arco da una risorsa a un processo vuol dire che un'istanza di quella risorsa è stata assegnata al processo. &amp;lt;br&amp;gt;&lt;br /&gt;
::* ''' teorema dei cicli in grafi di Holt con un solo tipo di risorsa&lt;br /&gt;
:::Se il grafo non contiene cicli allora di sicuro non possono esserci deadlock. &lt;br /&gt;
:::Se il grafo contiene un ciclo allora &amp;lt;u&amp;gt;può&amp;lt;/u&amp;gt; esserci un deadlock.&lt;br /&gt;
:::Se le risorse coinvolte in un ciclo hanno solo un'istanza o ogni loro istanza e coinvolta in un ciclo allora c'è di sicuro un deadlock.&lt;br /&gt;
:::Questo tecnica di permette di analizzare uno stato ''attuale'' di un sistema, ma dobbiamo trovare un modo per prevedere lo stato ''futuro'' del sistema e quindi prevedere i deadlock.&lt;br /&gt;
::* '''processo di riduzione&lt;br /&gt;
:::Per vedere lo stato di un sistema futuro possiamo usare un processo di riduzione del grafo di allocazione delle risorse.&lt;br /&gt;
:::Se una risorsa allocata a un processo può essere data dopo un certo periodo a un altro processo che l'ha richiesta allora si può fare una riduzione. Una riduzione consiste nell'eliminare un arco risorsa-processo perchè quel processo ha finito di utilizzare la risorsa oppure è stata prerilasciata e invertire un arco processo-risorsa di un processo che aveva richiesto la risorsa in un arco risorsa-processo.&lt;br /&gt;
:::Se un grafo può essere ridotto per tutti i suoi processi allora non può esserci deadlock.&lt;br /&gt;
:::Il problema di questo metodo/algoritmo è che nel momento che deallochiamo una risorsa a un processo e più processi avevano richiesto quella risorsa come scegliamo deterministicamente a quale processo assegnare la risorsa? Dovremmo provare tutti i casi per accertarsi che non si crei deadlock, ciò significa fare backtrack e perdere la polinomialità dell'algoritmo.&lt;br /&gt;
::* '''definizione di Knot&lt;br /&gt;
:::Il Knot è una caratteristica di un grafo che si verifica velocemente e permette di determinare possibili deadlock.&lt;br /&gt;
:::Un knot è un insieme di nodi in cui, per ogni nodo del knot, tutti e solo i nodi del knot sono raggiungibili da quel nodo.&lt;br /&gt;
:::Un knot è una condizione sufficiente ma non necessaria per i deadlock. Ciò vuol dire che possono capitare deadlock anche senza la presenta di un knot.&lt;br /&gt;
::* '''teorema dei knot nei grafi di Holt con più tipi di risorse&lt;br /&gt;
:::Dato un grafo di Holt con una sola richiesta sospesa per processo.&lt;br /&gt;
:::Il grafo rappresenta uno stato di deadlock solo se esiste un knot.&lt;br /&gt;
:* '''prevention'''&lt;br /&gt;
::Se il sistema cerca di non far verificare almeno una delle condizioni necessarie al deadlock allora questo non si verificherà mai.&lt;br /&gt;
::Questo però comporta anche sottoutilizzo delle risorse del sistema.&lt;br /&gt;
:* '''avoidance''' &lt;br /&gt;
::*'''Condizione di safety'''&lt;br /&gt;
:::Un sistema è in una condizione di safety se esiste una safe sequence.&lt;br /&gt;
:::Una safe sequence è una sequenza di processi dove per ogni richiesta di risorse di un processo questa richiesta può essere soddisfatta con le richieste disponibili al momento più tutte le risorse allocate precedentemente ai precedenti processi della sequenza. &lt;br /&gt;
:::Finchè c'è safety non possono verificarsi deadlock. In una situazione unsafe non è detto che capiti deadlock, ma è possibile.&lt;br /&gt;
::*'''algoritmo del Banchiere (mono e multivaluta).&lt;br /&gt;
:::L'algoritmo del banchiere si basa sul fatto che una banca non dovrebbe mai rimanere senza denaro da prestare ai clienti.&lt;br /&gt;
:::Vengono mantenute varie strutture dati come il capitale iniziale della banca, quanto è disponibile in cassa, quanto è stato prestato ad ogni cliente e quanto si può ancora prestare ad ogni cliente (in base al ''credito'' che ogni cliente ha).&lt;br /&gt;
:::La condizione di safety è data dalla condizione che bisogna mantenere almeno uno dei clienti soddisfacibile cioè che il denaro in cassa sia maggiore del possibile richiesta di prestito di almeno un cliente.&lt;br /&gt;
:::Se questa condizione non si verificasse alloca il banchiere deve aspettare che un cliente restituisca il prestito.&lt;br /&gt;
:::In caso di più valute (yen, dollari, euro, ...) che rappresentano i tipi diversi di risorse del sistema. Tutte le strutture dati aumentano di una dimensione, per esempio il denaro iniziale diventa un vettore.&lt;br /&gt;
::Teorema: il processo di controllo di safety non ha necessita' di backtrack.&lt;br /&gt;
:* '''Tecnica dell'ostrica/struzzo'''&lt;br /&gt;
::Un altro modo per gestire i deadlock e quello di ignorarli completamente.&lt;br /&gt;
::La maggior parte dei sistemi operativi utilizza questo metodo.&lt;br /&gt;
::Soprattuto in sistemi in cui è statisticamente determinato che i deadlock si verificano poco frequentemente i sistemi di prevenzione e avoidance costituirebbero un overhead troppo elevato.&lt;br /&gt;
::Sta allora alle singole applicazioni evitare di crearli.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 9 marzo 2017 ==&lt;br /&gt;
&lt;br /&gt;
* awk&lt;br /&gt;
* phase2: prime fasi del boot&lt;br /&gt;
* python: presentazione del linguaggio ed analisi delle caratteristiche&lt;br /&gt;
&lt;br /&gt;
== Lezione del 14 marzo 2017 ==&lt;br /&gt;
'''Gestione della memoria'''&lt;br /&gt;
* Binding a tempo di esecuzione, compilazione o esecuzione&lt;br /&gt;
* Memory Management Unit (MMU)&lt;br /&gt;
* Allocazione statica o dinamica&lt;br /&gt;
* Allocazione continua o non contigua&lt;br /&gt;
* Metodo delle partizioni fisse&lt;br /&gt;
: Problema della frammentazione interna.&lt;br /&gt;
* Metodo delle partizioni variabili&lt;br /&gt;
:Problema della frammentazione esterna.&lt;br /&gt;
:* Algoritmi di scelta dell'area di allocazione&lt;br /&gt;
:** First fit&lt;br /&gt;
:** Best fit&lt;br /&gt;
:** Worst fit&lt;br /&gt;
:** Next fit&lt;br /&gt;
:* Paginazione&lt;br /&gt;
:* Segmentazione&lt;br /&gt;
:* Memoria virtuale&lt;br /&gt;
&lt;br /&gt;
== Lezione del 16 marzo 2017 ==&lt;br /&gt;
* '''Progetto phase2''': analisi del manuale di uARM&lt;br /&gt;
* '''Python''': funzioni, oop, @memoize, @trace&lt;br /&gt;
&lt;br /&gt;
== Lezione del 21 marzo 2017 ==&lt;br /&gt;
&lt;br /&gt;
La lezione del 21 marzo tace per impegno del docente.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 23 marzo 2017 ==&lt;br /&gt;
'''Memoria virtuale''': &amp;lt;br&amp;gt;&lt;br /&gt;
'''Algoritmi di paginazione:'''&lt;br /&gt;
Quando avviene un page fault serve un algoritmo per trovare la pagina &amp;quot;meno utile&amp;quot; in memoria a cui sostituire la pagina richiesta.&lt;br /&gt;
* '''FIFO''' viene sostituita la pagina che da più tempo è in memoria&lt;br /&gt;
* '''LFU''' ''Least Frequently Used'' contando il numero di richieste alla pagina possiamo calcolare quali sono le pagine più richieste e tenere solo quelle in memoria. Il problema è che una pagina potrebbe essere richiesta molto in una fase iniziale di un processo, quindi avere un rank elevato, però poi non essere più utilizzata. Una soluzione a questo problema è quella di shiftare a destra di 1 i bit del contatore degli utilizzi a intervalli regolari, ponendo questo in un decadimento esponenziale. &lt;br /&gt;
* '''LRU''' ''Least Recently Used'' si sostituisce la pagina che non è stata utilizzata da più tempo. &lt;br /&gt;
* '''Algoritmo ottimo''' algoritmo teorico utile per fare ''benchmark'' degli altri algoritmi. Se conosciamo a priori la ''reference string'' una sequenza che indica quali saranno le richieste delle pagine nel futuro, possiamo sostituire le pagine che per più tempo non saranno richieste, riducendo al minimo i page fault.&lt;br /&gt;
'''Anomalia di Belady'''&lt;br /&gt;
Abbiamo dimostrato che l'algoritmo FIFO all'aumentare della memoria può peggiorare di performance.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 28 marzo 2017 ==&lt;br /&gt;
'''Algoritmo second chance o dell'orologio''' &amp;lt;br&amp;gt;&lt;br /&gt;
'''Linking statico'''&amp;lt;br&amp;gt;&lt;br /&gt;
'''Loading dinamico'''&amp;lt;br&amp;gt;&lt;br /&gt;
'''Memoria secondaria'''&amp;lt;br&amp;gt;&lt;br /&gt;
'''Algoritmi di scheduling delle seek'''&amp;lt;br&amp;gt;&lt;br /&gt;
*'''FIFO'''&lt;br /&gt;
*'''SSF'''&lt;br /&gt;
*'''c-look'''&lt;br /&gt;
&lt;br /&gt;
== Lezione del 30 marzo 2017 ==&lt;br /&gt;
'''Discussione sulle specifiche del progetto'''&amp;lt;br&amp;gt;&lt;br /&gt;
:Creazione pagina [[Specifiche_phase_2_2016/17]]&lt;br /&gt;
'''Python standard library'''&lt;br /&gt;
* libreria sys: Permette di prendere parametri da linea di comando. Si utilizza ''sys.argv''.&lt;br /&gt;
* libreria os: Permette di navigare nella gerarchia di directory con ''os.walk''.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 4 aprile 2017 ==&lt;br /&gt;
'''RAID''' &amp;lt;br&amp;gt;&lt;br /&gt;
(Redundant Array Inexpensive/Independent Disks)&lt;br /&gt;
&lt;br /&gt;
l’idea principale nasce per ottenere maggiori performance (raid 0 ed 1), non maggiore affidabilità (raid 4 e 5).&lt;br /&gt;
&lt;br /&gt;
* RAID0 (Striping): i dischi sono posti in sequenza.Vengono create le stripe, sequenze di bit che sono posti su n dischi sequenzialmente. Lo striping può essere a vari livelli, a livello di bit, di byte, di settore o di blocco. Quello più utilizzato e il livello di blocco. Cioè per scrivere un file che è composto da più blocchi si suddividono i blocchi in più dischi. Attenzione, file piccoli potrebbero sostare su una sola stripe, quindi su un solo disco.&lt;br /&gt;
:Il miglioramento di performance si paga in maggiore faultness. dipende dal costo dei dati salvati.&lt;br /&gt;
&lt;br /&gt;
* RAID1 (mirroring): ogni disco ospita la copia dei dati. doppia velocità in lettura (teoricamente). Stessa velocita di scrittura.&lt;br /&gt;
: Size = ½ readSpeed = N wSpeed = ½ N FT = 1&lt;br /&gt;
* RAID10 = 1+0 striping su 2 mirror.&lt;br /&gt;
&lt;br /&gt;
* RAID 2-3 (deprecated):Agisce sui singoli blocchi i dati vengono salvati in maniera sincrona con meccanismi di parità (hoffman per correzione di errori nel RAID2, ciclica per RAID3).&lt;br /&gt;
&lt;br /&gt;
* RAID4: usa dischi normali, non agisce su singoli blocchi ma su stripe ampi. &lt;br /&gt;
&lt;br /&gt;
parityMap = S0 XOR S1 XOR S2 XOR S3 (in caso di 4+1 dischi).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
nel caso si rompa S4, i dati non avranno problemi. Se si rompesse un qualunque dei primi 4 dischi, basterà effettuare lo XOR degli altri e copiare tali dati su un nuovo HD.&lt;br /&gt;
&lt;br /&gt;
S1 rotto.&lt;br /&gt;
new_S1 = S0 XOR S2 XOR S3 XOR P0123&lt;br /&gt;
Sopporta una sola rottura.&lt;br /&gt;
&lt;br /&gt;
Per aggiornare il disco di parità in caso di modifica a S2 (chiamato ora S2’)&lt;br /&gt;
&lt;br /&gt;
P0123 = P0123 XOR S2 XOR S2’.&lt;br /&gt;
il primo xor toglie il valore da P0123 di S2, il secondo XOR aggiunge il valore S2’.&lt;br /&gt;
in questo modo, aggiorno solo 2 dischi, e uso gli stessi in lettura per ricalcolare la parità.&lt;br /&gt;
&lt;br /&gt;
Le parità ciclano su tutti i dischi. Esistono controller che effettuano RAID5, oppure e’ possibile ottenere le funzionalità raid via sistema operativo.&lt;br /&gt;
&lt;br /&gt;
* RAID5: &lt;br /&gt;
:Senza fault&lt;br /&gt;
:Size = N-1/N, rspeed=(N-1), wspeed=(N-2), FT=1&lt;br /&gt;
&lt;br /&gt;
I dischi RAID soffrono alquanto in caso di terremoti.&lt;br /&gt;
&lt;br /&gt;
'''File System:''' &amp;lt;br&amp;gt;&lt;br /&gt;
Servizio per dare una interfaccia più comoda e generale per l’accesso alla memoria secondaria.&lt;br /&gt;
&lt;br /&gt;
Nasce come informatizzazione delle pratiche di ufficio. le pratiche sono salvate in folders, fascicoli, poste poi nelle directories. come visualizzo questa struttura per l’utente? E ad i programmatori? come viene implementato?&lt;br /&gt;
&lt;br /&gt;
Nasce l’idea di “aprire” i file. Nell'accedere da un programma ad il file-system (operazione non banale) si preferisce chiamare un’operazione di apertura, e utilizzare un descrittore al file per agire sul file richiesto.&lt;br /&gt;
&lt;br /&gt;
La struttura di file/folder/directories possono inoltre supportare le astrazioni di condivisione e proprieta’.&lt;br /&gt;
Tutti i controlli sono effettuati sono in fase di apertura (in modo da migliorare le performance). &lt;br /&gt;
&lt;br /&gt;
Quali attributi caratterizzano un file?&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Name,&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Type,&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Location,&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Size,&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Ownership,&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Protection,&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Time stamp&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
UNIX opta per una scelta minimalista. Tutti i file sono stringhe di byte.&lt;br /&gt;
AFS (Apple) da un tag di tipo e di creator per ogni file&lt;br /&gt;
&lt;br /&gt;
maggiore e’ il numero di tipi di file, maggiore supporto per essi, ma piu’ codice nel kernel.&lt;br /&gt;
I suffissi possono indicare ad altri programmi il tipo.&lt;br /&gt;
&lt;br /&gt;
Semantica della coerenza vs Semantica delle sessioni (Andrew File System)&lt;br /&gt;
&lt;br /&gt;
Per quali motivo vengono create le partizioni?&lt;br /&gt;
/var andrebbe in una nuova partizione per un fattore di sicurezza.&lt;br /&gt;
&lt;br /&gt;
Cosa c’e’ effettivamente sul disco? Una sequenza di blocchi, il cui primo (boot block) ha spazio per il bootloader e la tabella delle partizioni (MBR), con 4 elementi. 4 record che indicano dove ogni partizione inizia e finisce. Si possono usare partizioni estese, usando le prime 3 e nella quarta tutto il resto.&lt;br /&gt;
&lt;br /&gt;
Ogni partizione ha all'inizio un BootRecord.&lt;br /&gt;
L’ultima partizione contiene nel BootRecord le altre partizioni. &lt;br /&gt;
GBT consente di avere un numero arbitrario di partizioni.&lt;br /&gt;
&lt;br /&gt;
Raizer FS,&lt;br /&gt;
&lt;br /&gt;
Read only file systems.&lt;br /&gt;
&lt;br /&gt;
Initrd, ISO9660.&lt;br /&gt;
&lt;br /&gt;
Quando si costruisce l’immagine, in realtà si sta creando il file-system ISO9660.&lt;br /&gt;
&lt;br /&gt;
Come gestire invece file che possono cambiare?&lt;br /&gt;
Allocazione indicizzata (a lista): creo un (o vari) blocco/i degli indici che punta ad i blocchi dati.&lt;br /&gt;
l’accesso diretto cresce come il logaritmo dell’ampiezza. &lt;br /&gt;
Allocazione concatenata (ad albero): (in fondo al blocco indico il successivo). Gestisce bene la dinamicità. Non piace perché’ non é per nulla efficiente l’accesso diretto. &lt;br /&gt;
&lt;br /&gt;
Quali allocazioni usano i filesystem reali oggi?&lt;br /&gt;
&lt;br /&gt;
fat: concatenata. I blocchi tengono solo i dati, esiste una file allocation table (vettore) che indica il prossimo blocco. I puntatori sono tutti vicini e quindi ponendo il vettore in cache, abbiamo disponibili diversi puntatori.&lt;br /&gt;
&lt;br /&gt;
BerkleyFastFileSystem (progenitore dei EXT4…) usano allocazione indicizzata. Nasce per garantire performance ad i file piccoli. Nel descrittore del file (iNode) avra’ N (13 attualmente) puntatori. Uno, il numero 10, ad i blocchi, uno (11) indiretto alla tabella, uno (12) come duplice indiretto e il 13esimo come triplice indiretto.&lt;br /&gt;
&lt;br /&gt;
I file hanno quindi una lunghezza massima. EX4 pero’ risolve indicizzando non i singoli blocchi ma le serie.&lt;br /&gt;
&lt;br /&gt;
Gestione spazio libero&lt;br /&gt;
&lt;br /&gt;
nella struttura indicizzata serve una struttura per tenere conto degli spazi liberi. Si puo’ usare una bitmap per tenere la contabilità degli spazi liberi e quelli utilizzati.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 6 aprile 2017 ==&lt;br /&gt;
&lt;br /&gt;
File System. Ext2 e VFAT. Sicurezza, principi generali.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 11 aprile 2017 ==&lt;br /&gt;
&lt;br /&gt;
Sicurezza: crittografia (chiave pubblica/chiave segreta) autorizzazione autenticazione firma elettronica.&lt;br /&gt;
Virus Worm Cavalli di Troia.&lt;br /&gt;
&lt;br /&gt;
Attacchi buffer overflow e toctou.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 20 aprile 2017 ==&lt;br /&gt;
&lt;br /&gt;
(tace per impossibilita' del docente)&lt;br /&gt;
&lt;br /&gt;
== Lezione del 27 aprile 2017 ==&lt;br /&gt;
&lt;br /&gt;
P2test (prestest2).&lt;br /&gt;
&lt;br /&gt;
DIscussione specifiche.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 2 maggio 2017 ==&lt;br /&gt;
== Lezione del 4 maggio 2017 ==&lt;br /&gt;
* Discussione su proposta da parte degli studenti di modifiche al test di progetto&lt;br /&gt;
* Soluzione all'esercizio C1 del 2014-06-16 (Monitor)&lt;br /&gt;
* Soluzione all'esercizio 1 del [[http://www.cs.unibo.it/~renzo/so/pratiche/2015.05.29.pdf 2016-05-20]] ( C )&lt;br /&gt;
* Soluzione all'esercizio 2 del 2008-06-13 (Dimostrazione algoritmo di rimpiazzamento a stack)&lt;br /&gt;
* Soluzione all'esercizio 1 del [[http://www.cs.unibo.it/~renzo/so/compiti/2013.07.19.tot.pdf 2013-07-19]] (Monitor)&lt;br /&gt;
&lt;br /&gt;
== Lezione del 9 maggio 2017 ==&lt;br /&gt;
*Esercizio 2 prova teorica 2014.09.24&lt;br /&gt;
*Esercizio g.1 prova teorica 2013.07.19&lt;br /&gt;
*Esercizio c1 e c2 prova teorica 2015.01.20(2014.01.20)&lt;br /&gt;
*Esercizio c2 2013.05.30&lt;br /&gt;
&lt;br /&gt;
== Lezione del 11 maggio 2017 ==&lt;br /&gt;
== Lezione del 16 maggio 2017 ==&lt;br /&gt;
* Chiarimenti su svolgimento esame&lt;br /&gt;
* Commenti su recenti eventi (WannaCry)&lt;br /&gt;
* Visione di programma python per costruzione di stringhe palindrome con monitor&lt;br /&gt;
* Correzione esercizio g2 prova teorica 20170209&lt;br /&gt;
* Correzione esercizio 1 [[Prova pratica 2016.09.13]]&lt;br /&gt;
* Esercizio c2 prova teorica 20140122&lt;br /&gt;
&lt;br /&gt;
== Lezione del 18 maggio 2017 ==&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2017.05.29&amp;diff=1924</id>
		<title>Prova teorica 2017.05.29</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2017.05.29&amp;diff=1924"/>
		<updated>2017-06-15T09:41:15Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Creata pagina con link a foto del compito e struttura. Aggiunta risposa domanda 2 esercizio g.2&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Media:20170529.jpg | link al compito]]&lt;br /&gt;
&lt;br /&gt;
Ho caricato questa foto del compito intanto che non è ancora disponibile il pdf. Quando sarà disponibile sostituire con il link al pdf. --[[User:FedericoB|FedericoB]] ([[User talk:FedericoB|talk]]) 11:41, 15 June 2017 (CEST)&lt;br /&gt;
&lt;br /&gt;
==Esercizio c.1==&lt;br /&gt;
&lt;br /&gt;
==Esercizio c.2==&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.1==&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.2==&lt;br /&gt;
#'''Un i-node di un file system tipo ext2 per un errore viene riportato un numero di link maggiore del reale. Cosa può succedere se si continua ad usare il file system? (perchè?) E se il numero di link errato fosse il contrario inferiore al reale cosa potrebbe succedere? (perchè?)&lt;br /&gt;
#'''Perchè è necessario usare spinlock in sistemi multiprocessore per implementare kernel di tipo simmetrico (SMP)?''' &amp;lt;br&amp;gt;Perchè la semplice disabilitazione degli interrupt non sarebbe sufficiente in caso di parallelismo reale a prevenire l'accesso a memoria condivisa in concorrenza. Bisogna perciò utilizzare strumenti come gli spinlock che grazie a istruzioni atomiche, per esempio la test-and-set, bloccano l'esecuzione di un processo fino al verificarsi di una condizione. Per evitare il busy wait si possono usare gli spinlock solo per rendere atomiche le istruzioni P e V di un semaforo in modo che la maggior parte del tempo di attesa per entrare in una sezione critica un processo lo passi in stato di wait invece che consumare inutilmente cicli di CPU.&lt;br /&gt;
#'''Perchè nei sistemi reali l'algoritmo di rimpiazzamento second chance (orologio) viene preferito a LRU sebbene il primo non sia a stack e il secondo sì?&lt;br /&gt;
#'''Perchè revocare un'autorizzazione espressa come capability è più difficile che revocare lo stesso diritto quando espresso come access control list?'''&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=File:20170529.jpg&amp;diff=1923</id>
		<title>File:20170529.jpg</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=File:20170529.jpg&amp;diff=1923"/>
		<updated>2017-06-15T09:24:42Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Foto della prova teorica del 2017.05.29. Quando sarà disponibile online il pdf si potrà eliminare questa immagine.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Foto della prova teorica del 2017.05.29. Quando sarà disponibile online il pdf si potrà eliminare questa immagine.&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2016.01.21&amp;diff=1922</id>
		<title>Prova teorica 2016.01.21</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2016.01.21&amp;diff=1922"/>
		<updated>2017-06-15T09:16:45Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Creata pagina e aggiunta risposta alla domanda 3 del esercizio g.2&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/compiti/2016.01.21.ris.pdf link al compito]&lt;br /&gt;
==Esercizio g.2==&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
# '''Quali sono i casi di frammentazione interna ed esterna che possono avvenire usando la paginazione.'''&lt;br /&gt;
# '''Per I file system di tipo fat non e' efficiente l'accesso diretto (ad esempio con la system call lseek) a file di grandi dimensioni. Perche'?'''&lt;br /&gt;
# '''Per quali tipi di processo e' indicato uno scheduler a priorita' statica? Quale problema puo' essere causato da uno scheduler a priorita' statica? perche'?''' &amp;lt;br&amp;gt; Lo scheduler a priorità statica è indicato per i processi i cui fattori che determinano la priorità rimangono costanti per tutta la durata del processo. Un processo che riesce a ottenere una priorità elevata rispetto agli altri la manterrà per tutta l'esecuzione. Però questo può causare starvation dei processi con meno priorità. Per ovviare a questo si utilizza il meccanismo di ''aging'' cioé si riduce progressivamente la priorità di un processo in base al tempo in cui questo è stato in esecuzione.&lt;br /&gt;
# '''Cosa e' un knot in un grafo? Quale teorema lega la definizione di knot in un grafo e la presenza di deadlock fra processi?'''&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2017.01.18&amp;diff=1921</id>
		<title>Prova teorica 2017.01.18</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2017.01.18&amp;diff=1921"/>
		<updated>2017-06-15T08:55:39Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Creata pagina e aggiunte risposte a domande 3,4,5 esercizio g.2&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/compiti/2017.01.18.ris.pdf link al compito]&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.2==&lt;br /&gt;
&lt;br /&gt;
# '''Perché un algoritmo di rimpiazzamento a stack non soffre dell’anomalia di Belady?'''&lt;br /&gt;
# '''Perché si può usare un meccanismo di crittografia simmetrica nella gestione delle capability di accesso alle risorse?'''&lt;br /&gt;
# '''Quali operazioni svolge il processore per la gestione di un interrupt e quali invece sono a carico del kernel del sistema operativo?''' &amp;lt;br&amp;gt; Il processore si occupa, all'attivazione della linea elettronica dell interrupt di recuperare dalla memoria in locazioni fisse l'indirizzo delle istruzioni di gestione degli interrupt. In seguito carica questo indirizzo nel program counter e quindi &amp;quot;salta&amp;quot; a queste istruzioni attivando la modalità di esecuzione kernel mode. &amp;lt;br&amp;gt; Le routine di gestione degli interrupt fanno parte del codice kernel e si occupano di gestire opportunamente le operazioni necessarie in base al tipo di interrupt. Per esempio operazioni come eseguire lo scheduler o recuperare l'output di un device.&lt;br /&gt;
# '''Perche’ il mascheramento degli interrupt non si può utilizzare per creare sezioni critiche in sistemi multicore o multiprocessore?''' &amp;lt;br&amp;gt; Il mascheramento degli interrupt previene l'interruzione del esecuzione del processo corrente e il context switch a un altro processo e di conseguenza la non atomicità delle istruzioni che stava eseguendo il processo. &amp;lt;br&amp;gt; Anche disabilitando gli interrupt (è uguale se su un solo processore o su tutti) c'è comunque la possibilità che altro codice eseguito in ''parallelismo reale'' vada in concorrenza con quello dove si sta cercando di ottenere una sezione critica.&lt;br /&gt;
# '''Per quali tipi di processo l’algoritmo di schedulazione round-robin è efficiente e per quali no?''' &amp;lt;br&amp;gt; L'algoritmo di schedulazione round-robin è efficente per i processi I/O bound in quanto è ''fair'', cioè permette a tutti i processi il diritto di esecuzione entro un certo lasso di tempo. Può non essere efficiente per i processi CPU bound che hanno tempi di esecuzione lunghi e il round robin li costringe a ritardare il loro completamento. Inoltre un processo CPU bound potrebbe essere interrotto quando manca davvero poco al suo termine ma il time slice è terminato oppure necessita di I/O.Il processo viene messo in coda e bisognerà aspettare l'esecuzione di tutti gli altri processi prima di poter finire l'esecuzione.Uno scheduler shortest remaining time first avrebbe invece rimesso subito in esecuzione il processo CPU bound se il tempo di esecuzione previsto rimanente era davvero ridotto.&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prove_teoriche&amp;diff=1920</id>
		<title>Prove teoriche</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prove_teoriche&amp;diff=1920"/>
		<updated>2017-06-15T08:24:22Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Aggiunti link a soluzioni mancanti da febbraio 2015 a oggi&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;''Qui troverete alcune prove teoriche svolte, in modo da poter discutere sull'elaborato''&lt;br /&gt;
&lt;br /&gt;
''Le prove sono svolte da studenti e servono come base per la discussione. Le soluzioni possono essere errate''&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2017.05.29]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2017.02.09]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2017.01.18]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2016.09.15]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2016.07.27]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2016.05.30]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2016.02.10]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2016.01.21]]  &lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2015.09.14]]  &lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2015.07.29]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2015.06.17]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2015.05.28]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2015.02.14]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2015.01.20]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2014.09.24]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2014.07.16]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2014.06.16]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2014.06.03]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica 2014.02.22]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica 2014.01.22]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Teorica 2013.09.12]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2013.07.19]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica 2013.07.19]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Teorica 2013.06.21]]&lt;br /&gt;
&lt;br /&gt;
[[Prova_Teorica 2013.02.14]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica 2013.01.24]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica_2012.09.18]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica 2012.07.16]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica_2012.06.15]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica_2012.05.24]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica_2012.02.09]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeoria 2012.01.12]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeoria_2011.07.25]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Teorica 2011.05.13]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica_2011.02.11]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeoria 2011.01.17]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeoria 2010.05.12]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Teorica 2009.09.18]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeoria_2009.01.30]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Teorica 2008.09.17]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Teorica 2008.01.16]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Teorica 2007.09.07]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Teorica 2007.07.12]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica 2005.11.04]]&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2017.02.09&amp;diff=1919</id>
		<title>Prova teorica 2017.02.09</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2017.02.09&amp;diff=1919"/>
		<updated>2017-06-15T08:13:51Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Creata pagina e aggiunta risposta a domanda 1 esercizio g.2&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/compiti/2017.02.09.ris.pdf link al compito]&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.2==&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
&lt;br /&gt;
#'''Perché uno scheduler non preemptive non è adatto per processi interattivi?''' &lt;br /&gt;
&lt;br /&gt;
:Perchè la presenza di un processo CPU bound potrebbe rallentare l'accesso alla risorsa a processi che necessitano di tempi di risposta all'utente rapidi. Essendo lo scheduler preemptive uno processo potrebbe detenere l'esecuzione per un tempo considerevole e altri processi dovrebbero attendere in quando non è possibile utilizzare un time slice per far terminare prima il processo CPU bound.&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prove_teoriche&amp;diff=1918</id>
		<title>Prove teoriche</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prove_teoriche&amp;diff=1918"/>
		<updated>2017-06-15T08:04:10Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Aggiunta link alla prova teorica del 2017.02.09&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;''Qui troverete alcune prove teoriche svolte, in modo da poter discutere sull'elaborato''&lt;br /&gt;
&lt;br /&gt;
''Le prove sono svolte da studenti e servono come base per la discussione. Le soluzioni possono essere errate''&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2017.02.09]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2015.02.14]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2015.01.20]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2014.09.24]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2014.07.16]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2014.06.16]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2014.06.03]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica 2014.02.22]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica 2014.01.22]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Teorica 2013.09.12]]&lt;br /&gt;
&lt;br /&gt;
[[Prova teorica 2013.07.19]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica 2013.07.19]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Teorica 2013.06.21]]&lt;br /&gt;
&lt;br /&gt;
[[Prova_Teorica 2013.02.14]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica 2013.01.24]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica_2012.09.18]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica 2012.07.16]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica_2012.06.15]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica_2012.05.24]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica_2012.02.09]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeoria 2012.01.12]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeoria_2011.07.25]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Teorica 2011.05.13]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica_2011.02.11]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeoria 2011.01.17]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeoria 2010.05.12]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Teorica 2009.09.18]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeoria_2009.01.30]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Teorica 2008.09.17]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Teorica 2008.01.16]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Teorica 2007.09.07]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Teorica 2007.07.12]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaTeorica 2005.11.04]]&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1917</id>
		<title>Lezioni Anno Accademico 2016/17</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1917"/>
		<updated>2017-06-14T20:27:50Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: /* Lezione del 28 febbraio 2017 */ Corretto typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;''scrivete qui idee, riassunti dei concetti espressi, commenti approfondimenti sulle lezioni.''&lt;br /&gt;
&lt;br /&gt;
== Lezione del 23 settembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
'''YY'''&lt;br /&gt;
&lt;br /&gt;
* Presentazione del corso.&lt;br /&gt;
* Strumenti del corso: web, wiki.mailing list&lt;br /&gt;
* Indice dei contenuti&lt;br /&gt;
* Prove di esame (Scritto, Prova Pratica, Progetto).&lt;br /&gt;
&lt;br /&gt;
* Hardware Software&lt;br /&gt;
* Informatica&lt;br /&gt;
* Informazione/Dato&lt;br /&gt;
* Rivoluzione digitale (secondo rinascimento, Terza rivoluzione Industriale).&lt;br /&gt;
* Software Libero&lt;br /&gt;
&lt;br /&gt;
== Lezione del 27 settembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
'''OS:Y'''&lt;br /&gt;
&lt;br /&gt;
* '''Organizzazione (martedi' vs. venerdi')'''&lt;br /&gt;
In genere la lezione del martedì sarà più teorica mentre quella del venerdì più orientata agli aspetti pratici di laboratorio. Questa suddivisione potrebbe cambiare in quanto qualche martedì verrà saltato. &lt;br /&gt;
* '''Universit&amp;amp;agrave;'''&lt;br /&gt;
Università = studenti + docenti &lt;br /&gt;
* '''Digitale/Analogico'''&lt;br /&gt;
Digit significa cifra. Nell'informatica è fondamentale il passaggio dal continuo al discreto.&lt;br /&gt;
* '''Binario/Decimale'''&lt;br /&gt;
Nei calcolatori l'informazione è memorizzata utilizzando il sistema binario ...&lt;br /&gt;
(''in quanto in passato i relè consentivano di avere due stati: &amp;quot; 1 e 0 &amp;quot;'': &amp;amp;egrave; impreciso, qualcuno sa dare una definizione migliore? [[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 12:00, 29 September 2016 (CEST)) &amp;lt;br&amp;gt;&lt;br /&gt;
In quanto è molto semplice implementare le operazioni aritmetiche con dei circuiti elettronici utilizzando la codifica binaria. Per esempio è possibile creare un [https://it.wikipedia.org/wiki/Half-adder circuito] che somma due bit con riporto con solo due porte logiche. [[User:FedericoB|FedericoB]] ([[User talk:FedericoB|talk]]) 17:05, 29 September 2016 (CEST)&lt;br /&gt;
* '''I linguaggi e i problemi dell'Informatica'''&lt;br /&gt;
**Processing dell'informazione (Linguaggi di programmazione)&lt;br /&gt;
**Comunicazione (Il problema che i dati che mi servono sono in un altro luogo) (Linguaggi dei protocolli di rete, che però sono dei linguaggi temporizzati)&lt;br /&gt;
**Memorizzazione (Il problema che i dati mi serviranno in futuro) (Linguaggio del formato dei dati)&lt;br /&gt;
* '''Conoscenze a lungo, medio e breve termine nell'Informatica.'''&lt;br /&gt;
**BREVE: tecnologia, determinati programmi di sviluppo.&lt;br /&gt;
**MEDIO: singoli linguaggi, singoli OS, protocolli di comunicazione.&lt;br /&gt;
**LUNGO: algoritmi, informatica teorica, fondamenti costruttivi di reti e di OS.&lt;br /&gt;
&lt;br /&gt;
* '''Cos'è una distribuzione'''&lt;br /&gt;
E' un antologia di applicazioni e librerie in modo che siano compatibili tra loro.&lt;br /&gt;
&lt;br /&gt;
* '''Sistema Operativo: perch&amp;amp;eacute;'''&lt;br /&gt;
Un sistema operativo è un programma che controlla l’esecuzione di programmi applicativi e agisce come interfaccia tra le applicazioni e l’hardware del calcolatore. &amp;lt;br&amp;gt;&lt;br /&gt;
E' uno strumento software complesso ed implementabile in diversi modi (monolitico, microkernel, etc.). I microcontroller non hanno OS. &lt;br /&gt;
Permette: contabilità delle risorse (consente a sua volta attività di benchmarking); continuità dei servizi; controllo errori&lt;br /&gt;
(programmi, dispositivi); multiuser (gli utenti hanno diversi diritti e proprietà sulle risorse); protezione delle attività dei                     &lt;br /&gt;
processi; astrazione file system.&lt;br /&gt;
* '''Struttura a livelli (layer)'''&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
  ---------------&lt;br /&gt;
  ---------------&lt;br /&gt;
        OS&lt;br /&gt;
  ---------------L'&lt;br /&gt;
        hw&lt;br /&gt;
  ---------------L&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
Consente di: separare la complessità, di avere portabilità. &lt;br /&gt;
Ogni livello rappresenta un cambio di linguaggio. &lt;br /&gt;
Più aggiungiamo livelli più aumenta il livello di astrazione.&lt;br /&gt;
* '''Programma/processo'''&lt;br /&gt;
Algoritmo: sequenza finita di istruzioni non ambigue, atta a risolvere un problema.&lt;br /&gt;
&lt;br /&gt;
Programma: traduzione degli algoritmi in un linguaggio di programmazione.&lt;br /&gt;
&lt;br /&gt;
Processo: istanza esecutiva di un programma. Il programma è testo statico, quando viene eseguito diviene processo. Possiamo avere&lt;br /&gt;
uno stesso programma in esecuzione su più processi (e.g apertura di più terminali che eseguono un editor di testo).&lt;br /&gt;
&lt;br /&gt;
System call: richieste di processi al OS. e.g. printf (visto nel codice compilato in assembly tramite gcc -s), invoca write().&lt;br /&gt;
vi è una sequenza di azioni del tipo: richiesta di stampa al kernel --&amp;gt; attesa --&amp;gt; risposta.&lt;br /&gt;
* '''Storia dell'Informatica prima dei transistor'''&lt;br /&gt;
&lt;br /&gt;
La storia dei calcolatori non ha un inizio preciso. Nel 1900 venne trovato sul relitto di una nave greca risalente al II secolo a.C un meccanismo in grado di calcolare i movimenti degli astri, si ipotizza con una sorprendente meccanica. Oggi questo sistema viene conosciuto come Macchina di Anticitera. Negli ultimi anni di ricerche archeologiche e storiche è venuta sempre più in superficie una vena pionieristica nel mondo classico per quanto riguarda lo studio sul calcolo automatizzato e addirittura di automi (si veda Erone di Alessandria). Ma facciamo un salto in avanti di qualche secolo... &amp;lt;br&amp;gt;&lt;br /&gt;
Già nel XVII secolo Pascal teorizza e mette in pratica uno strumento in grado di addizionare e sottrarre, e nel 1672 G.W. von Leibniz lo perfeziona con l'aggiunta della moltiplicazione e della divisione. Ma è solamente nei primi anni del 1800 che entra in scena la macchina analitica di Babbage. Estremamente complessa, la macchina era dotata di un sistema di input e di elaborazione dati e di un sistema di output. Ed è con Babbage che nascono le schede perforate. La macchina analitica venne presentata per la prima volta in Italia, durante un convegno torinese del 1840. Tra i vari collaboratori scientifici alla macchina è da annotare la presenza di Luigi Federico Menabrea, che tra le altre cose fu Ministro della Marina Militare, ingegnere e Presidente del Consiglio dei Ministri del Regno d'Italia (1867-1869). Il primo vero programmatore della storia, collaboratrice di Babbage, è Ada Lovelace (figlia del poeta Lord Byron!): progettò un algoritmo in grado di elaborare i numeri di Bernulli. &amp;lt;br&amp;gt;&lt;br /&gt;
Nel 1896 viene fondata la Tabulating Machine Company (futura IBM) e agli albori del '900, vari scienziati (trai quali Thomas Edison e Guglielmo Marconi) portarono avanti gli studi sulla valvola termoionica, elemento indispensabile dei primi calcolatori elettronici. La valvola incrementò vertiginosamente i tempi di calcolo ma l'estrema fragilità della stessa causò una serie di problematiche che si risolsero solamente con l'entrata in gioco del transistor.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 30 settembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
'''YC'''&lt;br /&gt;
&lt;br /&gt;
(Storia dopo l'avvento del transistor)&lt;br /&gt;
&lt;br /&gt;
Tre innovazioni indipendenti:&lt;br /&gt;
* Multitasking: Inizialmente ideato per mantenere la CPU occupata con altri processi mentre si faceva Input/Output. Permette di dare l'idea al utente di poter eseguire più processi contemporaneamente anche se questi in realtà sono eseguiti dalla cpu in modo intervallato. Ci sono certi computer che hanno più esecutori e quindi possono realmente eseguire multipli processi contemporaneamente. &lt;br /&gt;
* Supporto Multiuser&lt;br /&gt;
* Interattivit&amp;amp;agrave;:l'interazione con l'utente cambia il flusso del programma.&lt;br /&gt;
&lt;br /&gt;
Queste tre proprietà sono ortogonali quindi possono esistere nei sistemi varie combinazioni di queste.&lt;br /&gt;
&lt;br /&gt;
Iniziano anche a separarsi i ruoli tra programmatori e utenti.Prima i computer venivano utilizzati solo da chi creava programmi ora anche da chi li utilizza senza avere le conoscenze per crearli.&lt;br /&gt;
&lt;br /&gt;
Il Time Sharing.&lt;br /&gt;
&lt;br /&gt;
Sistemi Paralleli (SIMD/MIMD)&lt;br /&gt;
Sistemi Real Time&lt;br /&gt;
&lt;br /&gt;
Dal 1969: UNIX.&lt;br /&gt;
&lt;br /&gt;
Il linguaggio C e la portabilit&amp;amp;agrave; dei sistemi operativi.&lt;br /&gt;
Caratteristiche del linguaggio C:&lt;br /&gt;
* grande controllo sul codice generato&lt;br /&gt;
* costrutti per l'elaborazione a basso livello&lt;br /&gt;
* supporto per la programmazione strutturata&lt;br /&gt;
* linguaggio &amp;quot;leggero&amp;quot;: poche parole chiave, I/O gestito tramite librerie&lt;br /&gt;
* Il compilatore C &amp;amp;egrave; scritto in C.&lt;br /&gt;
&lt;br /&gt;
Portabilit&amp;amp;agrave; di un compilatore. I cross-compiler.&lt;br /&gt;
&lt;br /&gt;
Il comando UNIX cc (o gcc): un comando unico per attivare l'intera toolchain per generare un eseguibile.&lt;br /&gt;
&lt;br /&gt;
Il punto e virgola trasforma espressioni in istruzioni (terminatore).&lt;br /&gt;
&lt;br /&gt;
1975? l'avvento dei personal computer. (in realt&amp;amp;agrave; il primo PC forse e' la Perrottina Olivetti del 1964).&lt;br /&gt;
 &lt;br /&gt;
Compiti a casa:&lt;br /&gt;
Portare esperimenti in C ritenuti &amp;quot;difficili&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 10:41, 1 October 2016 (CEST)&lt;br /&gt;
&lt;br /&gt;
== Lezione del 7 ottobre 2016 ==&lt;br /&gt;
Audio Lezione: [https://drive.google.com/open?id=0B8G3NNuJ7MA7TzJXUTJZM3RBUlE 7\10\16]&lt;br /&gt;
* '''Esempi di programmi in linguaggio C'''&lt;br /&gt;
Abbiamo commentato i programmi dal 1.1 al 1.7 che si possono trovare in questa [[2016-17_Programmi_C|pagina]].&lt;br /&gt;
* '''Economia Push/Economia Pull'''&lt;br /&gt;
Nell'economia push si basa la produzione sulla previsione di domanda e la si cerca di aumentare tramite la pubblicità. Questa è stata la principale metodologia utilizzata nei secoli scorsi.&lt;br /&gt;
Al giorno d'oggi si sta facendo avanti un differente approccio chiamato &amp;quot;economia pull&amp;quot; dove la produzione è basata sulla domanda effettiva. Per esempio per richiedere un finanziamento per l'economia push l'approccio comune è tramite una banca a cui si chiede un prestito in base alle previsioni di ricavo. Invece nell'economia pull è esemplare il fenomeno del crowdfounding dove si riceve un finanziamento dalla &amp;quot;folla&amp;quot; cioè da un gruppo di persone che dona una piccola parte di denaro ciascuna in base al reale interesse per il prodotto/servizio.&lt;br /&gt;
* '''Sistemi Operativi e parallelismo'''&lt;br /&gt;
Più processi si dicono in esecuzione concorrente quando accedono a risorse condivise.&lt;br /&gt;
Un programma è multithread quando tutti i thread condividono la stessa memoria. Ogni thread deve avere un suo stack.&lt;br /&gt;
La concorrenza si studia in particolare in sistemi operativi in quanto un sistema operativo multitasking è un sistema concorrente.&lt;br /&gt;
Concorrenza di processi in architetture SMP (Symmetric multiprocessor system) e non. (RD: non mi pare di aver spiegato cosa sia l'SMP) &amp;lt;br&amp;gt;&lt;br /&gt;
Simulazione del fenomeno della '''race condition'''. Si ha '''race condition''' quando un programma concorrente pu&amp;amp;ograve; puo' produrre diversi risultati se eseguito piu' volte per colpa della diversa evoluzione temporale delle elaborazioni.&amp;lt;br&amp;gt;&lt;br /&gt;
Per risolvere questo dovremmo raggruppare le istruzioni in ''sezioni critiche'' (sequenze inscindibili di istruzioni che devono essere eseguite come se fossero atomiche, tutta la sequenza o niente). &lt;br /&gt;
* '''Architettura a livelli e virtualizzazione'''&lt;br /&gt;
Dato un A e un A’ e riusciamo a usare A’ come A, allora A’ lo possiamo chiamare A virtuale.&lt;br /&gt;
L'idea che alla base delle macchine virtuali è di creare un interprete di un ISA differente o uguale a quello del sistema in modo da virtualizzare una reale macchina hardware e quindi poterla usare per gli stessi scopi di una macchina reale. E' quindi possibile, per esempio, installare sistemi operativi su hardware virtuale.&amp;lt;br&amp;gt;&lt;br /&gt;
L'insieme dei componenti hardware virtuali e il software installato su di questi prende il nome di ''macchina virtuale''.&lt;br /&gt;
Uno dei principali vantaggi, oggi molto utilizzato in ambito cloud, è quello di poter suddividere facilmente risorse hardware fisiche, per esempio spazio su disco e potenza di elaborazione, tra macchine virtuali differenti. &lt;br /&gt;
* '''Laboratorio di cross-compiling'''&lt;br /&gt;
Nel ultima parte della lezione abbiamo fatto un esperimento sulla portabilità dei compilatori. &amp;lt;br&amp;gt;&lt;br /&gt;
Abbiamo a disposizione un linguaggio ad alto livello che chiameremo L, due interpreti hw0 e hw1 per linguaggi a basso livello che chiameremo lhw0 e lhw1 e infine due compilatori L-&amp;gt;lhw0 uno scritto in python e l'altro in L. &amp;lt;br&amp;gt;&lt;br /&gt;
Se compiliamo con il compilatore scritto in python (abbiamo un interprete python installato sulla macchina che stiamo utilizzando) il compilatore scritto in L otteniamo un compilatore scritto in lhw0 L-&amp;gt;lhw0. &amp;lt;br&amp;gt;&lt;br /&gt;
Se ora modifichiamo il compilatore scritto in L L-&amp;gt;lhw0 in modo che crei codice per hw1 (basta modificare gli indici numerici delle istruzioni) otteniamo un compilatore in L L-&amp;gt;lhw1. Se lo compiliamo con il compilatore L-&amp;gt;lhw0 otteniamo un ''cross-compiler'' scritto in lhw0 L-&amp;gt;lhw1, cioè un compilatore che crea codice eseguibile su una macchina diversa da quella attuale. &amp;lt;br&amp;gt;&lt;br /&gt;
Se ora utilizziamo il cross compiler L-&amp;gt;lhw1 per compilare il compilatore scritto in L L-&amp;gt;hw1 otteniamo invece un compilatore che può essere eseguito su hw1. &amp;lt;br&amp;gt;&lt;br /&gt;
A questo punto su hw1 potremmo utilizzare l'ultimo compilatore creato per compilare sempre il compilatore scritto in L L-&amp;gt;hw1 ma vedremo che otterremo sempre lo stesso file perché avremo raggiunto il limite.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Il materiale per poter replicare l'esperimento sulla portabilit&amp;amp;agrave; dei compilatori &amp;amp;egrave; [http://www.cs.unibo.it/~renzo/so/tools/portability3.tgz qui]. &lt;br /&gt;
[[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 13:42, 8 October 2016 (CEST)&lt;br /&gt;
&lt;br /&gt;
== Lezione del 11 ottobre 2016 ==&lt;br /&gt;
*'''C'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
  #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  void dump (void* f, size_t len){ //size_t is the sizeof() return type&lt;br /&gt;
        unsigned char *s = f;&lt;br /&gt;
        size_t i;&lt;br /&gt;
        for (i = 0; i &amp;lt; len; i++)&lt;br /&gt;
                printf(&amp;quot;%02x &amp;quot;,s[i]); //%02x means print at least 2 digits and add 0's if there are less&lt;br /&gt;
        printf(&amp;quot;\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  int main(){&lt;br /&gt;
&lt;br /&gt;
  int v[] = {1, 2, 3, 4}; //v is (int*) 0x32ef...&lt;br /&gt;
  v[2] = 42; //*((int*) 0x32ef) + 2 = 42, moves the address by 2 &amp;quot;positions&amp;quot;, aka 4+4 bytes&lt;br /&gt;
  v[2] = 0x33;//(int*) 0x32ef.. = 0x33 (i.e.) 4 = 42&lt;br /&gt;
&lt;br /&gt;
  char s[] = &amp;quot;ciao&amp;quot;;&lt;br /&gt;
  int t[] = {0x63, 0x69, 0x61, 0x6f, 0x00}; //ciao in ASCII hex. if int OUT:'c'. if char OUT:&amp;quot;ciao&amp;quot;&lt;br /&gt;
  char u[] = {99, 105,97, 111, 0 }; //ciao in ASCII dec. OUT:&amp;quot;ciao&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  char *q = &amp;quot;ciao&amp;quot;; &lt;br /&gt;
  char *w = &amp;quot;mare&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  printf(&amp;quot;%s\n&amp;quot;, s);&lt;br /&gt;
  printf(&amp;quot;%s\n&amp;quot;, t);&lt;br /&gt;
  printf(&amp;quot;%s\n&amp;quot;, u);&lt;br /&gt;
&lt;br /&gt;
  dump(s, sizeof(s));&lt;br /&gt;
  dump(t, sizeof(t));&lt;br /&gt;
  dump(q, sizeof(*q)); //*q size is 1B (it points to 1 char). OUT: 63('c')&lt;br /&gt;
  dump(q, sizeof(q)); //q size is 8B. OUT: 5B of &amp;quot;ciao&amp;quot; + 3B of contiguous string &amp;quot;mare&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OUTPUT:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  ciao&lt;br /&gt;
  c  &lt;br /&gt;
  ciao&lt;br /&gt;
  63 69 61 6f 00&lt;br /&gt;
  63 00 00 00  69 00 00 00  61 00 00  00 6f 00 00  00 00 00 00 00 //int are 4B, char 1B, so we have empty bytes. &lt;br /&gt;
  Little endian pattern is evident. %s allow to print untill the first null, so printf prints just 'c' with int declaration of t[]&lt;br /&gt;
  63 &lt;br /&gt;
  63 69 61 6f  00 6d 61 72 // 6d = m, 61 = a, 72 = r.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''Assioma di finite progress.'''&lt;br /&gt;
&amp;quot;Tutto è vero purchè ogni statement abbia durata sconosciuta, ma finita&amp;quot;&lt;br /&gt;
* '''Modalità di concorrenza.'''&lt;br /&gt;
1- Inconsapevolmente concorrenti: ogni processo elabora ciò per cui è stato creato. Il kernel si occupa di coordinazione e comunicazione.&lt;br /&gt;
&lt;br /&gt;
2- Indirettamente concorrenti: processi che accedono a file condivisi.&lt;br /&gt;
&lt;br /&gt;
3- Consapevolmente concorrenti: il programma ha diversi processi in esecuzione.&lt;br /&gt;
*''' Convenzioni usate nel corso (e negli esami) relative alle istruzioni atomiche'''&lt;br /&gt;
Le istruzioni atomiche vengono compiute in modo indivisibile, garantendo che l'azione non interferisca con altri processi durante la sua esecuzione.&lt;br /&gt;
Vengono rappresentate nel seguente modo:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;istruzione atomica&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Anche gli statement sono istruzioni atomiche(escluso il caso long long).&lt;br /&gt;
*''' Proprieta' di Safety e Liveness'''&lt;br /&gt;
-Safety: se il programma evolve, evolve bene(S) &lt;br /&gt;
&lt;br /&gt;
&amp;quot;''Something good'' happens&amp;quot;&lt;br /&gt;
&lt;br /&gt;
-Liveness: il programma va avanti(L)&lt;br /&gt;
&lt;br /&gt;
&amp;quot;''Something eventually'' happens&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* '''Problema del consenso.'''&lt;br /&gt;
Il problema è così definito: ci sono n processi, ognuno propone un valore. Al termine tutti i processi devono accordarsi su di 1 dei valori proposti.Il suddetto problema è risolvibile  se e solo se vengono rispettate le seguenti proprietà (2 di Safety + 1 di Liveness)&lt;br /&gt;
&lt;br /&gt;
Proprietà:&lt;br /&gt;
&lt;br /&gt;
S(1)- Tutti i processi devono ritornare lo stesso valore&lt;br /&gt;
&lt;br /&gt;
S(2)- Ogni processo corretto decide uno tra i valori proposti&lt;br /&gt;
&lt;br /&gt;
L(1)- Ogni processo corretto deve dare una risposta&lt;br /&gt;
&lt;br /&gt;
w/o S(1): f(x) = x&lt;br /&gt;
&lt;br /&gt;
w/o S(2): f(x) = 42&lt;br /&gt;
&lt;br /&gt;
w/o L(1): f(x) = while(1)&lt;br /&gt;
&lt;br /&gt;
*w/o = without&lt;br /&gt;
&lt;br /&gt;
* '''Problemi delle elaborazioni concorrenti. Prima definizione (non formale) di Deadlock e Starvation'''&lt;br /&gt;
Introduzione alla notazione per la rappresentazione dei processi concorrenti&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1.0&lt;br /&gt;
&lt;br /&gt;
  process p {&lt;br /&gt;
    ...&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  process q {&lt;br /&gt;
    ...&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Due processi eseguiti in concorrenza.&lt;br /&gt;
  &lt;br /&gt;
  ...&lt;br /&gt;
  cobegin&lt;br /&gt;
  ...&lt;br /&gt;
  //&lt;br /&gt;
  ...&lt;br /&gt;
  //&lt;br /&gt;
  ...&lt;br /&gt;
  coend&lt;br /&gt;
&lt;br /&gt;
cobegin e coend indicano la sezione di codice con le istruzioni (rappresentate da &amp;quot;...&amp;quot; e separate da &amp;quot;//&amp;quot;)che verranno eseguite in concorrenza.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1.1 - Merge sort&lt;br /&gt;
  cobegin &lt;br /&gt;
    sort(v[1 : N])&lt;br /&gt;
  //&lt;br /&gt;
    sort(v[N : N])&lt;br /&gt;
  //&lt;br /&gt;
    merge(v[1 : N]), v[N : M])&lt;br /&gt;
  coend&lt;br /&gt;
&lt;br /&gt;
Se isoliamo le istruzioni senza considerare cobegin e coend, lo pseudocodice è corretto. &lt;br /&gt;
Se consideriamo anche cobegin e coend il programma esegue la funzione di merge() in concorrenza con le due chiamate a sort(), utilizzando risorse in comune e pertanto cercando di ordinare  elementi non ancora ordinati nelle due metà dell'array.&lt;br /&gt;
Se spostiamo l'invocazione di merge al di fuori della sezione cobegin ... coend, lo pseudocodice (vedi in basso) è corretto.&lt;br /&gt;
&lt;br /&gt;
  cobegin &lt;br /&gt;
    sort(v[1 : N])&lt;br /&gt;
  //&lt;br /&gt;
    sort(v[N : N])&lt;br /&gt;
  coend&lt;br /&gt;
    merge(v[1 : N]), v[N : M])&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In particolare nella programmazione concorrente si devono affrontare due problemi che riguardano la liveness, parliamo di deadlock e starvation.&lt;br /&gt;
&lt;br /&gt;
'''DEADLOCK''': uno o più processi non possono continuare l'esecuzione perchè ognuno di essi è in attesa che gli altri facciano qualcosa&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1.2&lt;br /&gt;
&lt;br /&gt;
  process p {&lt;br /&gt;
  while 1:&lt;br /&gt;
    prendi a&lt;br /&gt;
    prendi b&lt;br /&gt;
    calcola f(a,b)&lt;br /&gt;
    lascia a&lt;br /&gt;
    lascia b&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  process q {&lt;br /&gt;
  while 1:&lt;br /&gt;
    prendi a&lt;br /&gt;
    prendi b&lt;br /&gt;
    calcola f(a,b)&lt;br /&gt;
    lascia a&lt;br /&gt;
    lascia b&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Supponiamo di invertire l'ordine di prendi a e prendi b nel processo p. Dopo che entrambi i processi eseguono la prima istruzione lo scenario che si sviluppa è il seguente: al processo p serve la risorsa a, presa dal processo q, al quale servirebbe la risorsa b, presa dal processo p. I processi aspettano a vicenda che uno rilasci la risorsa che possiede l'altro. Risorsa necessaria ad entrambi i processi per eseguire l'istruzione (f(a,b)) che le richiederebbe entrambe. I processi sono deadlocked.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''STARVATION''': quando un processo attende una risorsa indefinitamente, e questa non gli viene mai allocata&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1.3&lt;br /&gt;
  process p {&lt;br /&gt;
  while 1:&lt;br /&gt;
    prendi a&lt;br /&gt;
    calcola f(a)&lt;br /&gt;
    lascia a&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  process q {&lt;br /&gt;
  while 1:&lt;br /&gt;
    prendi a&lt;br /&gt;
    calcola f(a)&lt;br /&gt;
    lascia a&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Supponiamo che i processi per realizzare &amp;quot;prendi a&amp;quot; verifichino la disponibilit&amp;amp;agrave; di a tramite polling (provando ripetutamente se a &amp;amp;egrave; libero). Il processo q pu&amp;amp;ograve; essere sfortunato e non riuscire mai ad avere accesso ad a (tutte le volte che cerca di &amp;quot;prenderlo&amp;quot; lo trover&amp;amp;agrave; impegnato da p). Il processo q è in starvation.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1.4&lt;br /&gt;
&amp;quot;Supponiamo di avere tre processi (P1, P2, P3) i quali richiedono periodicamente accesso alla risorsa R. Consideriamo la situazione nella quale P1 è in possesso della risorsa, e sia P2 che P3 attendono per quella risorsa. Quando P1 esce dalla sua sezione critica, a P2 o P3 dovrebbe essere permesso l'accesso a R. Assumiamo che il SO garantisca l'accesso a P3 e che P1  richieda nuovamente la risorsa prima che P3 termini la sua sezione critica. Se il SO dovesse garantire l'accesso a P1 dopo che P3 abbia finito, e successivamente dovesse garantire alternativamente a P1 e P3 l'accesso alla risorsa, allora P2 potrebbe vedersi indefinitamente negato l'accesso alla risorsa, sebbene non vi sia una situazione di deadlock&amp;quot; (Stallings W.(2015). ''Operating System: internals and design principles''. (8th ed). Pearson)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Problema: posso creare sezioni critiche, tramite la programmazione, senza aiuti hardware?'''&lt;br /&gt;
&lt;br /&gt;
ESEMPIO&lt;br /&gt;
&lt;br /&gt;
  process p&lt;br /&gt;
    /*enter_cs*/&lt;br /&gt;
    A = A + 1&lt;br /&gt;
    /*exit_cs*/&lt;br /&gt;
&lt;br /&gt;
  process q&lt;br /&gt;
    /*enter_cs*/&lt;br /&gt;
    A = A - 1&lt;br /&gt;
    /*exit_cs*/&lt;br /&gt;
&lt;br /&gt;
'''Le 4 proprietà della critical section(CS)'''&lt;br /&gt;
&lt;br /&gt;
SAFETY(1): una operazione all'interno della CS &lt;br /&gt;
&lt;br /&gt;
ESEMPIO&lt;br /&gt;
  process p&lt;br /&gt;
  while 1:&lt;br /&gt;
    /*enter_cs*/&lt;br /&gt;
    A = A + 1&lt;br /&gt;
    /*exit_cs*/&lt;br /&gt;
    ...  &amp;lt;--- process q deve poter &amp;quot;entrare&amp;quot; qui&lt;br /&gt;
&lt;br /&gt;
LIVENESS(1): no deadlock&lt;br /&gt;
&lt;br /&gt;
LIVENESS(2): no starvation&lt;br /&gt;
&lt;br /&gt;
LIVENESS(3): no useless wait&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Dekker(1st attempt) //KEY: turno''&lt;br /&gt;
&lt;br /&gt;
  turn = p //var globale condivisa&lt;br /&gt;
&lt;br /&gt;
  process p{&lt;br /&gt;
    while (turn != p)&lt;br /&gt;
      ;&lt;br /&gt;
    /*A = A + 1*/&lt;br /&gt;
    turn = q&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  process q{&lt;br /&gt;
    while(turn != q)&lt;br /&gt;
      ;&lt;br /&gt;
    /*A = A - 1*/&lt;br /&gt;
    turn = p&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Non rispetta la proprietà di liveness(3), infatti uno dei due processi può dover attendere a lungo il suo turno eseguendo ciclicamente l'empty statement all'interno&lt;br /&gt;
del corpo del while. Questa situazione può verificarsi se l'altro processo fallisce dentro o fuori la sua CS (in questo caso l'attesa è infinita). Inoltre visto che i processi si alternano strettamente nell'utilizzo della loro critical section, il ritmo di esecuzione è dettato dal processo più lento dei due.&lt;br /&gt;
&lt;br /&gt;
''Dekker(2nd attempt) //KEY: flag, ma senza turno''&lt;br /&gt;
&lt;br /&gt;
  inp = false&lt;br /&gt;
&lt;br /&gt;
  inq = false&lt;br /&gt;
&lt;br /&gt;
  process p{&lt;br /&gt;
    while 1:&lt;br /&gt;
      while(inq)&lt;br /&gt;
         ;&lt;br /&gt;
      inp = true&lt;br /&gt;
      /*A = A + 1*/&lt;br /&gt;
      inp = false&lt;br /&gt;
      ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  process q{&lt;br /&gt;
    while 1:&lt;br /&gt;
      while(inp)&lt;br /&gt;
         ;&lt;br /&gt;
      inq = true&lt;br /&gt;
      /*A = A - 1*/&lt;br /&gt;
      inq = false&lt;br /&gt;
      ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In questo caso se uno dei processi fallisce fuori dalla CS (dopo aver settato il flag a false) l'altro processo non viene bloccato. Se invece un processo fallisce nella sua CS oppure dopo aver settato il flag a true allora l'altro processo resta bloccato indefinitamente. Non è garantita inoltre la mutua esclusione in quanto entrambi potrebbero essere al contempo nelle loro rispettive CS, ad esempio:&lt;br /&gt;
&lt;br /&gt;
p valuta la condizione del while e trova inq a false&lt;br /&gt;
 &lt;br /&gt;
q valuta la condizione del while e trova inp a false&lt;br /&gt;
&lt;br /&gt;
p assegna true a inp ed entra nella sua CS&lt;br /&gt;
&lt;br /&gt;
q assegna true a inq ed entra nella sua CS &lt;br /&gt;
&lt;br /&gt;
....entrambi sono nella rispettiva CS --&amp;gt; no liveness(1)&lt;br /&gt;
&lt;br /&gt;
''Dekker(3nd attempt) //KEY: come 2nd ma con due istruzioni scambiate di ordine''&lt;br /&gt;
&lt;br /&gt;
  process p{&lt;br /&gt;
    while 1:&lt;br /&gt;
      inp = true&lt;br /&gt;
      while(inq)&lt;br /&gt;
         ;    &lt;br /&gt;
      /*A = A + 1*/&lt;br /&gt;
      inp = false&lt;br /&gt;
      ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  process q{&lt;br /&gt;
    while 1:&lt;br /&gt;
      inq = true&lt;br /&gt;
      while(inp)&lt;br /&gt;
         ;&lt;br /&gt;
      /*A = A - 1*/&lt;br /&gt;
      inq = false&lt;br /&gt;
      ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Scambiando l'ordine di due istruzioni vi è la mutua esclusione, tuttavia se entrambi i processi assegnano true al loro rispettivo flag, prima di valutare la condizione&lt;br /&gt;
del while, entrambi resteranno all'interno del while in attesa che l'altro processo &amp;quot;esca dalla CS&amp;quot; (non vi è mai entrato) --&amp;gt; deadlock&lt;br /&gt;
&lt;br /&gt;
''Dekker(4nd attempt) //KEY: flag + delay''&lt;br /&gt;
&lt;br /&gt;
  process p{&lt;br /&gt;
    while 1:&lt;br /&gt;
      inp = true&lt;br /&gt;
      while(inq){&lt;br /&gt;
        inp = false; &amp;lt; delay &amp;gt;; inp = true;&lt;br /&gt;
      }          &lt;br /&gt;
      /*A = A + 1*/&lt;br /&gt;
      inp = false&lt;br /&gt;
      ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  process q{&lt;br /&gt;
    while 1:&lt;br /&gt;
      inq = true&lt;br /&gt;
      while(inp){&lt;br /&gt;
        inq = false; &amp;lt; delay &amp;gt;; inq = true;&lt;br /&gt;
      }&lt;br /&gt;
      /*A = A - 1*/&lt;br /&gt;
      inq = false&lt;br /&gt;
      ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In questo caso il settare il proprio flag a true è indice del desiderio di entrare nella propria CS. Vi è ancora mutua esclusione. Se però dovesse verificarsi una sequenza del tipo:&lt;br /&gt;
&lt;br /&gt;
p imposta flag inp a true&lt;br /&gt;
&lt;br /&gt;
q imposta falg inq a true&lt;br /&gt;
&lt;br /&gt;
p valuta la condizione del while &lt;br /&gt;
&lt;br /&gt;
q valuta la condizione del while&lt;br /&gt;
&lt;br /&gt;
p imposta flag inp a false&lt;br /&gt;
&lt;br /&gt;
q imposta flag inq a false&lt;br /&gt;
&lt;br /&gt;
p imposta flag inp a true&lt;br /&gt;
&lt;br /&gt;
q imposta flag inq a true&lt;br /&gt;
&lt;br /&gt;
allora c'è la possibilità che si ripeta all'infinito (entrambi i processi potrebbero rimanere nel while). Essendo però una condizione che dipende dalla velocità relativa dei due processi, esiseranno delle situazioni nelle quali uno dei due processi potrà entrare, così come vi saranno delle situazioni nelle quali nessun processo avrà accesso alla propria CS --&amp;gt; livelock&lt;br /&gt;
&lt;br /&gt;
'''Dekker...finally //KEY: flag + turni (&amp;quot;cambio delay con turno)'''&lt;br /&gt;
&lt;br /&gt;
  needp = false &lt;br /&gt;
&lt;br /&gt;
  needq = false&lt;br /&gt;
&lt;br /&gt;
  turn = p&lt;br /&gt;
&lt;br /&gt;
  process p{&lt;br /&gt;
    while 1:&lt;br /&gt;
      needp = true&lt;br /&gt;
      while (needq) { &lt;br /&gt;
        needp = false &lt;br /&gt;
        while(turn == q)&lt;br /&gt;
         ; &lt;br /&gt;
        needp = true&lt;br /&gt;
      }&lt;br /&gt;
     /*A = A + 1*/&lt;br /&gt;
     needp = false&lt;br /&gt;
     turn = q&lt;br /&gt;
     ...&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
  process q{&lt;br /&gt;
   while 1:&lt;br /&gt;
     needq = true&lt;br /&gt;
     while(needp) {&lt;br /&gt;
       needq = false; &lt;br /&gt;
       while(turn == p)&lt;br /&gt;
        ; &lt;br /&gt;
       needq = true&lt;br /&gt;
    }&lt;br /&gt;
    /*A = A - 1*/&lt;br /&gt;
    needq = false&lt;br /&gt;
    turn = p&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In questo caso il processo che vuole entrare nella sua CS ne manifesta il bisogno settando need a true. Se entrambi i processi entrano nel corpo del while il tie-break è espletato dal while la cui condizione è un turn che sarà uguale soltanto ad uno dei due processi (in questo caso turn è inizializzata a p), e dunque non si verificherànno situazioni di deadlock o livelock.&lt;br /&gt;
*'''Proprietà della sezione critica'''&lt;br /&gt;
Mutua esclusione &amp;lt;br&amp;gt;&lt;br /&gt;
Assenza di deadlock &amp;lt;br&amp;gt;&lt;br /&gt;
Assenza di starvation &amp;lt;br&amp;gt;&lt;br /&gt;
Assenza di delay non necessari&lt;br /&gt;
&lt;br /&gt;
*'''La test&amp;amp;set'''&lt;br /&gt;
E' un istruzione atomica per impostare il valore di una variabile a 1 (lock) ma ritornare il vecchio valore. Questa istruzione può essere utilizzata per gestire sezioni critiche. &amp;lt;br&amp;gt; &lt;br /&gt;
Il valore ritornato dalla test and set viene comparato a 1, se è uguale si aspetta altrimenti si procede.&amp;lt;br&amp;gt;&lt;br /&gt;
In questo modo il primo processo che cercherà di entrare nella sezione critica ci riuscirà e imposterà il lock a 1, gli altri processi si troveranno il lock a 1 e dovranno aspettare. Quando il primo processo avrà finito ripristinerà il vecchio valore di lock e permetterà a uno degli altri processi di eseguire la test and set e di entrare nella sezione critica.&lt;br /&gt;
----&lt;br /&gt;
Altri argomenti trattati che non sono stati qui riportati in forma di appunti:&lt;br /&gt;
Gestione algoritmica della critical section.&lt;br /&gt;
Algoritmo di Dekker.&lt;br /&gt;
Gestione architetturale della critical section.&lt;br /&gt;
&lt;br /&gt;
[[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 10:35, 17 October 2016 (CEST)&lt;br /&gt;
&lt;br /&gt;
== Lezione del 14 ottobre 2016 ==&lt;br /&gt;
Audio Lezione: [https://drive.google.com/open?id=0B8G3NNuJ7MA7UlJadTRVbzhWRFE 14\10\16]&lt;br /&gt;
* Sistemi Operativi per elaboratori multiprocessore SMP/non SMP&lt;br /&gt;
* Macchine di von Neumann (e di Harvard).&lt;br /&gt;
* Il bus.&lt;br /&gt;
* Interrupt e Trap&lt;br /&gt;
* Gestione degli interrupt&lt;br /&gt;
* Mascheramento degli interrupt&lt;br /&gt;
* Interrupt Nidificati&lt;br /&gt;
* DMA&lt;br /&gt;
* Device a carattere e device a blocchi&lt;br /&gt;
* System call come trap.&lt;br /&gt;
* La gerarchia delle memorie&lt;br /&gt;
* cache&lt;br /&gt;
&lt;br /&gt;
== Lezione del 18 ottobre 2016 ==&lt;br /&gt;
Audio Lezione: [https://drive.google.com/open?id=0B8G3NNuJ7MA7eVlFdWR0cUkxd2M 18/10/16]&lt;br /&gt;
&lt;br /&gt;
Argomenti trattati:&lt;br /&gt;
*'''Implementazione in C della Test&amp;amp;Set, degli Algoritmi di Dekker e Peterson.'''&lt;br /&gt;
Codice sorgente disponibile [[Dekker,_Peterson_e_Test%26Set_in_C|qui]].&lt;br /&gt;
*'''Definizione di Semaforo.'''&lt;br /&gt;
Un semaforo è una variabile intera che può essere modificata solo da 2 operazioni atomiche: P(Proberen) e V(Verhogen).&amp;lt;br&amp;gt;&lt;br /&gt;
P aspetta che il valore sia minore o uguale a zero. Quando lo diventa decrementa il valore.&amp;lt;br&amp;gt;&lt;br /&gt;
S incrementa il valore. &amp;lt;br&amp;gt;&lt;br /&gt;
*'''Invariante del Semaforo.'''&lt;br /&gt;
Il numero di Proberen deve essere minore o uguale al numero di Verhogen più il valore d'inizializzazione del semaforo. &amp;lt;br&amp;gt;&lt;br /&gt;
In questo modo si può gestire per esempio l'allocazione di risorse. Il semaforo viene inizializzato al numero di risorse e ogni volta che un processo ha bisogno della risorsa esegue un proberen, quindi decrementa il valore se la risorsa è disponibile. Quando un processo rilascia la risorsa esegue un verhogen.&lt;br /&gt;
*'''Esempio: implementazione di sezione critica e di semplice sincronizzazione con semafori.'''&lt;br /&gt;
&lt;br /&gt;
== Lezione del 21 ottobre 2016 ==&lt;br /&gt;
*'''Make'''&lt;br /&gt;
Make è un sistema per la compilazione intelligente dei programmi. &amp;lt;br&amp;gt;&lt;br /&gt;
Permette invece di compilare tutto il programma tutte le volte di compilarne solo una parte, solo quella che è stata modificata.&lt;br /&gt;
Per capirne il funzionamento è utile un parallelismo con le ricette di cucina. Si specificano dei target che sono gli obbiettivi della ricetta.Per fare una ricetta servono degli ingredienti e magari quegli ingredienti hanno a loro volta delle ricette. Make allo stesso modo definisce dei target che possono avere come dipendenze altri target.  &amp;lt;br&amp;gt;&lt;br /&gt;
Nel installazione del programma il programma ./configure cerca nel sistema se le librerie da cui è dipendente il programma sono presenti. &amp;lt;br&amp;gt;&lt;br /&gt;
*'''Git'''&lt;br /&gt;
Git è un sistema di versioning, serve a tener traccia delle modifiche apportante a un progetto nel tempo. E' utile per collaborare a un progetto condiviso. &amp;lt;br&amp;gt;&lt;br /&gt;
Possibilità per ogni membro del repository di lavorare in maniera indipendente e poi aggiornare il progetto comune. &amp;lt;br&amp;gt;&lt;br /&gt;
Ce ne sono stati tanti di sistemi di versioning tipo SVN, Mercurial ma attualmente Git è il più utilizzato.&amp;lt;br&amp;gt;&lt;br /&gt;
Git non serve solo per scrivere programmi ma per esempio anche libri, in modo collaborativo.&amp;lt;br&amp;gt;&lt;br /&gt;
Git è completamente distribuito, non esiste in un repository una copia principale e delle copie secondarie ma ogni copia può essere principale. &amp;lt;br&amp;gt;&lt;br /&gt;
Per iniziare un repository git si utilizza il comando ''git init''.&amp;lt;br&amp;gt;&lt;br /&gt;
Per aggiungere un file al repository si utilizza il comando ''git add''.&amp;lt;br&amp;gt;&lt;br /&gt;
Ma ad ogni singola modifica git non aggiorna automaticamente il repository ma è l'utente che tramite il comando git commit indica che una certa versione del progetto deve essere salvata.&amp;lt;br&amp;gt;&lt;br /&gt;
Il comando ''git log'' serve a visualizzare la storia del repository.&amp;lt;br&amp;gt;&lt;br /&gt;
Il comando ''git diff'' serve a vedere la differenza tra ciò che è nel repository e le modifiche di cui non si è ancora fatto il commit.&amp;lt;br&amp;gt;&lt;br /&gt;
Dopo aver dato il comando add si dice che i file sono 'on stage' prima del commit.&amp;lt;br&amp;gt;&lt;br /&gt;
Il comando ''git checkout'' serve a spostarsi nella storia del repository.&amp;lt;br&amp;gt;&lt;br /&gt;
Il comando ''git clone'' serve a copiare un repository remoto.&amp;lt;br&amp;gt;&lt;br /&gt;
Il comando ''git push'' serve a inviare le modifiche al repository remoto mentre il comando ''git pull'' serve a ricevere le modifiche più aggiornate dal repository remoto.&amp;lt;br&amp;gt;&lt;br /&gt;
Il ''merge'' è il processo in cui uno degli utenti che crea un conflitto deve &amp;quot;a mano&amp;quot; scegliere quali parti dei due commit in conflitto vuole tenere.&amp;lt;br&amp;gt;&lt;br /&gt;
Git è utile per trovare i bug che non erano presenti in versioni precedenti del progetto. Si può tornare alla versione precedente stabile e avanzare nella storia fino a trovare una versione instabile. A questo punto è probabile che l'origine del bug sia nelle modifiche che hanno creato questa versione instabile.&amp;lt;br&amp;gt;&lt;br /&gt;
Github è una piattaforma di repository dove possono essere mantenuti dei progetti gratuitamente se sono con licenza libera.&amp;lt;br&amp;gt;&lt;br /&gt;
In una directory in cui è presente un repository git e sempre la presente la cartella ''.git'' dove il sistema di versioning mantiene il database della storia. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lezione del 25 ottobre 2016 ==&lt;br /&gt;
Audio Lezione: [https://drive.google.com/open?id=0B8G3NNuJ7MA7WjZJMm11cjhuQXc 25/10/16]&lt;br /&gt;
&lt;br /&gt;
Argomenti trattati:&lt;br /&gt;
* '''Semafori fair'''&lt;br /&gt;
Un semaforo è &amp;quot;fair&amp;quot; quando tende a non sbloccare dallo stato di attesa sempre lo stesso processo ma cerca di distribuire la possibilità di esecuzione a più processi.&lt;br /&gt;
* '''Problemi classici della concorrenza risolti con semafori'''&lt;br /&gt;
** '''Produttore/consumatore'''&lt;br /&gt;
::Due processi: produttore e consumatore. Il produttore genera continuamente dei valori e il consumatore continuamente li legge. Il consumatore deve leggere i valori nello stesso ordine in cui il produttore li genera. &amp;lt;br&amp;gt;&lt;br /&gt;
::Un meccanismo di sincronizzazione e richiesto perchè sorgono immediatamente due problemi:&lt;br /&gt;
::* Produttore troppo veloce: il consumatore non fa in tempo a consumare il valore e c'è la possibilità che i valori non ancora letti vengano sovrascritti.&lt;br /&gt;
::* Consumatore troppo veloce: possibilità che il consumatore legga un valore che ha già letto perchè il produttore non ha fatto in tempo a produrne uno nuovo.&lt;br /&gt;
::Per risolvere il problema utilizziamo due semafori: empty e full. &amp;lt;br&amp;gt;&lt;br /&gt;
::Il produttore aspetta che il segnale del semaforo empty (Proberen), scrive il valore e dà segnale sul semaforo full (Verhogen).&amp;lt;br&amp;gt;&lt;br /&gt;
::Il consumatore aspetta  il segnale del semaforo full (Proberen), legge il valore e dà segnale sul semaforo empty (Verhogen).&amp;lt;br&amp;gt;&lt;br /&gt;
:* '''Buffer-limitato'''&lt;br /&gt;
::Simile al problema produttore/consumatore ma qui lo scambio avviene tramite un buffer limitato. &amp;lt;br&amp;gt;&lt;br /&gt;
::In questo modo nel caso di rallentamento temporaneo del consumer il producer non deve aspettare ma può produrre più unità e viceversa nel caso di rallentamento del producer, se il buffer ha più di un elemento, il consumer può utilizzare il tempo in attesa del producer per consumare più dati. &amp;lt;br&amp;gt;&lt;br /&gt;
::Nel caso il buffer sia completamente pieno/vuoto rimangono comunque i problemi di sincronizzazione tipici del problema: il producer non deve sovrascrivere dei valori che il consumer non ha ancora letto e il consumer non deve leggere più volte lo stesso valore se il producer non è stato abbastanza veloce da cambiarlo. &amp;lt;br&amp;gt;&lt;br /&gt;
::Come soluzione al problema si può utilizzare un coda circolare. Il produttore esegue una enqueue e il consumatore una dequeue. &amp;lt;br&amp;gt;&lt;br /&gt;
::Il problema può essere generalizzato facilmente a produttori/consumatori multipli ma in questo caso dobbiamo creare un semaforo per rendere atomiche la enqueue e la dequeue altrimenti incorriamo in una race condition nel accesso alla coda.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 28 ottobre 2016 ==&lt;br /&gt;
&lt;br /&gt;
'''Implementazione dei semafori''' &amp;lt;br&amp;gt;&lt;br /&gt;
Un processo che esegue una Proberen in teoria dovrebbe aspettare continuando a controllare se può entrare nella sezione critica. Questo meccanismo si chiama &amp;quot;busy waiting&amp;quot; e consuma molte istruzioni della CPU. &amp;lt;br&amp;gt;&lt;br /&gt;
Il busy waiting è diverso dal polling in quando il polling prevede di eseguire altre azioni mentre si aspetta tra un tentativo e l'altro. &amp;lt;br&amp;gt;&lt;br /&gt;
Di solito si utilizza il busy waiting solo quando il tempo di lock di un processo è molto breve. Questo perchè anche se esoso di risorse del processore il busy waiting non comporta un context switch, cioè salvare tutto lo stato del processo in memoria, e quindi permette di risparmiare memoria. &lt;br /&gt;
Se invece il tempo di lock è più lungo allora è preferibile utilizzare un context-switch dove il processo si blocca da solo e chiama un processo di blocking che lo mette in una coda di attesa associata al semaforo che sta aspettando. Il controllo passa poi allo scheduler della CPU che seleziona un altro processo da eseguire.&lt;br /&gt;
Le primitive suspend and wakeup indicano rispettivamente lo spostamento di un processo nella/dalla coda dei processi ready.&lt;br /&gt;
* nei kernel monoprocessore&lt;br /&gt;
: Implementazione dei semafori tramite mutex che possono essere a loro volta implementate con un sistema di mascheramento degli interrupt.&lt;br /&gt;
* nei kernel multiprocessore&lt;br /&gt;
: Utilizzo di soluzioni hardware come test and set. Sono soluzioni che utilizzano il busy waiting ma in questo caso solo per rendere atomico le operazioni del semaforo, che sono poche istruzioni.&lt;br /&gt;
Certe implementazioni dei semafori potrebbero permettere di far andare il valore in negativo. Questo serve per misurare il numero dei processi in attesa di quel semaforo. &amp;lt;br&amp;gt;&lt;br /&gt;
'''Il problema della cena dei filosofi''': &amp;lt;br&amp;gt;&lt;br /&gt;
5 filosofi passano la loro vita pensando e mangiando. Condividono un tavolo circolare con 5 sedie e ogni sedia appartiene a un solo filosofo. Alla sinistra e alla destra di ogni piatto c'è una bacchetta per un totale di 5 bacchette. La bacchetta destra che un filosofo usa per mangiare viene quindi utilizzata come bacchetta sinistra dal filosofo alla sua destra e simmetricamente è uguale per il filosofo che siede a sinistra. &amp;lt;br&amp;gt;&lt;br /&gt;
Ogni tanto un filosofo smette di pensare e cerca di prendere le rispettive bacchette, se ci riesce può mangiare e poi rilasciare le bacchette. &amp;lt;br&amp;gt;&lt;br /&gt;
Questo problema è un modello di problema ad accesso a ''insiemi'' di risorse. Mentre nel produttore/consumatore e buffer limitato gli attori in gioco avevano la necessità di avere l'accesso esclusivo a una sola risorsa in questo problema è richiesto l'accesso a insiemi di risorse (le bacchette) le cui parti possono essere desiderate da altri richiedenti.&lt;br /&gt;
* soluzione con contesa sulle posate e casi di deadlock&lt;br /&gt;
:Poniamo un semaforo per ogni bacchetta. Può succedere però, se nel implementazione i filosofi &amp;quot;prendono in mano&amp;quot; prima la bacchetta destra e poi quella sinistra, che tutti prendono in mano contemporaneamente la bacchetta destra e rimangono in un'attesa infinita della bacchetta sinistra che non gli verrà mai rilasciata perché anche il rispettivo filosofo alla propria sinistra è in attesa. &amp;lt;br&amp;gt; Questo è un caso di deadlock. &amp;lt;br&amp;gt;&lt;br /&gt;
:Una soluzione può essere quella di imporre che un filosofo sia &amp;quot;mancino&amp;quot; cioè prenda prima la bacchetta a sinistra e poi la destra. &amp;lt;br&amp;gt;&lt;br /&gt;
* soluzione con acquisizione atomica di entrambe le posate, casi di starvation&lt;br /&gt;
:Un'altra soluzione è quella di rendere atomico l'acquisizione di entrambe le posate. &amp;lt;br&amp;gt;&lt;br /&gt;
:Può succede che 4 filosofi compiano una &amp;quot;congiura&amp;quot;, cioè siano d'accordo a non lasciar risorse a un quinto filosofo. Questo è un caso di starvation e non è irreale in quando i 4 filosofi potrebbero essere processi malevoli che siano stati programmati di proposito per questo.&lt;br /&gt;
'''System Call''':&lt;br /&gt;
* POSIX.&lt;br /&gt;
:Il POSIX è uno standard (IEEE 1003) che definisce le signature delle system call. Un sistema operativo deve rispettare il POSIX per essere definito &amp;quot;Unix-like&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
:Di solito i parametri delle system call sono pensati per essere contenuti in un registro. Quasi sempre le system call ritornano un intero: 0 se la procedura è stata eseguita correttamente, -1 se c'è un errore. Quando una system call ritorna -1 la variabile &amp;quot;globale&amp;quot; errno (error number) contiene il codice di errore. La variabile errno non è realmente globale perché ne esiste una per ogni processo. &amp;lt;br&amp;gt;&lt;br /&gt;
:Unix è un sistema operativo file-system centrico quindi quando facciamo input/output anche da periferiche come stampanti e lettori CD usiamo le system call per la scrittura/lettura dei file. &amp;lt;br&amp;gt;&lt;br /&gt;
:Un catalogo delle system call principali può essere trovato [[Il ''catalogo'' delle System Call | qui]].&lt;br /&gt;
* fork/exec&lt;br /&gt;
:fork e exec sono system call importanti e servono per creare un nuovo processo: &amp;lt;br&amp;gt;&lt;br /&gt;
:fork: genera un processo clone di quello che ha chiamato fork. &amp;lt;br&amp;gt;&lt;br /&gt;
:exec: &amp;quot;cambia&amp;quot; il codice eseguito dal processo corrente. &amp;lt;br&amp;gt;&lt;br /&gt;
:Quindi per &amp;quot;aprire un nuovo programma&amp;quot; si utilizza una fork seguita da un exec.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 4 novembre 2016 ==&lt;br /&gt;
Audio Lezione: [https://drive.google.com/open?id=0B8G3NNuJ7MA7blhMT0tSNDRGSUU 4/11/16]&lt;br /&gt;
&lt;br /&gt;
* '''Problema della Cena dei filosofi'''. Soluzione con acquisizione atomica delle bacchette (con starvation)&lt;br /&gt;
* '''Tecnica del passaggio del testimone''' (Passing the Baton).&lt;br /&gt;
&lt;br /&gt;
: Non uscire dalla mutua esclusione se c'è qualcun altro che vuole entrarci e semplicemente fare in modo che lui l'acquisisca. La mutua esclusione termina solo se non c'è nessuno che più la richiede.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO IMPLEMENAZIONE BATON&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
  void baton(int i) {&lt;br /&gt;
    int j;&lt;br /&gt;
    for (j=1; j&amp;lt;5; j++) {&lt;br /&gt;
       if(philo_status[(i+j)%5] == 'W' &amp;amp;&amp;amp; philo_status[(i+j+1)%5] != 'E' &amp;amp;&amp;amp; philo_status[(i+j+4)%5] != 'E') {&lt;br /&gt;
          semaphore_V(philowait[(i+j)%5]);&lt;br /&gt;
          return;&lt;br /&gt;
       }&lt;br /&gt;
    }&lt;br /&gt;
    semaphore_V(mutex);&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* '''Cena dei filosofi, acquisizione atomica delle bacchette con passaggio del testimone'''.&lt;br /&gt;
* '''Implementazione della congiura dei filosofi'''&lt;br /&gt;
&lt;br /&gt;
* '''Le system call di gestione file (open, read, write, close, lseek)'''&lt;br /&gt;
Quando lavoriamo con dei file dobbiamo sempre specificare se vogliamo solo leggerli oppure anche scrivere, magari in append mode. Ci sono quindi varie modalità con cui possiamo interagire con i file. &amp;lt;br&amp;gt;&lt;br /&gt;
Un utente potrebbe essere autorizzato a gestire il file in certe modalità oppure no. &amp;lt;br&amp;gt;&lt;br /&gt;
Il descrittore di un file è un intero. &amp;lt;br&amp;gt;&lt;br /&gt;
Le system call, a differenza delle chiamate di funzioni di libreria, non sono bufferizzate. &lt;br /&gt;
&lt;br /&gt;
ESEMPIO&lt;br /&gt;
&lt;br /&gt;
Se si decommenta printf stampa 2 volte hello world perchè la stringa resta nel buffer. &lt;br /&gt;
&lt;br /&gt;
Se si decommenta write stampa 1 volta hello world.&lt;br /&gt;
&lt;br /&gt;
Per vedere le chiamate di sistema effettuate in fase di esecuzione, compilare il codice sorgente e poi utilizzare strace -f &amp;quot;eseguibile&amp;quot;.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
   #include&amp;lt;stdio.h&amp;gt;&lt;br /&gt;
   #include&amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  int main(int argc, char* argv[]){&lt;br /&gt;
    //printf(&amp;quot;hello world&amp;quot;); //if you ends the strings with \n is like to &amp;quot;fflushing&amp;quot; the buffer &lt;br /&gt;
    //write(1, &amp;quot;hello world&amp;quot;, 11); // 1 and 11 are file descriptors&lt;br /&gt;
    switch(fork()) {&lt;br /&gt;
      case 0: //child&lt;br /&gt;
         break;&lt;br /&gt;
      default: //parent&lt;br /&gt;
         wait(NULL);&lt;br /&gt;
         break;&lt;br /&gt;
      case -1: //error&lt;br /&gt;
         break;&lt;br /&gt;
    }&lt;br /&gt;
    printf(&amp;quot;\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Copia il file passato come primo argomento (argv[1]) nel file passato come secondo argomento (argv[2]), se quest'ultimo non esiste, allora lo crea.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
  #include&amp;lt;stdio.h&amp;gt;&lt;br /&gt;
  #define BUFSIZE 2048&lt;br /&gt;
  char buffer[BUFSIZE];&lt;br /&gt;
  int main(int argc, char *argv[]){&lt;br /&gt;
     int fdin = open(argv[1], O_RDONLY);&lt;br /&gt;
     int fdout = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666); //0666 is the umask used to modify the permissions of new files&lt;br /&gt;
&lt;br /&gt;
     int n;&lt;br /&gt;
     while((n = read(fdin, buffer, BUFSIZE)) &amp;gt; 0)&lt;br /&gt;
        write(fdout, buffer, n);&lt;br /&gt;
    &lt;br /&gt;
     close(fdin);&lt;br /&gt;
     close(fdout);&lt;br /&gt;
     return 0;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''umask''' &amp;lt;br&amp;gt;&lt;br /&gt;
Con la open possiamo usare dei permessi al massimo per quelli garantiti dal umask. &amp;lt;br&amp;gt;&lt;br /&gt;
Un numero composto da quattro cifre ottali. Controlla la maschera di creazione dei file. I bit di permesso contenuti nella maschera NON vengono settati nel nuovo file creato. Corrisponde all'opereazione:&lt;br /&gt;
&lt;br /&gt;
  R:(D &amp;amp;(~M)) &lt;br /&gt;
&lt;br /&gt;
Può essere vista come una sottrazione, i bit accesi nella umask vengono spenti nel file creato. &lt;br /&gt;
&lt;br /&gt;
e.g. se i permessi di default corrispondono a 0666(D) e la maschera corrisponde a 0022(M), R è uguale a 0666-0022 = 0644. Infatti&lt;br /&gt;
&lt;br /&gt;
   0022 =        |000|000|010|010|&lt;br /&gt;
  ~0022 =        |111|111|101|101|(7755)&lt;br /&gt;
   0666 =        |000|110|110|110|&lt;br /&gt;
  ~0022 &amp;amp; 0666 = |000|110|100|100|(0644)&lt;br /&gt;
&lt;br /&gt;
Tabella significato cifre umask&lt;br /&gt;
&lt;br /&gt;
  OTTALE  BINARIO  SIGNIFICATO&lt;br /&gt;
   0       000      Nessun permesso&lt;br /&gt;
   1       001      Solo esecuzione&lt;br /&gt;
   2       010      Solo scrittura&lt;br /&gt;
   3       011      Scrittura ed esecuzione&lt;br /&gt;
   4       100      Solo lettura&lt;br /&gt;
   5       101      Lettura ed esecuzione&lt;br /&gt;
   6       110      Lettura e scrittura&lt;br /&gt;
   7       111      Lettura, scrittura ed esecuzione&lt;br /&gt;
&lt;br /&gt;
I permessi sono rappresentati in simboli come segue:&lt;br /&gt;
&lt;br /&gt;
  r: read&lt;br /&gt;
  w: write&lt;br /&gt;
  x: execute&lt;br /&gt;
&lt;br /&gt;
'''umask - S''' da shell per visualizzare la umask attuale con rappresentazione simbolica&lt;br /&gt;
&lt;br /&gt;
'''ls -l &amp;quot;nomefile&amp;quot;''' da shell per visualizzare i permessi del file in notazione simbolica: '-' seguito da 3 triple che indicano i permessi di accesso per '''ugo''',  ovvero, rispettivamente, per user,  per group, per other.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO&lt;br /&gt;
  -rw-r--r--&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''File aperti durante fork/exec'''https://www.kernel.org/&lt;br /&gt;
&lt;br /&gt;
I file aperti vengono ereditati dai processi figli.&lt;br /&gt;
Processo padre e processo figlio condividono lo stesso offset nei file comuni. In questo modo si evita di sovrascriversi a vicenda. Ogni volta che il padre o il figlio scrivono sul file aggiornano l'offset comune.In questo modo se l'altro processo vuole anche lui scrivere in quel file partirà dal offset aggiornato e non sovrascriverà i dati scritti dal altro processo. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''La system call dup e la ridirezione'''&lt;br /&gt;
E' possibile fare in modo che più file descriptor siano associati a un unico file (per la precisione a un unica file table, che serve per gestire un file). &amp;lt;br&amp;gt;&lt;br /&gt;
La system call dup permette di fare ciò. Ritorna un nuovo file descriptor che punta a una file table già esistente.&amp;lt;br&amp;gt;&lt;br /&gt;
Possiamo quindi &amp;quot;ridirezionare&amp;quot; l'output o l'input di un processo figlio modificando i file descriptor prima di creare il processo figlio. Per esempio possiamo modificare un file descriptor che puntava a un normale file in modo che punti a un file di standard output e quindi anche se al processo figlio sembrerà di scrivere in un normale file starà scrivendo nel terminale.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
  #include&amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
  #include&amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
  #include&amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
  #include&amp;lt;stdio.h&amp;gt;&lt;br /&gt;
  #include&amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  int main(int argc, char * argv[]){&lt;br /&gt;
    int fdout;&lt;br /&gt;
    if(argc &amp;gt; 1);&lt;br /&gt;
    fdout = open (argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0666);&lt;br /&gt;
    switch(fork()) {&lt;br /&gt;
        case 0: //child&lt;br /&gt;
          dup2(fdout, 1); // duplica descrittore file, posso ridirezionare l'output. E' come fare '&amp;gt;' dalla shell &lt;br /&gt;
          execlp(”ls”, “-l”, 0); &lt;br /&gt;
          close(fdout);&lt;br /&gt;
          break;&lt;br /&gt;
        default: // parent&lt;br /&gt;
          wait(NULL);&lt;br /&gt;
          break;&lt;br /&gt;
        case -1: // error&lt;br /&gt;
          break;&lt;br /&gt;
    }&lt;br /&gt;
    printf(“\n”);&lt;br /&gt;
    return 0;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lezione del 8 novembre 2016 ==&lt;br /&gt;
'''Codice per la congiura dei filosofi'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;quot;semaphore.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
semaphore philowait[5];&lt;br /&gt;
semaphore mutex;&lt;br /&gt;
semaphore cospsem;&lt;br /&gt;
char philo_status[]=&amp;quot;TTTTT&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
void baton(int i){&lt;br /&gt;
    int j = 0;&lt;br /&gt;
    //iterate through remaining philosophers&lt;br /&gt;
    for(j = 1; j &amp;lt; 5;j++){&lt;br /&gt;
        //if there's any waiting philosopher, and no other philosophers(on his left and right side) are eating&lt;br /&gt;
        if(philo_status[(i+j)%5]=='W' &amp;amp;&amp;amp; philo_status[(i+j+1)%5]!= 'E' &amp;amp;&amp;amp; philo_status[(i+j+4)%5]!='E'){&lt;br /&gt;
            semaphore_V(philowait[(i+j)%5]);&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    semaphore_V(mutex);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ok2eat(int i){&lt;br /&gt;
    semaphore_P(mutex);&lt;br /&gt;
    philo_status[i]='W';&lt;br /&gt;
    if(philo_status[(i+1)%5] == 'E' || philo_status[(i+4)%5] == 'E'){&lt;br /&gt;
        semaphore_V(mutex);&lt;br /&gt;
        semaphore_P(philowait[i]); //wait for baton&lt;br /&gt;
    }&lt;br /&gt;
    philo_status[i] = 'E';&lt;br /&gt;
    printf(&amp;quot;philo eating:   %d |%s|\n&amp;quot;,i,philo_status);&lt;br /&gt;
    baton(i);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ok2think(int i){&lt;br /&gt;
    semaphore_P(mutex);&lt;br /&gt;
    philo_status[i] = 'T';&lt;br /&gt;
    printf(&amp;quot;philo thinking: %d |%s|\n&amp;quot;,i,philo_status);&lt;br /&gt;
    baton(i);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void conspiracy_in(){&lt;br /&gt;
    static int first_time = 1; //this variable's value is preserved between calls to this function&lt;br /&gt;
    semaphore_P(mutex);&lt;br /&gt;
    if(first_time){&lt;br /&gt;
        //first conspirator entering here will act like nothing happened&lt;br /&gt;
        first_time = 0;&lt;br /&gt;
    }&lt;br /&gt;
    else{&lt;br /&gt;
        //conspire&lt;br /&gt;
        semaphore_V(cospsem);&lt;br /&gt;
    }&lt;br /&gt;
    semaphore_V(mutex);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void conspiracy_out(){&lt;br /&gt;
    semaphore_P(cospsem);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void *philo(void *arg) {&lt;br /&gt;
    int i = (uintptr_t)arg;&lt;br /&gt;
    while (1) {&lt;br /&gt;
        //thinking/still thinking&lt;br /&gt;
        usleep(random() % 200000);&lt;br /&gt;
        ok2eat(i);&lt;br /&gt;
        //eating&lt;br /&gt;
&lt;br /&gt;
        //conspirators need to be synchronized to conspire&lt;br /&gt;
        if(i==1 || i==3) conspiracy_in();&lt;br /&gt;
        usleep(random() % 200000);&lt;br /&gt;
        if(i==1 || i==3) conspiracy_out();&lt;br /&gt;
        ok2think(i);&lt;br /&gt;
        //thinking again&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[]) {&lt;br /&gt;
    int i;&lt;br /&gt;
    pthread_t philo_t[5];&lt;br /&gt;
    srandom(time(NULL));&lt;br /&gt;
    mutex = semaphore_create(1);&lt;br /&gt;
    cospsem = semaphore_create(0);&lt;br /&gt;
    for (i=0; i&amp;lt;5; i++)&lt;br /&gt;
        philowait[i]=semaphore_create(0);&lt;br /&gt;
    for (i=0; i&amp;lt;5; i++)&lt;br /&gt;
        pthread_create(&amp;amp;philo_t[i], NULL, philo, (void *)(uintptr_t) i);&lt;br /&gt;
    for (i=0; i&amp;lt;5; i++)&lt;br /&gt;
        pthread_join(philo_t[i], NULL);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''Problema dei lettori e scrittori'''&lt;br /&gt;
:Spesso quando gestiamo la concorrenza all’accesso a strutture dati separiamo i ruoli di solo lettori con anche quello di scrittori (che per fare ciò possono dover anche leggere).&lt;br /&gt;
:Mentre che per i lettori non c’è un limite a quanti contemporaneamente possono avere accesso alle informazioni, gli scrittori devono avere accesso esclusivo alla struttura dati.&lt;br /&gt;
:Questo perché incorriamo in una race-condition solo quando modifichiamo la struttura dati.&lt;br /&gt;
:Dare precedenza di accesso a una categoria o all’altra porta starvation nella categoria con meno priorità.&lt;br /&gt;
:Una soluzione che abbiamo dato è quella di dare esclusività ai ruoli a turno:&lt;br /&gt;
:dopo che uno scrittore ha finito di scrivere garantisce l’accesso prima a tutti i lettori in attesa e se non sono presenti agli scrittori in attesa. Per i lettori è il contrario: dopo che l’ultimo lettore ha finito di leggere garantisce l’accesso a uno dei possibili scrittori in attesa e se non ce ne sono ai lettori.&lt;br /&gt;
:La soluzione in C al problema lettori e scrittori può essere trovata [[Zibaldone|qui]].&lt;br /&gt;
* '''Semafori Binari(Introduzione)'''&lt;br /&gt;
: Un semaforo binario è un semaforo ordinario che rispetta la seguente invariante: '''nP&amp;lt;=nV+Init&amp;lt;=nP+1''', ovvero è un semaforo che può assumere solo valori 0 o 1.&lt;br /&gt;
: Si può dimostrare che i semafori ordinari sono espressivi tanto quanto quelli binari(e viceversa) fornendo una implementazione di semafori binari tramite i semafori ordinari e viceversa una implementazione di semafori ordinari tramite semafori binari. (l'invariante dei semafori binari si pu&amp;amp;ograve; anche scrivere '''0 &amp;lt;= nV + Init - nP &amp;lt;= 1''' dove '''nV + Init - nP''' rappresenta proprio il valore del semaforo).&lt;br /&gt;
&lt;br /&gt;
== Lezione del 11 novembre 2016 ==&lt;br /&gt;
* '''Valore di ritorno di un processo'''&lt;br /&gt;
:Un processo ritorna un valore che è un numero intero. &amp;lt;br&amp;gt;&lt;br /&gt;
:Questo può essere deciso come valore di ritorno del main, oppure con la funzione ''exit''.&lt;br /&gt;
:La system call wait aspetta che lo stato di un processo figlio cambi e quando cambia scrive il nuovo in una variabile. &amp;lt;br&amp;gt;&lt;br /&gt;
:Quando un programma termina il suo valore di ritorno viene salvato nel descrittore del processo. Se il padre non esegue la wait il descrittore del processo figlio non verrà mai eliminato e il processo figlio rimarrà uno &amp;quot;zombie&amp;quot;.&lt;br /&gt;
:Un esempio del uso si queste system call si può trovare [[2016-17_Programmi_C#Print_child_exit_status|qui]].&lt;br /&gt;
* '''Pipe'''&lt;br /&gt;
:La system call pipe crea un canale monodirezionale che può essere usato per la comunicazione tra processi. Dimostrazione del uso di pipe si può trovare [[2016-17_Programmi_C#Demonstration_of_the_use_of_pipe_system_call|qui]].&lt;br /&gt;
* '''Lo zoo delle exec'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang='C'&amp;gt;&lt;br /&gt;
int execl(const char *path, const char *arg, ...); &lt;br /&gt;
int execlp(const char *file, const char *arg, ...);&lt;br /&gt;
int execle(const char *path, const char *arg, ..., char * const envp[]);&lt;br /&gt;
int execv(const char *path, char *const argv[]);&lt;br /&gt;
int execvp(const char *file, char *const argv[]);&lt;br /&gt;
int execvpe(const char *file, char *const argv[],char *const envp[]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Spiegazione dei postfissi:&lt;br /&gt;
* l = elenco dei parametri passato tramite parametri a cardinalità variabile (variadic function)&lt;br /&gt;
* v = elenco dei parametri passato come array&lt;br /&gt;
* p = bisogna specificare il path completo dell'eseguibile&lt;br /&gt;
* e = bisogna specificare anche l'enviroment con cui verrà eseguito il programma. L'enviroment è una serie di variabili globali, tra cui il path. Il path è l'elenco dei percorsi dei programmi più usati.&lt;br /&gt;
Tutte queste funzioni alla fine sono solo delle interfacce per execve.&lt;br /&gt;
* '''Programmazione ad eventi'''&lt;br /&gt;
:E' un paradigma di programmazione dove il flusso del programma è determinato dal avverarsi di eventi. Il programmatore specifica il codice (Handlers) che deve essere eseguito al verificarsi di un certo evento. &amp;lt;br&amp;gt;&lt;br /&gt;
:System call che sono utili per far questo sono select,pselect,poll,ppoll,epoll.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 15 novembre 2016 ==&lt;br /&gt;
* '''Differenza tra select,poll,pselect,ppoll e epoll'''&lt;br /&gt;
:La poll a differenza della select richiede un array di strutture pollfd invece che una bitmask. Permette di specificare precisi eventi per cui stare in ascolto. &amp;lt;br&amp;gt;&lt;br /&gt;
:Le versioni con prefisso &amp;quot;p&amp;quot; implementano anche la gestione dei segnali. &amp;lt;br&amp;gt;&lt;br /&gt;
:L'epoll è linux only. E' moderna (2002) e fornisce un file dove leggere direttamente quali file descriptor sono stati modificati in modo da non dover scorrere tutta la lista.&lt;br /&gt;
* '''Dimostrazione della equivalenza di potere espressivo fra semafori e semafori binari.'''&lt;br /&gt;
:I semafori implementati per dimostrare l'equivalenza possono essere trovati a [[Esperimenti_con_semafori_e_monitor_in_C | questa pagina]].&lt;br /&gt;
* '''Definizione di Monitor.'''&lt;br /&gt;
Un monitor è un costrutto ad alto livello per la sincronizzazione pensato ispirandosi alla programmazione ad oggetti. &amp;lt;br&amp;gt;&lt;br /&gt;
E' una classe che incapsula la risorsa condivisa, la rende privata e permette di accedere a questa solo tramite metodi. Questi metodi implementano i meccanismi di sincronizzazione. &amp;lt;br&amp;gt;&lt;br /&gt;
Uno solo processo può essere al interno del monitor e avere accesso ai dati protetti da mutua esclusione. &amp;lt;br&amp;gt;&lt;br /&gt;
E' presente una coda di attesa per l'accesso al monitor. &amp;lt;br&amp;gt;&lt;br /&gt;
Definiamo inoltre delle variabili ''condition''. &amp;lt;br&amp;gt;&lt;br /&gt;
Gli unici metodi che possono essere chiamati su una condition sono wait e signal. Ogni condition ha una propria coda di attesa &amp;lt;br&amp;gt;&lt;br /&gt;
L'operazione wait sospende il chiamante e lo sposta nella coda di attesa della condition.&lt;br /&gt;
L'operazione signal risveglia un processo in attesa. Se non c'è nessun processo in attesa all'interno del monitor la signal fa entrare un eventuale processo in attesa di entrare nel monitor. Il chiamante di signal dopo aver risvegliato l'altro possibile processo deve mettersi in attesa per evitare che ci siano due processi in concorrenza al interno del monitor, ma invece di andare nella coda di attesa della condition viene messo in un ''urgent stack'' che ha più priorità rispetto alla coda esterna dei processi in attesa di entrare nel monitor. &amp;lt;br&amp;gt;&lt;br /&gt;
Ricapitolando le strutture dati per un monitor sono una coda esterna per l'accesso al monitor, una coda interna (non accessibile da fuori ma non dentro la mutua esclusione) per ogni variabile condizionale e l'urgent stack. &amp;lt;br&amp;gt;&lt;br /&gt;
* '''Implementazione dei Semafori tramite monitor.'''&lt;br /&gt;
&lt;br /&gt;
== Lezione del 18 novembre 2016 ==&lt;br /&gt;
* '''File speciali a blocchi o a caratteri'''&lt;br /&gt;
: In Unix molte cose sono rappresentate come file. Per esempio i device come i terminali e le partizioni del disco sono file. Si chiamano ''file speciali'' ma non sono tanto differenti dai file comuni. Utilizzano anche gli stessi sistemi di protezione all'accesso. Si dividono i due categori:&lt;br /&gt;
:* File speciali a blocchi: utilizzati maggiormente dai sistemi di storage tipo hard-disk e CD-ROMs. Se per esempio il blocco è di 1024 bytes allora possiamo scrivere e leggere con tale granularità, cioè non potremo mai scrivere soltato 500 bytes ma dovremo scrivere, appunto a &amp;quot;blocchi&amp;quot;.&lt;br /&gt;
:* File speciali a caratteri: utilizzati maggiormente per dispositivi di input-output tipo mouse,tastiera,terminali ecc.. permettono di lavorare con una granularità di 1 byte ma hanno un accesso sequenziale invece che diretto come i file a blocchi.&lt;br /&gt;
:Inoltre nei file speciali l'ampiezza è sostituita da 2 numeri, uno che indica il driver del dispositivo e l'altro le possibili parti del dispositivo, per esempio le partizioni.&lt;br /&gt;
* '''System call ioctl'''&lt;br /&gt;
: Ioctl serve per controllare dei device. Per esempio permette di fermare la testina di lettura del disco oppure far partire il motore del lettore CD. E' necessaria in quando si è voluto uniformare la lettura e scrittura di stream per tutti i device tramite le system call read/write ma chiaramente ogni dispositivo ha anche le sue diverse necessità e queste vengono soddisfatte da ioctl.&lt;br /&gt;
* '''Syscall'''&lt;br /&gt;
: Syscall è una funzione di libreria C che serve a chiamare system calls che non hanno una loro interfaccia per il linguaggi C. Per esempio getdents.&lt;br /&gt;
* '''System call e funzioni di libreria per directory'''&lt;br /&gt;
:Le directories sono sempre file ma si può fare l'accesso solo in lettura, per esempio per ottenere la lista di tutti i file e directory all'interno di quella directory. &amp;lt;br&amp;gt;&lt;br /&gt;
:getdents legge un file directory ed è la system call principale, tutte le funzioni di libreria C tipo opendir,readdir,readdir_r(per multithread) chiamano getdents. &amp;lt;br&amp;gt;&lt;br /&gt;
:Tutte queste funzioni lavorano con una struct dirent che rappresenta una directory entry. &amp;lt;br&amp;gt;&lt;br /&gt;
:Si può usare anche scandir che è reetrant (nessun problema di race condition) e invece di leggere un elemento della directory per volta ritorna direttamente un array di tutti gli element allocato dinamicamente (occorre ricordarsi di deallocare con free tutti gli elementi e l'array stesso).&lt;br /&gt;
&lt;br /&gt;
== Lezione del 22 novembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
Problema dei filosofi a cena risolto con i monitor. Soluzione con le condizioni associate alle &amp;quot;bacchette&amp;quot; e con le condizioni associate ai filosofi.&lt;br /&gt;
discussione dei problemi di deadlock e starvation nell'implementazione tramite monitor.&lt;br /&gt;
&lt;br /&gt;
Problema dei lettori e scrittori tramite monitor. Soluzione con privilegio ai lettori, agli scrittori e modifiche necessarie per la soluzione priva di starvation.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 25 novembre 2016 ==&lt;br /&gt;
[[Coding Contest]]&lt;br /&gt;
&lt;br /&gt;
== Lezione del 29 novembre 2016 ==&lt;br /&gt;
* '''Implementazione dei monitor tramite semafori'''&lt;br /&gt;
:Un soluzione simile a quella vista a lezione può essere trovata [[Esperimenti_con_semafori_e_monitor_in_C#Monitor implementati con semafori | qui]].&lt;br /&gt;
* '''Introduzione al message passing'''&lt;br /&gt;
:E' un altro modo per fare programmazione concorrente alternativo alla memoria condivisa che abbiamo visto fin ora.&lt;br /&gt;
:Se i programmi non condividono memoria ma devono sincronizzarsi per lavorare assieme hanno bisogno di scambiarsi messaggi.&lt;br /&gt;
:Per l'indirizzamento serve un sistema di naming, come il pid dei processi.&lt;br /&gt;
:Due primitive: send e receive. Nella receive come indirizzo da chi ricevere si può mettere una wildcard in modo da riceve da tutti.&lt;br /&gt;
:Nella send invece occorre indicare l'identificativo del destinatario (se si ammettesse di poter inserire una wildcard si spedirebbero messaggi in broadcast/multicast. E' una problematica pi&amp;amp;ugrave; complessa che viene trattata nei corsi di sistemi distribuiti).&lt;br /&gt;
:Lo scambio di messaggi può essere:&lt;br /&gt;
:* Sincrono: send bloccante e receive bloccante, senza memoria di supporto.&lt;br /&gt;
:* Asincrono: send non bloccante e receive bloccante, con memoria di supporto. I messaggi spediti rimangono in una coda (di ampiezza massima illimitata nel modello).&lt;br /&gt;
:* Completamente asincrono: send non bloccante e receive non bloccante. Poco utilizzato, il destinatario deve fare un polling per controllare se il messaggio è arrivato o no.&lt;br /&gt;
:Per dimostrare la stessa espressività dei metodi dobbiamo sempre creare delle librerie e non dobbiamo aggiungere nessun processo.&lt;br /&gt;
:La metodologia asincrona è espressiva almeno quanto quella sincrona mentre non è vero il contrario.&lt;br /&gt;
:Per creare un metodo sincrono con chiamate asincrone basta bloccare il programma finché non si riceve un acknowledgement.&lt;br /&gt;
:Invece per creare un metodo asincrono con chiamate sincrone bisogna aggiungere un processo e quindi questo dimostra che non sono equivalentemente espressive.&lt;br /&gt;
* '''Soluzioni di esercizi d'esame'''&lt;br /&gt;
** Problema della lavatrice&lt;br /&gt;
** Problema del ponte a senso unico&lt;br /&gt;
&lt;br /&gt;
== Lezione del 2 dicembre 2016 ==&lt;br /&gt;
* '''Implementazione della libreria per message passing'''&lt;br /&gt;
:I sorgenti della implementazione possono essere trovati [[Esperimenti con message passing in C|qui]]&lt;br /&gt;
* '''I segnali'''&lt;br /&gt;
:I segnali sono un metodo di comunicazione tra processi utilizzato in UNIX.&lt;br /&gt;
:Ogni segnale è rappresentato da un numero. Per esempio &amp;quot;segmentation fault&amp;quot; è il numero 11. &lt;br /&gt;
:La system call kill() serve per inviare un segnale. signal() serve per definire l'handler, una funzione che viene eseguita quando il processo riceve uno specifico segnale.&lt;br /&gt;
:Ogni segnale ha il suo handler di default e tramite signal() noi sovrascriviamo il default handler. &lt;br /&gt;
* '''Permesso setuid'''&lt;br /&gt;
:Permette a dei programmi di eseguire operazioni che hanno bisogno dei permessi di root. L'attributo setUserId attivato vuol dire che il programma verrà eseguito con i privilegi del possessore del file piuttosto di quelli di chi lo esegue.&lt;br /&gt;
* '''Proposta di soluzione esercizio 4 del [[Coding Contest 25 novembre 2016]]'''&lt;br /&gt;
&lt;br /&gt;
== Lezione del 6 dicembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
* Implementazione di un servizio di Message Passing Asincrono dato uno sincrono (con processo server).&lt;br /&gt;
* Implementazione di un servizio di Message Passing completamente asincrono dato uno sincrono.&lt;br /&gt;
* Analisi del potere espressivo dei servizi di message passing.&lt;br /&gt;
&lt;br /&gt;
* kernel monolitici e microkernel&lt;br /&gt;
* macchine virtuali a livello di processo e a livello di sistema&lt;br /&gt;
&lt;br /&gt;
I sorgenti degli esperimenti con il message passing sono in [http://www.cs.unibo.it/~renzo/so/tools/mp.extra.tgz questo file].&lt;br /&gt;
&lt;br /&gt;
== Lezione del 9 dicembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
* Il problema della Cena dei filosofi con il message passing (asincrono)&lt;br /&gt;
&lt;br /&gt;
* Buffer Limitato con message passing (asincrono).&lt;br /&gt;
&lt;br /&gt;
== Lezione del 13 dicembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
La lezione TACE per malattia (influenza) del docente.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 16 dicembre 2016 ==&lt;br /&gt;
*'''Specifiche del progetto fase uno'''&lt;br /&gt;
Il documento delle specifiche può essere trovato [http://www.cs.unibo.it/~renzo/so/mikaboo/phase1.pdf qui].&lt;br /&gt;
:*List.h&lt;br /&gt;
:*Lista dei processi&lt;br /&gt;
:*Coda dei thread&lt;br /&gt;
:*Code dei messaggi&lt;br /&gt;
*'''Autotools'''&lt;br /&gt;
:Insieme di strumenti per la generazione automatica di Makefiles. &amp;lt;br&amp;gt;&lt;br /&gt;
:comando ''autoreconf'' per generale il file ''configure'' &amp;lt;br&amp;gt;&lt;br /&gt;
:''./configure'' per generare makefile.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 21 febbraio 2017 ==&lt;br /&gt;
'''Argomenti trattati:'''&lt;br /&gt;
* '''Visione a strati di un elaboratore'''&lt;br /&gt;
:Gli strati sono livelli di astrazione. Ogni strato deve &amp;quot;parlare&amp;quot; il linguaggio del livello sottostante e deve fornire un proprio linguaggio ai livelli superiori.&lt;br /&gt;
:Per esempio il sistema operativo parla l'ISA del livello hardware e fornisce alle applicazioni le system call. Il sistema operativo maschera il livello hardware in quanto fornisce la possibilità alle applicazioni di utilizzare istruzioni ISA ma non tutte, per ragioni di sicurezza.&lt;br /&gt;
* '''Componenti fondamentali di un sistema operativo'''&lt;br /&gt;
:A sua volta il sistema operativo può essere visto come un insieme di strati. Esistono sistemi operativi a singolo strato (come MS-DOS) e più strati (layered).&lt;br /&gt;
:Le parti principali in cui possiamo suddividere un sistema operativo sono:&lt;br /&gt;
:*&amp;lt;u&amp;gt;Lo scheduler&amp;lt;/u&amp;gt;&lt;br /&gt;
:*&amp;lt;u&amp;gt;Il memory manager&amp;lt;/u&amp;gt;&lt;br /&gt;
:*&amp;lt;u&amp;gt;Il file system&amp;lt;/u&amp;gt;&lt;br /&gt;
:*&amp;lt;u&amp;gt;L'I/O manager&amp;lt;/u&amp;gt;&lt;br /&gt;
:*&amp;lt;u&amp;gt;Il networking manager&amp;lt;/u&amp;gt;&lt;br /&gt;
:Di questa lista, gli elementi assolutamente fondamentali per considera un programma un sistema operativo sono lo scheduler e una parte essenziale del memory e I/O manager.&lt;br /&gt;
:Il networking e file manager si appoggiano al I/O manager per svolgere i loro compiti.&lt;br /&gt;
*'''Bootstrap'''&lt;br /&gt;
:Letteralmente &amp;quot;mettersi in piedi tirandosi dai lacci degli stivali&amp;quot;, nel senso di attivarsi senza aiuto esterno. All'accensione il computer legge da una ROM le prime istruzioni necessarie. Viene sempre letto dai primi settori della memoria di massa il bootloader. :Il bootloader (per esempio GRUB) conosce già molti filesystem, ma non abbastanza. Il bootloader carica un file system temporaneo apposito chiamato initrd per caricare i moduli necessari al kernel.&lt;br /&gt;
*'''Come rappresentiamo i processi'''&lt;br /&gt;
:Dobbiamo sia tener conto a che punto siamo arrivati nell'esecuzione del processo sia tener conto delle risorse di cui il processo è detentore.&lt;br /&gt;
:Utilizziamo la struttura ''Process Control Block'' (task_struct in linux) per i processi e &amp;quot;Thread Control Block&amp;quot; (task_struct in linux) per i thread.&lt;br /&gt;
:Process control block:&lt;br /&gt;
:*PID (Process ID) &lt;br /&gt;
:*PPID (Parent Process ID) per poter creare una gerarchia di processi&lt;br /&gt;
:*punti di accesso alla memoria (codice,dati,tabella di rilocazione,paginazione)&lt;br /&gt;
:*lista dei thread&lt;br /&gt;
:*owner (se il sistema è multiuser)&lt;br /&gt;
:*Risorse del processo&lt;br /&gt;
:*Coda dei messaggi per IPC (Inter-Process Communication)&lt;br /&gt;
:*accounting (tempo di esecuzione sia in modalità kernel sia in modalità user)&lt;br /&gt;
:*valore di ritorno del processo&lt;br /&gt;
:Thread control block:&lt;br /&gt;
:*TID (Thread ID)&lt;br /&gt;
:*stato del processore&lt;br /&gt;
:*call stack&lt;br /&gt;
:*ready/running/waiting&lt;br /&gt;
*'''Avvicendamento dei processi'''&lt;br /&gt;
:Definizione di avvicendamento: ''Successione prevista o regolata nel tempo, alternanza.''&lt;br /&gt;
:I context switch avvengono a seguito di trap/interrupt. &lt;br /&gt;
:Il ciclo di vita di un processo passa tra gli stati ''ready, running, waiting'' e infine ''term''.  &lt;br /&gt;
:Lo scheduling (processo decisionale) può venire applicato nelle seguenti 4 situazioni:&lt;br /&gt;
:# Quando un processo passa dallo stato di running a quello di waiting&lt;br /&gt;
:# Quando un processo passa dallo stato di running a quello di ready&lt;br /&gt;
:# Quando un processo passa dallo stato di waiting a quello di ready&lt;br /&gt;
:# Quando un processo passa dallo stato di running a quello di term&lt;br /&gt;
:Se lo scheduling avviene solo nella situazione 1 e 4 allora si utilizza un metodo non preemptive. Dal momento che la CPU è allocata a un processo questo la detiene finchè non termina o entra di sua spontanea volontà in stato di waiting. La CPU non può decidere di far aspettare un processo spostandolo temporaneamente nella coda dei processi ready. Questo sistema era utilizzato da vecchi sistemi operativi come Windows 3.x e dalle versione di Mac OS prima della versione X.&lt;br /&gt;
:Nel preemptive scheduling invece la CPU può decidere di sospendere un processo, questo può creare race condition ma anche un sistema più equo di distribuzione delle risorse. Le race condition vengono risolte con i metodi che abbiamo visto nel primo semestre.&lt;br /&gt;
'&lt;br /&gt;
&lt;br /&gt;
== Lezione del 23 febbraio 2017 ==&lt;br /&gt;
&lt;br /&gt;
'''System call'''&lt;br /&gt;
&lt;br /&gt;
Abbiamo analizzato le system call che erano già presenti nella [http://man.cat-v.org/unix-6th/2/ versione 6 di Unix] (1975). &amp;lt;br&amp;gt;&lt;br /&gt;
Le abbiamo poi confrontate con quelle attuali del [[Il_%27%27catalogo%27%27_delle_System_Call]].&lt;br /&gt;
&lt;br /&gt;
'''Shell Scripting'''&lt;br /&gt;
&lt;br /&gt;
Gli script vengono utilizzati per lanciare un demone, guardare i processi, etc.&lt;br /&gt;
Anche l'amministrazione di sistema avviene tramite scripting. Ad esempio, dopo il boot, il processo init avvia le funzionalità di sistema tramite script.&lt;br /&gt;
&lt;br /&gt;
Unix unifica l'idea di script con quella di shell.&lt;br /&gt;
La shell è un processo che interagisce con l'utente. In Unix la novità introdotta è rappresentata dalla presenza di un linguaggio che riguarda i comandi, utile per scrivere gli script per la shell.&lt;br /&gt;
&lt;br /&gt;
Come indichiamo quale shell usare? Scrivendo nella prima riga #! seguito dal path e dal nome della shell. Il kernel prende ciò che è scritto nella prima riga e lo esegue per interpretare lo script. &lt;br /&gt;
Nell'esempio sottostante Script è un commento perchè è preceduto da #.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO&lt;br /&gt;
&lt;br /&gt;
   #!/bin/bash&lt;br /&gt;
   #Script&lt;br /&gt;
&lt;br /&gt;
   echo &amp;quot;Hello World!&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Per scrivere script abbiamo bisogno di variabili. Nelle shell queste possono essere di 2 tipi:&lt;br /&gt;
&lt;br /&gt;
- Locali: viste solo dalla shell, non perturbano comportamento dei processi.&lt;br /&gt;
&lt;br /&gt;
- D'ambiente: recepita dai comandi come ambiente.&lt;br /&gt;
&lt;br /&gt;
'''ASSEGNAMENTO E VARIABILI'''&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1&lt;br /&gt;
&lt;br /&gt;
  a=2  /* Non devono esserci spazi!!! a = 2 doesn't work */&lt;br /&gt;
  echo $a  /* Stampa 2 */&lt;br /&gt;
  echo ${a}a  /* Stampa 2a */&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 2 - Nuova shell&lt;br /&gt;
&lt;br /&gt;
  bash  /* Apro nuova shell */&lt;br /&gt;
  echo $a /* Stamperà una riga vuota perchè la var. a non è locale della nuova shell */&lt;br /&gt;
  exit  /* chiudo nuova shell */&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 3 - Esportare variabile locale&lt;br /&gt;
&lt;br /&gt;
  export a /* La var. a diventa variabile d'ambiente */&lt;br /&gt;
  bash &lt;br /&gt;
  echo $a /* Ora possiamo chiamare la var. a perchè è diventata d'ambiente */&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 4 - csh &lt;br /&gt;
&lt;br /&gt;
csh in molti casi dovrebbe già essere installato di default. &lt;br /&gt;
Se non è così è possibile installarlo eseguendo&lt;br /&gt;
&lt;br /&gt;
  sudo apt-get install csh&lt;br /&gt;
&lt;br /&gt;
  csh /* Apro cshell, noterete il simbolo di percentuale % a inizio riga di comando */&lt;br /&gt;
  set a=42 /* assegnamento */&lt;br /&gt;
  csh /* apro nuova shell */&lt;br /&gt;
  echo $a /* non possiamo chiamare a(42) perchè locale alla prima shell, viene chiamata a d'ambiente se presente */&lt;br /&gt;
  exit&lt;br /&gt;
  setenv a 42 /* Per creare una variabile d'ambiente, da notare la mancanza di = */&lt;br /&gt;
  csh&lt;br /&gt;
  echo $a&lt;br /&gt;
&lt;br /&gt;
'''ARGOMENTI'''&lt;br /&gt;
&lt;br /&gt;
Negli script possiamo avere degli argomenti. &lt;br /&gt;
Vengono visti come $1, ..., $9, ${10}, etc.&lt;br /&gt;
$* indica tutti gli argomenti&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1&lt;br /&gt;
  #!/bin/bash&lt;br /&gt;
  echo $*  /* Richiama tutti gli argomenti */&lt;br /&gt;
  echo $1 $2 /* Richiama solo i primi due argomenti */&lt;br /&gt;
  shift /* Sposta la riga di comando degli argomenti di una posizione verso sx */&lt;br /&gt;
  echo $* &lt;br /&gt;
&lt;br /&gt;
Una volta salvato il file con estensione .sh, eseguire da terminale:&lt;br /&gt;
  chmod +x nomefile.sh&lt;br /&gt;
  ./nomefile.sh  arg1 arg2 ...&lt;br /&gt;
&lt;br /&gt;
Se passiamo come argomenti: uno due tre quattro,&lt;br /&gt;
l'output sarà: &lt;br /&gt;
  uno due tre quattro&lt;br /&gt;
  uno due&lt;br /&gt;
  due tre quattro&lt;br /&gt;
&lt;br /&gt;
A volte dobbiamo proteggere dei caratteri. Alcuni di essi possono essere delle wildcard. Ci sono 3 modi per proteggerli:&lt;br /&gt;
  \*&lt;br /&gt;
  '*'&lt;br /&gt;
  &amp;quot;*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Analizziamone le differenze tramite un esempio.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1 &lt;br /&gt;
&lt;br /&gt;
  echo \*  // OUTPUT: *&lt;br /&gt;
  echo '*$a' // OUTPUT: *$a&lt;br /&gt;
  echo &amp;quot;*$a&amp;quot; // OUTPUT: *2 &lt;br /&gt;
&lt;br /&gt;
Nel primo caso l'escape vale solo per *. Nel secondo caso vale per tutta la stringa. Nel terzo caso vale per * e richiama la variabile.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 2&lt;br /&gt;
  echo ab // OUTPUT: ab&lt;br /&gt;
  echo &amp;quot;ab&amp;quot; //OUTPUT: ab&lt;br /&gt;
  &lt;br /&gt;
  echo a b //OUTPUT: ab &amp;lt;-- non rileva gli spazi&lt;br /&gt;
  echo &amp;quot;a b&amp;quot; //OUTPUT: a b&lt;br /&gt;
&lt;br /&gt;
== Lezione del 28 febbraio 2017 ==&lt;br /&gt;
'''Argomenti trattati''':&lt;br /&gt;
* '''Ciclo di vita di un kernel'''&lt;br /&gt;
: Dopo l'inizializzazione (Creazione di code, rilevazione di device, ecc..) si crea a &amp;quot;mano&amp;quot; il primo processo. &lt;br /&gt;
: L'esecuzione dello scheduler si alterna a quella dei processi. Quando un processo utente viene eseguito il kernel non ha più il controllo del flusso e gli viene restituito solo quando avviene un interrupt/trap.&lt;br /&gt;
* '''Parametri di valutazione di un algoritmo di scheduling'''&lt;br /&gt;
** % Utilizzo CPU: Bisogna cercare di tener utilizzata il processore il più possibile riducendo i momenti di inutilizzo (idle).&lt;br /&gt;
** Produttività (Throughput) Numero di processi completati per unità di tempo&lt;br /&gt;
** Tempo totale (turnaround time) Tempo totale impiegato dall'esecuzione del processo al completamente. Tiene conto del tempo speso in attesa oltre al tempo in esecuzione.&lt;br /&gt;
** Tempo di risposta: Tempo prima che un primo output venga generato dal momento in cui il processo viene eseguito. In un sistema interattivo è importante per non dare all'utente l'idea di malfunzionamento.&lt;br /&gt;
** Tempo di attesa. Non aspettando l'I/O ma bensì tempo speso nella ready queue.&lt;br /&gt;
* '''Processi CPU Bound e I/O Bound'''&lt;br /&gt;
:: I processi si possono dividere in queste due categorie. I processi che utilizzano il processore per tempi più lunghi (per esempio quelli di calcolo scientifico) si chiamano CPU Bound mentre quelli che richiedono spesso interazioni e si fermano ad aspettare l'I/O si chiamano I/O Bound. Algoritmi di scheduling differenti possono favorire o sfavorire una delle due categorie. L'obbiettivo e trovare un algoritmo che sia ottimale per tutti i processi.&lt;br /&gt;
* '''Algoritmi di scheduling'''&lt;br /&gt;
** First come first served&lt;br /&gt;
:: Lo scheduler implementa una semplice coda dei processi che hanno richiesto l'esecuzione. E' semplice da capire e creare ma il tempo medio di attesa per un processo può essere lungo. Dipende molto dall'ordine in cui vengono avvicendati processi CPU bound e I/O Bound. Un processo CPU bound può creare un ''effetto convoglio'' e far dipendere il tempo di attesa di altri processi che sono I/O bound al suo tempo di esecuzione. Questo algoritmo non è quindi equo.&lt;br /&gt;
:* Shortest job first&lt;br /&gt;
:: Forse più correttamente nominato algoritmo ''shortest next CPU burst''. Ad ogni processo viene associato il suo tempo di esecuzione e viene scelto dalla ready queue sempre il processo con il tempo più basso. Per determinare il tempo di esecuzione a priori si utilizza una media esponenziale dei tempi di esecuzione dello stesso processo nel passato. [[File:Media_esponenziale.png]] ''t'' sono le misurazioni recenti mentre 'tau' sono la storia più ''remota''. Il parametro alfa determina quanto la media esponenziale sia basata sulla storia recente o su quella passata.&lt;br /&gt;
:* Shortest remaining time first&lt;br /&gt;
:: SJF può essere preemptive oppure no. Nel caso preemptive prende il nome di ''Shortest remaining time first''. Se un processo A è in esecuzione e arriva un'altro processo B con tempo di esecuzione minore allora A viene fermato e viene eseguito B. La priorità di A nella coda è ora il tempo stimato rimanente.&lt;br /&gt;
:* Round robin&lt;br /&gt;
:: E' definita un unità di tempo, di solito tra i 10 e i 100 millisecondi, e ogni processo ha un tempo massimo di esecuzione pari a questa unità. I processi vengono avvicendati a turno in una coda. Un processo termina l'esecuzione o per sua volontà (il tempo rimasto del quanto di tempo non è considerato) oppure perchè è scattato l'interval timer. Nell'ultimo caso il processo viene inserito in coda come se fosse un processo appena arrivato. La performance di questo algoritmo dipendono molto da che tipi di processi si gestiscono e dalla dimensione dell'unita di tempo. Se questa è larga allora l'algoritmo è più simile a FCFS mentre un'unita di tempo piccola porta a più context switch e quindi a più overhead. L'unità di tempo andrebbe decisa in proporzione al tempo di context switch.&lt;br /&gt;
:* Scheduling a priorità&lt;br /&gt;
:: Ad ogni processo è assegnata una priorità che si riflette nella priorità che ha il processo nella ready queue.&lt;br /&gt;
:: La priorità di un processo può essere influenzata da fattori interni (come i requisiti di memoria) o esterni al sistema operativo (come ''l'importanza'' di un processo). L'algoritmo può essere non preemptive o premtive, nel caso in cui interrompa il processo attuale se arriva un processo con più priorità.&lt;br /&gt;
:: L'SJF può essere visto come un caso particolare di scheduling a priorità, in cui la priorità e appunto inversamente proporzionale al tempo di esecuzione.&lt;br /&gt;
:: Un problema dello scheduling a priorità è la starvation, che può essere risolta con il meccanismo di ''aging'' in cui un processo aumenta di priorità proporzionalmente al tempo cui rimane nella ready queue.&lt;br /&gt;
:* Scheduler multilivello&lt;br /&gt;
:: Questo è lo scheduler più completo in quanto divide i processi in gruppi e utilizza differenti algoritmi per ogni gruppo.&lt;br /&gt;
:: Una tipica suddivisione è quella di processi foreground e background, rispettivamente I/O bound e CPU bound. Per ogni gruppo viene creata una coda apposita.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 2 marzo 2017 ==&lt;br /&gt;
&lt;br /&gt;
'''Shell Scripting'''&lt;br /&gt;
&lt;br /&gt;
'$' seguito da caratteri esegue una sostituzione, questa può essere:&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 1 (contenuto)'''&lt;br /&gt;
&lt;br /&gt;
  echo $SHELL&lt;br /&gt;
&lt;br /&gt;
  echo $USER&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 2 (comando)'''&lt;br /&gt;
&lt;br /&gt;
  echo $ls&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 2.1'''&lt;br /&gt;
&lt;br /&gt;
  du -s $(ls -d *es) //du -s prende come input l'output del comando compreso tra parentesi tonde&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 3 (wildcard)'''&lt;br /&gt;
&lt;br /&gt;
  echo *       // tutti file/dir&lt;br /&gt;
  echo *es     // file/dir che terminanon con es&lt;br /&gt;
  echo *[lg]es // file/dir che terminanon con les/ges&lt;br /&gt;
  echo ?????   // file/dir con 5 caratteri &lt;br /&gt;
&lt;br /&gt;
Queste wildcard servono per poter individuare insiemi di file in un colpo solo.&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 4 ($ come sintassi riconosciuta da grep)'''&lt;br /&gt;
&lt;br /&gt;
  grep '&amp;gt;$' *.c // trova tutte le linee che terminano con '&amp;gt;' all'interno dei file .c&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 5 (PID)'''&lt;br /&gt;
&lt;br /&gt;
  echo $$ // restituisce PID dello script nel quale viene eseguito&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 6a'''&lt;br /&gt;
&lt;br /&gt;
  echo $1 $2 $3 &amp;gt;/tmp/tmpscript //scrive i 3 argomenti in tmpscript&lt;br /&gt;
 &lt;br /&gt;
  cat /tmp/tmpscript  //lettura tmpscript&lt;br /&gt;
  rm -f /tmp/tmpscript  //rimozione tmpscript&lt;br /&gt;
&lt;br /&gt;
Se cerchiamo di lanciare quasi contemporaneamente lo script dell'esempio 6 in due shell differenti, lo script non funziona perchè lavorano sullo stesso file. Possiamo quindi ricorrere all'utilizzo di $$.&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 6b'''&lt;br /&gt;
&lt;br /&gt;
  echo $1 $2 $3 &amp;gt;/tmp/tmpscript$$&lt;br /&gt;
  &lt;br /&gt;
  sleep 3 //in modo da poter lanciare lo script in un'altra shell &lt;br /&gt;
  cat /tmp/tmpscript$$  &lt;br /&gt;
  rm -f /tmp/tmpscript$$&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 7'''&lt;br /&gt;
  grep bash myscript.sh &amp;gt;&amp;amp; dev/null //ridireziono l'output di grep sul &amp;quot;null device&amp;quot;&lt;br /&gt;
  echo $? //restituisce 0 se il comando precedente è stato eseguito correttamente, 1 altrimenti&lt;br /&gt;
&lt;br /&gt;
'''Gli operatori &amp;gt;&amp;gt; e &amp;lt;&amp;lt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;gt;&amp;gt; è simile a &amp;gt; ma scrive alla fine del file invece che all'inizio. &amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;&amp;lt; serve invece per inserire l'input di un comando direttamente nello script, come mostrato nel successivo esempio della mail.&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 8 (script per inviare mail)'''&lt;br /&gt;
  &lt;br /&gt;
  #!/bin/bash&lt;br /&gt;
   &lt;br /&gt;
  mail $1 &amp;lt;&amp;lt; ENDOFMESSAGE&lt;br /&gt;
  ciao $1 questa è una mail di prova&lt;br /&gt;
  ENDOFMESSAGE //etichetta usata per indicare il termine del messaggio&lt;br /&gt;
&lt;br /&gt;
Con&lt;br /&gt;
&lt;br /&gt;
  echo $1; echo $2; ls &lt;br /&gt;
&lt;br /&gt;
raggruppiamo una sequenza di comandi sulla stessa riga&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 9((|| &amp;amp;&amp;amp;)'''&lt;br /&gt;
&lt;br /&gt;
Gli operatori || e &amp;amp;&amp;amp; possono essere utilizzati per processare più comandi consecutivamente.&lt;br /&gt;
&lt;br /&gt;
  command || die&lt;br /&gt;
&lt;br /&gt;
  gcc -o hwx hw.c &amp;amp;&amp;amp; ./hwx&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 10 (sottoshell)'''&lt;br /&gt;
&lt;br /&gt;
Una lista di comandi all'interno di () viene eseguita in una sottoshell. Le variabili all'interno della sottoshell non sono visibili al di fuori del blocco di codice nella sottoshell. Sono variabili locali.&lt;br /&gt;
&lt;br /&gt;
  (cd subdir; ls) //eseguo un comando in un'altra directory&lt;br /&gt;
&lt;br /&gt;
'''Pushd e popd'''&lt;br /&gt;
&lt;br /&gt;
Simili al comando ''cd'' ma pushd inserisce i percorsi visitati in una pila e tramite popd e possibile ripercorrere a ritroso le directory visitate.&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 11 (background jobs)&lt;br /&gt;
  ping google.com &amp;amp; //esegue ping in background&lt;br /&gt;
  jobs //mostra la lista dei jobs in esecuzione&lt;br /&gt;
  //poniamo che l'output di jobs sia &amp;quot;[1]+  In esecuzione           ping google.com &amp;amp;&amp;quot;&lt;br /&gt;
  kill -9 %1 //termina il processo con identificatore 1&lt;br /&gt;
''bg'' repristina un processo fermato con CTRL+Z. &amp;lt;br&amp;gt;&lt;br /&gt;
''fg'' riporta in primo piano un processo mandato in background.&lt;br /&gt;
&lt;br /&gt;
'''test e type'''&lt;br /&gt;
&lt;br /&gt;
''test'' serve a comparare valori e controllare il tipo dei file. &amp;lt;br&amp;gt;&lt;br /&gt;
la sintassi ''[]'' è un alias di test.&lt;br /&gt;
  if [ $$ -eq '10853' ] ; then echo &amp;quot;il processo corrente ha pid 10853&amp;quot;; else echo &amp;quot;il processo corrente non ha pid 10853&amp;quot;; fi;&lt;br /&gt;
  //che è equivalente a &lt;br /&gt;
  if test $$ -eq '10853'; then echo &amp;quot;il processo corrente ha pid 10853&amp;quot;; else echo &amp;quot;il processo corrente non ha pid 10853&amp;quot;; fi;&lt;br /&gt;
''type'' invece mostra l'origine di un comando, se sono integrati nella shell oppure è un programma a parte.&lt;br /&gt;
&lt;br /&gt;
'''Switch'''&lt;br /&gt;
Sintassi degli switch in bash.  &lt;br /&gt;
  case expression in&lt;br /&gt;
    pattern1 )&lt;br /&gt;
        statements ;;&lt;br /&gt;
    pattern2 )&lt;br /&gt;
        statements ;;&lt;br /&gt;
    ...&lt;br /&gt;
    esac&lt;br /&gt;
'''Comando cron''' &amp;lt;br&amp;gt;&lt;br /&gt;
''cron'' serve per eseguire operazioni pianificate. Gli script in /etc/init.d vengono eseguiti automaticamente da cron.&lt;br /&gt;
&lt;br /&gt;
'''xarg''' &amp;lt;br&amp;gt;&lt;br /&gt;
xarg permette di eseguire un comando passandogli come parametri i valori letti da standard input. &lt;br /&gt;
&lt;br /&gt;
'''uniq''' &amp;lt;br&amp;gt;&lt;br /&gt;
uniq può essere usato per trovare e eliminare righe duplicate.&lt;br /&gt;
&lt;br /&gt;
'''wc''' &amp;lt;br&amp;gt;&lt;br /&gt;
''wc'' crea il ''word count'' di un file. Non conta solo le parole ma anche i caratteri e le righe.&lt;br /&gt;
&lt;br /&gt;
'''diff e patch''' &amp;lt;br&amp;gt;&lt;br /&gt;
Il comando ''diff'' può essere utilizzato per mostrare le differenze tra due file testuali. E' utilizzato internamente da git. &amp;lt;br&amp;gt;&lt;br /&gt;
Patch serve per applicare un ''diff file'' a un altro file in modo da aggiornarlo.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 7 marzo 2017 ==&lt;br /&gt;
Una risorsa è ogni elemento utile all'elaborazione.&lt;br /&gt;
* '''Classificazione di risorse'''&lt;br /&gt;
** '''Fisiche e logiche'''&lt;br /&gt;
::Esempi:&lt;br /&gt;
::CPU, memoria e dispositivi sono risorse ''fisiche''.&lt;br /&gt;
::file, strutture dati, critical section sono risorse ''logiche''.&lt;br /&gt;
:* '''Fungibili'''&lt;br /&gt;
::Una risorsa è fungibile se è uguale prendere un bene o un'altro se sono dello stesso tipo. Richiedere un area di memoria o un altra è indifferente se tutte e due soddisfano i requisiti del processo. Richiedere file o dispositivi differenti invece cambia.&lt;br /&gt;
:* '''Assegnazione statica o dinamica'''&lt;br /&gt;
::L'assegnazione statica prevede che una risorsa sia monopolio di un processo.&lt;br /&gt;
:* '''Accetta richieste multiple o solo singole'''&lt;br /&gt;
:: Se può ricevere richieste d'uso da più processi e memorizzare/servire contemporaneamente le richieste oppure no.&lt;br /&gt;
:* '''condivisibili e non condivisibili'''&lt;br /&gt;
:: Un file in lettura è una risorsa condivisibile, più processi possono accedere contemporaneamente. Un file in scrittura invece è una risorsa non condivisibile perché potrebbe capitare una race condition.&lt;br /&gt;
:* '''bloccante e non bloccante'''&lt;br /&gt;
::Se l'accesso alla risorsa implica dei tempi di attesa relativamente lunghi al processo, la risorsa è da considerarsi bloccante. &lt;br /&gt;
:* '''prelasciabile o non prelasciabile'''&lt;br /&gt;
::Il processo può rilasciare in qualsiasi momento la risorsa e permetterne l'uso ad un altro processo? Una stampante e un esempio di rirosa non prelasciabile. &lt;br /&gt;
* '''Deadlock''' &lt;br /&gt;
:Un deadlock può avvenire in presenza di risorse bloccanti, non condivisibili, non prerilasciabili e con una situazione di attesa circolare. &lt;br /&gt;
:* '''detection: grafo di Holt ''' &lt;br /&gt;
::I grafi di allocazione delle risorse, introdotti da Holt nel 1972 sono un buon modo per descrivere lo stato di un sistema e prevedere possibili deadlock. Ogni nodo rappresenta una risorsa o un processo. Di solito le risorse sono rappresentate da quadrati e i processi da cerchi. Un arco da un processo a una risorsa significa che quel processo ha richiesto un'istanza della risorsa. Un arco da una risorsa a un processo vuol dire che un'istanza di quella risorsa è stata assegnata al processo. &amp;lt;br&amp;gt;&lt;br /&gt;
::* ''' teorema dei cicli in grafi di Holt con un solo tipo di risorsa&lt;br /&gt;
:::Se il grafo non contiene cicli allora di sicuro non possono esserci deadlock. &lt;br /&gt;
:::Se il grafo contiene un ciclo allora &amp;lt;u&amp;gt;può&amp;lt;/u&amp;gt; esserci un deadlock.&lt;br /&gt;
:::Se le risorse coinvolte in un ciclo hanno solo un'istanza o ogni loro istanza e coinvolta in un ciclo allora c'è di sicuro un deadlock.&lt;br /&gt;
:::Questo tecnica di permette di analizzare uno stato ''attuale'' di un sistema, ma dobbiamo trovare un modo per prevedere lo stato ''futuro'' del sistema e quindi prevedere i deadlock.&lt;br /&gt;
::* '''processo di riduzione&lt;br /&gt;
:::Per vedere lo stato di un sistema futuro possiamo usare un processo di riduzione del grafo di allocazione delle risorse.&lt;br /&gt;
:::Se una risorsa allocata a un processo può essere data dopo un certo periodo a un altro processo che l'ha richiesta allora si può fare una riduzione. Una riduzione consiste nell'eliminare un arco risorsa-processo perchè quel processo ha finito di utilizzare la risorsa oppure è stata prerilasciata e invertire un arco processo-risorsa di un processo che aveva richiesto la risorsa in un arco risorsa-processo.&lt;br /&gt;
:::Se un grafo può essere ridotto per tutti i suoi processi allora non può esserci deadlock.&lt;br /&gt;
:::Il problema di questo metodo/algoritmo è che nel momento che deallochiamo una risorsa a un processo e più processi avevano richiesto quella risorsa come scegliamo deterministicamente a quale processo assegnare la risorsa? Dovremmo provare tutti i casi per accertarsi che non si crei deadlock, ciò significa fare backtrack e perdere la polinomialità dell'algoritmo.&lt;br /&gt;
::* '''definizione di Knot&lt;br /&gt;
:::Il Knot è una caratteristica di un grafo che si verifica velocemente e permette di determinare possibili deadlock.&lt;br /&gt;
:::Un knot è un insieme di nodi in cui, per ogni nodo del knot, tutti e solo i nodi del knot sono raggiungibili da quel nodo.&lt;br /&gt;
:::Un knot è una condizione sufficiente ma non necessaria per i deadlock. Ciò vuol dire che possono capitare deadlock anche senza la presenta di un knot.&lt;br /&gt;
::* '''teorema dei knot nei grafi di Holt con più tipi di risorse&lt;br /&gt;
:* '''prevention'''&lt;br /&gt;
::Se il sistema cerca di non far verificare almeno una delle condizioni necessarie al deadlock allora questo non si verificherà mai.&lt;br /&gt;
::Questo però comporta anche sottoutilizzo delle risorse del sistema.&lt;br /&gt;
:* '''avoidance''' &lt;br /&gt;
::*'''Condizione di safety'''&lt;br /&gt;
:::Un sistema è in una condizione di safety se esiste una safe sequence.&lt;br /&gt;
:::Una safe sequence è una sequenza di processi dove per ogni richiesta di risorse di un processo questa richiesta può essere soddisfatta con le richieste disponibili al momento più tutte le risorse allocate precedentemente ai precedenti processi della sequenza. &lt;br /&gt;
:::Finchè c'è safety non possono verificarsi deadlock. In una situazione unsafe non è detto che capiti deadlock, ma è possibile.&lt;br /&gt;
::*'''algoritmo del Banchiere (mono e multivaluta).&lt;br /&gt;
:::L'algoritmo del banchiere si basa sul fatto che una banca non dovrebbe mai rimanere senza denaro da prestare ai clienti.&lt;br /&gt;
:::Vengono mantenute varie strutture dati come il capitale iniziale della banca, quanto è disponibile in cassa, quanto è stato prestato ad ogni cliente e quanto si può ancora prestare ad ogni cliente (in base al ''credito'' che ogni cliente ha).&lt;br /&gt;
:::La condizione di safety è data dalla condizione che bisogna mantenere almeno uno dei clienti soddisfacibile cioè che il denaro in cassa sia maggiore del possibile richiesta di prestito di almeno un cliente.&lt;br /&gt;
:::Se questa condizione non si verificasse alloca il banchiere deve aspettare che un cliente restituisca il prestito.&lt;br /&gt;
:::In caso di più valute (yen, dollari, euro, ...) che rappresentano i tipi diversi di risorse del sistema. Tutte le strutture dati aumentano di una dimensione, per esempio il denaro iniziale diventa un vettore.&lt;br /&gt;
::Teorema: il processo di controllo di safety non ha necessita' di backtrack.&lt;br /&gt;
:* '''Tecnica dell'ostrica/struzzo'''&lt;br /&gt;
::Un altro modo per gestire i deadlock e quello di ignorarli completamente.&lt;br /&gt;
::La maggior parte dei sistemi operativi utilizza questo metodo.&lt;br /&gt;
::Soprattuto in sistemi in cui è statisticamente determinato che i deadlock si verificano poco frequentemente i sistemi di prevenzione e avoidance costituirebbero un overhead troppo elevato.&lt;br /&gt;
::Sta allora alle singole applicazioni evitare di crearli.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 9 marzo 2017 ==&lt;br /&gt;
&lt;br /&gt;
* awk&lt;br /&gt;
* phase2: prime fasi del boot&lt;br /&gt;
* python: presentazione del linguaggio ed analisi delle caratteristiche&lt;br /&gt;
&lt;br /&gt;
== Lezione del 14 marzo 2017 ==&lt;br /&gt;
'''Gestione della memoria'''&lt;br /&gt;
* Binding a tempo di esecuzione, compilazione o esecuzione&lt;br /&gt;
* Memory Management Unit (MMU)&lt;br /&gt;
* Allocazione statica o dinamica&lt;br /&gt;
* Allocazione continua o non contigua&lt;br /&gt;
* Metodo delle partizioni fisse&lt;br /&gt;
: Problema della frammentazione interna.&lt;br /&gt;
* Metodo delle partizioni variabili&lt;br /&gt;
:Problema della frammentazione esterna.&lt;br /&gt;
:* Algoritmi di scelta dell'area di allocazione&lt;br /&gt;
:** First fit&lt;br /&gt;
:** Best fit&lt;br /&gt;
:** Worst fit&lt;br /&gt;
:** Next fit&lt;br /&gt;
:* Paginazione&lt;br /&gt;
:* Segmentazione&lt;br /&gt;
:* Memoria virtuale&lt;br /&gt;
&lt;br /&gt;
== Lezione del 16 marzo 2017 ==&lt;br /&gt;
* '''Progetto phase2''': analisi del manuale di uARM&lt;br /&gt;
* '''Python''': funzioni, oop, @memoize, @trace&lt;br /&gt;
&lt;br /&gt;
== Lezione del 21 marzo 2017 ==&lt;br /&gt;
&lt;br /&gt;
La lezione del 21 marzo tace per impegno del docente.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 23 marzo 2017 ==&lt;br /&gt;
'''Memoria virtuale''': &amp;lt;br&amp;gt;&lt;br /&gt;
'''Algoritmi di paginazione:'''&lt;br /&gt;
Quando avviene un page fault serve un algoritmo per trovare la pagina &amp;quot;meno utile&amp;quot; in memoria a cui sostituire la pagina richiesta.&lt;br /&gt;
* '''FIFO''' viene sostituita la pagina che da più tempo è in memoria&lt;br /&gt;
* '''LFU''' ''Least Frequently Used'' contando il numero di richieste alla pagina possiamo calcolare quali sono le pagine più richieste e tenere solo quelle in memoria. Il problema è che una pagina potrebbe essere richiesta molto in una fase iniziale di un processo, quindi avere un rank elevato, però poi non essere più utilizzata. Una soluzione a questo problema è quella di shiftare a destra di 1 i bit del contatore degli utilizzi a intervalli regolari, ponendo questo in un decadimento esponenziale. &lt;br /&gt;
* '''LRU''' ''Least Recently Used'' si sostituisce la pagina che non è stata utilizzata da più tempo. &lt;br /&gt;
* '''Algoritmo ottimo''' algoritmo teorico utile per fare ''benchmark'' degli altri algoritmi. Se conosciamo a priori la ''reference string'' una sequenza che indica quali saranno le richieste delle pagine nel futuro, possiamo sostituire le pagine che per più tempo non saranno richieste, riducendo al minimo i page fault.&lt;br /&gt;
'''Anomalia di Belady'''&lt;br /&gt;
Abbiamo dimostrato che l'algoritmo FIFO all'aumentare della memoria può peggiorare di performance.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 28 marzo 2017 ==&lt;br /&gt;
'''Algoritmo second chance o dell'orologio''' &amp;lt;br&amp;gt;&lt;br /&gt;
'''Linking statico'''&amp;lt;br&amp;gt;&lt;br /&gt;
'''Loading dinamico'''&amp;lt;br&amp;gt;&lt;br /&gt;
'''Memoria secondaria'''&amp;lt;br&amp;gt;&lt;br /&gt;
'''Algoritmi di scheduling delle seek'''&amp;lt;br&amp;gt;&lt;br /&gt;
*'''FIFO'''&lt;br /&gt;
*'''SSF'''&lt;br /&gt;
*'''c-look'''&lt;br /&gt;
&lt;br /&gt;
== Lezione del 30 marzo 2017 ==&lt;br /&gt;
'''Discussione sulle specifiche del progetto'''&amp;lt;br&amp;gt;&lt;br /&gt;
:Creazione pagina [[Specifiche_phase_2_2016/17]]&lt;br /&gt;
'''Python standard library'''&lt;br /&gt;
* libreria sys: Permette di prendere parametri da linea di comando. Si utilizza ''sys.argv''.&lt;br /&gt;
* libreria os: Permette di navigare nella gerarchia di directory con ''os.walk''.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 4 aprile 2017 ==&lt;br /&gt;
'''RAID''' &amp;lt;br&amp;gt;&lt;br /&gt;
(Redundant Array Inexpensive/Independent Disks)&lt;br /&gt;
&lt;br /&gt;
l’idea principale nasce per ottenere maggiori performance (raid 0 ed 1), non maggiore affidabilità (raid 4 e 5).&lt;br /&gt;
&lt;br /&gt;
* RAID0 (Striping): i dischi sono posti in sequenza.Vengono create le stripe, sequenze di bit che sono posti su n dischi sequenzialmente. Lo striping può essere a vari livelli, a livello di bit, di byte, di settore o di blocco. Quello più utilizzato e il livello di blocco. Cioè per scrivere un file che è composto da più blocchi si suddividono i blocchi in più dischi. Attenzione, file piccoli potrebbero sostare su una sola stripe, quindi su un solo disco.&lt;br /&gt;
:Il miglioramento di performance si paga in maggiore faultness. dipende dal costo dei dati salvati.&lt;br /&gt;
&lt;br /&gt;
* RAID1 (mirroring): ogni disco ospita la copia dei dati. doppia velocità in lettura (teoricamente). Stessa velocita di scrittura.&lt;br /&gt;
: Size = ½ readSpeed = N wSpeed = ½ N FT = 1&lt;br /&gt;
* RAID10 = 1+0 striping su 2 mirror.&lt;br /&gt;
&lt;br /&gt;
* RAID 2-3 (deprecated):Agisce sui singoli blocchi i dati vengono salvati in maniera sincrona con meccanismi di parità (hoffman per correzione di errori nel RAID2, ciclica per RAID3).&lt;br /&gt;
&lt;br /&gt;
* RAID4: usa dischi normali, non agisce su singoli blocchi ma su stripe ampi. &lt;br /&gt;
&lt;br /&gt;
parityMap = S0 XOR S1 XOR S2 XOR S3 (in caso di 4+1 dischi).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
nel caso si rompa S4, i dati non avranno problemi. Se si rompesse un qualunque dei primi 4 dischi, basterà effettuare lo XOR degli altri e copiare tali dati su un nuovo HD.&lt;br /&gt;
&lt;br /&gt;
S1 rotto.&lt;br /&gt;
new_S1 = S0 XOR S2 XOR S3 XOR P0123&lt;br /&gt;
Sopporta una sola rottura.&lt;br /&gt;
&lt;br /&gt;
Per aggiornare il disco di parità in caso di modifica a S2 (chiamato ora S2’)&lt;br /&gt;
&lt;br /&gt;
P0123 = P0123 XOR S2 XOR S2’.&lt;br /&gt;
il primo xor toglie il valore da P0123 di S2, il secondo XOR aggiunge il valore S2’.&lt;br /&gt;
in questo modo, aggiorno solo 2 dischi, e uso gli stessi in lettura per ricalcolare la parità.&lt;br /&gt;
&lt;br /&gt;
Le parità ciclano su tutti i dischi. Esistono controller che effettuano RAID5, oppure e’ possibile ottenere le funzionalità raid via sistema operativo.&lt;br /&gt;
&lt;br /&gt;
* RAID5: &lt;br /&gt;
:Senza fault&lt;br /&gt;
:Size = N-1/N, rspeed=(N-1), wspeed=(N-2), FT=1&lt;br /&gt;
&lt;br /&gt;
I dischi RAID soffrono alquanto in caso di terremoti.&lt;br /&gt;
&lt;br /&gt;
'''File System:''' &amp;lt;br&amp;gt;&lt;br /&gt;
Servizio per dare una interfaccia più comoda e generale per l’accesso alla memoria secondaria.&lt;br /&gt;
&lt;br /&gt;
Nasce come informatizzazione delle pratiche di ufficio. le pratiche sono salvate in folders, fascicoli, poste poi nelle directories. come visualizzo questa struttura per l’utente? E ad i programmatori? come viene implementato?&lt;br /&gt;
&lt;br /&gt;
Nasce l’idea di “aprire” i file. Nell'accedere da un programma ad il file-system (operazione non banale) si preferisce chiamare un’operazione di apertura, e utilizzare un descrittore al file per agire sul file richiesto.&lt;br /&gt;
&lt;br /&gt;
La struttura di file/folder/directories possono inoltre supportare le astrazioni di condivisione e proprieta’.&lt;br /&gt;
Tutti i controlli sono effettuati sono in fase di apertura (in modo da migliorare le performance). &lt;br /&gt;
&lt;br /&gt;
Quali attributi caratterizzano un file?&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Name,&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Type,&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Location,&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Size,&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Ownership,&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Protection,&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Time stamp&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
UNIX opta per una scelta minimalista. Tutti i file sono stringhe di byte.&lt;br /&gt;
AFS (Apple) da un tag di tipo e di creator per ogni file&lt;br /&gt;
&lt;br /&gt;
maggiore e’ il numero di tipi di file, maggiore supporto per essi, ma piu’ codice nel kernel.&lt;br /&gt;
I suffissi possono indicare ad altri programmi il tipo.&lt;br /&gt;
&lt;br /&gt;
Semantica della coerenza vs Semantica delle sessioni (Andrew File System)&lt;br /&gt;
&lt;br /&gt;
Per quali motivo vengono create le partizioni?&lt;br /&gt;
/var andrebbe in una nuova partizione per un fattore di sicurezza.&lt;br /&gt;
&lt;br /&gt;
Cosa c’e’ effettivamente sul disco? Una sequenza di blocchi, il cui primo (boot block) ha spazio per il bootloader e la tabella delle partizioni (MBR), con 4 elementi. 4 record che indicano dove ogni partizione inizia e finisce. Si possono usare partizioni estese, usando le prime 3 e nella quarta tutto il resto.&lt;br /&gt;
&lt;br /&gt;
Ogni partizione ha all'inizio un BootRecord.&lt;br /&gt;
L’ultima partizione contiene nel BootRecord le altre partizioni. &lt;br /&gt;
GBT consente di avere un numero arbitrario di partizioni.&lt;br /&gt;
&lt;br /&gt;
Raizer FS,&lt;br /&gt;
&lt;br /&gt;
Read only file systems.&lt;br /&gt;
&lt;br /&gt;
Initrd, ISO9660.&lt;br /&gt;
&lt;br /&gt;
Quando si costruisce l’immagine, in realtà si sta creando il file-system ISO9660.&lt;br /&gt;
&lt;br /&gt;
Come gestire invece file che possono cambiare?&lt;br /&gt;
Allocazione indicizzata (a lista): creo un (o vari) blocco/i degli indici che punta ad i blocchi dati.&lt;br /&gt;
l’accesso diretto cresce come il logaritmo dell’ampiezza. &lt;br /&gt;
Allocazione concatenata (ad albero): (in fondo al blocco indico il successivo). Gestisce bene la dinamicità. Non piace perché’ non é per nulla efficiente l’accesso diretto. &lt;br /&gt;
&lt;br /&gt;
Quali allocazioni usano i filesystem reali oggi?&lt;br /&gt;
&lt;br /&gt;
fat: concatenata. I blocchi tengono solo i dati, esiste una file allocation table (vettore) che indica il prossimo blocco. I puntatori sono tutti vicini e quindi ponendo il vettore in cache, abbiamo disponibili diversi puntatori.&lt;br /&gt;
&lt;br /&gt;
BerkleyFastFileSystem (progenitore dei EXT4…) usano allocazione indicizzata. Nasce per garantire performance ad i file piccoli. Nel descrittore del file (iNode) avra’ N (13 attualmente) puntatori. Uno, il numero 10, ad i blocchi, uno (11) indiretto alla tabella, uno (12) come duplice indiretto e il 13esimo come triplice indiretto.&lt;br /&gt;
&lt;br /&gt;
I file hanno quindi una lunghezza massima. EX4 pero’ risolve indicizzando non i singoli blocchi ma le serie.&lt;br /&gt;
&lt;br /&gt;
Gestione spazio libero&lt;br /&gt;
&lt;br /&gt;
nella struttura indicizzata serve una struttura per tenere conto degli spazi liberi. Si puo’ usare una bitmap per tenere la contabilità degli spazi liberi e quelli utilizzati.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 6 aprile 2017 ==&lt;br /&gt;
&lt;br /&gt;
File System. Ext2 e VFAT. Sicurezza, principi generali.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 11 aprile 2017 ==&lt;br /&gt;
&lt;br /&gt;
Sicurezza: crittografia (chiave pubblica/chiave segreta) autorizzazione autenticazione firma elettronica.&lt;br /&gt;
Virus Worm Cavalli di Troia.&lt;br /&gt;
&lt;br /&gt;
Attacchi buffer overflow e toctou.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 20 aprile 2017 ==&lt;br /&gt;
&lt;br /&gt;
(tace per impossibilita' del docente)&lt;br /&gt;
&lt;br /&gt;
== Lezione del 27 aprile 2017 ==&lt;br /&gt;
&lt;br /&gt;
P2test (prestest2).&lt;br /&gt;
&lt;br /&gt;
DIscussione specifiche.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 2 maggio 2017 ==&lt;br /&gt;
== Lezione del 4 maggio 2017 ==&lt;br /&gt;
* Discussione su proposta da parte degli studenti di modifiche al test di progetto&lt;br /&gt;
* Soluzione all'esercizio C1 del 2014-06-16 (Monitor)&lt;br /&gt;
* Soluzione all'esercizio 1 del [[http://www.cs.unibo.it/~renzo/so/pratiche/2015.05.29.pdf 2016-05-20]] ( C )&lt;br /&gt;
* Soluzione all'esercizio 2 del 2008-06-13 (Dimostrazione algoritmo di rimpiazzamento a stack)&lt;br /&gt;
* Soluzione all'esercizio 1 del [[http://www.cs.unibo.it/~renzo/so/compiti/2013.07.19.tot.pdf 2013-07-19]] (Monitor)&lt;br /&gt;
&lt;br /&gt;
== Lezione del 9 maggio 2017 ==&lt;br /&gt;
*Esercizio 2 prova teorica 2014.09.24&lt;br /&gt;
*Esercizio g.1 prova teorica 2013.07.19&lt;br /&gt;
*Esercizio c1 e c2 prova teorica 2015.01.20(2014.01.20)&lt;br /&gt;
*Esercizio c2 2013.05.30&lt;br /&gt;
&lt;br /&gt;
== Lezione del 11 maggio 2017 ==&lt;br /&gt;
== Lezione del 16 maggio 2017 ==&lt;br /&gt;
* Chiarimenti su svolgimento esame&lt;br /&gt;
* Commenti su recenti eventi (WannaCry)&lt;br /&gt;
* Visione di programma python per costruzione di stringhe palindrome con monitor&lt;br /&gt;
* Correzione esercizio g2 prova teorica 20170209&lt;br /&gt;
* Correzione esercizio 1 [[Prova pratica 2016.09.13]]&lt;br /&gt;
* Esercizio c2 prova teorica 20140122&lt;br /&gt;
&lt;br /&gt;
== Lezione del 18 maggio 2017 ==&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2014.06.03&amp;diff=1916</id>
		<title>Prova teorica 2014.06.03</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2014.06.03&amp;diff=1916"/>
		<updated>2017-06-14T12:35:33Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Aggiunta soluzione&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Testo: [http://www.cs.unibo.it/~renzo/so/compiti/2014.06.03.tot.pdf]&lt;br /&gt;
&lt;br /&gt;
== Esercizio c.2 ==&lt;br /&gt;
&lt;br /&gt;
Siano dati 4 processi:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Python&amp;quot;&amp;gt;&lt;br /&gt;
prchar=”IATL”&lt;br /&gt;
Process i: i=range(4)&lt;br /&gt;
while True:&lt;br /&gt;
 sync(i)&lt;br /&gt;
 print(prchar[i])&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Scrivere la funzione sync in modo che in un sistema di processi a memoria privata, usando message passing asincrono, l'unico&lt;br /&gt;
output possibile sia la stringa di lunghezza infinita: “ITALIAITALIAITALIA....”&lt;br /&gt;
&lt;br /&gt;
===Soluzione di S.G===&lt;br /&gt;
&lt;br /&gt;
Solo il processo 2 (lettera T) permette di iniziare la sincronizzazione dei processi.&lt;br /&gt;
&lt;br /&gt;
Oltre al processo 2 anche il processo 1 (lettera A) permette di riavviare la sequenza.&lt;br /&gt;
&lt;br /&gt;
''Nella descrizione di seguito chiameremo T il processo incaricato a stampare tale lettera, e cosi anche per gli altri.''&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Python&amp;quot;&amp;gt;&lt;br /&gt;
def sync(i):&lt;br /&gt;
	if i == 0:			# Processo 0 (processo I)&lt;br /&gt;
		mittente = recv(*)			# Aspetto un messaggio da T, L o A&lt;br /&gt;
		if mittente == 2 or mittente == 1:	# Se il mittente è il processo T o A (avvio o riavvio sequenza)&lt;br /&gt;
			send(2, 0)			# Invio un messaggio al processo T&lt;br /&gt;
		&lt;br /&gt;
		elif mittente == 3:	# Se il mittente è il processo L&lt;br /&gt;
			send(1, 0)	# Invio un messaggio al processo A&lt;br /&gt;
	&lt;br /&gt;
	elif i == 1:		# Processo 1 (processo A)&lt;br /&gt;
		mittente = recv(*)	# Aspetto un messaggio dal processo T o I&lt;br /&gt;
		if mittente == 0:	# Se il mittente è il processo I&lt;br /&gt;
			send(0, 1)	# Invio un messaggio al processo I (riavvio la sequenza)&lt;br /&gt;
		elif mittente == 2:	# Se il mittente è il processo T&lt;br /&gt;
			send(3, 1)	# Invio un messaggio al processo L&lt;br /&gt;
	&lt;br /&gt;
	elif i == 2:		# Processo 2 (processo T)&lt;br /&gt;
		send (0, 2)		# Invio un messaggio al processo I (Avvia la sequenza)&lt;br /&gt;
		mittente = recv(0)	# Aspetto un messaggio dal processo I&lt;br /&gt;
		if mittente == 0:	# Se il mittente è il processo I&lt;br /&gt;
			send(1, 2)	# Invio un messaggio al processo A&lt;br /&gt;
	&lt;br /&gt;
	elif i == 3:		# Processo 3 (processo L)&lt;br /&gt;
		mittente = recv(1)	# Aspetto un messaggio dal processo A&lt;br /&gt;
		if mittente == 1:	# Se il mittente è il processo A&lt;br /&gt;
			send(0, 3)	# Invio un messaggio al processo I&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Una volta avviato T invierà un messaggio a I e si bloccherà in attesa di I.&lt;br /&gt;
&lt;br /&gt;
Il processo I in attesa riceverà un messaggio da T, allora sbloccherà T e stamperà I.&lt;br /&gt;
&lt;br /&gt;
A questo punto il processo T invierà un messaggio ad A, e stamperà T.&lt;br /&gt;
&lt;br /&gt;
A aspetterà messaggi da più mittenti, in questo caso riceverà un messaggio da T, invierà un messaggio ad L e stamperà A.&lt;br /&gt;
&lt;br /&gt;
L aspetterà il messaggio da A che appena ricevuto invierà un messaggio ad I, e stamperà L.&lt;br /&gt;
&lt;br /&gt;
Il processo I in attesa, riceverà un messaggio da L sbloccandosi invierà un messaggio ad A e stamperà I.&lt;br /&gt;
&lt;br /&gt;
A ancora una volta in attesa, riceverà un messaggio da I, risponderà ad I e stamperà A.&lt;br /&gt;
&lt;br /&gt;
I riceverà un messaggio da A, e riavvierà la sequenza.&lt;br /&gt;
[[User:S.G|S.G]] ([[User talk:S.G|talk]]) 10:35, 4 December 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
Penso che la soluzione di S.G. sia sbagliata in quanto appena si fa una send un processo bloccato su una receive viene sbloccato. Quindi per esempio verrà stampato T prima di I. L'idea è di sbloccare T alla seconda chiamata di sync(0). Per sapere il numero della chiamata si può utilizzare un contatore statico, che mantiene il suo valore tra le chiamate di funzione e viene inizializzata a 0.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
sync(i)&lt;br /&gt;
	if i==0: #processo &amp;quot;I&amp;quot;&lt;br /&gt;
		if (count=1):&lt;br /&gt;
			send(2)&lt;br /&gt;
			recv(3)&lt;br /&gt;
		elif: (count=2)&lt;br /&gt;
			send(1)&lt;br /&gt;
			recv(1)&lt;br /&gt;
			count = 0&lt;br /&gt;
	elif i==1: #processo &amp;quot;A&amp;quot;&lt;br /&gt;
		if (count==0)&lt;br /&gt;
			recv(2)&lt;br /&gt;
		elif (count=1):&lt;br /&gt;
			send(3)&lt;br /&gt;
			recv(0)&lt;br /&gt;
		elif: (count=2)&lt;br /&gt;
			send(0)&lt;br /&gt;
			recv(2)&lt;br /&gt;
			count=0&lt;br /&gt;
	elif i==2: #processo &amp;quot;T&amp;quot;&lt;br /&gt;
		if (count==0)&lt;br /&gt;
			recv(0)&lt;br /&gt;
		elif (count=1):&lt;br /&gt;
			send(1)&lt;br /&gt;
			recv(0)&lt;br /&gt;
			count=0&lt;br /&gt;
	elif i==3: #processo &amp;quot;L&amp;quot;&lt;br /&gt;
		if (count==0)&lt;br /&gt;
			recv(1)&lt;br /&gt;
		elif (count=1):&lt;br /&gt;
			send(0)&lt;br /&gt;
			recv(1)&lt;br /&gt;
			count=0&lt;br /&gt;
	count++&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Sequenzialemente le chiamate sono in quest'ordine&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
sync(0) non fa nulla&lt;br /&gt;
print(I)&lt;br /&gt;
sync(0) count=1 manda messaggio a T e si bloccherà in attesa di un messaggio da L&lt;br /&gt;
sync(2) count=0 aspetta messaggio da I&lt;br /&gt;
print(T)&lt;br /&gt;
sync(2) count = 1 manda messaggio a A e setto count=0&lt;br /&gt;
sync(1) count = 0 aspetta messaggio da T&lt;br /&gt;
print(A)&lt;br /&gt;
sync(1) count = 1 manda messaggio a L e aspetto un messaggio da I&lt;br /&gt;
sync(3) count=0 aspetta messaggio da A&lt;br /&gt;
print(L)&lt;br /&gt;
sync(3) count = 1 manda messaggio a I e setto count=0&lt;br /&gt;
print(I) #si sblocca per il messaggio da L&lt;br /&gt;
sync(0) count=2 mando un messaggio ad A e mi blocco in attesa di un messaggio da A quando lo ricevo metto count=0&lt;br /&gt;
print(A)&lt;br /&gt;
sync(1) count=2 mando messaggio a I e aspetto messaggio da T e quando mi arriverà imposto count=0&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ma in realtà è tutto in parallelo quindi:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Processo &amp;quot;I&amp;quot; !! Processo &amp;quot;A&amp;quot; !! Processo &amp;quot;T&amp;quot; !! Processo &amp;quot;L&amp;quot;&lt;br /&gt;
|- &lt;br /&gt;
|sync(0) non fa nulla &amp;lt;br&amp;gt; print(I) &amp;lt;br&amp;gt; sync(0) count=1 manda messaggio a T e si bloccherà in attesa di un messaggio da L &amp;lt;br&amp;gt; print(I) #si sblocca per il messaggio da L &amp;lt;br&amp;gt; sync(0) count=2 mando un messaggio ad A e mi blocco in attesa di un messaggio da A quando lo ricevo metto count=0&lt;br /&gt;
| sync(1) count = 0 aspetta messaggio da T &amp;lt;br&amp;gt; print(A) &amp;lt;br&amp;gt; sync(1) count = 1 manda messaggio a L e aspetto un messaggio da I &amp;lt;br&amp;gt; print(A) &amp;lt;br&amp;gt; sync(1) count=2 mando messaggio a I e aspetto messaggio da T e quando mi arriverà imposto count=0 &lt;br /&gt;
|sync(2) count=0 aspetta messaggio da I &amp;lt;br&amp;gt; print(T) &amp;lt;br&amp;gt; sync(2) count = 1 manda messaggio a A e setto count=0&lt;br /&gt;
|sync(3) count=0 aspetta messaggio da A &amp;lt;br&amp;gt; print(L) &amp;lt;br&amp;gt; sync(3) count = 1 manda messaggio a I e setto count=0&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2015.01.20&amp;diff=1915</id>
		<title>Prova teorica 2015.01.20</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2015.01.20&amp;diff=1915"/>
		<updated>2017-06-14T06:51:17Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: /* Soluzione di FedericoB */ Eliminati controlli e contatori inutili.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Testo: [http://www.cs.unibo.it/~renzo/so/compiti/2015.01.20.tot.pdf]&lt;br /&gt;
&lt;br /&gt;
== Esercizio c.1 ==&lt;br /&gt;
===Soluzione di S.G===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Esercizio c.1: Scrivere il monitor lwlrbb. Il monitor deve implementare le seguenti procedure entry:&lt;br /&gt;
 * void write(generic_type val);&lt;br /&gt;
 * generic_type read(void);&lt;br /&gt;
 * Il lwlrbb si comporta come un bounded buffer di MAX elementi che coordina l'attivita' di numerosi processi&lt;br /&gt;
 * produttori/scrittori e numerosi lettori/consumatori. lwlrbb ammette un numero massimo (sempre MAX) di lettori &lt;br /&gt;
 * e scrittori in attesa. Se il buffer e' vuoto e ci sono piu' gia' MAX lettori in attesa, il lettore che e' in &lt;br /&gt;
 * attesa da piu' tempo esce resituendo NULL. In ugual modo se il buffer e' completamente pieno e ci sono gia' MAX &lt;br /&gt;
 * scrittori che attendono di scrivere viene perduto il valore che da piu' tempo nel buffer attende di venir letto, &lt;br /&gt;
 * il primo processo in attesa di scrivere puo' cosi' scrivere il suo elemento nel buffer e sbloccarsi.&lt;br /&gt;
 */&lt;br /&gt;
#define MAX &lt;br /&gt;
&lt;br /&gt;
monitor lwlrbb{&lt;br /&gt;
	generic_type bb[];&lt;br /&gt;
	int count, rear, front, nw, nr;&lt;br /&gt;
	bool exitNull;&lt;br /&gt;
	conditions lw, lr;&lt;br /&gt;
	&lt;br /&gt;
	void lwlrbb(void){&lt;br /&gt;
		bb = new generic_type[MAX];&lt;br /&gt;
		exitNull = FALSE;&lt;br /&gt;
		rear = count = front = nw = nr = 0;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure entry void write(generic_type val){&lt;br /&gt;
		if(count == MAX){				// Il buffer è completamente pieno&lt;br /&gt;
			if(nw == MAX){				// ci sono già MAX scrittori che attendono di scrivere&lt;br /&gt;
				rear = (rear + 1) % MAX;	// Salto l'elemento da leggere da più tempo&lt;br /&gt;
				count--;&lt;br /&gt;
				lw.signal();			// il primo processo in attesa di scrivere puo' sbloccarsi.&lt;br /&gt;
			}&lt;br /&gt;
			nw++;&lt;br /&gt;
			lw.wait();&lt;br /&gt;
			nw--;&lt;br /&gt;
		}&lt;br /&gt;
		bb[front] = val;				// Il buffer non è pieno, scrivo&lt;br /&gt;
		count++;&lt;br /&gt;
		front = (front + 1) % MAX;&lt;br /&gt;
		lr.signal();&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure entry generic_type read(void){&lt;br /&gt;
		generic_type retval;&lt;br /&gt;
		if(count == 0){				// Il buffer è vuoto&lt;br /&gt;
			if(nr == MAX){			// ci sono gia MAX lettori in attesa&lt;br /&gt;
				exitNull = TRUE;	// quindi riattivio il lettore che e' in attesa da piu' tempo, restituendo NULL&lt;br /&gt;
				lr.signal();&lt;br /&gt;
			}&lt;br /&gt;
			nr++;				// incremento il contatore dei lettori in coda&lt;br /&gt;
			lr.wait();			// metto in attesa il lettore&lt;br /&gt;
							// Riprendo l'esecuzione da dove era stata interrotta&lt;br /&gt;
			nr--;				// decremento il contatore dei lettori in coda&lt;br /&gt;
			if(exitNull){			// Controllo se è una richiesta di uscita forzata&lt;br /&gt;
				exitNull = FALSE;&lt;br /&gt;
				return NULL;&lt;br /&gt;
			}&lt;br /&gt;
		}			&lt;br /&gt;
		count--;				// Il buffer non è vuoto, leggo&lt;br /&gt;
		retval = bb[rear];&lt;br /&gt;
		rear = (rear + 1) % MAX;&lt;br /&gt;
		lw.signal();&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[User:S.G|S.G]] ([[User talk:S.G|talk]]) 14:39, 18 November 2016 (CET)&amp;lt;br&amp;gt;&lt;br /&gt;
Ho scritto una possibile soluzione dell'esercizio c.1. Iniziare qui una discussione&lt;br /&gt;
&lt;br /&gt;
'''&amp;amp;egrave; uno stack! bounded buffer deve implementare una coda.''' [[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 19:29, 21 November 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
Ho implementato la coda. [[User:S.G|S.G]] ([[User talk:S.G|talk]]) 10:12, 22 November 2016 (CET)&lt;br /&gt;
----&lt;br /&gt;
===Soluzione di BAGG===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define MAX 42&lt;br /&gt;
&lt;br /&gt;
monitor lwlrbb {&lt;br /&gt;
    generic_type bb[] ;&lt;br /&gt;
    int count, front, rear ; 	/* rispettivamente: numero elementi, dove scrivere nel buffer, dove leggere nel buffer */&lt;br /&gt;
    int nw, nr ; 		/* rispettivamente numero di writer e reader */&lt;br /&gt;
    bool exit ; 		/* server ai reader per sapere se devono ritornare NULL al loro rientro nel monitor  */&lt;br /&gt;
    condition canr, canw ;&lt;br /&gt;
&lt;br /&gt;
    lwlrbb(void) {&lt;br /&gt;
        bb = new generic_type[MAX] ;&lt;br /&gt;
        count = front = rear = nw = nr = exit = 0 ;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    entry void write(generic_type val) {&lt;br /&gt;
        if(count == MAX) {			/* se count è MAX non si può scrivere  */&lt;br /&gt;
            if(nw == MAX) {			/* se la coda è piena devo fare spazio */	&lt;br /&gt;
                rear = (rear + 1)%MAX ;         /* rear indica il più vecchio elemento ancora da leggere, lo incremento --&amp;gt; salto l'elemento */&lt;br /&gt;
                count -= 1 ;&lt;br /&gt;
                nw += 1 ;			/* incremento prima perchè anche se la coda è piena ora dopo la signal ci sarà un posto...  */&lt;br /&gt;
                canw.signal() ;			/* ora il primo che aspettava può scrivere --&amp;gt; lo sveglio  */&lt;br /&gt;
                canw.wait() ;&lt;br /&gt;
                nw -= 1 ;&lt;br /&gt;
            } else {				/* se la coda non è piena devo comunque aspettare...  */&lt;br /&gt;
                nw += 1 ;&lt;br /&gt;
                canw.wait() ;&lt;br /&gt;
                nw -=1 ;&lt;br /&gt;
            }&lt;br /&gt;
        } &lt;br /&gt;
	bb[front] = val ;					&lt;br /&gt;
        front = (front + 1)%MAX ;&lt;br /&gt;
        count += 1 ;&lt;br /&gt;
        canr.signal() ;				/* potrebbe esserci un reader che aspetta --&amp;gt; segnalo che ho scritto e si può leggere  */&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    entry generic_type read(void) {&lt;br /&gt;
        if(count == 0) {			/* se non c'è nulla da leggere allora devo aspettare  */&lt;br /&gt;
            if(nr == MAX) {			/* se la coda è piena setto exit, che i reader controllano per sapere se sono stati svegliati per ritornare NULL  */&lt;br /&gt;
                exit = 1 ;&lt;br /&gt;
                canr.signal() ;		        /* sveglio il condannato...  */&lt;br /&gt;
            } nr += 1 ;				/* mi accodo... */&lt;br /&gt;
            canr.wait() ;&lt;br /&gt;
            nr -= 1 ;&lt;br /&gt;
            if(exit) {				/* se sono il condannato resetto la variabile ed esco...  */&lt;br /&gt;
                 exit = 0 ;&lt;br /&gt;
                 return NULL ;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        generic_type r = bb[rear] ;&lt;br /&gt;
        rear = (rear + 1)%MAX ;&lt;br /&gt;
        count -= 1 ;&lt;br /&gt;
        canw.signal() ;				/* ho letto, quindi si è liberato un posto e almeno un writer può scrivere... */&lt;br /&gt;
        return r ;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
Essendo l'accesso al monitor in mutua esclusione non c'è da considerare il caso in cui ci siano più lettori in concorrenza. &amp;lt;br&amp;gt;&lt;br /&gt;
La mia soluzione cerca di essere equa alternando scrittori a lettori.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;Python&amp;quot;&amp;gt;&lt;br /&gt;
monitor lwlrbb:&lt;br /&gt;
	&lt;br /&gt;
	Queue[generic_type] buffer&lt;br /&gt;
	int  waitingReaders, waitingWriters&lt;br /&gt;
	condition ok2write, ok2read &lt;br /&gt;
	boolean exitNull;&lt;br /&gt;
	int max;&lt;br /&gt;
	&lt;br /&gt;
	lwlrbb (int max):&lt;br /&gt;
		self.max = max&lt;br /&gt;
		buffer = new List(max)&lt;br /&gt;
		waitingReaders = 0&lt;br /&gt;
		waitingWriters = 0&lt;br /&gt;
		exitNull = False&lt;br /&gt;
	&lt;br /&gt;
	@entry&lt;br /&gt;
	void write(generic_type val): &lt;br /&gt;
		if (buffer.size()&amp;gt;=MAX) :&lt;br /&gt;
				if (waitingWriters&amp;gt;Max):&lt;br /&gt;
					buffer.dequeue() #discard element&lt;br /&gt;
					ok2write.signal()&lt;br /&gt;
			waitingWriters+=1&lt;br /&gt;
			ok2write.wait()&lt;br /&gt;
			waitingWriters-=1&lt;br /&gt;
		buffer.enqueue(val)&lt;br /&gt;
		if (waitingReaders&amp;gt;0) ok2read.signal()&lt;br /&gt;
		else if (waitingWriters&amp;gt;0) ok2write.signal()&lt;br /&gt;
	&lt;br /&gt;
	@entry&lt;br /&gt;
	generic_type read():&lt;br /&gt;
		if (buffer.isEmpty(): &lt;br /&gt;
			if (waitingReaders&amp;gt;Max):&lt;br /&gt;
				exitNull = True #set flag to make the woke up reader return null&lt;br /&gt;
				ok2read.signal()&lt;br /&gt;
			waitingReaders++&lt;br /&gt;
			ok2read.wait()&lt;br /&gt;
			waitingReaders--&lt;br /&gt;
			if (exitNull):&lt;br /&gt;
				exitNull = False&lt;br /&gt;
				return null&lt;br /&gt;
		element = buffer.dequeue()&lt;br /&gt;
		if (waitingWriters&amp;gt;0) ok2write.signal()&lt;br /&gt;
		else if (waitingReaders&amp;gt;0) ok2read.signal()&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Soluzione di sav, correggetemi se sbaglio ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;Python&amp;quot;&amp;gt;&lt;br /&gt;
monitor lwlbb &lt;br /&gt;
{&lt;br /&gt;
      buffer buf, wr  //buf = buffer di elementi e wr buffer di lettori&lt;br /&gt;
      condition oktoread, oktowrite&lt;br /&gt;
   &lt;br /&gt;
  Procedure entry read {&lt;br /&gt;
  &lt;br /&gt;
       if ( wr.len() &amp;lt; MAXLETTORI)&lt;br /&gt;
           if (buf.len() == 0)&lt;br /&gt;
                wr. enqueue(lettore)&lt;br /&gt;
                oktoread.wait(lettore)&lt;br /&gt;
                wr.dequeue(lettore)&lt;br /&gt;
                buf.dequeue()&lt;br /&gt;
           oktoread.signal()&lt;br /&gt;
       wr.dequeue()&lt;br /&gt;
       wr.enqueue(lettore)&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
   procedure entry write (val) {        //lo scrittore non resterà mai in attesa perché se non c'è spazio nel buffer toglie &lt;br /&gt;
                                        //il primo elemento inserito e mette il suo nuovo e sblocca un eventuale lettore in attesa&lt;br /&gt;
&lt;br /&gt;
        if (buf.len()&amp;lt; MAXBUFFER)&lt;br /&gt;
                 buf.enqueue(val)&lt;br /&gt;
                 oktoread.signal()&lt;br /&gt;
        buf.dequeue()&lt;br /&gt;
        buf.enqueue(val)&lt;br /&gt;
        oktoread.signal()&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Esercizio c.2 ==&lt;br /&gt;
===Soluzione di S.G===&lt;br /&gt;
a) Si consideri la funzione atomica dualshift(a,b) che opera su due operandi di tipo byte passati per indirizzo. L'operazione fa lo shift a&lt;br /&gt;
destra dei due operandi. Il bit piu' significativo di a viene posto a zero, il bit piu' significativo di b diviene quello che all'atto della&lt;br /&gt;
attivazione di dualshift era il bit meno significativo di a.&lt;br /&gt;
es. se a vale 6 e b vale 4 dopo la chiamata di dualshift(a,b) a vale 3 e b 2. Se a vale 5 e b 6 dopo la chiamata dualshift (a,b) a vale 2&lt;br /&gt;
e b vale 131 (128+3).&lt;br /&gt;
La funzione dualshift puo' essere usata al posto della test&amp;amp;set per la sincronizzazione fra processi? Dimostrare la risposta.&lt;br /&gt;
b) Si consideri ora la funzione andor(a,b) che opera su due opera su due parametri di tipo booleano passati per indirizzo e cosi'&lt;br /&gt;
definita:&lt;br /&gt;
andor(a,b)=&amp;lt;c=a or b; b=a and b; a=c&amp;gt;&lt;br /&gt;
Puo' la funzione andor essere usata al posto della test&amp;amp;set per la sincronizzazione fra processi? Dimostrare la risposta.&lt;br /&gt;
Si ricorda che le operazioni di assegnazione di valori costanti a variabili vengono considerati atomici.&lt;br /&gt;
&lt;br /&gt;
Svolgimento&lt;br /&gt;
&lt;br /&gt;
a) Non è possibile utilizzare la funzione atomica dualshift(a,b) in quanto per la definizione di test&amp;amp;set(a,b)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
test&amp;amp;set(a,b){&lt;br /&gt;
    a = b;&lt;br /&gt;
    b = X;   // Dove X è un valore costante&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
La funzione dualshift dovrebbe memorizzare (in qualche modo) il valore precedente di a (o b), e successivamente settare a (o b) ad un valore costante. Questo non accade perchè la stessa funzione divide per 2 i valori di a e b ogni volta che viene richiamata (caso particolare in cui venga copiato il bit meno significativo di a nel bit più significativo di b). Quindi è impossibile soddisfare la richiesta.&lt;br /&gt;
&lt;br /&gt;
b) La funzione andor(a,b) può essere utilizzata al posto della test&amp;amp;set per la sincronizzazione fra processi.&lt;br /&gt;
Dimostrazione.&lt;br /&gt;
La funzione andor(a,b) è definita come segue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
andor(a, b){&lt;br /&gt;
	c = a | b;&lt;br /&gt;
	b = b &amp;amp; a;&lt;br /&gt;
	a = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Posto a = 0, non avremo altro che:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
andor(a, b){&lt;br /&gt;
	c = b;&lt;br /&gt;
	b = 0;&lt;br /&gt;
	a = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Quindi la funzione andor, non è altro che una semplice test&amp;amp;set.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
a = 0;&lt;br /&gt;
b = 1;&lt;br /&gt;
&lt;br /&gt;
Process P {&lt;br /&gt;
	do{&lt;br /&gt;
		andor(a, b);&lt;br /&gt;
	} while(!a);&lt;br /&gt;
	//critical section&lt;br /&gt;
	b = 1;&lt;br /&gt;
	// non-critical section&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[User:S.G|S.G]] ([[User talk:S.G|talk]]) 14:39, 18 November 2016 (CET)&amp;lt;br&amp;gt;&lt;br /&gt;
Ho scritto una possibile soluzione dell'esercizio c.2. Iniziare qui una discussione&lt;br /&gt;
&lt;br /&gt;
'''a &amp;amp;egrave; errato, pensateci meglio. Per B la variabile a va indicata come locale al processo P (generico) che deve avere un loop infinito.''' [[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 19:25, 21 November 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
Quindi una soluzione del genere potrebbe andar bene? [[User:S.G|S.G]] ([[User talk:S.G|talk]]) 09:23, 22 November 2016 (CET)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
b = 0;&lt;br /&gt;
&lt;br /&gt;
Process P {&lt;br /&gt;
	do{&lt;br /&gt;
		a = 1;&lt;br /&gt;
		dualshift(a, b);&lt;br /&gt;
	} while(b != 128);&lt;br /&gt;
	//critical section&lt;br /&gt;
	b = 0;&lt;br /&gt;
	// non-critical section&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dualshift(a,b) |xxxxxxxx|yyyyyyyy| -&amp;gt; |0xxxxxxx|xyyyyyyy|&lt;br /&gt;
G=1&lt;br /&gt;
MUTEXIN&lt;br /&gt;
 do:&lt;br /&gt;
   L=0&lt;br /&gt;
   dualshift(G,L)&lt;br /&gt;
 while L==0;&lt;br /&gt;
MUTEXOUT&lt;br /&gt;
G=1&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Esercizio g.1 ==&lt;br /&gt;
&lt;br /&gt;
a) Sia data la seguente stringa di riferimenti: 123451234123121.&lt;br /&gt;
mostrare il comportamento degli algoritmi MIN e LRU quando operano su una memoria di 3 frame.&lt;br /&gt;
b) Data una memoria di 4 frame contenente la pagina 4 nel frame 1, la pagina 3 nel frame 2, la pagina 2 nel frame 3 e infine la&lt;br /&gt;
pagina 1 nel frame 4. Mostrare una stringa di riferimenti di un programma che usi 5 pagine (esiste la pagina 5 non ancora mappata in&lt;br /&gt;
memoria oltre alle 4 cariate nei frame) e che consenta alla fine dell'esecuzione di avere tutte le pagine nel frame di indice&lt;br /&gt;
corrispondente. La pagina 1 nel frame 1, la pagina 2 nel frame 2 e cosi' via.&lt;br /&gt;
&lt;br /&gt;
Esecuzione algoritmo MIN&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
| 1 | 2 | 3 | 4 | 5 | 1 | 2 | 3 | 4 | 1 | 2 | 3 | 1 | 2 | 1 |&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
|   | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
|   |   | 3 | 4 | 5 | 5 | 5 | 3 | 4 | 4 | 4 | 3 | 3 | 3 | 3 |&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Esecuzione algoritmo LRU&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
| 1 | 2 | 3 | 4 | 5 | 1 | 2 | 3 | 4 | 1 | 2 | 3 | 1 | 2 | 1 |&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
| 1 | 1 | 1 | 4 | 4 | 4 | 2 | 2 | 2 | 1 | 1 | 1 | 1 | 1 | 1 |&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
|   | 2 | 2 | 2 | 5 | 5 | 5 | 3 | 3 | 3 | 2 | 2 | 2 | 2 | 2 |&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
|   |   | 3 | 3 | 3 | 1 | 1 | 1 | 4 | 4 | 4 | 3 | 3 | 3 | 3 |&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Una possibile sequenza utilizzando l'algoritmo con sostituzione FIFO&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
| 4 | 3 | 2 | 1 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | 4 | 5 |&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[User:S.G|S.G]] ([[User talk:S.G|talk]]) 12:19, 19 November 2016 (CET)&amp;lt;br&amp;gt;&lt;br /&gt;
Ho scritto una possibile soluzione dell'esercizio g.1. Iniziare qui una discussione&lt;br /&gt;
&lt;br /&gt;
'''Cosa significa proporre a novembre le risposte a quesiti relativi a argomenti che spieghero' a marzo 2017?'''[[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 19:20, 21 November 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
Mi scuso per l'inconveniente, avendo seguito il corso di SO 2015/2016 ho pensato di creare una sezione PROVE SVOLTE che fosse ben visibile a tutti i corsi sia per gli anni precedenti sia per gli anni successi&lt;br /&gt;
[[User:S.G|S.G]] ([[User talk:S.G|talk]]) 09:17, 22 November 2016 (CET)&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Esercizio g.2 ==&lt;br /&gt;
&lt;br /&gt;
a) Se un sistema ha una RAM molto ampia puo' non essere utile usare la memoria virtuale. In questo caso ha senso egualmente&lt;br /&gt;
usare la paginazione di memoria? Perche'?&lt;br /&gt;
&lt;br /&gt;
b) Perche' i metodi di sincronizzazione tipo test&amp;amp;set (detti anche spinlock) assumono grande rilevanza nella scrittura di sistemi&lt;br /&gt;
operativi multiprocessore?&lt;br /&gt;
&lt;br /&gt;
c) Confrontare gli alogritmi di scheduling roud robin e a priorita' statica. Indicare in quali casi sono da usare algoritmi di tipo round&lt;br /&gt;
robin e quando quelli a priorita' statica.&lt;br /&gt;
&lt;br /&gt;
Svolgimento&lt;br /&gt;
&lt;br /&gt;
a)	Paginazione è un metodo di gestione della memoria che permette che lo spazio degli indirizzi fisici di un processo&lt;br /&gt;
	non sia contiguo, inoltre ha i seguenti vantaggi:&lt;br /&gt;
	- riduce il fenomeno della frammentazione interna&lt;br /&gt;
	- minimizza (elimina) il fenomeno della frammentazione esterna&lt;br /&gt;
&lt;br /&gt;
b)	Perchè permette di risolve il problema della sezione critica in modo relativamente semplice. Infatti l'istruzione&lt;br /&gt;
	test&amp;amp;set è eseguita atomicamente, cioè come un'unita non soggetta a interruzzioni; quindi se si eseguono contemporaneamente&lt;br /&gt;
	due istruzioni test&amp;amp;set, ciascuna su processori diversi, queste vengono eseguite in modo sequenziale in un ordine arbitrario.&lt;br /&gt;
&lt;br /&gt;
c)	Scheduling a priorità statica associa una priorità a ogni processo e si assegna la CPU&lt;br /&gt;
	al processo con priorità più alta; i processi con priorità uguali si ordinano secondo una politica&lt;br /&gt;
	FCFS. Questo algoritmo può essere sia con prelazione sia senza prelazione, inoltre soffre dell'attesa&lt;br /&gt;
	indefinita (starvation). Infatti un flusso costante di processi con priorità maggiore può impedire a &lt;br /&gt;
	un processo con priorità più bassa di accedere alla CPU. E' possibile utilizzare questo algoritmo quando&lt;br /&gt;
	alcuni processi hanno più importanza di altri. es: nel caso di sistemi multimediali, come una smart TV, il&lt;br /&gt;
	processo di riproduzione video è più importante rispetto ad un processo gestore di posta, altrimenti il flusso video&lt;br /&gt;
	potrebbe essere discontinuo.&lt;br /&gt;
	&lt;br /&gt;
	Scheduling circolare (round-robin) è un algoritmo con prelazione in cui ogni processo viene eseguito al massimo&lt;br /&gt;
	per un quanto di tempo. E' bene che il quanto di tempo sia maggiore del tempo necessario al cambio di contesto.&lt;br /&gt;
	Questo algoritmo a differenza dello scheduling a priorità statica non soffre di starvation. E' possibile utilizzare&lt;br /&gt;
	questo algoritmo in sistemi in cui si pretende un finto parallelismo. es: nel caso in cui più studenti accedano ad &lt;br /&gt;
	una determinata macchina del laboratorio.&lt;br /&gt;
&lt;br /&gt;
[[User:S.G|S.G]] ([[User talk:S.G|talk]]) 11:24, 21 November 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
'''Cosa significa proporre a novembre le risposte a quesiti relativi a argomenti che spieghero' nel secondo semestre?'''[[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 19:21, 21 November 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
Mi scuso per l'inconveniente, avendo seguito il corso di SO 2015/2016 ho pensato di creare una sezione PROVE SVOLTE che fosse ben visibile a tutti i corsi sia per gli anni precedenti sia per gli anni successi. [[User:S.G|S.G]] ([[User talk:S.G|talk]]) 09:19, 22 November 2016 (CET)&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2015.01.20&amp;diff=1914</id>
		<title>Prova teorica 2015.01.20</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2015.01.20&amp;diff=1914"/>
		<updated>2017-06-13T20:25:56Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: /* Soluzione di FedericoB */ Corretta soluzione precendente che era sbagliata, quella attuale si può migliorare.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Testo: [http://www.cs.unibo.it/~renzo/so/compiti/2015.01.20.tot.pdf]&lt;br /&gt;
&lt;br /&gt;
== Esercizio c.1 ==&lt;br /&gt;
===Soluzione di S.G===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Esercizio c.1: Scrivere il monitor lwlrbb. Il monitor deve implementare le seguenti procedure entry:&lt;br /&gt;
 * void write(generic_type val);&lt;br /&gt;
 * generic_type read(void);&lt;br /&gt;
 * Il lwlrbb si comporta come un bounded buffer di MAX elementi che coordina l'attivita' di numerosi processi&lt;br /&gt;
 * produttori/scrittori e numerosi lettori/consumatori. lwlrbb ammette un numero massimo (sempre MAX) di lettori &lt;br /&gt;
 * e scrittori in attesa. Se il buffer e' vuoto e ci sono piu' gia' MAX lettori in attesa, il lettore che e' in &lt;br /&gt;
 * attesa da piu' tempo esce resituendo NULL. In ugual modo se il buffer e' completamente pieno e ci sono gia' MAX &lt;br /&gt;
 * scrittori che attendono di scrivere viene perduto il valore che da piu' tempo nel buffer attende di venir letto, &lt;br /&gt;
 * il primo processo in attesa di scrivere puo' cosi' scrivere il suo elemento nel buffer e sbloccarsi.&lt;br /&gt;
 */&lt;br /&gt;
#define MAX &lt;br /&gt;
&lt;br /&gt;
monitor lwlrbb{&lt;br /&gt;
	generic_type bb[];&lt;br /&gt;
	int count, rear, front, nw, nr;&lt;br /&gt;
	bool exitNull;&lt;br /&gt;
	conditions lw, lr;&lt;br /&gt;
	&lt;br /&gt;
	void lwlrbb(void){&lt;br /&gt;
		bb = new generic_type[MAX];&lt;br /&gt;
		exitNull = FALSE;&lt;br /&gt;
		rear = count = front = nw = nr = 0;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure entry void write(generic_type val){&lt;br /&gt;
		if(count == MAX){				// Il buffer è completamente pieno&lt;br /&gt;
			if(nw == MAX){				// ci sono già MAX scrittori che attendono di scrivere&lt;br /&gt;
				rear = (rear + 1) % MAX;	// Salto l'elemento da leggere da più tempo&lt;br /&gt;
				count--;&lt;br /&gt;
				lw.signal();			// il primo processo in attesa di scrivere puo' sbloccarsi.&lt;br /&gt;
			}&lt;br /&gt;
			nw++;&lt;br /&gt;
			lw.wait();&lt;br /&gt;
			nw--;&lt;br /&gt;
		}&lt;br /&gt;
		bb[front] = val;				// Il buffer non è pieno, scrivo&lt;br /&gt;
		count++;&lt;br /&gt;
		front = (front + 1) % MAX;&lt;br /&gt;
		lr.signal();&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure entry generic_type read(void){&lt;br /&gt;
		generic_type retval;&lt;br /&gt;
		if(count == 0){				// Il buffer è vuoto&lt;br /&gt;
			if(nr == MAX){			// ci sono gia MAX lettori in attesa&lt;br /&gt;
				exitNull = TRUE;	// quindi riattivio il lettore che e' in attesa da piu' tempo, restituendo NULL&lt;br /&gt;
				lr.signal();&lt;br /&gt;
			}&lt;br /&gt;
			nr++;				// incremento il contatore dei lettori in coda&lt;br /&gt;
			lr.wait();			// metto in attesa il lettore&lt;br /&gt;
							// Riprendo l'esecuzione da dove era stata interrotta&lt;br /&gt;
			nr--;				// decremento il contatore dei lettori in coda&lt;br /&gt;
			if(exitNull){			// Controllo se è una richiesta di uscita forzata&lt;br /&gt;
				exitNull = FALSE;&lt;br /&gt;
				return NULL;&lt;br /&gt;
			}&lt;br /&gt;
		}			&lt;br /&gt;
		count--;				// Il buffer non è vuoto, leggo&lt;br /&gt;
		retval = bb[rear];&lt;br /&gt;
		rear = (rear + 1) % MAX;&lt;br /&gt;
		lw.signal();&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[User:S.G|S.G]] ([[User talk:S.G|talk]]) 14:39, 18 November 2016 (CET)&amp;lt;br&amp;gt;&lt;br /&gt;
Ho scritto una possibile soluzione dell'esercizio c.1. Iniziare qui una discussione&lt;br /&gt;
&lt;br /&gt;
'''&amp;amp;egrave; uno stack! bounded buffer deve implementare una coda.''' [[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 19:29, 21 November 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
Ho implementato la coda. [[User:S.G|S.G]] ([[User talk:S.G|talk]]) 10:12, 22 November 2016 (CET)&lt;br /&gt;
----&lt;br /&gt;
===Soluzione di BAGG===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define MAX 42&lt;br /&gt;
&lt;br /&gt;
monitor lwlrbb {&lt;br /&gt;
    generic_type bb[] ;&lt;br /&gt;
    int count, front, rear ; 	/* rispettivamente: numero elementi, dove scrivere nel buffer, dove leggere nel buffer */&lt;br /&gt;
    int nw, nr ; 		/* rispettivamente numero di writer e reader */&lt;br /&gt;
    bool exit ; 		/* server ai reader per sapere se devono ritornare NULL al loro rientro nel monitor  */&lt;br /&gt;
    condition canr, canw ;&lt;br /&gt;
&lt;br /&gt;
    lwlrbb(void) {&lt;br /&gt;
        bb = new generic_type[MAX] ;&lt;br /&gt;
        count = front = rear = nw = nr = exit = 0 ;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    entry void write(generic_type val) {&lt;br /&gt;
        if(count == MAX) {			/* se count è MAX non si può scrivere  */&lt;br /&gt;
            if(nw == MAX) {			/* se la coda è piena devo fare spazio */	&lt;br /&gt;
                rear = (rear + 1)%MAX ;         /* rear indica il più vecchio elemento ancora da leggere, lo incremento --&amp;gt; salto l'elemento */&lt;br /&gt;
                count -= 1 ;&lt;br /&gt;
                nw += 1 ;			/* incremento prima perchè anche se la coda è piena ora dopo la signal ci sarà un posto...  */&lt;br /&gt;
                canw.signal() ;			/* ora il primo che aspettava può scrivere --&amp;gt; lo sveglio  */&lt;br /&gt;
                canw.wait() ;&lt;br /&gt;
                nw -= 1 ;&lt;br /&gt;
            } else {				/* se la coda non è piena devo comunque aspettare...  */&lt;br /&gt;
                nw += 1 ;&lt;br /&gt;
                canw.wait() ;&lt;br /&gt;
                nw -=1 ;&lt;br /&gt;
            }&lt;br /&gt;
        } &lt;br /&gt;
	bb[front] = val ;					&lt;br /&gt;
        front = (front + 1)%MAX ;&lt;br /&gt;
        count += 1 ;&lt;br /&gt;
        canr.signal() ;				/* potrebbe esserci un reader che aspetta --&amp;gt; segnalo che ho scritto e si può leggere  */&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    entry generic_type read(void) {&lt;br /&gt;
        if(count == 0) {			/* se non c'è nulla da leggere allora devo aspettare  */&lt;br /&gt;
            if(nr == MAX) {			/* se la coda è piena setto exit, che i reader controllano per sapere se sono stati svegliati per ritornare NULL  */&lt;br /&gt;
                exit = 1 ;&lt;br /&gt;
                canr.signal() ;		        /* sveglio il condannato...  */&lt;br /&gt;
            } nr += 1 ;				/* mi accodo... */&lt;br /&gt;
            canr.wait() ;&lt;br /&gt;
            nr -= 1 ;&lt;br /&gt;
            if(exit) {				/* se sono il condannato resetto la variabile ed esco...  */&lt;br /&gt;
                 exit = 0 ;&lt;br /&gt;
                 return NULL ;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        generic_type r = bb[rear] ;&lt;br /&gt;
        rear = (rear + 1)%MAX ;&lt;br /&gt;
        count -= 1 ;&lt;br /&gt;
        canw.signal() ;				/* ho letto, quindi si è liberato un posto e almeno un writer può scrivere... */&lt;br /&gt;
        return r ;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
Pseudocodice simil python. Forse soluzione troppo lunga, si può migliorare.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;Python&amp;quot;&amp;gt;&lt;br /&gt;
monitor lwlrbb&lt;br /&gt;
	&lt;br /&gt;
	Queue[generic_type] buffer&lt;br /&gt;
	int numWriter, numReader, waitingReader, waitingWriters&lt;br /&gt;
	condition ok2write, ok2read &lt;br /&gt;
	int max;&lt;br /&gt;
	&lt;br /&gt;
	lwlrbb (int max) &lt;br /&gt;
		self.max = max&lt;br /&gt;
		buffer = new List(max)&lt;br /&gt;
		numWriter = 0&lt;br /&gt;
		numReader = 0&lt;br /&gt;
		waitingReader = 0&lt;br /&gt;
		write_last = false;&lt;br /&gt;
	&lt;br /&gt;
	@entry&lt;br /&gt;
	void write(generic_type val) &lt;br /&gt;
		if (buffer.size()&amp;gt;=MAX) and (waitingWriters&amp;gt;Max)&lt;br /&gt;
				#wakeup for discard&lt;br /&gt;
				ok2write.signal()&lt;br /&gt;
		if (numWriter&amp;gt;0 or numReader&amp;gt;0) or (buffer.size&amp;gt;=MAX)&lt;br /&gt;
			waitingWriters+=1&lt;br /&gt;
			ok2write.wait()&lt;br /&gt;
			waitingWriters-=1&lt;br /&gt;
			if (waitingReader &amp;gt;=Max)&lt;br /&gt;
				buffer.dequeue()&lt;br /&gt;
				ok2write.signal()&lt;br /&gt;
				return;&lt;br /&gt;
		numWriter+=1&lt;br /&gt;
		buffer.enqueue(val)&lt;br /&gt;
		numWriter-=1&lt;br /&gt;
		ok2write.signal()&lt;br /&gt;
		if (waitingReader&amp;gt;0) ok2read.signal()&lt;br /&gt;
		else if (waitingWriters&amp;gt;0) ok2write.signal()&lt;br /&gt;
	&lt;br /&gt;
	@entry&lt;br /&gt;
	generic_type read() &lt;br /&gt;
		if (buffer.isEmpty() &lt;br /&gt;
			if (waitingReader&amp;gt;Max):&lt;br /&gt;
				#do not decrement waitingReader&lt;br /&gt;
				ok2read.signal()&lt;br /&gt;
			else &lt;br /&gt;
				waitingReader+=1&lt;br /&gt;
				ok2read.wait()&lt;br /&gt;
		if (nwriter&amp;gt;0 or waitingWriters&amp;gt;0) &lt;br /&gt;
			waitingReader +=1&lt;br /&gt;
			ok2read.wait()&lt;br /&gt;
			waitingReader-=1&lt;br /&gt;
			if (waitingReader &amp;gt;= max) &lt;br /&gt;
				ok2read.signal()&lt;br /&gt;
				return null&lt;br /&gt;
		else &lt;br /&gt;
			numReader+=1&lt;br /&gt;
			element = buffer.dequeue()&lt;br /&gt;
			numReader-=1&lt;br /&gt;
			if (waitingReader&amp;gt;0) ok2read.signal()&lt;br /&gt;
			else if (waitingWriters&amp;gt;0) ok2write.signal()&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Soluzione di sav, correggetemi se sbaglio ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;Python&amp;quot;&amp;gt;&lt;br /&gt;
monitor lwlbb &lt;br /&gt;
{&lt;br /&gt;
      buffer buf, wr  //buf = buffer di elementi e wr buffer di lettori&lt;br /&gt;
      condition oktoread, oktowrite&lt;br /&gt;
   &lt;br /&gt;
  Procedure entry read {&lt;br /&gt;
  &lt;br /&gt;
       if ( wr.len() &amp;lt; MAXLETTORI)&lt;br /&gt;
           if (buf.len() == 0)&lt;br /&gt;
                wr. enqueue(lettore)&lt;br /&gt;
                oktoread.wait(lettore)&lt;br /&gt;
                wr.dequeue(lettore)&lt;br /&gt;
                buf.dequeue()&lt;br /&gt;
           oktoread.signal()&lt;br /&gt;
       wr.dequeue()&lt;br /&gt;
       wr.enqueue(lettore)&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
   procedure entry write (val) {        //lo scrittore non resterà mai in attesa perché se non c'è spazio nel buffer toglie &lt;br /&gt;
                                        //il primo elemento inserito e mette il suo nuovo e sblocca un eventuale lettore in attesa&lt;br /&gt;
&lt;br /&gt;
        if (buf.len()&amp;lt; MAXBUFFER)&lt;br /&gt;
                 buf.enqueue(val)&lt;br /&gt;
                 oktoread.signal()&lt;br /&gt;
        buf.dequeue()&lt;br /&gt;
        buf.enqueue(val)&lt;br /&gt;
        oktoread.signal()&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Esercizio c.2 ==&lt;br /&gt;
===Soluzione di S.G===&lt;br /&gt;
a) Si consideri la funzione atomica dualshift(a,b) che opera su due operandi di tipo byte passati per indirizzo. L'operazione fa lo shift a&lt;br /&gt;
destra dei due operandi. Il bit piu' significativo di a viene posto a zero, il bit piu' significativo di b diviene quello che all'atto della&lt;br /&gt;
attivazione di dualshift era il bit meno significativo di a.&lt;br /&gt;
es. se a vale 6 e b vale 4 dopo la chiamata di dualshift(a,b) a vale 3 e b 2. Se a vale 5 e b 6 dopo la chiamata dualshift (a,b) a vale 2&lt;br /&gt;
e b vale 131 (128+3).&lt;br /&gt;
La funzione dualshift puo' essere usata al posto della test&amp;amp;set per la sincronizzazione fra processi? Dimostrare la risposta.&lt;br /&gt;
b) Si consideri ora la funzione andor(a,b) che opera su due opera su due parametri di tipo booleano passati per indirizzo e cosi'&lt;br /&gt;
definita:&lt;br /&gt;
andor(a,b)=&amp;lt;c=a or b; b=a and b; a=c&amp;gt;&lt;br /&gt;
Puo' la funzione andor essere usata al posto della test&amp;amp;set per la sincronizzazione fra processi? Dimostrare la risposta.&lt;br /&gt;
Si ricorda che le operazioni di assegnazione di valori costanti a variabili vengono considerati atomici.&lt;br /&gt;
&lt;br /&gt;
Svolgimento&lt;br /&gt;
&lt;br /&gt;
a) Non è possibile utilizzare la funzione atomica dualshift(a,b) in quanto per la definizione di test&amp;amp;set(a,b)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
test&amp;amp;set(a,b){&lt;br /&gt;
    a = b;&lt;br /&gt;
    b = X;   // Dove X è un valore costante&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
La funzione dualshift dovrebbe memorizzare (in qualche modo) il valore precedente di a (o b), e successivamente settare a (o b) ad un valore costante. Questo non accade perchè la stessa funzione divide per 2 i valori di a e b ogni volta che viene richiamata (caso particolare in cui venga copiato il bit meno significativo di a nel bit più significativo di b). Quindi è impossibile soddisfare la richiesta.&lt;br /&gt;
&lt;br /&gt;
b) La funzione andor(a,b) può essere utilizzata al posto della test&amp;amp;set per la sincronizzazione fra processi.&lt;br /&gt;
Dimostrazione.&lt;br /&gt;
La funzione andor(a,b) è definita come segue:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
andor(a, b){&lt;br /&gt;
	c = a | b;&lt;br /&gt;
	b = b &amp;amp; a;&lt;br /&gt;
	a = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Posto a = 0, non avremo altro che:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
andor(a, b){&lt;br /&gt;
	c = b;&lt;br /&gt;
	b = 0;&lt;br /&gt;
	a = c;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Quindi la funzione andor, non è altro che una semplice test&amp;amp;set.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
a = 0;&lt;br /&gt;
b = 1;&lt;br /&gt;
&lt;br /&gt;
Process P {&lt;br /&gt;
	do{&lt;br /&gt;
		andor(a, b);&lt;br /&gt;
	} while(!a);&lt;br /&gt;
	//critical section&lt;br /&gt;
	b = 1;&lt;br /&gt;
	// non-critical section&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[User:S.G|S.G]] ([[User talk:S.G|talk]]) 14:39, 18 November 2016 (CET)&amp;lt;br&amp;gt;&lt;br /&gt;
Ho scritto una possibile soluzione dell'esercizio c.2. Iniziare qui una discussione&lt;br /&gt;
&lt;br /&gt;
'''a &amp;amp;egrave; errato, pensateci meglio. Per B la variabile a va indicata come locale al processo P (generico) che deve avere un loop infinito.''' [[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 19:25, 21 November 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
Quindi una soluzione del genere potrebbe andar bene? [[User:S.G|S.G]] ([[User talk:S.G|talk]]) 09:23, 22 November 2016 (CET)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
b = 0;&lt;br /&gt;
&lt;br /&gt;
Process P {&lt;br /&gt;
	do{&lt;br /&gt;
		a = 1;&lt;br /&gt;
		dualshift(a, b);&lt;br /&gt;
	} while(b != 128);&lt;br /&gt;
	//critical section&lt;br /&gt;
	b = 0;&lt;br /&gt;
	// non-critical section&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dualshift(a,b) |xxxxxxxx|yyyyyyyy| -&amp;gt; |0xxxxxxx|xyyyyyyy|&lt;br /&gt;
G=1&lt;br /&gt;
MUTEXIN&lt;br /&gt;
 do:&lt;br /&gt;
   L=0&lt;br /&gt;
   dualshift(G,L)&lt;br /&gt;
 while L==0;&lt;br /&gt;
MUTEXOUT&lt;br /&gt;
G=1&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Esercizio g.1 ==&lt;br /&gt;
&lt;br /&gt;
a) Sia data la seguente stringa di riferimenti: 123451234123121.&lt;br /&gt;
mostrare il comportamento degli algoritmi MIN e LRU quando operano su una memoria di 3 frame.&lt;br /&gt;
b) Data una memoria di 4 frame contenente la pagina 4 nel frame 1, la pagina 3 nel frame 2, la pagina 2 nel frame 3 e infine la&lt;br /&gt;
pagina 1 nel frame 4. Mostrare una stringa di riferimenti di un programma che usi 5 pagine (esiste la pagina 5 non ancora mappata in&lt;br /&gt;
memoria oltre alle 4 cariate nei frame) e che consenta alla fine dell'esecuzione di avere tutte le pagine nel frame di indice&lt;br /&gt;
corrispondente. La pagina 1 nel frame 1, la pagina 2 nel frame 2 e cosi' via.&lt;br /&gt;
&lt;br /&gt;
Esecuzione algoritmo MIN&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
| 1 | 2 | 3 | 4 | 5 | 1 | 2 | 3 | 4 | 1 | 2 | 3 | 1 | 2 | 1 |&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
|   | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
|   |   | 3 | 4 | 5 | 5 | 5 | 3 | 4 | 4 | 4 | 3 | 3 | 3 | 3 |&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Esecuzione algoritmo LRU&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
| 1 | 2 | 3 | 4 | 5 | 1 | 2 | 3 | 4 | 1 | 2 | 3 | 1 | 2 | 1 |&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
| 1 | 1 | 1 | 4 | 4 | 4 | 2 | 2 | 2 | 1 | 1 | 1 | 1 | 1 | 1 |&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
|   | 2 | 2 | 2 | 5 | 5 | 5 | 3 | 3 | 3 | 2 | 2 | 2 | 2 | 2 |&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
|   |   | 3 | 3 | 3 | 1 | 1 | 1 | 4 | 4 | 4 | 3 | 3 | 3 | 3 |&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Una possibile sequenza utilizzando l'algoritmo con sostituzione FIFO&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
| 4 | 3 | 2 | 1 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | 4 | 5 |&lt;br /&gt;
-------------------------------------------------------------&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[User:S.G|S.G]] ([[User talk:S.G|talk]]) 12:19, 19 November 2016 (CET)&amp;lt;br&amp;gt;&lt;br /&gt;
Ho scritto una possibile soluzione dell'esercizio g.1. Iniziare qui una discussione&lt;br /&gt;
&lt;br /&gt;
'''Cosa significa proporre a novembre le risposte a quesiti relativi a argomenti che spieghero' a marzo 2017?'''[[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 19:20, 21 November 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
Mi scuso per l'inconveniente, avendo seguito il corso di SO 2015/2016 ho pensato di creare una sezione PROVE SVOLTE che fosse ben visibile a tutti i corsi sia per gli anni precedenti sia per gli anni successi&lt;br /&gt;
[[User:S.G|S.G]] ([[User talk:S.G|talk]]) 09:17, 22 November 2016 (CET)&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Esercizio g.2 ==&lt;br /&gt;
&lt;br /&gt;
a) Se un sistema ha una RAM molto ampia puo' non essere utile usare la memoria virtuale. In questo caso ha senso egualmente&lt;br /&gt;
usare la paginazione di memoria? Perche'?&lt;br /&gt;
&lt;br /&gt;
b) Perche' i metodi di sincronizzazione tipo test&amp;amp;set (detti anche spinlock) assumono grande rilevanza nella scrittura di sistemi&lt;br /&gt;
operativi multiprocessore?&lt;br /&gt;
&lt;br /&gt;
c) Confrontare gli alogritmi di scheduling roud robin e a priorita' statica. Indicare in quali casi sono da usare algoritmi di tipo round&lt;br /&gt;
robin e quando quelli a priorita' statica.&lt;br /&gt;
&lt;br /&gt;
Svolgimento&lt;br /&gt;
&lt;br /&gt;
a)	Paginazione è un metodo di gestione della memoria che permette che lo spazio degli indirizzi fisici di un processo&lt;br /&gt;
	non sia contiguo, inoltre ha i seguenti vantaggi:&lt;br /&gt;
	- riduce il fenomeno della frammentazione interna&lt;br /&gt;
	- minimizza (elimina) il fenomeno della frammentazione esterna&lt;br /&gt;
&lt;br /&gt;
b)	Perchè permette di risolve il problema della sezione critica in modo relativamente semplice. Infatti l'istruzione&lt;br /&gt;
	test&amp;amp;set è eseguita atomicamente, cioè come un'unita non soggetta a interruzzioni; quindi se si eseguono contemporaneamente&lt;br /&gt;
	due istruzioni test&amp;amp;set, ciascuna su processori diversi, queste vengono eseguite in modo sequenziale in un ordine arbitrario.&lt;br /&gt;
&lt;br /&gt;
c)	Scheduling a priorità statica associa una priorità a ogni processo e si assegna la CPU&lt;br /&gt;
	al processo con priorità più alta; i processi con priorità uguali si ordinano secondo una politica&lt;br /&gt;
	FCFS. Questo algoritmo può essere sia con prelazione sia senza prelazione, inoltre soffre dell'attesa&lt;br /&gt;
	indefinita (starvation). Infatti un flusso costante di processi con priorità maggiore può impedire a &lt;br /&gt;
	un processo con priorità più bassa di accedere alla CPU. E' possibile utilizzare questo algoritmo quando&lt;br /&gt;
	alcuni processi hanno più importanza di altri. es: nel caso di sistemi multimediali, come una smart TV, il&lt;br /&gt;
	processo di riproduzione video è più importante rispetto ad un processo gestore di posta, altrimenti il flusso video&lt;br /&gt;
	potrebbe essere discontinuo.&lt;br /&gt;
	&lt;br /&gt;
	Scheduling circolare (round-robin) è un algoritmo con prelazione in cui ogni processo viene eseguito al massimo&lt;br /&gt;
	per un quanto di tempo. E' bene che il quanto di tempo sia maggiore del tempo necessario al cambio di contesto.&lt;br /&gt;
	Questo algoritmo a differenza dello scheduling a priorità statica non soffre di starvation. E' possibile utilizzare&lt;br /&gt;
	questo algoritmo in sistemi in cui si pretende un finto parallelismo. es: nel caso in cui più studenti accedano ad &lt;br /&gt;
	una determinata macchina del laboratorio.&lt;br /&gt;
&lt;br /&gt;
[[User:S.G|S.G]] ([[User talk:S.G|talk]]) 11:24, 21 November 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
'''Cosa significa proporre a novembre le risposte a quesiti relativi a argomenti che spieghero' nel secondo semestre?'''[[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 19:21, 21 November 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
Mi scuso per l'inconveniente, avendo seguito il corso di SO 2015/2016 ho pensato di creare una sezione PROVE SVOLTE che fosse ben visibile a tutti i corsi sia per gli anni precedenti sia per gli anni successi. [[User:S.G|S.G]] ([[User talk:S.G|talk]]) 09:19, 22 November 2016 (CET)&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2015.02.14&amp;diff=1913</id>
		<title>Prova teorica 2015.02.14</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2015.02.14&amp;diff=1913"/>
		<updated>2017-06-13T18:58:57Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: /* Soluzione di Silas */ Migliorata formattazione e aggiunta peer review.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Testo: [http://www.cs.unibo.it/~renzo/so/compiti/2015.02.14.tot.pdf]&lt;br /&gt;
&lt;br /&gt;
== Esercizio c.1 ==&lt;br /&gt;
===Soluzione di Silas===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
#define MAX &lt;br /&gt;
&lt;br /&gt;
monitor altcolbb{&lt;br /&gt;
	queue buff;&lt;br /&gt;
	color_t last_color; //0: red, 1:blue, -1:&amp;quot;superstate&amp;quot;, means that both red and blue are accepted&lt;br /&gt;
	int wait_red, wait_blue;&lt;br /&gt;
	condition ok2read, redok, blueok;&lt;br /&gt;
&lt;br /&gt;
	void altcolbb(void){&lt;br /&gt;
		buff = new queue();&lt;br /&gt;
		last_color = -1;&lt;br /&gt;
		wait_red = wait_blue = 0;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure entry void write(color_t color, generic_type val){&lt;br /&gt;
		if(last_color == color || buff.length() == MAX){ //we can't enqueue if the colors are the same or if the buffer is full&lt;br /&gt;
			if(color == 0){ //enqueue to &amp;quot;reds&amp;quot;&lt;br /&gt;
				wait_red++;&lt;br /&gt;
				redok.wait();&lt;br /&gt;
				wait_red--;&lt;br /&gt;
			}else{ //enqueue to &amp;quot;blues&amp;quot;&lt;br /&gt;
				wait_blue++;&lt;br /&gt;
				blueok.wait();&lt;br /&gt;
				wait_blue--;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		buff.enqueue(val); //append val and update last_color&lt;br /&gt;
		last_color = color;&lt;br /&gt;
		ok2read.signal();&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure entry generic_type read(void){&lt;br /&gt;
		if(buff.length() == 0)&lt;br /&gt;
			ok2read.wait();&lt;br /&gt;
		generic_type ret = buff.dequeue();&lt;br /&gt;
		if(buff.length() == 0)&lt;br /&gt;
			last_color = -1; //if buff is empty both colors can now be added&lt;br /&gt;
		switch(last_color){&lt;br /&gt;
			case -1: {&lt;br /&gt;
					if(wait_red&amp;gt;wait_blue) //if there are more &amp;quot;reds&amp;quot; waiting to write we signal them&lt;br /&gt;
						redok.signal();&lt;br /&gt;
					else&lt;br /&gt;
						blueok.signal(); //otherwise we signal &amp;quot;blues&amp;quot;&lt;br /&gt;
					break;&lt;br /&gt;
			}&lt;br /&gt;
			case 0: {&lt;br /&gt;
					blueok.signal();&lt;br /&gt;
					break;&lt;br /&gt;
			}&lt;br /&gt;
			case 1: {&lt;br /&gt;
					redok.signal();&lt;br /&gt;
					break;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Soluzione di MarcoNegrini===&lt;br /&gt;
I corrected Silas's version&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAX &lt;br /&gt;
 &lt;br /&gt;
monitor altcolbb{&lt;br /&gt;
	queue buff;&lt;br /&gt;
	color_t last_color; //0: red, 1:blue, -1:&amp;quot;superstate&amp;quot;, means that both red and blue are accepted&lt;br /&gt;
	condition ok2read, ok2write, redok, blueok;&lt;br /&gt;
&lt;br /&gt;
	void altcolbb(void){&lt;br /&gt;
		buff = new queue();&lt;br /&gt;
		last_color = -1;&lt;br /&gt;
	}&lt;br /&gt;
 &lt;br /&gt;
	procedure entry void write(color_t color, generic_type val){&lt;br /&gt;
		if(buff.length() == MAX){ //here waits if buff is full&lt;br /&gt;
			ok2write.wait();&lt;br /&gt;
		if(last_color==-1){ //if buff was empty it can just enqueue and exit&lt;br /&gt;
			buff.enqueue(val);&lt;br /&gt;
			last_color = color;&lt;br /&gt;
		}&lt;br /&gt;
		else{	&lt;br /&gt;
			if(color == 0){ //red section&lt;br /&gt;
				if (last_color == 0) //if last was red it needs to wait&lt;br /&gt;
					redok.wait();&lt;br /&gt;
				buff.enqueue(val); &lt;br /&gt;
				last_color = color; // it must set last color before signaling other writers&lt;br /&gt;
				blueok.signal(); //blue can now enqueue his value&lt;br /&gt;
			}else{ //blue section, same as above&lt;br /&gt;
				if (last_color == 1)&lt;br /&gt;
					blueok.wait();&lt;br /&gt;
				buff.enqueue(val);&lt;br /&gt;
				last_color = color;&lt;br /&gt;
				redok.signal();&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		ok2read.signal(); // reader can now read&lt;br /&gt;
	}&lt;br /&gt;
 &lt;br /&gt;
	procedure entry generic_type read(void){&lt;br /&gt;
		//here waits if needed, then dequeue&lt;br /&gt;
		if(buff.length() == 0) &lt;br /&gt;
			ok2read.wait();&lt;br /&gt;
		generic_type ret = buff.dequeue();&lt;br /&gt;
&lt;br /&gt;
		//here signal the one that has been waiting for more time&lt;br /&gt;
		// it MUST be of the last_color color&lt;br /&gt;
		if(buff.length() == 0){&lt;br /&gt;
			color_t tmp=last_color;&lt;br /&gt;
			last_color = -1; &lt;br /&gt;
			// last color MUST be set to -1 here because &lt;br /&gt;
			// if there were writers waiting on ok2write.wait() (full buffer case)&lt;br /&gt;
			// they woundn't be signaled here, this happens if MAX=1&lt;br /&gt;
			&lt;br /&gt;
			if (tmp == 1) //&lt;br /&gt;
				blueok.signal();&lt;br /&gt;
			else&lt;br /&gt;
				redok.signal();&lt;br /&gt;
		}&lt;br /&gt;
		ok2write.signal();&lt;br /&gt;
	}&lt;br /&gt;
 &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
relating to Silas and Marco solution. A bounded buffer is a queue, so i think is wrong to use last_color as head element color when it's used as tail element color. &lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
#define MAX&lt;br /&gt;
&lt;br /&gt;
monitor altcobb {&lt;br /&gt;
	deque buffer&lt;br /&gt;
	condition waitRed, waitBlue, ok2read, ok2write&lt;br /&gt;
	altcobb() {&lt;br /&gt;
		buffer = new Deque()&lt;br /&gt;
		&lt;br /&gt;
	}&lt;br /&gt;
	procedure entry void write(colot_t color,generic_type val) {&lt;br /&gt;
		if (deque.getSize()==MAX) ok2write.wait()&lt;br /&gt;
		if (buffer.getSize==0) queue.addLast((val,color))&lt;br /&gt;
		else &lt;br /&gt;
			if (color==RED)&lt;br /&gt;
				if (buffer.getLast().color==BLUE)&lt;br /&gt;
					queue.addLast((val,color))&lt;br /&gt;
					waitRed.signal()&lt;br /&gt;
				else &lt;br /&gt;
					waitBlue.wait()&lt;br /&gt;
					queue.addLast((val,color))&lt;br /&gt;
			else&lt;br /&gt;
				if (buffer.getLast().color==RED)&lt;br /&gt;
					queue.addLast((val,color))&lt;br /&gt;
					waitBlue.signal()&lt;br /&gt;
				else &lt;br /&gt;
					waitRed.wait()&lt;br /&gt;
					queue.addLast((val,color))&lt;br /&gt;
		ok2read.signal()&lt;br /&gt;
	}&lt;br /&gt;
	procedure entry generic_type read() {&lt;br /&gt;
		if (deque.isEmpty()) ok2read.wait()&lt;br /&gt;
		element = deque.dequeueFirst();&lt;br /&gt;
		ok2write.signal()&lt;br /&gt;
		return element;&lt;br /&gt;
                #needed explanation: after the ok2write.signal() the current process will be put in wait state in the urgent stack, &lt;br /&gt;
                #but after the woke up process has exited the monitor this process will be woke up and it will return the element.&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Esercizio c.2 ==&lt;br /&gt;
===Soluzione di Silas===&lt;br /&gt;
I semafori garantiscono che A e B vengano eseguiti in maniera sequenziale e che accedano in maniera mutualmente esclusiva ad n, quindi l'unica variazione possibile è il thread di inizio per la sequenza. &amp;lt;br&amp;gt;&lt;br /&gt;
Nel caso sia A ad accedere per primo alla CS si ha: n = ((1*2)+2)*3 = 12 &amp;lt;br&amp;gt;&lt;br /&gt;
Nel caso sia invece B ad accedere per primo alla C si ha: n = (((0*2)+1)*3)+2 = 5 &amp;lt;br&amp;gt;&lt;br /&gt;
Quindi n = 12 e n = 5 sono i due possibili valori. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anche secondo me è così --[[User:FedericoB|FedericoB]] ([[User talk:FedericoB|talk]]) 20:58, 13 June 2017 (CEST)&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2015.02.14&amp;diff=1912</id>
		<title>Prova teorica 2015.02.14</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_teorica_2015.02.14&amp;diff=1912"/>
		<updated>2017-06-13T16:33:04Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: /* Esercizio c.1 */ Aggiunta soluzione&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Testo: [http://www.cs.unibo.it/~renzo/so/compiti/2015.02.14.tot.pdf]&lt;br /&gt;
&lt;br /&gt;
== Esercizio c.1 ==&lt;br /&gt;
===Soluzione di Silas===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
#define MAX &lt;br /&gt;
&lt;br /&gt;
monitor altcolbb{&lt;br /&gt;
	queue buff;&lt;br /&gt;
	color_t last_color; //0: red, 1:blue, -1:&amp;quot;superstate&amp;quot;, means that both red and blue are accepted&lt;br /&gt;
	int wait_red, wait_blue;&lt;br /&gt;
	condition ok2read, redok, blueok;&lt;br /&gt;
&lt;br /&gt;
	void altcolbb(void){&lt;br /&gt;
		buff = new queue();&lt;br /&gt;
		last_color = -1;&lt;br /&gt;
		wait_red = wait_blue = 0;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure entry void write(color_t color, generic_type val){&lt;br /&gt;
		if(last_color == color || buff.length() == MAX){ //we can't enqueue if the colors are the same or if the buffer is full&lt;br /&gt;
			if(color == 0){ //enqueue to &amp;quot;reds&amp;quot;&lt;br /&gt;
				wait_red++;&lt;br /&gt;
				redok.wait();&lt;br /&gt;
				wait_red--;&lt;br /&gt;
			}else{ //enqueue to &amp;quot;blues&amp;quot;&lt;br /&gt;
				wait_blue++;&lt;br /&gt;
				blueok.wait();&lt;br /&gt;
				wait_blue--;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		buff.enqueue(val); //append val and update last_color&lt;br /&gt;
		last_color = color;&lt;br /&gt;
		ok2read.signal();&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure entry generic_type read(void){&lt;br /&gt;
		if(buff.length() == 0)&lt;br /&gt;
			ok2read.wait();&lt;br /&gt;
		generic_type ret = buff.dequeue();&lt;br /&gt;
		if(buff.length() == 0)&lt;br /&gt;
			last_color = -1; //if buff is empty both colors can now be added&lt;br /&gt;
		switch(last_color){&lt;br /&gt;
			case -1: {&lt;br /&gt;
					if(wait_red&amp;gt;wait_blue) //if there are more &amp;quot;reds&amp;quot; waiting to write we signal them&lt;br /&gt;
						redok.signal();&lt;br /&gt;
					else&lt;br /&gt;
						blueok.signal(); //otherwise we signal &amp;quot;blues&amp;quot;&lt;br /&gt;
					break;&lt;br /&gt;
			}&lt;br /&gt;
			case 0: {&lt;br /&gt;
					blueok.signal();&lt;br /&gt;
					break;&lt;br /&gt;
			}&lt;br /&gt;
			case 1: {&lt;br /&gt;
					redok.signal();&lt;br /&gt;
					break;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Soluzione di MarcoNegrini===&lt;br /&gt;
I corrected Silas's version&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAX &lt;br /&gt;
 &lt;br /&gt;
monitor altcolbb{&lt;br /&gt;
	queue buff;&lt;br /&gt;
	color_t last_color; //0: red, 1:blue, -1:&amp;quot;superstate&amp;quot;, means that both red and blue are accepted&lt;br /&gt;
	condition ok2read, ok2write, redok, blueok;&lt;br /&gt;
&lt;br /&gt;
	void altcolbb(void){&lt;br /&gt;
		buff = new queue();&lt;br /&gt;
		last_color = -1;&lt;br /&gt;
	}&lt;br /&gt;
 &lt;br /&gt;
	procedure entry void write(color_t color, generic_type val){&lt;br /&gt;
		if(buff.length() == MAX){ //here waits if buff is full&lt;br /&gt;
			ok2write.wait();&lt;br /&gt;
		if(last_color==-1){ //if buff was empty it can just enqueue and exit&lt;br /&gt;
			buff.enqueue(val);&lt;br /&gt;
			last_color = color;&lt;br /&gt;
		}&lt;br /&gt;
		else{	&lt;br /&gt;
			if(color == 0){ //red section&lt;br /&gt;
				if (last_color == 0) //if last was red it needs to wait&lt;br /&gt;
					redok.wait();&lt;br /&gt;
				buff.enqueue(val); &lt;br /&gt;
				last_color = color; // it must set last color before signaling other writers&lt;br /&gt;
				blueok.signal(); //blue can now enqueue his value&lt;br /&gt;
			}else{ //blue section, same as above&lt;br /&gt;
				if (last_color == 1)&lt;br /&gt;
					blueok.wait();&lt;br /&gt;
				buff.enqueue(val);&lt;br /&gt;
				last_color = color;&lt;br /&gt;
				redok.signal();&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		ok2read.signal(); // reader can now read&lt;br /&gt;
	}&lt;br /&gt;
 &lt;br /&gt;
	procedure entry generic_type read(void){&lt;br /&gt;
		//here waits if needed, then dequeue&lt;br /&gt;
		if(buff.length() == 0) &lt;br /&gt;
			ok2read.wait();&lt;br /&gt;
		generic_type ret = buff.dequeue();&lt;br /&gt;
&lt;br /&gt;
		//here signal the one that has been waiting for more time&lt;br /&gt;
		// it MUST be of the last_color color&lt;br /&gt;
		if(buff.length() == 0){&lt;br /&gt;
			color_t tmp=last_color;&lt;br /&gt;
			last_color = -1; &lt;br /&gt;
			// last color MUST be set to -1 here because &lt;br /&gt;
			// if there were writers waiting on ok2write.wait() (full buffer case)&lt;br /&gt;
			// they woundn't be signaled here, this happens if MAX=1&lt;br /&gt;
			&lt;br /&gt;
			if (tmp == 1) //&lt;br /&gt;
				blueok.signal();&lt;br /&gt;
			else&lt;br /&gt;
				redok.signal();&lt;br /&gt;
		}&lt;br /&gt;
		ok2write.signal();&lt;br /&gt;
	}&lt;br /&gt;
 &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
relating to Silas and Marco solution. A bounded buffer is a queue, so i think is wrong to use last_color as head element color when it's used as tail element color. &lt;br /&gt;
&amp;lt;source lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
#define MAX&lt;br /&gt;
&lt;br /&gt;
monitor altcobb {&lt;br /&gt;
	deque buffer&lt;br /&gt;
	condition waitRed, waitBlue, ok2read, ok2write&lt;br /&gt;
	altcobb() {&lt;br /&gt;
		buffer = new Deque()&lt;br /&gt;
		&lt;br /&gt;
	}&lt;br /&gt;
	procedure entry void write(colot_t color,generic_type val) {&lt;br /&gt;
		if (deque.getSize()==MAX) ok2write.wait()&lt;br /&gt;
		if (buffer.getSize==0) queue.addLast((val,color))&lt;br /&gt;
		else &lt;br /&gt;
			if (color==RED)&lt;br /&gt;
				if (buffer.getLast().color==BLUE)&lt;br /&gt;
					queue.addLast((val,color))&lt;br /&gt;
					waitRed.signal()&lt;br /&gt;
				else &lt;br /&gt;
					waitBlue.wait()&lt;br /&gt;
					queue.addLast((val,color))&lt;br /&gt;
			else&lt;br /&gt;
				if (buffer.getLast().color==RED)&lt;br /&gt;
					queue.addLast((val,color))&lt;br /&gt;
					waitBlue.signal()&lt;br /&gt;
				else &lt;br /&gt;
					waitRed.wait()&lt;br /&gt;
					queue.addLast((val,color))&lt;br /&gt;
		ok2read.signal()&lt;br /&gt;
	}&lt;br /&gt;
	procedure entry generic_type read() {&lt;br /&gt;
		if (deque.isEmpty()) ok2read.wait()&lt;br /&gt;
		element = deque.dequeueFirst();&lt;br /&gt;
		ok2write.signal()&lt;br /&gt;
		return element;&lt;br /&gt;
                #needed explanation: after the ok2write.signal() the current process will be put in wait state in the urgent stack, &lt;br /&gt;
                #but after the woke up process has exited the monitor this process will be woke up and it will return the element.&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Esercizio c.2 ==&lt;br /&gt;
===Soluzione di Silas===&lt;br /&gt;
I semafori garantiscono che A e B vengano eseguiti in maniera sequenziale e che accedano in maniera mutualmente esclusiva ad n, quindi l'unica variazione possibile è il thread di inizio per la sequenza.&lt;br /&gt;
Nel caso sia A ad accedere per primo alla CS si ha: n = ((1*2)+2)*3 = 12&lt;br /&gt;
Nel caso sia invece B ad accedere per primo alla C si ha: n = (((0*2)+1)*3)+2 = 5&lt;br /&gt;
Quindi n = 12 e n = 5 sono i due possibili valori.&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaTeorica_2014.02.22&amp;diff=1911</id>
		<title>ProvaTeorica 2014.02.22</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaTeorica_2014.02.22&amp;diff=1911"/>
		<updated>2017-06-12T14:12:57Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: /* Esercizio c.2 */ Aggiunta soluzione&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Testo: [http://www.cs.unibo.it/~renzo/so/compiti/2014.02.21.tot.pdf]&lt;br /&gt;
&lt;br /&gt;
==Esercizio c.1==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Coci===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
monitor bbwl {&lt;br /&gt;
	condition oktowrite;&lt;br /&gt;
	condition oktolog;&lt;br /&gt;
	condition oktoread;&lt;br /&gt;
	int logging = 0;&lt;br /&gt;
	queue q;&lt;br /&gt;
	&lt;br /&gt;
	procedure entry write (eltype elem) {&lt;br /&gt;
	if (q.length() == MAXELEM || logging)&lt;br /&gt;
			oktowrite.wait();&lt;br /&gt;
	q.enqueue (elem);&lt;br /&gt;
	logging = 1;&lt;br /&gt;
	oktolog.signal();&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure entry eltype log() {&lt;br /&gt;
	if (q.length == 0)&lt;br /&gt;
		oktolog.wait();&lt;br /&gt;
	eltype tmp = q.top();&lt;br /&gt;
	logging = 0;&lt;br /&gt;
	oktoread.signal();&lt;br /&gt;
	return tmp;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	procedure entry eltype read() {&lt;br /&gt;
	if (q.length == 0 || logging)&lt;br /&gt;
		oktoread.wait();&lt;br /&gt;
	eltype tmp = q.dequeue();&lt;br /&gt;
	oktowrite.signal();&lt;br /&gt;
	return tmp;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Save===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
condition oktowrite, oktoread, oktolog ;&lt;br /&gt;
queue buffer = MAXELEM ;&lt;br /&gt;
queue Llogger ;&lt;br /&gt;
&lt;br /&gt;
monitor bbwl&lt;br /&gt;
{&lt;br /&gt;
 procedure entry write (altype elem)&lt;br /&gt;
 {&lt;br /&gt;
	if (buffer.length &amp;gt;= MAXELEM)&lt;br /&gt;
		oktorite.wait() ;&lt;br /&gt;
	 buffer.enqueue(elem);&lt;br /&gt;
     oktolog.signal () ;	&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
procedur entry logger()                 # logger deve tenere traccia di tutti i pacchetti transitati mettendoli in una sua memoria.&lt;br /&gt;
 {&lt;br /&gt;
	if (buffer.length == 0)&lt;br /&gt;
		oktolog.wait();&lt;br /&gt;
	 Llogger.enqueue(buffer.front())    # restituisci il valore in testa di buffer e inseriscilo in coda a Llogger&lt;br /&gt;
	 ok toread.signal() ;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 procedure entry read() &lt;br /&gt;
 {&lt;br /&gt;
	if (buffer.length == 0)&lt;br /&gt;
		oktoread.wait();&lt;br /&gt;
	 buffer.dequeue()                   # restituisci il valore in testa di buffer e rimuovilo.&lt;br /&gt;
	 oktowrite.signal() ;&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
save&lt;br /&gt;
&lt;br /&gt;
==Esercizio c.2==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Coci===&lt;br /&gt;
&lt;br /&gt;
alpha(x,y): &amp;lt;x=4, y=sqrt(x)&amp;gt;&lt;br /&gt;
&lt;br /&gt;
il valore che viene salvato in y è sempre 2, quindi non va bene.&lt;br /&gt;
&lt;br /&gt;
bravo(x,y): &amp;lt;y=sqrt(x), x=4&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
xi yi xf yf&lt;br /&gt;
0  0  4  0&lt;br /&gt;
0  1  4  1&lt;br /&gt;
1  0  4  1&lt;br /&gt;
1  1  4  1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
il valore iniziale di x viene salvato in y, quindi va bene.&lt;br /&gt;
&lt;br /&gt;
charlie(x,y): &amp;lt;y=sqrt(x), x=4*y&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anche questo va bene, per le stesse ragioni di bravo.&lt;br /&gt;
&lt;br /&gt;
delta(z,t): &amp;lt;z=z xor t, t=z xor t&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
zi ti zf tf&lt;br /&gt;
0  0  0  0&lt;br /&gt;
0  1  1  0&lt;br /&gt;
1  0  1  1&lt;br /&gt;
1  1  0  1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il valore iniziale di z viene salvato in t, quindi va bene.&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Diego.tului===&lt;br /&gt;
&lt;br /&gt;
   alpha: non funziona perché y non andrà mai a 0, quindi non entrerà mai in CS.&lt;br /&gt;
&lt;br /&gt;
   bravo: funziona perché quando y va a 0 x rimane 4, e perciò finché x non ritorna 0 la CS è occupata.&lt;br /&gt;
&lt;br /&gt;
   charlie: dipende dal valore iniziale di x, perché se all'inizio x = 0 (y = 0, x = 4 * 0 = 0) quindi non ci&lt;br /&gt;
   sarà mutua esclusione.&lt;br /&gt;
&lt;br /&gt;
   delta: dipende dal valore iniziale di t, perché se all'inizio è 0, 0 xor 0 resta sempre 0, ergo come sopra.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Diego&lt;br /&gt;
&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
alpha assegna valori costanti quindi non è possibile utilizzarla per la test and set in quanto il suo risultato non può essere influenzato da parametri esterni.&lt;br /&gt;
&lt;br /&gt;
bravo(lock,vp) assegna a lock 4 e a y la radice del vecchio lock&lt;br /&gt;
Un possibile codice che utilizza bravo è il seguente:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lock=0&lt;br /&gt;
process p&lt;br /&gt;
	int vp;&lt;br /&gt;
	while(1) {&lt;br /&gt;
		do {&lt;br /&gt;
			bravo(lock,vp)&lt;br /&gt;
		} while (vp==2)&lt;br /&gt;
		//sezione critica&lt;br /&gt;
		lock=0&lt;br /&gt;
		}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
charlie(lock,vp) assegna a lock 4 moltiplicato per la radice del vecchio lock e a vp la radice del vecchio lock &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lock=1&lt;br /&gt;
process p&lt;br /&gt;
	int vp;&lt;br /&gt;
	while(1) {&lt;br /&gt;
		do {&lt;br /&gt;
			charlie(lock,vp)&lt;br /&gt;
		} while (vp!=1)&lt;br /&gt;
		//sezione critica&lt;br /&gt;
		lock=1&lt;br /&gt;
		}&lt;br /&gt;
&amp;lt;/pre&amp;gt;		&lt;br /&gt;
Ho cambiato il valore iniziale del lock per evitare l'effetto assorbente dello zero.&lt;br /&gt;
Inoltre ora quando il lock è impostato i processi in attesa avranno un vp completamente differente tra loro quindi ho dovuto utilizzare una diseguaglianza.&lt;br /&gt;
&lt;br /&gt;
delta(z,t) &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     zi ti zf tf&lt;br /&gt;
comb1 0  0  0  0&lt;br /&gt;
comb2 0  1  1  0&lt;br /&gt;
comb3 1  0  1  1&lt;br /&gt;
comb4 1  1  0  1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Impossibile perchè nessuna delle combinazioni di input e utilizzabile.&lt;br /&gt;
La combinazioni 1 e 3 non cambiano il lock e quindi non ci sarebbe mutua esclusione.&lt;br /&gt;
Utilizzando le combinazioni 2 e 3 (quindi con vp inizializzato a 1) non ci sarebbe comunque mutua esclusione perchè&lt;br /&gt;
se inizializziamo lock a 0 viene messo dalla combinazione 2 a 1 e vp=0, il primo processo ottiene la sezione critica.&lt;br /&gt;
Un altro processo si troverebbe il lock impostato a 1 e vp=1 per inizializzazione locale.&lt;br /&gt;
Verrebbe quindi utilizzata la combinazione 4 che però riporterebbe il lock a 0 e quindi un terzo processo potrebbe entrare in sezione critica.&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.1==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Coci===&lt;br /&gt;
&lt;br /&gt;
Sia n=3 il numero di pagine mantenute in memoria&lt;br /&gt;
&lt;br /&gt;
a) 123456789123456789...&lt;br /&gt;
&lt;br /&gt;
MIN = MINNUM&lt;br /&gt;
 11144777&lt;br /&gt;
  2225588&lt;br /&gt;
    333669&lt;br /&gt;
&lt;br /&gt;
b) 145231231231231...&lt;br /&gt;
&lt;br /&gt;
MINNUM&lt;br /&gt;
&lt;br /&gt;
 111232123&lt;br /&gt;
   44444444&lt;br /&gt;
     5555555&lt;br /&gt;
&lt;br /&gt;
MIN&lt;br /&gt;
 111111111&lt;br /&gt;
   44222222&lt;br /&gt;
     5333333&lt;br /&gt;
&lt;br /&gt;
Alessandro&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Diego.tului===&lt;br /&gt;
&lt;br /&gt;
Sia n=3 il numero di pagine mantenute in memoria&lt;br /&gt;
&lt;br /&gt;
la soluzione di Alessandro secondo noi presenta dei problemi, la integriamo:&lt;br /&gt;
&lt;br /&gt;
a) 1234431432431432431&lt;br /&gt;
&lt;br /&gt;
   111444444444444444&lt;br /&gt;
    222221112221112221&lt;br /&gt;
     3333333333333333&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaTeorica_2014.02.22&amp;diff=1910</id>
		<title>ProvaTeorica 2014.02.22</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaTeorica_2014.02.22&amp;diff=1910"/>
		<updated>2017-06-12T13:04:31Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Migliorata formattazione pagina&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Testo: [http://www.cs.unibo.it/~renzo/so/compiti/2014.02.21.tot.pdf]&lt;br /&gt;
&lt;br /&gt;
==Esercizio c.1==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Coci===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
monitor bbwl {&lt;br /&gt;
	condition oktowrite;&lt;br /&gt;
	condition oktolog;&lt;br /&gt;
	condition oktoread;&lt;br /&gt;
	int logging = 0;&lt;br /&gt;
	queue q;&lt;br /&gt;
	&lt;br /&gt;
	procedure entry write (eltype elem) {&lt;br /&gt;
	if (q.length() == MAXELEM || logging)&lt;br /&gt;
			oktowrite.wait();&lt;br /&gt;
	q.enqueue (elem);&lt;br /&gt;
	logging = 1;&lt;br /&gt;
	oktolog.signal();&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure entry eltype log() {&lt;br /&gt;
	if (q.length == 0)&lt;br /&gt;
		oktolog.wait();&lt;br /&gt;
	eltype tmp = q.top();&lt;br /&gt;
	logging = 0;&lt;br /&gt;
	oktoread.signal();&lt;br /&gt;
	return tmp;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	procedure entry eltype read() {&lt;br /&gt;
	if (q.length == 0 || logging)&lt;br /&gt;
		oktoread.wait();&lt;br /&gt;
	eltype tmp = q.dequeue();&lt;br /&gt;
	oktowrite.signal();&lt;br /&gt;
	return tmp;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Save===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
condition oktowrite, oktoread, oktolog ;&lt;br /&gt;
queue buffer = MAXELEM ;&lt;br /&gt;
queue Llogger ;&lt;br /&gt;
&lt;br /&gt;
monitor bbwl&lt;br /&gt;
{&lt;br /&gt;
 procedure entry write (altype elem)&lt;br /&gt;
 {&lt;br /&gt;
	if (buffer.length &amp;gt;= MAXELEM)&lt;br /&gt;
		oktorite.wait() ;&lt;br /&gt;
	 buffer.enqueue(elem);&lt;br /&gt;
     oktolog.signal () ;	&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
procedur entry logger()                 # logger deve tenere traccia di tutti i pacchetti transitati mettendoli in una sua memoria.&lt;br /&gt;
 {&lt;br /&gt;
	if (buffer.length == 0)&lt;br /&gt;
		oktolog.wait();&lt;br /&gt;
	 Llogger.enqueue(buffer.front())    # restituisci il valore in testa di buffer e inseriscilo in coda a Llogger&lt;br /&gt;
	 ok toread.signal() ;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 procedure entry read() &lt;br /&gt;
 {&lt;br /&gt;
	if (buffer.length == 0)&lt;br /&gt;
		oktoread.wait();&lt;br /&gt;
	 buffer.dequeue()                   # restituisci il valore in testa di buffer e rimuovilo.&lt;br /&gt;
	 oktowrite.signal() ;&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
save&lt;br /&gt;
&lt;br /&gt;
==Esercizio c.2==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Coci===&lt;br /&gt;
&lt;br /&gt;
alpha(x,y): &amp;lt;x=4, y=sqrt(x)&amp;gt;&lt;br /&gt;
&lt;br /&gt;
il valore che viene salvato in y è sempre 2, quindi non va bene.&lt;br /&gt;
&lt;br /&gt;
bravo(x,y): &amp;lt;y=sqrt(x), x=4&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
xi yi xf yf&lt;br /&gt;
0  0  4  0&lt;br /&gt;
0  1  4  1&lt;br /&gt;
1  0  4  1&lt;br /&gt;
1  1  4  1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
il valore iniziale di x viene salvato in y, quindi va bene.&lt;br /&gt;
&lt;br /&gt;
charlie(x,y): &amp;lt;y=sqrt(x), x=4*y&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anche questo va bene, per le stesse ragioni di bravo.&lt;br /&gt;
&lt;br /&gt;
delta(z,t): &amp;lt;z=z xor t, t=z xor t&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
zi ti zf tf&lt;br /&gt;
0  0  0  0&lt;br /&gt;
0  1  1  0&lt;br /&gt;
1  0  1  1&lt;br /&gt;
1  1  0  1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Il valore iniziale di z viene salvato in t, quindi va bene.&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Diego.tului===&lt;br /&gt;
&lt;br /&gt;
   alpha: non funziona perché y non andrà mai a 0, quindi non entrerà mai in CS.&lt;br /&gt;
&lt;br /&gt;
   bravo: funziona perché quando y va a 0 x rimane 4, e perciò finché x non ritorna 0 la CS è occupata.&lt;br /&gt;
&lt;br /&gt;
   charlie: dipende dal valore iniziale di x, perché se all'inizio x = 0 (y = 0, x = 4 * 0 = 0) quindi non ci&lt;br /&gt;
   sarà mutua esclusione.&lt;br /&gt;
&lt;br /&gt;
   delta: dipende dal valore iniziale di t, perché se all'inizio è 0, 0 xor 0 resta sempre 0, ergo come sopra.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Diego&lt;br /&gt;
&lt;br /&gt;
==Esercizio g.1==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Coci===&lt;br /&gt;
&lt;br /&gt;
Sia n=3 il numero di pagine mantenute in memoria&lt;br /&gt;
&lt;br /&gt;
a) 123456789123456789...&lt;br /&gt;
&lt;br /&gt;
MIN = MINNUM&lt;br /&gt;
 11144777&lt;br /&gt;
  2225588&lt;br /&gt;
    333669&lt;br /&gt;
&lt;br /&gt;
b) 145231231231231...&lt;br /&gt;
&lt;br /&gt;
MINNUM&lt;br /&gt;
&lt;br /&gt;
 111232123&lt;br /&gt;
   44444444&lt;br /&gt;
     5555555&lt;br /&gt;
&lt;br /&gt;
MIN&lt;br /&gt;
 111111111&lt;br /&gt;
   44222222&lt;br /&gt;
     5333333&lt;br /&gt;
&lt;br /&gt;
Alessandro&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Diego.tului===&lt;br /&gt;
&lt;br /&gt;
Sia n=3 il numero di pagine mantenute in memoria&lt;br /&gt;
&lt;br /&gt;
la soluzione di Alessandro secondo noi presenta dei problemi, la integriamo:&lt;br /&gt;
&lt;br /&gt;
a) 1234431432431432431&lt;br /&gt;
&lt;br /&gt;
   111444444444444444&lt;br /&gt;
    222221112221112221&lt;br /&gt;
     3333333333333333&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Specifiche_phase_2_2016/17&amp;diff=1909</id>
		<title>Specifiche phase 2 2016/17</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Specifiche_phase_2_2016/17&amp;diff=1909"/>
		<updated>2017-06-10T16:56:13Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Aggiunto link al documento delle specifiche di progetto&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Documento specifiche==&lt;br /&gt;
[http://www.cs.unibo.it/~renzo/so/mikaboo/phase2.pdf Link al documento]&lt;br /&gt;
==System call==&lt;br /&gt;
=== send ===&lt;br /&gt;
  int send(struct tcb_t *dest, uintptr_t msg)&lt;br /&gt;
  a0 = 1, a1 = dest. a2 = msg &lt;br /&gt;
=== recv ===&lt;br /&gt;
  struct tcb_t *recv(struct tcb_t *src, uintptr_t *pmsg)&lt;br /&gt;
  a0 = 2, a1 = src, a2 = *pmsg&lt;br /&gt;
&lt;br /&gt;
NB: system call 1 e 2 possono essere chiamate solo in kernel mode&lt;br /&gt;
system call 0 e' errore&lt;br /&gt;
system call non 1 o 2 vengono trasformate in messaggi al thread definito tramite SETSYSMGR se esiste altrimenti msg SETPGMMGR se esiste&lt;br /&gt;
altrimenti TERMINATE_THREAD&lt;br /&gt;
&lt;br /&gt;
==servizi della SSI==&lt;br /&gt;
&lt;br /&gt;
solo da kernel mode&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Nome servizio !! Codice servizio !! Parametri !! Tipo di ritorno !! Note&lt;br /&gt;
|- &lt;br /&gt;
| GET_ERRNO || 0 || || int || 0 okay&lt;br /&gt;
|-&lt;br /&gt;
| CREATE_PROCESS || 1 || state_t* || strcut tcb_t* || NULL per errore&lt;br /&gt;
|-&lt;br /&gt;
| CREATE_THREAD || 2 || state_t* || strcut tcb_t* || NULL per errore&lt;br /&gt;
|-&lt;br /&gt;
| TERMINATE_PROCESS || 3 || state_t* ||  || &lt;br /&gt;
|-&lt;br /&gt;
| TERMINATE_THREAD || 4 || state_t* ||  || (se e' l'ultimo thread diventa come TERMINATE_PROCESS)&lt;br /&gt;
|-&lt;br /&gt;
| SETPGMMGR || 5 || struct tcb_t* || &lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; |  SET*MGR restituiscono lo stesso tcb_t, NULL per errore. &amp;lt;br&amp;gt;&lt;br /&gt;
(puà essere chiamato una volta sola per processo, TERMINATE_PROCESS alla seconda chiamata). &amp;lt;br&amp;gt;&lt;br /&gt;
I manager ricevono messaggi che hanno come sender il tcb_t del thread che ha causato la trap, &amp;lt;br&amp;gt;&lt;br /&gt;
un puntatore al suo state_t come payload.&lt;br /&gt;
|-&lt;br /&gt;
| SETTLBMGR || 6 || struct tcb_t* ||  || &lt;br /&gt;
|-&lt;br /&gt;
| SETSYSMGR || 7 || struct tcb_t* ||  ||&lt;br /&gt;
|-&lt;br /&gt;
| GETCPUTIME || 8 || || unsigned int || in microsecondi, relativo al processo&lt;br /&gt;
|-&lt;br /&gt;
| WAIT_FOR_CLOCK || 9 || || Sospende il thread fino al prossimo tick di pseudo-clock ||&lt;br /&gt;
|-&lt;br /&gt;
| DO_IO || 10 || DEVICE_REG_ADDR, COMMAND, ...  || status &lt;br /&gt;
|&lt;br /&gt;
*Per Dischi: DEVICE_REG_ADDR, COMMAND, DATA1 (DATA1 e' l'indirizzo per il DMA, settore/testina e cyl, sono in COMMAND) &lt;br /&gt;
*Per Nastri: DEVICE_REG_ADDR, COMMAND, DATA1 (DATA1 e' l'indirizzo per il DMA) &lt;br /&gt;
*Per Rete: DEVICE_REG_ADDR, COMMAND, DATA1, DATA2 (DATA1 e' l'indirizzo del pacchetto, DATA2 la lunghezza)&lt;br /&gt;
*Per Stampante:  DEVICE_REG_ADDR, COMMAND, DATA1 (DATA1 contiene il carattere da stampare) &lt;br /&gt;
*Per Terminale IN: DEVICE_REG_ADDR, COMMAND &lt;br /&gt;
*Per Terminale OUT: DEVICE_REG_ADDR, COMMAND &lt;br /&gt;
|-&lt;br /&gt;
| GET_PROCESSID || 11 || struct tcb_t* || struct pcb_t * ||&lt;br /&gt;
|-&lt;br /&gt;
| GET_MYTHREADID || 12 || || struct tcb_t * || &lt;br /&gt;
|-&lt;br /&gt;
| GET_PARENTPROCID || 13 || pcb_t* || pcb_t* ||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Sorgenti di test==&lt;br /&gt;
===nucleus.h===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
#ifndef NUCLEUS_H&lt;br /&gt;
#define NUCLEUS_H&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;quot;mikabooq.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/* Syscall mnemonic values */&lt;br /&gt;
#define SYS_SEND    1&lt;br /&gt;
#define SYS_RECV    2&lt;br /&gt;
&lt;br /&gt;
/* SSI requests */&lt;br /&gt;
#define GET_ERRNO 0&lt;br /&gt;
#define CREATE_PROCESS 1&lt;br /&gt;
#define CREATE_THREAD 2&lt;br /&gt;
#define TERMINATE_PROCESS 3&lt;br /&gt;
#define TERMINATE_THREAD 4&lt;br /&gt;
#define SETPGMMGR 5&lt;br /&gt;
#define SETTLBMGR 6&lt;br /&gt;
#define SETSYSMGR 7&lt;br /&gt;
#define GET_CPUTIME 8&lt;br /&gt;
#define WAIT_FOR_CLOCK 9&lt;br /&gt;
#define DO_IO 10&lt;br /&gt;
#define GET_PROCESSID 11&lt;br /&gt;
#define GET_MYTHREADID 12&lt;br /&gt;
#define GET_PARENTPROCID 13&lt;br /&gt;
&lt;br /&gt;
typedef uintptr_t memaddr;&lt;br /&gt;
typedef uintptr_t cputime;&lt;br /&gt;
typedef uintptr_t devaddr;&lt;br /&gt;
&lt;br /&gt;
extern void* SSI;&lt;br /&gt;
&lt;br /&gt;
#define msgsend(dest, payload) (SYSCALL(SYS_SEND,(unsigned int) (dest),(unsigned int) (payload),0))&lt;br /&gt;
&lt;br /&gt;
#define msgrecv(source, reply) (((struct tcb_t *) SYSCALL(SYS_RECV,(unsigned int) (source),(unsigned int) (reply),0)))&lt;br /&gt;
&lt;br /&gt;
static inline uintptr_t geterrno(void) {&lt;br /&gt;
    uintptr_t retval;&lt;br /&gt;
    struct {&lt;br /&gt;
        uintptr_t reqtag;&lt;br /&gt;
    } req = {GET_ERRNO};&lt;br /&gt;
    msgsend(SSI, &amp;amp;req);&lt;br /&gt;
    msgrecv(SSI, &amp;amp;retval);&lt;br /&gt;
    return retval;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline struct tcb_t* create_process(state_t* s) {&lt;br /&gt;
    struct tcb_t* retval;&lt;br /&gt;
    struct {&lt;br /&gt;
        uintptr_t reqtag;&lt;br /&gt;
        state_t* s;&lt;br /&gt;
    } req = {CREATE_PROCESS, s};&lt;br /&gt;
    msgsend(SSI, &amp;amp;req);&lt;br /&gt;
    msgrecv(SSI, &amp;amp;retval);&lt;br /&gt;
    return retval;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline struct tcb_t* create_thread(state_t* s) {&lt;br /&gt;
    struct tcb_t* retval;&lt;br /&gt;
    struct {&lt;br /&gt;
        uintptr_t reqtag;&lt;br /&gt;
        state_t* s;&lt;br /&gt;
    } req = {CREATE_THREAD, s};&lt;br /&gt;
    msgsend(SSI, &amp;amp;req);&lt;br /&gt;
    msgrecv(SSI, &amp;amp;retval);&lt;br /&gt;
    return retval;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline void terminate_process(void) {&lt;br /&gt;
    struct {&lt;br /&gt;
        uintptr_t reqtag;&lt;br /&gt;
    } req = {TERMINATE_PROCESS};&lt;br /&gt;
    msgsend(SSI, &amp;amp;req);&lt;br /&gt;
    msgrecv(SSI, NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline void terminate_thread(void) {&lt;br /&gt;
    struct {&lt;br /&gt;
        uintptr_t reqtag;&lt;br /&gt;
    } req = {TERMINATE_THREAD};&lt;br /&gt;
    msgsend(SSI, &amp;amp;req);&lt;br /&gt;
    msgrecv(SSI, NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline struct tcb_t* setpgmmgr(struct tcb_t* s) {&lt;br /&gt;
    struct tcb_t* retval;&lt;br /&gt;
    struct {&lt;br /&gt;
        uintptr_t reqtag;&lt;br /&gt;
        struct tcb_t* s;&lt;br /&gt;
    } req = {SETPGMMGR, s};&lt;br /&gt;
    msgsend(SSI, &amp;amp;req);&lt;br /&gt;
    msgrecv(SSI, &amp;amp;retval);&lt;br /&gt;
    return retval;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline struct tcb_t* settlbmgr(struct tcb_t* s) {&lt;br /&gt;
    struct tcb_t* retval;&lt;br /&gt;
    struct {&lt;br /&gt;
        uintptr_t reqtag;&lt;br /&gt;
        struct tcb_t* s;&lt;br /&gt;
    } req = {SETTLBMGR, s};&lt;br /&gt;
    msgsend(SSI, &amp;amp;req);&lt;br /&gt;
    msgrecv(SSI, &amp;amp;retval);&lt;br /&gt;
    return retval;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline struct tcb_t* setsysmgr(struct tcb_t* s) {&lt;br /&gt;
    struct tcb_t* retval;&lt;br /&gt;
    struct {&lt;br /&gt;
        uintptr_t reqtag;&lt;br /&gt;
        struct tcb_t* s;&lt;br /&gt;
    } req = {SETSYSMGR, s};&lt;br /&gt;
    msgsend(SSI, &amp;amp;req);&lt;br /&gt;
    msgrecv(SSI, &amp;amp;retval);&lt;br /&gt;
    return retval;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline cputime getcputime(void) {&lt;br /&gt;
    uintptr_t retval;&lt;br /&gt;
    struct {&lt;br /&gt;
        uintptr_t reqtag;&lt;br /&gt;
    } req = {GET_CPUTIME};&lt;br /&gt;
    msgsend(SSI, &amp;amp;req);&lt;br /&gt;
    msgrecv(SSI, &amp;amp;retval);&lt;br /&gt;
    return retval;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline void waitforclock(void) {&lt;br /&gt;
    struct {&lt;br /&gt;
        uintptr_t reqtag;&lt;br /&gt;
    } req = {WAIT_FOR_CLOCK};&lt;br /&gt;
    msgsend(SSI, &amp;amp;req);&lt;br /&gt;
    msgrecv(SSI, NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline unsigned int do_io(devaddr device, uintptr_t command, uintptr_t data1, uintptr_t data2) {&lt;br /&gt;
    uintptr_t retval;&lt;br /&gt;
    struct {&lt;br /&gt;
        uintptr_t reqtag;&lt;br /&gt;
        devaddr device;&lt;br /&gt;
        uintptr_t command;&lt;br /&gt;
        uintptr_t data1;&lt;br /&gt;
        uintptr_t data2;&lt;br /&gt;
    } req = {DO_IO, device, command, data1, data2};&lt;br /&gt;
    msgsend(SSI, &amp;amp;req);&lt;br /&gt;
    msgrecv(SSI, &amp;amp;retval);&lt;br /&gt;
    return retval;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define do_disk_io(dev, cmd, d1) do_io((dev),(cmd),(d1),0)&lt;br /&gt;
#define do_tape_io(dev, cmd, d1) do_io((dev),(cmd),(d1),0)&lt;br /&gt;
#define do_net_io(dev, cmd, d1, d2) do_io((dev),(cmd),(d1),(d2))&lt;br /&gt;
#define do_printer_io(dev, cmd, d1) do_io((dev),(cmd),(d1),0)&lt;br /&gt;
#define do_terminal_io(dev, cmd) do_io((dev),(cmd),0,0)&lt;br /&gt;
&lt;br /&gt;
static inline struct pcb_t* get_processid(struct tcb_t* s) {&lt;br /&gt;
    struct pcb_t* retval;&lt;br /&gt;
    struct {&lt;br /&gt;
        uintptr_t reqtag;&lt;br /&gt;
        struct tcb_t* s;&lt;br /&gt;
    } req = {GET_PROCESSID, s};&lt;br /&gt;
    msgsend(SSI, &amp;amp;req);&lt;br /&gt;
    msgrecv(SSI, &amp;amp;retval);&lt;br /&gt;
    return retval;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline struct pcb_t* get_parentprocid(struct pcb_t* s) {&lt;br /&gt;
    struct pcb_t* retval;&lt;br /&gt;
    struct {&lt;br /&gt;
        uintptr_t reqtag;&lt;br /&gt;
        struct pcb_t* s;&lt;br /&gt;
    } req = {GET_PARENTPROCID, s};&lt;br /&gt;
    msgsend(SSI, &amp;amp;req);&lt;br /&gt;
    msgrecv(SSI, &amp;amp;retval);&lt;br /&gt;
    return retval;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline struct tcb_t* get_mythreadid(void) {&lt;br /&gt;
    struct tcb_t* retval;&lt;br /&gt;
    struct {&lt;br /&gt;
        uintptr_t reqtag;&lt;br /&gt;
    } req = {GET_MYTHREADID};&lt;br /&gt;
    msgsend(SSI, &amp;amp;req);&lt;br /&gt;
    msgrecv(SSI, &amp;amp;retval);&lt;br /&gt;
    return retval;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===p2test.c===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Copyright (C) 2017 Renzo Davoli&lt;br /&gt;
 *&lt;br /&gt;
 * This program is free software; you can redistribute it and/or modify&lt;br /&gt;
 * it under the terms of the GNU General Public License as published by&lt;br /&gt;
 * the Free Software Foundation; either version 2 of the License, or&lt;br /&gt;
 * (at your option) any later version.&lt;br /&gt;
 *&lt;br /&gt;
 * This program is distributed in the hope that it will be useful,&lt;br /&gt;
 * but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;
 * GNU General Public License for more details.&lt;br /&gt;
 *&lt;br /&gt;
 * You should have received a copy of the GNU General Public License&lt;br /&gt;
 * along with this program; if not, write to the Free Software&lt;br /&gt;
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;uARMconst.h&amp;gt;&lt;br /&gt;
#include &amp;lt;uARMtypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libuarm.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;nucleus.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define QPAGE FRAME_SIZE&lt;br /&gt;
#define TERM0ADDR               0x24C&lt;br /&gt;
&lt;br /&gt;
static struct tcb_t* printid;&lt;br /&gt;
&lt;br /&gt;
static void ttyprintstring(devaddr device, char* s) {&lt;br /&gt;
    uintptr_t status;&lt;br /&gt;
&lt;br /&gt;
    for (; *s; s++) {&lt;br /&gt;
        status = do_terminal_io(device, DEV_TTRS_C_TRSMCHAR | (*s &amp;lt;&amp;lt; 8));&lt;br /&gt;
        switch (status &amp;amp; 0xff) {&lt;br /&gt;
            case DEV_S_READY:&lt;br /&gt;
            case DEV_TTRS_S_CHARTRSM:&lt;br /&gt;
                break;&lt;br /&gt;
            default:&lt;br /&gt;
                return;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void tty0out_thread(void) {&lt;br /&gt;
    uintptr_t payload;&lt;br /&gt;
    struct tcb_t* sender;&lt;br /&gt;
&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        sender = msgrecv(NULL, &amp;amp;payload);&lt;br /&gt;
        ttyprintstring(TERM0ADDR, (char*) payload);&lt;br /&gt;
        msgsend(sender, NULL);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline void tty0print(char* s) {&lt;br /&gt;
    msgsend(printid, s);&lt;br /&gt;
    msgrecv(printid, NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static inline void panic(char* s) {&lt;br /&gt;
    tty0print(&amp;quot;!!! PANIC: &amp;quot;);&lt;br /&gt;
    tty0print(s);&lt;br /&gt;
    PANIC();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static struct tcb_t* csid;&lt;br /&gt;
&lt;br /&gt;
static inline void CSIN() {&lt;br /&gt;
    msgsend(csid, NULL);&lt;br /&gt;
    msgrecv(csid, NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define CSOUT msgsend(csid, NULL)&lt;br /&gt;
&lt;br /&gt;
void cs_thread(void) {&lt;br /&gt;
    struct tcb_t* sender;&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        sender = msgrecv(NULL, NULL);&lt;br /&gt;
        msgsend(sender, NULL);&lt;br /&gt;
        msgrecv(sender, NULL);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define SYNCCODE 0x01000010&lt;br /&gt;
&lt;br /&gt;
void p2(), p3(), p4(), p5(), p6(), p7(), p8();&lt;br /&gt;
&lt;br /&gt;
struct tcb_t* testt, * p2t, * p3t, * p4t, * p5t, * p6t, * p7t, * p8t;&lt;br /&gt;
&lt;br /&gt;
static state_t tmpstate;&lt;br /&gt;
memaddr stackalloc;&lt;br /&gt;
&lt;br /&gt;
uintptr_t p5sys = 0;&lt;br /&gt;
uintptr_t p5send = 0;&lt;br /&gt;
&lt;br /&gt;
void test(void) {&lt;br /&gt;
    ttyprintstring(TERM0ADDR, &amp;quot;NUCLEUS TEST: starting...\n&amp;quot;);&lt;br /&gt;
    STST(&amp;amp;tmpstate);&lt;br /&gt;
    stackalloc = (tmpstate.sp + (QPAGE - 1)) &amp;amp; (~(QPAGE - 1));&lt;br /&gt;
    tmpstate.sp = (stackalloc -= QPAGE);&lt;br /&gt;
    tmpstate.pc = (memaddr) tty0out_thread;&lt;br /&gt;
    tmpstate.cpsr = STATUS_ALL_INT_ENABLE(tmpstate.cpsr);&lt;br /&gt;
    printid = create_thread(&amp;amp;tmpstate);&lt;br /&gt;
    tty0print(&amp;quot;NUCLEUS: first msg printed by tty0out_thread\n&amp;quot;);&lt;br /&gt;
    testt = get_mythreadid();&lt;br /&gt;
&lt;br /&gt;
    tmpstate.sp = (stackalloc -= QPAGE);&lt;br /&gt;
    tmpstate.pc = (memaddr) cs_thread;&lt;br /&gt;
    csid = create_process(&amp;amp;tmpstate);&lt;br /&gt;
    tty0print(&amp;quot;NUCLEUS: critical section thread started\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    CSIN();&lt;br /&gt;
    tmpstate.sp = (stackalloc -= QPAGE);&lt;br /&gt;
    CSOUT;&lt;br /&gt;
    tmpstate.pc = (memaddr) p2;&lt;br /&gt;
    p2t = create_process(&amp;amp;tmpstate);&lt;br /&gt;
    msgsend(p2t, SYNCCODE);&lt;br /&gt;
    msgrecv(p2t, NULL);&lt;br /&gt;
&lt;br /&gt;
    tty0print(&amp;quot;p2 completed\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    CSIN();&lt;br /&gt;
    tmpstate.sp = (stackalloc -= QPAGE);&lt;br /&gt;
    CSOUT;&lt;br /&gt;
    tmpstate.pc = (memaddr) p3;&lt;br /&gt;
    p3t = create_process(&amp;amp;tmpstate);&lt;br /&gt;
    msgrecv(p3t, NULL);&lt;br /&gt;
&lt;br /&gt;
    tty0print(&amp;quot;p3 completed\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    CSIN();&lt;br /&gt;
    tmpstate.sp = (stackalloc -= QPAGE);&lt;br /&gt;
    CSOUT;&lt;br /&gt;
    tmpstate.pc = (memaddr) p4;&lt;br /&gt;
    p4t = create_process(&amp;amp;tmpstate);&lt;br /&gt;
    msgsend(p4t, NULL);&lt;br /&gt;
    msgrecv(p4t, NULL);&lt;br /&gt;
    msgsend(p4t, tmpstate.sp);&lt;br /&gt;
    msgrecv(p4t, NULL);&lt;br /&gt;
&lt;br /&gt;
    if (geterrno() == 0)&lt;br /&gt;
        panic(&amp;quot;p1 wrong errno: recv from p4 should abort, p4 terminated\n&amp;quot;);&lt;br /&gt;
    else {&lt;br /&gt;
        tty0print(&amp;quot;p4 errno ok\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    tty0print(&amp;quot;p4 completed\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    CSIN();&lt;br /&gt;
    tmpstate.sp = (stackalloc -= QPAGE);&lt;br /&gt;
    CSOUT;&lt;br /&gt;
    tmpstate.pc = (memaddr) p5;&lt;br /&gt;
    p5t = create_process(&amp;amp;tmpstate);&lt;br /&gt;
    msgsend(p5t, NULL);&lt;br /&gt;
    msgrecv(p5t, NULL);&lt;br /&gt;
&lt;br /&gt;
    if (p5sys == 1) tty0print(&amp;quot;p5a usermode sys passup ok\n&amp;quot;);&lt;br /&gt;
    else panic(&amp;quot;p5a usermode passup error\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    if (p5send != 2) tty0print(&amp;quot;p5a usermode msg priv kill is ok\n&amp;quot;);&lt;br /&gt;
    else panic(&amp;quot;p5a usermode msg priv kill error\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    CSIN();&lt;br /&gt;
    tmpstate.sp = (stackalloc -= QPAGE);&lt;br /&gt;
    CSOUT;&lt;br /&gt;
    tmpstate.pc = (memaddr) p6;&lt;br /&gt;
    p6t = create_process(&amp;amp;tmpstate);&lt;br /&gt;
    msgrecv(p6t, NULL);&lt;br /&gt;
    tty0print(&amp;quot;p6 completed\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    CSIN();&lt;br /&gt;
    tmpstate.sp = (stackalloc -= QPAGE);&lt;br /&gt;
    CSOUT;&lt;br /&gt;
    tmpstate.pc = (memaddr) p7;&lt;br /&gt;
    p7t = create_process(&amp;amp;tmpstate);&lt;br /&gt;
    msgrecv(p7t, NULL);&lt;br /&gt;
    tty0print(&amp;quot;p7 completed\n&amp;quot;);&lt;br /&gt;
    //check total number of thread&lt;br /&gt;
&lt;br /&gt;
    CSIN();&lt;br /&gt;
    tmpstate.sp = (stackalloc -= QPAGE);&lt;br /&gt;
    CSOUT;&lt;br /&gt;
    tmpstate.pc = (memaddr) p8;&lt;br /&gt;
    p8t = create_process(&amp;amp;tmpstate);&lt;br /&gt;
    msgrecv(p8t, NULL);&lt;br /&gt;
    tty0print(&amp;quot;p8 completed\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    tty0print(&amp;quot;IT'S ALIVE! IT'S ALIVE! THE KERNEL IS ALIVE!\n&amp;quot;);&lt;br /&gt;
    HALT();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define MINLOOPTIME             10000&lt;br /&gt;
#define LOOPNUM                 10000&lt;br /&gt;
&lt;br /&gt;
void p2(void) {&lt;br /&gt;
    struct tcb_t* p1t;&lt;br /&gt;
    uintptr_t value;&lt;br /&gt;
    cputime cpu_t1, cpu_t2;&lt;br /&gt;
    int i;&lt;br /&gt;
&lt;br /&gt;
    tty0print(&amp;quot;p2 started\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    /* test: GET_MYTHREADID GET_PROCESSID GET_PARENTPROCID */&lt;br /&gt;
    if (get_mythreadid() != p2t)&lt;br /&gt;
        panic(&amp;quot;p2 get_mythreadid: wrong pid returned\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    p1t = msgrecv(NULL, &amp;amp;value);&lt;br /&gt;
    if (value != SYNCCODE)&lt;br /&gt;
        panic(&amp;quot;p2 recv: got the wrong value\n&amp;quot;);&lt;br /&gt;
    if (p1t != testt)&lt;br /&gt;
        panic(&amp;quot;p2 recv: got the wrong sender\n&amp;quot;);&lt;br /&gt;
    if (get_processid(p1t) != get_parentprocid(get_processid(get_mythreadid())))&lt;br /&gt;
        panic(&amp;quot;p2 get_parentprocid get_processid error\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    /* test: GET_CPUTIME */&lt;br /&gt;
&lt;br /&gt;
    cpu_t1 = getcputime();&lt;br /&gt;
    /* delay for several milliseconds */&lt;br /&gt;
    for (i = 1; i &amp;lt; LOOPNUM; i++);&lt;br /&gt;
&lt;br /&gt;
    cpu_t2 = getcputime();&lt;br /&gt;
&lt;br /&gt;
    if ((cpu_t2 - cpu_t1) &amp;gt;= MINLOOPTIME)&lt;br /&gt;
        tty0print(&amp;quot;p2 GET_CPUTIME sounds okay\n&amp;quot;);&lt;br /&gt;
    else&lt;br /&gt;
        panic(&amp;quot;p2 GETCPUTIME sounds faulty\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    msgsend(p1t, NULL);&lt;br /&gt;
    msgrecv(p1t, NULL);&lt;br /&gt;
&lt;br /&gt;
    terminate_thread();&lt;br /&gt;
&lt;br /&gt;
    panic(&amp;quot;p2 survived TERMINATE_THREAD\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define PSEUDOCLOCK 100000&lt;br /&gt;
#define NWAIT 10&lt;br /&gt;
&lt;br /&gt;
void p3(void) {&lt;br /&gt;
    tty0print(&amp;quot;p3 started\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    cputime time1, time2;&lt;br /&gt;
    int i;&lt;br /&gt;
    time1 = getTODLO();&lt;br /&gt;
    for (i = 0; i &amp;lt; NWAIT; i++) {&lt;br /&gt;
        waitforclock();&lt;br /&gt;
    }&lt;br /&gt;
    time2 = getTODLO();&lt;br /&gt;
&lt;br /&gt;
    if ((time2 - time1) &amp;lt; (PSEUDOCLOCK * (NWAIT - 1))) {&lt;br /&gt;
        panic(&amp;quot;WAITCLOCK too small\n&amp;quot;);&lt;br /&gt;
    } else if ((time2 - time1) &amp;gt; (PSEUDOCLOCK * (NWAIT + 1))) {&lt;br /&gt;
        panic(&amp;quot;WAITCLOCK too big\n&amp;quot;);&lt;br /&gt;
    } else {&lt;br /&gt;
        tty0print(&amp;quot;WAITCLOCK OK\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    msgsend(testt, &amp;quot;NULL&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    terminate_process();&lt;br /&gt;
&lt;br /&gt;
    panic(&amp;quot;p3 survived TERMINATE_PROCESS\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void p4(void) {&lt;br /&gt;
    static int p4inc = 1;&lt;br /&gt;
    struct tcb_t* parent;&lt;br /&gt;
    struct tcb_t* child;&lt;br /&gt;
    state_t p4childstate;&lt;br /&gt;
&lt;br /&gt;
    switch (p4inc) {&lt;br /&gt;
        case 1:&lt;br /&gt;
            tty0print(&amp;quot;first incarnation of p4 starts\n&amp;quot;);&lt;br /&gt;
            break;&lt;br /&gt;
        case 2:&lt;br /&gt;
            tty0print(&amp;quot;second incarnation of p4 starts\n&amp;quot;);&lt;br /&gt;
            break;&lt;br /&gt;
    }&lt;br /&gt;
    p4inc++;&lt;br /&gt;
    parent = msgrecv(NULL, NULL);&lt;br /&gt;
    msgsend(parent, NULL);&lt;br /&gt;
&lt;br /&gt;
    msgrecv(NULL, NULL);&lt;br /&gt;
    /* only the first incarnation reaches this point */&lt;br /&gt;
&lt;br /&gt;
    STST(&amp;amp;p4childstate);&lt;br /&gt;
    CSIN();&lt;br /&gt;
    p4childstate.sp = (stackalloc -= QPAGE);&lt;br /&gt;
    CSOUT;&lt;br /&gt;
    p4childstate.pc = (memaddr) p4;&lt;br /&gt;
&lt;br /&gt;
    child = create_process(&amp;amp;p4childstate);&lt;br /&gt;
    msgsend(child, NULL);&lt;br /&gt;
    msgrecv(child, NULL);&lt;br /&gt;
&lt;br /&gt;
    terminate_process();&lt;br /&gt;
&lt;br /&gt;
    panic(&amp;quot;p4 survived TERMINATE_PROCESS\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void p5a();&lt;br /&gt;
&lt;br /&gt;
void p5p(void) {&lt;br /&gt;
    struct tcb_t* sender;&lt;br /&gt;
    state_t* state;&lt;br /&gt;
    short int should_terminate = 0;&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        sender = msgrecv(NULL, &amp;amp;state);&lt;br /&gt;
        switch (CAUSE_EXCCODE_GET(state-&amp;gt;CP15_Cause)) {&lt;br /&gt;
            case EXC_RESERVEDINSTR:&lt;br /&gt;
                tty0print(&amp;quot;p5a got RESERVEDINSTR\n&amp;quot;);&lt;br /&gt;
                should_terminate = 1;&lt;br /&gt;
                break;&lt;br /&gt;
            default:&lt;br /&gt;
                PANIC();&lt;br /&gt;
        }&lt;br /&gt;
        if (should_terminate) {&lt;br /&gt;
            terminate_process();&lt;br /&gt;
        } else {&lt;br /&gt;
            msgsend(sender, NULL);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void p5m(void) {&lt;br /&gt;
    struct tcb_t* sender;&lt;br /&gt;
    state_t* state;&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        sender = msgrecv(NULL, &amp;amp;state);&lt;br /&gt;
        switch (CAUSE_EXCCODE_GET(state-&amp;gt;CP15_Cause)) {&lt;br /&gt;
            default:&lt;br /&gt;
                tty0print(&amp;quot;p5 mem error passup okay\n&amp;quot;);&lt;br /&gt;
                sender-&amp;gt;t_s.pc = (memaddr) p5a;&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        msgsend(sender, NULL);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void p5s(void) {&lt;br /&gt;
    uintptr_t retval;&lt;br /&gt;
    struct tcb_t* sender;&lt;br /&gt;
    state_t* state;&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        sender = msgrecv(NULL, &amp;amp;state);&lt;br /&gt;
        switch (state-&amp;gt;a1) {&lt;br /&gt;
            case 42:&lt;br /&gt;
                retval = 42;&lt;br /&gt;
                break;&lt;br /&gt;
            case 3:&lt;br /&gt;
                retval = 0;&lt;br /&gt;
                break;&lt;br /&gt;
            default:&lt;br /&gt;
                retval = -1;&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
        state-&amp;gt;a1 = retval;&lt;br /&gt;
        msgsend(sender, NULL);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define BADADDR 0xFFFFFFFF&lt;br /&gt;
&lt;br /&gt;
void p5(void) {&lt;br /&gt;
    state_t mgrstate;&lt;br /&gt;
    uintptr_t retval = 200;&lt;br /&gt;
&lt;br /&gt;
    tty0print(&amp;quot;p5 started\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    STST(&amp;amp;mgrstate);&lt;br /&gt;
    msgrecv(NULL, &amp;amp;mgrstate.pc);&lt;br /&gt;
    CSIN();&lt;br /&gt;
    mgrstate.sp = (stackalloc -= QPAGE);&lt;br /&gt;
    CSOUT;&lt;br /&gt;
    mgrstate.pc = (memaddr) p5p;&lt;br /&gt;
    setpgmmgr(create_thread(&amp;amp;mgrstate));&lt;br /&gt;
&lt;br /&gt;
    CSIN();&lt;br /&gt;
    mgrstate.sp = (stackalloc -= QPAGE);&lt;br /&gt;
    CSOUT;&lt;br /&gt;
    mgrstate.pc = (memaddr) p5m;&lt;br /&gt;
    settlbmgr(create_thread(&amp;amp;mgrstate));&lt;br /&gt;
&lt;br /&gt;
    CSIN();&lt;br /&gt;
    mgrstate.sp = (stackalloc -= QPAGE);&lt;br /&gt;
    CSOUT;&lt;br /&gt;
    mgrstate.pc = (memaddr) p5s;&lt;br /&gt;
&lt;br /&gt;
    setsysmgr(create_thread(&amp;amp;mgrstate));&lt;br /&gt;
&lt;br /&gt;
    /* this should me handled by p5s */&lt;br /&gt;
&lt;br /&gt;
    retval = SYSCALL(42, 42, 42, 42);&lt;br /&gt;
    if (retval == 42)&lt;br /&gt;
        tty0print(&amp;quot;p5 syscall passup okay\n&amp;quot;);&lt;br /&gt;
    else&lt;br /&gt;
        panic(&amp;quot;p5 syscall passup error\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    *((memaddr*) BADADDR) = 0;&lt;br /&gt;
&lt;br /&gt;
    panic(&amp;quot;p5 survived mem error&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void p5a(void) {&lt;br /&gt;
    uintptr_t retval = 200;&lt;br /&gt;
    /* switch to user mode */&lt;br /&gt;
    setSTATUS((getSTATUS() &amp;amp; STATUS_CLEAR_MODE) | STATUS_USER_MODE);&lt;br /&gt;
&lt;br /&gt;
    p5sys = 0;&lt;br /&gt;
&lt;br /&gt;
    retval = SYSCALL(3, 2, 1, 0);&lt;br /&gt;
    if (retval == 0) {&lt;br /&gt;
        p5sys = 1;&lt;br /&gt;
    } else {&lt;br /&gt;
        p5sys = 2;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* this should fail! */&lt;br /&gt;
    msgsend(SSI, NULL);&lt;br /&gt;
&lt;br /&gt;
    p5send = 2;&lt;br /&gt;
    //infinite loop: this should not be reaached.&lt;br /&gt;
    // The loop is added in order to allow debug in case of failure (manually check p5send)&lt;br /&gt;
    for (;;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void p6(void) {&lt;br /&gt;
    tty0print(&amp;quot;p6 started\n&amp;quot;);&lt;br /&gt;
    *((memaddr*) BADADDR) = 0;&lt;br /&gt;
&lt;br /&gt;
    panic(&amp;quot;p6 survived mem error&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void p7(void) {&lt;br /&gt;
    tty0print(&amp;quot;p7 started\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    SYSCALL(3, 2, 1, 0);&lt;br /&gt;
&lt;br /&gt;
    panic(&amp;quot;p7 survived syscall without manager&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void p8child(), p8grandchild();&lt;br /&gt;
&lt;br /&gt;
void p8(void) {&lt;br /&gt;
    tty0print(&amp;quot;p8 started\n&amp;quot;);&lt;br /&gt;
    state_t childstate;&lt;br /&gt;
    struct tcb_t* pid = get_mythreadid();&lt;br /&gt;
    STST(&amp;amp;childstate);&lt;br /&gt;
    childstate.pc = (memaddr) p8child;&lt;br /&gt;
    CSIN();&lt;br /&gt;
    childstate.sp = (stackalloc -= QPAGE);&lt;br /&gt;
    CSOUT;&lt;br /&gt;
    msgsend(create_process(&amp;amp;childstate), pid);&lt;br /&gt;
    CSIN();&lt;br /&gt;
    childstate.sp = (stackalloc -= QPAGE);&lt;br /&gt;
    CSOUT;&lt;br /&gt;
    msgsend(create_process(&amp;amp;childstate), pid);&lt;br /&gt;
    msgrecv(NULL, NULL);&lt;br /&gt;
    msgrecv(NULL, NULL);&lt;br /&gt;
    msgrecv(NULL, NULL);&lt;br /&gt;
    msgrecv(NULL, NULL);&lt;br /&gt;
    msgrecv(NULL, NULL);&lt;br /&gt;
    msgrecv(NULL, NULL);&lt;br /&gt;
&lt;br /&gt;
    terminate_process();&lt;br /&gt;
&lt;br /&gt;
    panic(&amp;quot;p8 survived TERMINATE_PROCESS\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#define NGRANDCHILDREN 3&lt;br /&gt;
&lt;br /&gt;
void p8child() {&lt;br /&gt;
    int i;&lt;br /&gt;
    state_t childstate;&lt;br /&gt;
    struct tcb_t* ppid;&lt;br /&gt;
    msgrecv(NULL, &amp;amp;ppid);&lt;br /&gt;
    for (i = 0; i &amp;lt; NGRANDCHILDREN; i++) {&lt;br /&gt;
        STST(&amp;amp;childstate);&lt;br /&gt;
        childstate.pc = (memaddr) p8grandchild;&lt;br /&gt;
        CSIN();&lt;br /&gt;
        childstate.sp = (stackalloc -= QPAGE);&lt;br /&gt;
        CSOUT;&lt;br /&gt;
        msgsend(create_process(&amp;amp;childstate), ppid);&lt;br /&gt;
    }&lt;br /&gt;
    msgrecv(NULL, NULL);&lt;br /&gt;
&lt;br /&gt;
    panic(&amp;quot;p8child survived parent termination\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void p8grandchild() {&lt;br /&gt;
    struct tcb_t* gpid;&lt;br /&gt;
    msgrecv(NULL, &amp;amp;gpid);&lt;br /&gt;
    msgsend(gpid, NULL);&lt;br /&gt;
    msgrecv(NULL, NULL);&lt;br /&gt;
&lt;br /&gt;
    panic(&amp;quot;p8grandchild survived grandparent termination\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2017.02.17&amp;diff=1906</id>
		<title>Prova pratica 2017.02.17</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2017.02.17&amp;diff=1906"/>
		<updated>2017-05-29T17:25:30Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: /* Soluzione di FedericoB */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[http://www.cs.unibo.it/~renzo/so/pratiche/2017.02.17.pdf link al testo]]&lt;br /&gt;
==Esercizio 1==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Scrivere un programma ager in grado di invecchiare file.&lt;br /&gt;
Il programma deve poter prendere una lista di file come parametri o nessun parametro, nel qual caso invecchierà&lt;br /&gt;
tutti I file della directory corrente. “invecchiare” significa predatare il tempo di ultimo accesso e di modifica di 10&lt;br /&gt;
giorni.&lt;br /&gt;
Esempio:&lt;br /&gt;
$ ls -l file&lt;br /&gt;
-rw-r--r-- 1 renzo renzo 0 Feb 17 09:07 file&lt;br /&gt;
$ ./ager file&lt;br /&gt;
$ ls -l file&lt;br /&gt;
-rw-r--r-- 1 renzo renzo 0 Feb 7 09:07 file&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utime.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]) {&lt;br /&gt;
    if (argc&amp;gt;1) {&lt;br /&gt;
        int i=0;&lt;br /&gt;
        for (i=1;i&amp;lt;argc;i++) {&lt;br /&gt;
            struct stat structstat;&lt;br /&gt;
            if (stat(argv[i],&amp;amp;structstat)==0) {&lt;br /&gt;
                //read last access time&lt;br /&gt;
                time_t atim = structstat.st_atim.tv_sec;&lt;br /&gt;
                //read last modification time&lt;br /&gt;
                time_t mtim = structstat.st_mtim.tv_sec;&lt;br /&gt;
                atim -= 10 * 24 * 60 * 60;&lt;br /&gt;
                mtim -= 10 * 24 * 60 * 60;&lt;br /&gt;
                //create structure for utime&lt;br /&gt;
                struct utimbuf structutimbuf = {atim, mtim};&lt;br /&gt;
                //call utime for setting the new date&lt;br /&gt;
                if (utime(argv[i], &amp;amp;structutimbuf)!=0) {&lt;br /&gt;
                    //if utime failed print errno&lt;br /&gt;
                    perror(NULL);&lt;br /&gt;
                }&lt;br /&gt;
            } else {&lt;br /&gt;
                //if stat failed print errno&lt;br /&gt;
                perror(NULL);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
==Esercizio 2==&lt;br /&gt;
&amp;lt;source lang='text'&amp;gt;&lt;br /&gt;
Completare l’esercizio 1 ponendo una gestione opportuna dei parametri in linea comando che preveda di&lt;br /&gt;
poter modificare solo la data di accesso o quella di modifica, di definire il tempo di “invecchiamento” e un help.&lt;br /&gt;
Si possono inserire altri parametri ritenuti opportuni.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utime.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]) {&lt;br /&gt;
    int access = 0;&lt;br /&gt;
    int modification = 0;&lt;br /&gt;
    int c;&lt;br /&gt;
    int index = 0;&lt;br /&gt;
    time_t time = 10 * 24 * 60 * 60;&lt;br /&gt;
    while ((c = getopt(argc, argv, &amp;quot;amt:h&amp;quot;)) != -1) {&lt;br /&gt;
        switch (c) {&lt;br /&gt;
            case 'a': access=1; break;&lt;br /&gt;
            case 'm': modification=1; break;&lt;br /&gt;
            case 't':&lt;br /&gt;
                time = strtol(optarg,NULL,10);&lt;br /&gt;
                break;&lt;br /&gt;
            case 'h':&lt;br /&gt;
            case '?':&lt;br /&gt;
                printf(&amp;quot;usage: ager [-a] [-m] [-t time] [file ...]&amp;quot;);&lt;br /&gt;
            default:&lt;br /&gt;
                abort ();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    for (index = optind; index &amp;lt; argc; index++) {&lt;br /&gt;
        struct stat structstat;&lt;br /&gt;
        if (stat(argv[index], &amp;amp;structstat) == 0) {&lt;br /&gt;
            //read last access time&lt;br /&gt;
            time_t atim = structstat.st_atim.tv_sec;&lt;br /&gt;
            //read last modification time&lt;br /&gt;
            time_t mtim = structstat.st_mtim.tv_sec;&lt;br /&gt;
            if (!modification || access) {&lt;br /&gt;
                atim -= time;&lt;br /&gt;
            }&lt;br /&gt;
            if (!access || modification) {&lt;br /&gt;
                mtim -= time;&lt;br /&gt;
            }&lt;br /&gt;
            //create structure for utime&lt;br /&gt;
            struct utimbuf structutimbuf = {atim, mtim};&lt;br /&gt;
            //call utime for setting the new date&lt;br /&gt;
            if (utime(argv[index], &amp;amp;structutimbuf) != 0) {&lt;br /&gt;
                //if utime failed print errno&lt;br /&gt;
                perror(NULL);&lt;br /&gt;
            }&lt;br /&gt;
        } else {&lt;br /&gt;
            //if stat failed print errno&lt;br /&gt;
            perror(NULL);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
==Esercizio 3==&lt;br /&gt;
&amp;lt;source lang='text'&amp;gt;&lt;br /&gt;
Il programma deve elencare I file della directory corrente divisi per suffisso (e al termine l’elenco di quelli privi di&lt;br /&gt;
suffisso.&lt;br /&gt;
es:&lt;br /&gt;
.c: primo.c var.c main.c&lt;br /&gt;
.h: primo.h const.h&lt;br /&gt;
.odt: relazione.odt&lt;br /&gt;
makefile README COPYING&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
&amp;lt;source lang='Python'&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
from pathlib import Path&lt;br /&gt;
&lt;br /&gt;
nosuffix = []&lt;br /&gt;
suffix = {}&lt;br /&gt;
for root, dirs, files in os.walk(&amp;quot;.&amp;quot;):&lt;br /&gt;
    for filename in files:&lt;br /&gt;
      path = Path(filename)&lt;br /&gt;
      if (len(path.suffix)&amp;gt;0):&lt;br /&gt;
        suffix.setdefault(path.suffix,[])&lt;br /&gt;
        suffix[path.suffix].append(filename)&lt;br /&gt;
      else:&lt;br /&gt;
        nosuffix.append(filename)&lt;br /&gt;
for k in suffix.keys():&lt;br /&gt;
    print(k, end=&amp;quot;: &amp;quot;)&lt;br /&gt;
    for v in suffix[k]:&lt;br /&gt;
        print(v,end=&amp;quot; &amp;quot;)&lt;br /&gt;
    print(&amp;quot;&amp;quot;)&lt;br /&gt;
for v in nosuffix: print(v, end=&amp;quot; &amp;quot;)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2017.02.17&amp;diff=1905</id>
		<title>Prova pratica 2017.02.17</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2017.02.17&amp;diff=1905"/>
		<updated>2017-05-29T17:25:01Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[http://www.cs.unibo.it/~renzo/so/pratiche/2017.02.17.pdf link al testo]]&lt;br /&gt;
==Esercizio 1==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Scrivere un programma ager in grado di invecchiare file.&lt;br /&gt;
Il programma deve poter prendere una lista di file come parametri o nessun parametro, nel qual caso invecchierà&lt;br /&gt;
tutti I file della directory corrente. “invecchiare” significa predatare il tempo di ultimo accesso e di modifica di 10&lt;br /&gt;
giorni.&lt;br /&gt;
Esempio:&lt;br /&gt;
$ ls -l file&lt;br /&gt;
-rw-r--r-- 1 renzo renzo 0 Feb 17 09:07 file&lt;br /&gt;
$ ./ager file&lt;br /&gt;
$ ls -l file&lt;br /&gt;
-rw-r--r-- 1 renzo renzo 0 Feb 7 09:07 file&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utime.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]) {&lt;br /&gt;
    if (argc&amp;gt;1) {&lt;br /&gt;
        int i=0;&lt;br /&gt;
        for (i=1;i&amp;lt;argc;i++) {&lt;br /&gt;
            struct stat structstat;&lt;br /&gt;
            if (stat(argv[i],&amp;amp;structstat)==0) {&lt;br /&gt;
                //read last access time&lt;br /&gt;
                time_t atim = structstat.st_atim.tv_sec;&lt;br /&gt;
                //read last modification time&lt;br /&gt;
                time_t mtim = structstat.st_mtim.tv_sec;&lt;br /&gt;
                atim -= 10 * 24 * 60 * 60;&lt;br /&gt;
                mtim -= 10 * 24 * 60 * 60;&lt;br /&gt;
                //create structure for utime&lt;br /&gt;
                struct utimbuf structutimbuf = {atim, mtim};&lt;br /&gt;
                //call utime for setting the new date&lt;br /&gt;
                if (utime(argv[i], &amp;amp;structutimbuf)!=0) {&lt;br /&gt;
                    //if utime failed print errno&lt;br /&gt;
                    perror(NULL);&lt;br /&gt;
                }&lt;br /&gt;
            } else {&lt;br /&gt;
                //if stat failed print errno&lt;br /&gt;
                perror(NULL);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
==Esercizio 2==&lt;br /&gt;
&amp;lt;source lang='text'&amp;gt;&lt;br /&gt;
Completare l’esercizio 1 ponendo una gestione opportuna dei parametri in linea comando che preveda di&lt;br /&gt;
poter modificare solo la data di accesso o quella di modifica, di definire il tempo di “invecchiamento” e un help.&lt;br /&gt;
Si possono inserire altri parametri ritenuti opportuni.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utime.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]) {&lt;br /&gt;
    int access = 0;&lt;br /&gt;
    int modification = 0;&lt;br /&gt;
    int c;&lt;br /&gt;
    int index = 0;&lt;br /&gt;
    time_t time = 10 * 24 * 60 * 60;&lt;br /&gt;
    while ((c = getopt(argc, argv, &amp;quot;amt:h&amp;quot;)) != -1) {&lt;br /&gt;
        switch (c) {&lt;br /&gt;
            case 'a': access=1; break;&lt;br /&gt;
            case 'm': modification=1; break;&lt;br /&gt;
            case 't':&lt;br /&gt;
                time = strtol(optarg,NULL,10);&lt;br /&gt;
                break;&lt;br /&gt;
            case 'h':&lt;br /&gt;
            case '?':&lt;br /&gt;
                printf(&amp;quot;usage: ager [-a] [-m] [-t time] [file ...]&amp;quot;);&lt;br /&gt;
            default:&lt;br /&gt;
                abort ();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    for (index = optind; index &amp;lt; argc; index++) {&lt;br /&gt;
        struct stat structstat;&lt;br /&gt;
        if (stat(argv[index], &amp;amp;structstat) == 0) {&lt;br /&gt;
            //read last access time&lt;br /&gt;
            time_t atim = structstat.st_atim.tv_sec;&lt;br /&gt;
            //read last modification time&lt;br /&gt;
            time_t mtim = structstat.st_mtim.tv_sec;&lt;br /&gt;
            if (!modification || access) {&lt;br /&gt;
                atim -= time;&lt;br /&gt;
            }&lt;br /&gt;
            if (!access || modification) {&lt;br /&gt;
                mtim -= time;&lt;br /&gt;
            }&lt;br /&gt;
            //create structure for utime&lt;br /&gt;
            struct utimbuf structutimbuf = {atim, mtim};&lt;br /&gt;
            //call utime for setting the new date&lt;br /&gt;
            if (utime(argv[index], &amp;amp;structutimbuf) != 0) {&lt;br /&gt;
                //if utime failed print errno&lt;br /&gt;
                perror(NULL);&lt;br /&gt;
            }&lt;br /&gt;
        } else {&lt;br /&gt;
            //if stat failed print errno&lt;br /&gt;
            perror(NULL);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
==Esercizio 3==&lt;br /&gt;
&amp;lt;source lang='text'&amp;gt;&lt;br /&gt;
Il programma deve elencare I file della directory corrente divisi per suffisso (e al termine l’elenco di quelli privi di&lt;br /&gt;
suffisso.&lt;br /&gt;
es:&lt;br /&gt;
.c: primo.c var.c main.c&lt;br /&gt;
.h: primo.h const.h&lt;br /&gt;
.odt: relazione.odt&lt;br /&gt;
makefile README COPYING&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
&amp;lt;source lang='C'&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
from pathlib import Path&lt;br /&gt;
&lt;br /&gt;
nosuffix = []&lt;br /&gt;
suffix = {}&lt;br /&gt;
for root, dirs, files in os.walk(&amp;quot;.&amp;quot;):&lt;br /&gt;
    for filename in files:&lt;br /&gt;
      path = Path(filename)&lt;br /&gt;
      if (len(path.suffix)&amp;gt;0):&lt;br /&gt;
        suffix.setdefault(path.suffix,[])&lt;br /&gt;
        suffix[path.suffix].append(filename)&lt;br /&gt;
      else:&lt;br /&gt;
        nosuffix.append(filename)&lt;br /&gt;
for k in suffix.keys():&lt;br /&gt;
    print(k, end=&amp;quot;: &amp;quot;)&lt;br /&gt;
    for v in suffix[k]:&lt;br /&gt;
        print(v,end=&amp;quot; &amp;quot;)&lt;br /&gt;
    print(&amp;quot;&amp;quot;)&lt;br /&gt;
for v in nosuffix: print(v, end=&amp;quot; &amp;quot;)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2017.02.17&amp;diff=1904</id>
		<title>Prova pratica 2017.02.17</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2017.02.17&amp;diff=1904"/>
		<updated>2017-05-29T17:24:30Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Aggiunta soluzione esercizio 3&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[http://www.cs.unibo.it/~renzo/so/pratiche/2017.02.17.pdf link al testo]]&lt;br /&gt;
==Esercizio 1==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Scrivere un programma ager in grado di invecchiare file.&lt;br /&gt;
Il programma deve poter prendere una lista di file come parametri o nessun parametro, nel qual caso invecchierà&lt;br /&gt;
tutti I file della directory corrente. “invecchiare” significa predatare il tempo di ultimo accesso e di modifica di 10&lt;br /&gt;
giorni.&lt;br /&gt;
Esempio:&lt;br /&gt;
$ ls -l file&lt;br /&gt;
-rw-r--r-- 1 renzo renzo 0 Feb 17 09:07 file&lt;br /&gt;
$ ./ager file&lt;br /&gt;
$ ls -l file&lt;br /&gt;
-rw-r--r-- 1 renzo renzo 0 Feb 7 09:07 file&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utime.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]) {&lt;br /&gt;
    if (argc&amp;gt;1) {&lt;br /&gt;
        int i=0;&lt;br /&gt;
        for (i=1;i&amp;lt;argc;i++) {&lt;br /&gt;
            struct stat structstat;&lt;br /&gt;
            if (stat(argv[i],&amp;amp;structstat)==0) {&lt;br /&gt;
                //read last access time&lt;br /&gt;
                time_t atim = structstat.st_atim.tv_sec;&lt;br /&gt;
                //read last modification time&lt;br /&gt;
                time_t mtim = structstat.st_mtim.tv_sec;&lt;br /&gt;
                atim -= 10 * 24 * 60 * 60;&lt;br /&gt;
                mtim -= 10 * 24 * 60 * 60;&lt;br /&gt;
                //create structure for utime&lt;br /&gt;
                struct utimbuf structutimbuf = {atim, mtim};&lt;br /&gt;
                //call utime for setting the new date&lt;br /&gt;
                if (utime(argv[i], &amp;amp;structutimbuf)!=0) {&lt;br /&gt;
                    //if utime failed print errno&lt;br /&gt;
                    perror(NULL);&lt;br /&gt;
                }&lt;br /&gt;
            } else {&lt;br /&gt;
                //if stat failed print errno&lt;br /&gt;
                perror(NULL);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
==Esercizio 2==&lt;br /&gt;
&amp;lt;source lang='text'&amp;gt;&lt;br /&gt;
Completare l’esercizio 1 ponendo una gestione opportuna dei parametri in linea comando che preveda di&lt;br /&gt;
poter modificare solo la data di accesso o quella di modifica, di definire il tempo di “invecchiamento” e un help.&lt;br /&gt;
Si possono inserire altri parametri ritenuti opportuni.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utime.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]) {&lt;br /&gt;
    int access = 0;&lt;br /&gt;
    int modification = 0;&lt;br /&gt;
    int c;&lt;br /&gt;
    int index = 0;&lt;br /&gt;
    time_t time = 10 * 24 * 60 * 60;&lt;br /&gt;
    while ((c = getopt(argc, argv, &amp;quot;amt:h&amp;quot;)) != -1) {&lt;br /&gt;
        switch (c) {&lt;br /&gt;
            case 'a': access=1; break;&lt;br /&gt;
            case 'm': modification=1; break;&lt;br /&gt;
            case 't':&lt;br /&gt;
                time = strtol(optarg,NULL,10);&lt;br /&gt;
                break;&lt;br /&gt;
            case 'h':&lt;br /&gt;
            case '?':&lt;br /&gt;
                printf(&amp;quot;usage: ager [-a] [-m] [-t time] [file ...]&amp;quot;);&lt;br /&gt;
            default:&lt;br /&gt;
                abort ();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    for (index = optind; index &amp;lt; argc; index++) {&lt;br /&gt;
        struct stat structstat;&lt;br /&gt;
        if (stat(argv[index], &amp;amp;structstat) == 0) {&lt;br /&gt;
            //read last access time&lt;br /&gt;
            time_t atim = structstat.st_atim.tv_sec;&lt;br /&gt;
            //read last modification time&lt;br /&gt;
            time_t mtim = structstat.st_mtim.tv_sec;&lt;br /&gt;
            if (!modification || access) {&lt;br /&gt;
                atim -= time;&lt;br /&gt;
            }&lt;br /&gt;
            if (!access || modification) {&lt;br /&gt;
                mtim -= time;&lt;br /&gt;
            }&lt;br /&gt;
            //create structure for utime&lt;br /&gt;
            struct utimbuf structutimbuf = {atim, mtim};&lt;br /&gt;
            //call utime for setting the new date&lt;br /&gt;
            if (utime(argv[index], &amp;amp;structutimbuf) != 0) {&lt;br /&gt;
                //if utime failed print errno&lt;br /&gt;
                perror(NULL);&lt;br /&gt;
            }&lt;br /&gt;
        } else {&lt;br /&gt;
            //if stat failed print errno&lt;br /&gt;
            perror(NULL);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Esercizio 3===&lt;br /&gt;
&amp;lt;source lang='text'&amp;gt;&lt;br /&gt;
Il programma deve elencare I file della directory corrente divisi per suffisso (e al termine l’elenco di quelli privi di&lt;br /&gt;
suffisso.&lt;br /&gt;
es:&lt;br /&gt;
.c: primo.c var.c main.c&lt;br /&gt;
.h: primo.h const.h&lt;br /&gt;
.odt: relazione.odt&lt;br /&gt;
makefile README COPYING&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
&amp;lt;source lang='C'&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
from pathlib import Path&lt;br /&gt;
&lt;br /&gt;
nosuffix = []&lt;br /&gt;
suffix = {}&lt;br /&gt;
for root, dirs, files in os.walk(&amp;quot;.&amp;quot;):&lt;br /&gt;
    for filename in files:&lt;br /&gt;
      path = Path(filename)&lt;br /&gt;
      if (len(path.suffix)&amp;gt;0):&lt;br /&gt;
        suffix.setdefault(path.suffix,[])&lt;br /&gt;
        suffix[path.suffix].append(filename)&lt;br /&gt;
      else:&lt;br /&gt;
        nosuffix.append(filename)&lt;br /&gt;
for k in suffix.keys():&lt;br /&gt;
    print(k, end=&amp;quot;: &amp;quot;)&lt;br /&gt;
    for v in suffix[k]:&lt;br /&gt;
        print(v,end=&amp;quot; &amp;quot;)&lt;br /&gt;
    print(&amp;quot;&amp;quot;)&lt;br /&gt;
for v in nosuffix: print(v, end=&amp;quot; &amp;quot;)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_Pratica_2012.05.30&amp;diff=1903</id>
		<title>Prova Pratica 2012.05.30</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_Pratica_2012.05.30&amp;diff=1903"/>
		<updated>2017-05-28T10:42:17Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: /* Esercizio 1 */ Aggiunta soluzione esercizio 1&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Esercizio 1==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Scrivere un minimake, cioe' una versione minimale del programma make.&lt;br /&gt;
Questo programma legge il file minimakefile nella directory corrente che ha il seguente formato:&lt;br /&gt;
target: comando&lt;br /&gt;
Il minimakefile e' scritto in modo che se le dipendenze sono generate da regole del minimakefile queste vengano definite&lt;br /&gt;
prima. Ad esempio il minimakefile per compilare ed eseguire il programma hw.c (hello world) sara':&lt;br /&gt;
hw.o: gcc -c hw.c&lt;br /&gt;
hw: gcc -o hw hw.o&lt;br /&gt;
run: ./hw&lt;br /&gt;
minimake deve scorrere il minimakefile, se il file indicato come target non esiste esegue il comando (e' vietato l'uso di popen,&lt;br /&gt;
system e simili!)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di Dado e Pierg===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;quot;s2argv/s2argv.h&amp;quot;&lt;br /&gt;
#define MAKEFILE &amp;quot;minimakefile&amp;quot;&lt;br /&gt;
#define RUN &amp;quot;run&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char * argv[]){&lt;br /&gt;
	FILE *mf;&lt;br /&gt;
	char *line=NULL;&lt;br /&gt;
	char *target,*command;&lt;br /&gt;
	int dim=0;&lt;br /&gt;
	mf=fopen(MAKEFILE,&amp;quot;r&amp;quot;);&lt;br /&gt;
	if(mf==NULL){&lt;br /&gt;
		fprintf(stderr,&amp;quot;minimakefile not found!\n&amp;quot;);&lt;br /&gt;
		exit(1);&lt;br /&gt;
	}&lt;br /&gt;
	while(getline(&amp;amp;line,&amp;amp;dim,mf)&amp;gt;0){&lt;br /&gt;
		target=strtok(line,&amp;quot;:&amp;quot;);&lt;br /&gt;
		command=strtok(NULL,&amp;quot;\n&amp;quot;);&lt;br /&gt;
		if(strcmp(target,RUN)==0){&lt;br /&gt;
			system_noshell(command);&lt;br /&gt;
		}&lt;br /&gt;
		else if(fopen(target,&amp;quot;r&amp;quot;)==NULL){&lt;br /&gt;
			system_noshell(command);&lt;br /&gt;
		}&lt;br /&gt;
        else{&lt;br /&gt;
			fprintf(stderr,&amp;quot;target %s già esistente: non eseguo\n&amp;quot;,target);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	free(line);&lt;br /&gt;
	fclose(mf);&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;execs.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]) {&lt;br /&gt;
    FILE* file;&lt;br /&gt;
    if ((file = fopen(&amp;quot;minimake&amp;quot;, &amp;quot;r&amp;quot;)) != NULL) {&lt;br /&gt;
        char target[256], path[256], args[256];&lt;br /&gt;
        while (fscanf(file, &amp;quot;%40[^:]: %99[^\n]\n&amp;quot;, target, path) != EOF) {&lt;br /&gt;
            //check if target file exist&lt;br /&gt;
            if (access(target, F_OK) != 0) {&lt;br /&gt;
                //if not fork and exec command&lt;br /&gt;
                int status;&lt;br /&gt;
                switch (fork()) {&lt;br /&gt;
                    case 0:&lt;br /&gt;
                        execsp(path);&lt;br /&gt;
                        break;&lt;br /&gt;
                    default:&lt;br /&gt;
                        wait(&amp;amp;status);&lt;br /&gt;
                        break;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        fclose(file);&lt;br /&gt;
    } else {&lt;br /&gt;
        perror(NULL);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Esercizio 2==&lt;br /&gt;
===Soluzione di Dado e Pierg===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
la sintassi del minimakefile per l'esercizio2 deve prevedere le dipendenze come il vero makefile.&lt;br /&gt;
target: dependence1 dependence2&lt;br /&gt;
comando&lt;br /&gt;
comando&lt;br /&gt;
(come per il makefile il target viene scritto a inizio linea, i comandi hanno un tab come primo carattere)&lt;br /&gt;
es per compilare hw.c.&lt;br /&gt;
hw.o: hw.c&lt;br /&gt;
gcc -c hw.c&lt;br /&gt;
hw: hw.o&lt;br /&gt;
gcc -o hw hw.o&lt;br /&gt;
run: hw&lt;br /&gt;
hw&lt;br /&gt;
in questo caso occorre eseguire i comandi che seguono la regola di dipendenza solo se il file target non esiste o&lt;br /&gt;
se un file indicato come dipendenza e' stato modificato piu' recentemente del target.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;quot;s2argv/s2argv.h&amp;quot;&lt;br /&gt;
#define MAKEFILE &amp;quot;minimakefile&amp;quot;&lt;br /&gt;
#define RUN &amp;quot;run&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char * argv[]){&lt;br /&gt;
	FILE *mf;&lt;br /&gt;
	char *line=NULL;&lt;br /&gt;
	char *target, *tmp;&lt;br /&gt;
	int dim = 0;&lt;br /&gt;
    int a = b = 0;&lt;br /&gt;
    struct stat st_target, st_tmp;&lt;br /&gt;
    &lt;br /&gt;
	mf=fopen(MAKEFILE,&amp;quot;r&amp;quot;);&lt;br /&gt;
	if(fd==NULL){&lt;br /&gt;
		fprintf(stderr,&amp;quot;minimakefile not found!\n&amp;quot;);&lt;br /&gt;
		exit(1);&lt;br /&gt;
	}&lt;br /&gt;
	while(getline(&amp;amp;line,&amp;amp;dim,mf)&amp;gt;0){&lt;br /&gt;
        if (line[0] == &amp;quot;\t&amp;quot;) {&lt;br /&gt;
            if(a || b){&lt;br /&gt;
                system_noshell(line + 1);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
		else{&lt;br /&gt;
			a = b = 0;&lt;br /&gt;
            target = strtok(line, &amp;quot;:&amp;quot;)&lt;br /&gt;
            if(fopen(target,&amp;quot;r&amp;quot;)==NULL){&lt;br /&gt;
                a = 1;&lt;br /&gt;
		    }&lt;br /&gt;
            else if(strcmp(target,RUN)==0){&lt;br /&gt;
                char *command = strtok(NULL, &amp;quot;\n&amp;quot;);&lt;br /&gt;
                system_noshell(command);&lt;br /&gt;
		    }&lt;br /&gt;
            else {&lt;br /&gt;
                stat(target, &amp;amp;st_target);&lt;br /&gt;
                while ((tmp = (strtok(NULL, &amp;quot; &amp;quot;))!=NULL){&lt;br /&gt;
                    stat(tmp, &amp;amp;st_tmp);&lt;br /&gt;
                    if (st_tmp.st_mtime &amp;gt; st_target.st_mtime ) {&lt;br /&gt;
                        b = 1;&lt;br /&gt;
                        break;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
		    }&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	free(line);&lt;br /&gt;
	fclose(fd);&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
==Esercizio 3==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Lo script o il programma python prende il nome di una directory come parametro e deve listare i file nella directory che hanno&lt;br /&gt;
un file corrispondente con nome scritto al contrario (il file ailati deve essere nella lista solo se nella directory c'e' anche italia, i&lt;br /&gt;
palindromi devono essere listati una sola volta).&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di Pierg===&lt;br /&gt;
&amp;lt;source lang =&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
import sys, os&lt;br /&gt;
&lt;br /&gt;
lines = []&lt;br /&gt;
directory = sys.argv[1]&lt;br /&gt;
&lt;br /&gt;
for file in os.listdir(directory):&lt;br /&gt;
    lines.append(file)&lt;br /&gt;
    &lt;br /&gt;
for el in lines:&lt;br /&gt;
    for le in lines:&lt;br /&gt;
        if el[::-1] == le:&lt;br /&gt;
            print el&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di Dado===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#! /bin/bash&lt;br /&gt;
cd $1&lt;br /&gt;
files=`ls`&lt;br /&gt;
ls &amp;gt; /tmp/tmp.txt&lt;br /&gt;
rev /tmp/tmp.txt &amp;gt; /tmp/tmp2.txt&lt;br /&gt;
for file in $files; do&lt;br /&gt;
	grep $file /tmp/tmp2.txt&lt;br /&gt;
done&lt;br /&gt;
rm /tmp/tmp.txt&lt;br /&gt;
rm /tmp/tmp2.txt&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaPratica_2011.05.30&amp;diff=1902</id>
		<title>ProvaPratica 2011.05.30</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaPratica_2011.05.30&amp;diff=1902"/>
		<updated>2017-05-27T14:33:20Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Aggiunta soluzione es 1&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/pratiche/2011.05.30.pdf Link to exam]&lt;br /&gt;
&lt;br /&gt;
==Esercizio 1==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Scrivere due programmi in modo che i parametri passati al&lt;br /&gt;
primo vengano stampati dal secondo. La comunicazione deve avvenire tramite una shared memory realizzata&lt;br /&gt;
con la chiamata POSIX shm_open, la sincronizzazione tramite segnali. Viene attivato per primo il programma&lt;br /&gt;
ricevente. (anche i pid dei processi possono venir scambiati attraverso la shared memory!).&lt;br /&gt;
Es: scrivere in un terminale:&lt;br /&gt;
$./receiver&lt;br /&gt;
l'esecuzione di “receiver rimane in attesa”. in un secondo terminale scrivere:&lt;br /&gt;
$./sender a bb ccc&lt;br /&gt;
nel primo deve comparire&lt;br /&gt;
./sender&lt;br /&gt;
a&lt;br /&gt;
bb&lt;br /&gt;
ccc&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di Gab===&lt;br /&gt;
sender.c&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/mman.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define COMMUNICATION_SIGNAL 25&lt;br /&gt;
#define MAXIMUM_LENGTH 4096&lt;br /&gt;
&lt;br /&gt;
#define PID_LENGTH 10&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(int argc,char *argv[])&lt;br /&gt;
{&lt;br /&gt;
	int sharedFD = shm_open(&amp;quot;/sharedPID&amp;quot;, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);&lt;br /&gt;
	if(!sharedFD)&lt;br /&gt;
		return;&lt;br /&gt;
	ftruncate(sharedFD,sizeof(char)*PID_LENGTH);&lt;br /&gt;
	char *sharedVariable = mmap(NULL,sizeof(char)*PID_LENGTH,PROT_READ | PROT_WRITE,MAP_SHARED,sharedFD,0);&lt;br /&gt;
	int effectivePID = atoi(sharedVariable);&lt;br /&gt;
	int sharedParametersFD = shm_open(&amp;quot;/sharedParameters&amp;quot;, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);&lt;br /&gt;
	if(!sharedParametersFD)&lt;br /&gt;
		return;&lt;br /&gt;
	ftruncate(sharedParametersFD,sizeof(char)*MAXIMUM_LENGTH);&lt;br /&gt;
	char *parameters = mmap(NULL,sizeof(char)*MAXIMUM_LENGTH,PROT_READ | PROT_WRITE,MAP_SHARED,sharedParametersFD,0);&lt;br /&gt;
	char parametersToPass[MAXIMUM_LENGTH];&lt;br /&gt;
	int i;&lt;br /&gt;
	for(i=0;i&amp;lt;MAXIMUM_LENGTH;i++)&lt;br /&gt;
		parametersToPass[i]='\0';&lt;br /&gt;
	for(i=0;i&amp;lt;argc;i++)&lt;br /&gt;
	{&lt;br /&gt;
		strcat(parametersToPass,argv[i]);&lt;br /&gt;
		strcat(parametersToPass,&amp;quot;\n&amp;quot;);&lt;br /&gt;
	}	&lt;br /&gt;
	strcpy(parameters,parametersToPass);&lt;br /&gt;
	kill(effectivePID,COMMUNICATION_SIGNAL);&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
receiver.c&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/mman.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define COMMUNICATION_SIGNAL 25&lt;br /&gt;
&lt;br /&gt;
#define MAXIMUM_LENGTH 4096&lt;br /&gt;
&lt;br /&gt;
#define PID_LENGTH 10&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
static void signalHandler (int sig, siginfo_t *siginfo, void *context)&lt;br /&gt;
{&lt;br /&gt;
	int sharedFD = shm_open(&amp;quot;/sharedParameters&amp;quot;, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);&lt;br /&gt;
	if(!sharedFD)&lt;br /&gt;
		return;&lt;br /&gt;
	ftruncate(sharedFD,sizeof(char)*MAXIMUM_LENGTH);&lt;br /&gt;
	char *parameters = mmap(NULL,sizeof(char)*MAXIMUM_LENGTH,PROT_READ | PROT_WRITE,MAP_SHARED,sharedFD,0);&lt;br /&gt;
	printf(&amp;quot;Parameters: %s&amp;quot;,parameters);&lt;br /&gt;
	exit(0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(int argc,char *argv[])&lt;br /&gt;
{&lt;br /&gt;
	int sharedFD = shm_open(&amp;quot;/sharedPID&amp;quot;, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);&lt;br /&gt;
	if(!sharedFD)&lt;br /&gt;
		return;&lt;br /&gt;
	char *currentPIDToPass; 	&lt;br /&gt;
	asprintf(&amp;amp;currentPIDToPass,&amp;quot;%d&amp;quot;,getpid());&lt;br /&gt;
	ftruncate(sharedFD,sizeof(char)*PID_LENGTH);&lt;br /&gt;
	char *sharedVariable = mmap(NULL,sizeof(char)*PID_LENGTH,PROT_READ | PROT_WRITE,MAP_SHARED,sharedFD,0);&lt;br /&gt;
	strcpy(sharedVariable,currentPIDToPass);&lt;br /&gt;
	struct sigaction signalStruct;&lt;br /&gt;
	signalStruct.sa_sigaction = &amp;amp;signalHandler;&lt;br /&gt;
	sigaction(COMMUNICATION_SIGNAL,&amp;amp;signalStruct,NULL);&lt;br /&gt;
	while(1){}&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Gab&lt;br /&gt;
===Soluzione di Dado e Pierg===&lt;br /&gt;
&lt;br /&gt;
sender.c&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/mman.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int main(int argc, char * argv[]){&lt;br /&gt;
	int fd,i;&lt;br /&gt;
	fd=shm_open(&amp;quot;/shared&amp;quot;,O_RDWR,'w');&lt;br /&gt;
	if(fd==-1){&lt;br /&gt;
		fprintf(stderr,&amp;quot;Error while opening the shared memory\n&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
	for(i=0;i&amp;lt;argc;i++){&lt;br /&gt;
		write(fd,argv[i],sizeof(argv[i]));&lt;br /&gt;
		write(fd,&amp;quot;\n&amp;quot;,1);&lt;br /&gt;
	}&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
receiver.c&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/mman.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt; &lt;br /&gt;
#define MAXLENGTH 1024&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char * argv[]){&lt;br /&gt;
	int fd;&lt;br /&gt;
	ssize_t dim;&lt;br /&gt;
	char buff[MAXLENGTH];&lt;br /&gt;
	fd=shm_open(&amp;quot;/shared&amp;quot;,O_RDWR,'r');&lt;br /&gt;
	while((dim=read(fd,&amp;amp;buff,MAXLENGTH))){&lt;br /&gt;
		write(stdout,buff,MAXLENGTH);&lt;br /&gt;
	}&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Manca la sincronizzazione tramite segnali --[[User:FedericoB|FedericoB]] ([[User talk:FedericoB|talk]]) 16:33, 27 May 2017 (CEST)&lt;br /&gt;
&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
receiver&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
#define _GNU_SOURCE&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/mman.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXSIZE 4096&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]) {&lt;br /&gt;
    int sharedFD;&lt;br /&gt;
    printf(&amp;quot;pid = %d\n&amp;quot;, getpid());&lt;br /&gt;
    //create shared memory object&lt;br /&gt;
    if ((sharedFD = shm_open(&amp;quot;/sharedPID&amp;quot;, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR)) != -1) {&lt;br /&gt;
        //set size of shared memory object&lt;br /&gt;
        if (ftruncate(sharedFD, MAXSIZE) == 0) {&lt;br /&gt;
            //create a memory mapping with sharedVariable and the shared memory object&lt;br /&gt;
            #define OFFSET 0&lt;br /&gt;
            #define ADDRESS NULL&lt;br /&gt;
            char* sharedVariable = mmap(ADDRESS, MAXSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, sharedFD, OFFSET);&lt;br /&gt;
            if (sharedVariable != MAP_FAILED) {&lt;br /&gt;
                sprintf(sharedVariable, &amp;quot;%d\n&amp;quot;, getpid());&lt;br /&gt;
                sigset_t mask;&lt;br /&gt;
                //initialize signal mask&lt;br /&gt;
                sigemptyset(&amp;amp;mask);&lt;br /&gt;
                sigaddset(&amp;amp;mask, SIGUSR1);&lt;br /&gt;
                //block default handler for SIGUSR1&lt;br /&gt;
                sigprocmask(SIG_BLOCK, &amp;amp;mask, NULL);&lt;br /&gt;
                int sig;&lt;br /&gt;
                //wait for a SIGURS1&lt;br /&gt;
                sigwait(&amp;amp;mask, &amp;amp;sig);&lt;br /&gt;
                printf(&amp;quot;%s\n&amp;quot;, sharedVariable);&lt;br /&gt;
                shm_unlink(&amp;quot;/sharedPID&amp;quot;);&lt;br /&gt;
                return 0;&lt;br /&gt;
            } else {&lt;br /&gt;
                perror(NULL);&lt;br /&gt;
            }&lt;br /&gt;
        } else {&lt;br /&gt;
            perror(NULL);&lt;br /&gt;
        }&lt;br /&gt;
    } else {&lt;br /&gt;
        perror(NULL);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
sender&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/mman.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAXSIZE 4096&lt;br /&gt;
&lt;br /&gt;
int main(int argc,char *argv[]) {&lt;br /&gt;
    int sharedFD;&lt;br /&gt;
    //create shared memory object&lt;br /&gt;
    if ((sharedFD = shm_open(&amp;quot;/sharedPID&amp;quot;, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR)) != -1) {&lt;br /&gt;
        ftruncate(sharedFD, MAXSIZE);&lt;br /&gt;
        #define OFFSET 0&lt;br /&gt;
        #define ADDRESS NULL&lt;br /&gt;
        char* sharedVariable = mmap(ADDRESS, MAXSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, sharedFD, OFFSET);&lt;br /&gt;
        int effectivePID = atoi(sharedVariable);&lt;br /&gt;
        printf(&amp;quot;pid = %d\n&amp;quot;,effectivePID);&lt;br /&gt;
        shm_unlink(&amp;quot;/sharedPID&amp;quot;);&lt;br /&gt;
        int i;&lt;br /&gt;
        for (i=0;i&amp;lt;MAXSIZE;i++) sharedVariable[i]='\0';&lt;br /&gt;
        for (i = 1; i &amp;lt; argc; i++) {&lt;br /&gt;
            strcat(sharedVariable, argv[i]);&lt;br /&gt;
            strcat(sharedVariable, &amp;quot; &amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        kill(effectivePID, SIGUSR1);&lt;br /&gt;
        return 0;&lt;br /&gt;
    } else {&lt;br /&gt;
        perror(NULL);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2017.02.17&amp;diff=1901</id>
		<title>Prova pratica 2017.02.17</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2017.02.17&amp;diff=1901"/>
		<updated>2017-05-27T08:19:20Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Aggiunta soluzione esercizio 2&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[http://www.cs.unibo.it/~renzo/so/pratiche/2017.02.17.pdf link al testo]]&lt;br /&gt;
==Esercizio 1==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Scrivere un programma ager in grado di invecchiare file.&lt;br /&gt;
Il programma deve poter prendere una lista di file come parametri o nessun parametro, nel qual caso invecchierà&lt;br /&gt;
tutti I file della directory corrente. “invecchiare” significa predatare il tempo di ultimo accesso e di modifica di 10&lt;br /&gt;
giorni.&lt;br /&gt;
Esempio:&lt;br /&gt;
$ ls -l file&lt;br /&gt;
-rw-r--r-- 1 renzo renzo 0 Feb 17 09:07 file&lt;br /&gt;
$ ./ager file&lt;br /&gt;
$ ls -l file&lt;br /&gt;
-rw-r--r-- 1 renzo renzo 0 Feb 7 09:07 file&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utime.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]) {&lt;br /&gt;
    if (argc&amp;gt;1) {&lt;br /&gt;
        int i=0;&lt;br /&gt;
        for (i=1;i&amp;lt;argc;i++) {&lt;br /&gt;
            struct stat structstat;&lt;br /&gt;
            if (stat(argv[i],&amp;amp;structstat)==0) {&lt;br /&gt;
                //read last access time&lt;br /&gt;
                time_t atim = structstat.st_atim.tv_sec;&lt;br /&gt;
                //read last modification time&lt;br /&gt;
                time_t mtim = structstat.st_mtim.tv_sec;&lt;br /&gt;
                atim -= 10 * 24 * 60 * 60;&lt;br /&gt;
                mtim -= 10 * 24 * 60 * 60;&lt;br /&gt;
                //create structure for utime&lt;br /&gt;
                struct utimbuf structutimbuf = {atim, mtim};&lt;br /&gt;
                //call utime for setting the new date&lt;br /&gt;
                if (utime(argv[i], &amp;amp;structutimbuf)!=0) {&lt;br /&gt;
                    //if utime failed print errno&lt;br /&gt;
                    perror(NULL);&lt;br /&gt;
                }&lt;br /&gt;
            } else {&lt;br /&gt;
                //if stat failed print errno&lt;br /&gt;
                perror(NULL);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
==Esercizio 2==&lt;br /&gt;
&amp;lt;source lang='text'&amp;gt;&lt;br /&gt;
Completare l’esercizio 1 ponendo una gestione opportuna dei parametri in linea comando che preveda di&lt;br /&gt;
poter modificare solo la data di accesso o quella di modifica, di definire il tempo di “invecchiamento” e un help.&lt;br /&gt;
Si possono inserire altri parametri ritenuti opportuni.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utime.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]) {&lt;br /&gt;
    int access = 0;&lt;br /&gt;
    int modification = 0;&lt;br /&gt;
    int c;&lt;br /&gt;
    int index = 0;&lt;br /&gt;
    time_t time = 10 * 24 * 60 * 60;&lt;br /&gt;
    while ((c = getopt(argc, argv, &amp;quot;amt:h&amp;quot;)) != -1) {&lt;br /&gt;
        switch (c) {&lt;br /&gt;
            case 'a': access=1; break;&lt;br /&gt;
            case 'm': modification=1; break;&lt;br /&gt;
            case 't':&lt;br /&gt;
                time = strtol(optarg,NULL,10);&lt;br /&gt;
                break;&lt;br /&gt;
            case 'h':&lt;br /&gt;
            case '?':&lt;br /&gt;
                printf(&amp;quot;usage: ager [-a] [-m] [-t time] [file ...]&amp;quot;);&lt;br /&gt;
            default:&lt;br /&gt;
                abort ();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    for (index = optind; index &amp;lt; argc; index++) {&lt;br /&gt;
        struct stat structstat;&lt;br /&gt;
        if (stat(argv[index], &amp;amp;structstat) == 0) {&lt;br /&gt;
            //read last access time&lt;br /&gt;
            time_t atim = structstat.st_atim.tv_sec;&lt;br /&gt;
            //read last modification time&lt;br /&gt;
            time_t mtim = structstat.st_mtim.tv_sec;&lt;br /&gt;
            if (!modification || access) {&lt;br /&gt;
                atim -= time;&lt;br /&gt;
            }&lt;br /&gt;
            if (!access || modification) {&lt;br /&gt;
                mtim -= time;&lt;br /&gt;
            }&lt;br /&gt;
            //create structure for utime&lt;br /&gt;
            struct utimbuf structutimbuf = {atim, mtim};&lt;br /&gt;
            //call utime for setting the new date&lt;br /&gt;
            if (utime(argv[index], &amp;amp;structutimbuf) != 0) {&lt;br /&gt;
                //if utime failed print errno&lt;br /&gt;
                perror(NULL);&lt;br /&gt;
            }&lt;br /&gt;
        } else {&lt;br /&gt;
            //if stat failed print errno&lt;br /&gt;
            perror(NULL);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2017.02.17&amp;diff=1896</id>
		<title>Prova pratica 2017.02.17</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2017.02.17&amp;diff=1896"/>
		<updated>2017-05-26T12:58:05Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Creata pagina con soluzione es 1&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[http://www.cs.unibo.it/~renzo/so/pratiche/2017.02.17.pdf link al testo]]&lt;br /&gt;
==Esercizio 1==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Scrivere un programma ager in grado di invecchiare file.&lt;br /&gt;
Il programma deve poter prendere una lista di file come parametri o nessun parametro, nel qual caso invecchierà&lt;br /&gt;
tutti I file della directory corrente. “invecchiare” significa predatare il tempo di ultimo accesso e di modifica di 10&lt;br /&gt;
giorni.&lt;br /&gt;
Esempio:&lt;br /&gt;
$ ls -l file&lt;br /&gt;
-rw-r--r-- 1 renzo renzo 0 Feb 17 09:07 file&lt;br /&gt;
$ ./ager file&lt;br /&gt;
$ ls -l file&lt;br /&gt;
-rw-r--r-- 1 renzo renzo 0 Feb 7 09:07 file&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;utime.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]) {&lt;br /&gt;
    if (argc&amp;gt;1) {&lt;br /&gt;
        int i=0;&lt;br /&gt;
        for (i=1;i&amp;lt;argc;i++) {&lt;br /&gt;
            struct stat structstat;&lt;br /&gt;
            if (stat(argv[i],&amp;amp;structstat)==0) {&lt;br /&gt;
                //read last access time&lt;br /&gt;
                time_t atim = structstat.st_atim.tv_sec;&lt;br /&gt;
                //read last modification time&lt;br /&gt;
                time_t mtim = structstat.st_mtim.tv_sec;&lt;br /&gt;
                atim -= 10 * 24 * 60 * 60;&lt;br /&gt;
                mtim -= 10 * 24 * 60 * 60;&lt;br /&gt;
                //create structure for utime&lt;br /&gt;
                struct utimbuf structutimbuf = {atim, mtim};&lt;br /&gt;
                //call utime for setting the new date&lt;br /&gt;
                if (utime(argv[i], &amp;amp;structutimbuf)!=0) {&lt;br /&gt;
                    //if utime failed print errno&lt;br /&gt;
                    perror(NULL);&lt;br /&gt;
                }&lt;br /&gt;
            } else {&lt;br /&gt;
                //if stat failed print errno&lt;br /&gt;
                perror(NULL);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prove_pratiche&amp;diff=1895</id>
		<title>Prove pratiche</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prove_pratiche&amp;diff=1895"/>
		<updated>2017-05-26T12:52:42Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Aggiunto link prova pratica 2017.02.17&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;''Qui troverete alcune prove teoriche pratiche (laboratorio), in modo da poter discutere sull'elaborato''&lt;br /&gt;
&lt;br /&gt;
''Le prove sono svolte da studenti e servono come base per la discussione. Le soluzioni possono essere errate''&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2017.02.17]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2017.01.17]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2016.09.13]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2016.05.31]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2015.05.20]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2015.01.21]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2014.09.25]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2014.07.17]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Pratica 2014.07.02]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Pratica 2014.06.17]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2014.05.29]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Pratica 2014.02.20]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2014.01.23]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.09.13]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.07.18]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.06.21]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.05.29]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.02.15]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.01.25]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2012.09.19]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2012.07.17]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2012.06.20]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Pratica 2012.05.30]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2011.09.12]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Pratica 2011.06.22]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2011.05.30]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2011.01.19]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2010.07.19]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica_2010.07.12]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2010.02.03]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2009.09.23]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2009.06.23]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2009.02.12]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2009.01.15]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2005.02.10]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2002.01.24]]&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prove_pratiche&amp;diff=1894</id>
		<title>Prove pratiche</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prove_pratiche&amp;diff=1894"/>
		<updated>2017-05-26T11:53:44Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Correct chronological order of tests&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;''Qui troverete alcune prove teoriche pratiche (laboratorio), in modo da poter discutere sull'elaborato''&lt;br /&gt;
&lt;br /&gt;
''Le prove sono svolte da studenti e servono come base per la discussione. Le soluzioni possono essere errate''&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2017.01.17]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2016.09.13]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2016.05.31]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2015.05.20]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2015.01.21]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2014.09.25]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2014.07.17]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Pratica 2014.07.02]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Pratica 2014.06.17]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2014.05.29]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Pratica 2014.02.20]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2014.01.23]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.09.13]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.07.18]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.06.21]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.05.29]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.02.15]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.01.25]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2012.09.19]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2012.07.17]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2012.06.20]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Pratica 2012.05.30]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2011.09.12]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Pratica 2011.06.22]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2011.05.30]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2011.01.19]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2010.07.19]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica_2010.07.12]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2010.02.03]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2009.09.23]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2009.06.23]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2009.02.12]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2009.01.15]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2005.02.10]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2002.01.24]]&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1884</id>
		<title>Lezioni Anno Accademico 2016/17</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1884"/>
		<updated>2017-05-16T14:03:30Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: /* Lezione del 16 maggio 2017 */ Aggiunti argomenti trattati&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;''scrivete qui idee, riassunti dei concetti espressi, commenti approfondimenti sulle lezioni.''&lt;br /&gt;
&lt;br /&gt;
== Lezione del 23 settembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
'''YY'''&lt;br /&gt;
&lt;br /&gt;
* Presentazione del corso.&lt;br /&gt;
* Strumenti del corso: web, wiki.mailing list&lt;br /&gt;
* Indice dei contenuti&lt;br /&gt;
* Prove di esame (Scritto, Prova Pratica, Progetto).&lt;br /&gt;
&lt;br /&gt;
* Hardware Software&lt;br /&gt;
* Informatica&lt;br /&gt;
* Informazione/Dato&lt;br /&gt;
* Rivoluzione digitale (secondo rinascimento, Terza rivoluzione Industriale).&lt;br /&gt;
* Software Libero&lt;br /&gt;
&lt;br /&gt;
== Lezione del 27 settembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
'''OS:Y'''&lt;br /&gt;
&lt;br /&gt;
* '''Organizzazione (martedi' vs. venerdi')'''&lt;br /&gt;
In genere la lezione del martedì sarà più teorica mentre quella del venerdì più orientata agli aspetti pratici di laboratorio. Questa suddivisione potrebbe cambiare in quanto qualche martedì verrà saltato. &lt;br /&gt;
* '''Universit&amp;amp;agrave;'''&lt;br /&gt;
Università = studenti + docenti &lt;br /&gt;
* '''Digitale/Analogico'''&lt;br /&gt;
Digit significa cifra. Nell'informatica è fondamentale il passaggio dal continuo al discreto.&lt;br /&gt;
* '''Binario/Decimale'''&lt;br /&gt;
Nei calcolatori l'informazione è memorizzata utilizzando il sistema binario ...&lt;br /&gt;
(''in quanto in passato i relè consentivano di avere due stati: &amp;quot; 1 e 0 &amp;quot;'': &amp;amp;egrave; impreciso, qualcuno sa dare una definizione migliore? [[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 12:00, 29 September 2016 (CEST)) &amp;lt;br&amp;gt;&lt;br /&gt;
In quanto è molto semplice implementare le operazioni aritmetiche con dei circuiti elettronici utilizzando la codifica binaria. Per esempio è possibile creare un [https://it.wikipedia.org/wiki/Half-adder circuito] che somma due bit con riporto con solo due porte logiche. [[User:FedericoB|FedericoB]] ([[User talk:FedericoB|talk]]) 17:05, 29 September 2016 (CEST)&lt;br /&gt;
* '''I linguaggi e i problemi dell'Informatica'''&lt;br /&gt;
**Processing dell'informazione (Linguaggi di programmazione)&lt;br /&gt;
**Comunicazione (Il problema che i dati che mi servono sono in un altro luogo) (Linguaggi dei protocolli di rete, che però sono dei linguaggi temporizzati)&lt;br /&gt;
**Memorizzazione (Il problema che i dati mi serviranno in futuro) (Linguaggio del formato dei dati)&lt;br /&gt;
* '''Conoscenze a lungo, medio e breve termine nell'Informatica.'''&lt;br /&gt;
**BREVE: tecnologia, determinati programmi di sviluppo.&lt;br /&gt;
**MEDIO: singoli linguaggi, singoli OS, protocolli di comunicazione.&lt;br /&gt;
**LUNGO: algoritmi, informatica teorica, fondamenti costruttivi di reti e di OS.&lt;br /&gt;
&lt;br /&gt;
* '''Cos'è una distribuzione'''&lt;br /&gt;
E' un antologia di applicazioni e librerie in modo che siano compatibili tra loro.&lt;br /&gt;
&lt;br /&gt;
* '''Sistema Operativo: perch&amp;amp;eacute;'''&lt;br /&gt;
Un sistema operativo è un programma che controlla l’esecuzione di programmi applicativi e agisce come interfaccia tra le applicazioni e l’hardware del calcolatore. &amp;lt;br&amp;gt;&lt;br /&gt;
E' uno strumento software complesso ed implementabile in diversi modi (monolitico, microkernel, etc.). I microcontroller non hanno OS. &lt;br /&gt;
Permette: contabilità delle risorse (consente a sua volta attività di benchmarking); continuità dei servizi; controllo errori&lt;br /&gt;
(programmi, dispositivi); multiuser (gli utenti hanno diversi diritti e proprietà sulle risorse); protezione delle attività dei                     &lt;br /&gt;
processi; astrazione file system.&lt;br /&gt;
* '''Struttura a livelli (layer)'''&lt;br /&gt;
&lt;br /&gt;
  ---------------&lt;br /&gt;
  ---------------&lt;br /&gt;
  ---------------&lt;br /&gt;
        OS&lt;br /&gt;
  ---------------L'&lt;br /&gt;
        hw&lt;br /&gt;
  ---------------L&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
Consente di: separare la complessità, di avere portabilità. &lt;br /&gt;
Ogni livello rappresenta un cambio di linguaggio. &lt;br /&gt;
Più aggiungiamo livelli più aumenta il livello di astrazione.&lt;br /&gt;
* '''Programma/processo'''&lt;br /&gt;
Algoritmo: sequenza finita di istruzioni non ambigue, atta a risolvere un problema.&lt;br /&gt;
&lt;br /&gt;
Programma: traduzione degli algoritmi in un linguaggio di programmazione.&lt;br /&gt;
&lt;br /&gt;
Processo: istanza esecutiva di un programma. Il programma è testo statico, quando viene eseguito diviene processo. Possiamo avere&lt;br /&gt;
uno stesso programma in esecuzione su più processi (e.g apertura di più terminali che eseguono un editor di testo).&lt;br /&gt;
&lt;br /&gt;
System call: richieste di processi al OS. e.g. printf (visto nel codice compilato in assembly tramite gcc -s), invoca write().&lt;br /&gt;
vi è una sequenza di azioni del tipo: richiesta di stampa al kernel --&amp;gt; attesa --&amp;gt; risposta.&lt;br /&gt;
* '''Storia dell'Informatica prima dei transistor'''&lt;br /&gt;
&lt;br /&gt;
La storia dei calcolatori non ha un inizio preciso. Nel 1900 venne trovato sul relitto di una nave greca risalente al II secolo a.C un meccanismo in grado di calcolare i movimenti degli astri, si ipotizza con una sorprendente meccanica. Oggi questo sistema viene conosciuto come Macchina di Anticitera. Negli ultimi anni di ricerche archeologiche e storiche è venuta sempre più in superficie una vena pionieristica nel mondo classico per quanto riguarda lo studio sul calcolo automatizzato e addirittura di automi (si veda Erone di Alessandria). Ma facciamo un salto in avanti di qualche secolo... &amp;lt;br&amp;gt;&lt;br /&gt;
Già nel XVII secolo Pascal teorizza e mette in pratica uno strumento in grado di addizionare e sottrarre, e nel 1672 G.W. von Leibniz lo perfeziona con l'aggiunta della moltiplicazione e della divisione. Ma è solamente nei primi anni del 1800 che entra in scena la macchina analitica di Babbage. Estremamente complessa, la macchina era dotata di un sistema di input e di elaborazione dati e di un sistema di output. Ed è con Babbage che nascono le schede perforate. La macchina analitica venne presentata per la prima volta in Italia, durante un convegno torinese del 1840. Tra i vari collaboratori scientifici alla macchina è da annotare la presenza di Luigi Federico Menabrea, che tra le altre cose fu Ministro della Marina Militare, ingegnere e Presidente del Consiglio dei Ministri del Regno d'Italia (1867-1869). Il primo vero programmatore della storia, collaboratrice di Babbage, è Ada Lovelace (figlia del poeta Lord Byron!): progettò un algoritmo in grado di elaborare i numeri di Bernulli. &amp;lt;br&amp;gt;&lt;br /&gt;
Nel 1896 viene fondata la Tabulating Machine Company (futura IBM) e agli albori del '900, vari scienziati (trai quali Thomas Edison e Guglielmo Marconi) portarono avanti gli studi sulla valvola termoionica, elemento indispensabile dei primi calcolatori elettronici. La valvola incrementò vertiginosamente i tempi di calcolo ma l'estrema fragilità della stessa causò una serie di problematiche che si risolsero solamente con l'entrata in gioco del transistor.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 30 settembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
'''YC'''&lt;br /&gt;
&lt;br /&gt;
(Storia dopo l'avvento del transistor)&lt;br /&gt;
&lt;br /&gt;
Tre innovazioni indipendenti:&lt;br /&gt;
* Multitasking: Inizialmente ideato per mantenere la CPU occupata con altri processi mentre si faceva Input/Output. Permette di dare l'idea al utente di poter eseguire più processi contemporaneamente anche se questi in realtà sono eseguiti dalla cpu in modo intervallato. Ci sono certi computer che hanno più esecutori e quindi possono realmente eseguire multipli processi contemporaneamente. &lt;br /&gt;
* Supporto Multiuser&lt;br /&gt;
* Interattivit&amp;amp;agrave;:l'interazione con l'utente cambia il flusso del programma.&lt;br /&gt;
&lt;br /&gt;
Queste tre proprietà sono ortogonali quindi possono esistere nei sistemi varie combinazioni di queste.&lt;br /&gt;
&lt;br /&gt;
Iniziano anche a separarsi i ruoli tra programmatori e utenti.Prima i computer venivano utilizzati solo da chi creava programmi ora anche da chi li utilizza senza avere le conoscenze per crearli.&lt;br /&gt;
&lt;br /&gt;
Il Time Sharing.&lt;br /&gt;
&lt;br /&gt;
Sistemi Paralleli (SIMD/MIMD)&lt;br /&gt;
Sistemi Real Time&lt;br /&gt;
&lt;br /&gt;
Dal 1969: UNIX.&lt;br /&gt;
&lt;br /&gt;
Il linguaggio C e la portabilit&amp;amp;agrave; dei sistemi operativi.&lt;br /&gt;
Caratteristiche del linguaggio C:&lt;br /&gt;
* grande controllo sul codice generato&lt;br /&gt;
* costrutti per l'elaborazione a basso livello&lt;br /&gt;
* supporto per la programmazione strutturata&lt;br /&gt;
* linguaggio &amp;quot;leggero&amp;quot;: poche parole chiave, I/O gestito tramite librerie&lt;br /&gt;
* Il compilatore C &amp;amp;egrave; scritto in C.&lt;br /&gt;
&lt;br /&gt;
Portabilit&amp;amp;agrave; di un compilatore. I cross-compiler.&lt;br /&gt;
&lt;br /&gt;
Il comando UNIX cc (o gcc): un comando unico per attivare l'intera toolchain per generare un eseguibile.&lt;br /&gt;
&lt;br /&gt;
Il punto e virgola trasforma espressioni in istruzioni (terminatore).&lt;br /&gt;
&lt;br /&gt;
1975? l'avvento dei personal computer. (in realt&amp;amp;agrave; il primo PC forse e' la Perrottina Olivetti del 1964).&lt;br /&gt;
 &lt;br /&gt;
Compiti a casa:&lt;br /&gt;
Portare esperimenti in C ritenuti &amp;quot;difficili&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 10:41, 1 October 2016 (CEST)&lt;br /&gt;
&lt;br /&gt;
== Lezione del 7 ottobre 2016 ==&lt;br /&gt;
Audio Lezione: [https://drive.google.com/open?id=0B8G3NNuJ7MA7TzJXUTJZM3RBUlE 7\10\16]&lt;br /&gt;
* '''Esempi di programmi in linguaggio C'''&lt;br /&gt;
Abbiamo commentato i programmi dal 1.1 al 1.7 che si possono trovare in questa [[2016-17_Programmi_C|pagina]].&lt;br /&gt;
* '''Economia Push/Economia Pull'''&lt;br /&gt;
Nell'economia push si basa la produzione sulla previsione di domanda e la si cerca di aumentare tramite la pubblicità. Questa è stata la principale metodologia utilizzata nei secoli scorsi.&lt;br /&gt;
Al giorno d'oggi si sta facendo avanti un differente approccio chiamato &amp;quot;economia pull&amp;quot; dove la produzione è basata sulla domanda effettiva. Per esempio per richiedere un finanziamento per l'economia push l'approccio comune è tramite una banca a cui si chiede un prestito in base alle previsioni di ricavo. Invece nell'economia pull è esemplare il fenomeno del crowdfounding dove si riceve un finanziamento dalla &amp;quot;folla&amp;quot; cioè da un gruppo di persone che dona una piccola parte di denaro ciascuna in base al reale interesse per il prodotto/servizio.&lt;br /&gt;
* '''Sistemi Operativi e parallelismo'''&lt;br /&gt;
Più processi si dicono in esecuzione concorrente quando accedono a risorse condivise.&lt;br /&gt;
Un programma è multithread quando tutti i thread condividono la stessa memoria. Ogni thread deve avere un suo stack.&lt;br /&gt;
La concorrenza si studia in particolare in sistemi operativi in quanto un sistema operativo multitasking è un sistema concorrente.&lt;br /&gt;
Concorrenza di processi in architetture SMP (Symmetric multiprocessor system) e non. (RD: non mi pare di aver spiegato cosa sia l'SMP) &amp;lt;br&amp;gt;&lt;br /&gt;
Simulazione del fenomeno della '''race condition'''. Si ha '''race condition''' quando un programma concorrente pu&amp;amp;ograve; puo' produrre diversi risultati se eseguito piu' volte per colpa della diversa evoluzione temporale delle elaborazioni.&amp;lt;br&amp;gt;&lt;br /&gt;
Per risolvere questo dovremmo raggruppare le istruzioni in ''sezioni critiche'' (sequenze inscindibili di istruzioni che devono essere eseguite come se fossero atomiche, tutta la sequenza o niente). &lt;br /&gt;
* '''Architettura a livelli e virtualizzazione'''&lt;br /&gt;
Dato un A e un A’ e riusciamo a usare A’ come A, allora A’ lo possiamo chiamare A virtuale.&lt;br /&gt;
L'idea che alla base delle macchine virtuali è di creare un interprete di un ISA differente o uguale a quello del sistema in modo da virtualizzare una reale macchina hardware e quindi poterla usare per gli stessi scopi di una macchina reale. E' quindi possibile, per esempio, installare sistemi operativi su hardware virtuale.&amp;lt;br&amp;gt;&lt;br /&gt;
L'insieme dei componenti hardware virtuali e il software installato su di questi prende il nome di ''macchina virtuale''.&lt;br /&gt;
Uno dei principali vantaggi, oggi molto utilizzato in ambito cloud, è quello di poter suddividere facilmente risorse hardware fisiche, per esempio spazio su disco e potenza di elaborazione, tra macchine virtuali differenti. &lt;br /&gt;
* '''Laboratorio di cross-compiling'''&lt;br /&gt;
Nel ultima parte della lezione abbiamo fatto un esperimento sulla portabilità dei compilatori. &amp;lt;br&amp;gt;&lt;br /&gt;
Abbiamo a disposizione un linguaggio ad alto livello che chiameremo L, due interpreti hw0 e hw1 per linguaggi a basso livello che chiameremo lhw0 e lhw1 e infine due compilatori L-&amp;gt;lhw0 uno scritto in python e l'altro in L. &amp;lt;br&amp;gt;&lt;br /&gt;
Se compiliamo con il compilatore scritto in python (abbiamo un interprete python installato sulla macchina che stiamo utilizzando) il compilatore scritto in L otteniamo un compilatore scritto in lhw0 L-&amp;gt;lhw0. &amp;lt;br&amp;gt;&lt;br /&gt;
Se ora modifichiamo il compilatore scritto in L L-&amp;gt;lhw0 in modo che crei codice per hw1 (basta modificare gli indici numerici delle istruzioni) otteniamo un compilatore in L L-&amp;gt;lhw1. Se lo compiliamo con il compilatore L-&amp;gt;lhw0 otteniamo un ''cross-compiler'' scritto in lhw0 L-&amp;gt;lhw1, cioè un compilatore che crea codice eseguibile su una macchina diversa da quella attuale. &amp;lt;br&amp;gt;&lt;br /&gt;
Se ora utilizziamo il cross compiler L-&amp;gt;lhw1 per compilare il compilatore scritto in L L-&amp;gt;hw1 otteniamo invece un compilatore che può essere eseguito su hw1. &amp;lt;br&amp;gt;&lt;br /&gt;
A questo punto su hw1 potremmo utilizzare l'ultimo compilatore creato per compilare sempre il compilatore scritto in L L-&amp;gt;hw1 ma vedremo che otterremo sempre lo stesso file perché avremo raggiunto il limite.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Il materiale per poter replicare l'esperimento sulla portabilit&amp;amp;agrave; dei compilatori &amp;amp;egrave; [http://www.cs.unibo.it/~renzo/so/tools/portability3.tgz qui]. &lt;br /&gt;
[[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 13:42, 8 October 2016 (CEST)&lt;br /&gt;
&lt;br /&gt;
== Lezione del 11 ottobre 2016 ==&lt;br /&gt;
*'''C'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
  #include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  void dump (void* f, size_t len){ //size_t is the sizeof() return type&lt;br /&gt;
        unsigned char *s = f;&lt;br /&gt;
        size_t i;&lt;br /&gt;
        for (i = 0; i &amp;lt; len; i++)&lt;br /&gt;
                printf(&amp;quot;%02x &amp;quot;,s[i]); //%02x means print at least 2 digits and add 0's if there are less&lt;br /&gt;
        printf(&amp;quot;\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  int main(){&lt;br /&gt;
&lt;br /&gt;
  int v[] = {1, 2, 3, 4}; //v is (int*) 0x32ef...&lt;br /&gt;
  v[2] = 42; //*((int*) 0x32ef) + 2 = 42, moves the address by 2 &amp;quot;positions&amp;quot;, aka 4+4 bytes&lt;br /&gt;
  v[2] = 0x33;//(int*) 0x32ef.. = 0x33 (i.e.) 4 = 42&lt;br /&gt;
&lt;br /&gt;
  char s[] = &amp;quot;ciao&amp;quot;;&lt;br /&gt;
  int t[] = {0x63, 0x69, 0x61, 0x6f, 0x00}; //ciao in ASCII hex. if int OUT:'c'. if char OUT:&amp;quot;ciao&amp;quot;&lt;br /&gt;
  char u[] = {99, 105,97, 111, 0 }; //ciao in ASCII dec. OUT:&amp;quot;ciao&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  char *q = &amp;quot;ciao&amp;quot;; &lt;br /&gt;
  char *w = &amp;quot;mare&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  printf(&amp;quot;%s\n&amp;quot;, s);&lt;br /&gt;
  printf(&amp;quot;%s\n&amp;quot;, t);&lt;br /&gt;
  printf(&amp;quot;%s\n&amp;quot;, u);&lt;br /&gt;
&lt;br /&gt;
  dump(s, sizeof(s));&lt;br /&gt;
  dump(t, sizeof(t));&lt;br /&gt;
  dump(q, sizeof(*q)); //*q size is 1B (it points to 1 char). OUT: 63('c')&lt;br /&gt;
  dump(q, sizeof(q)); //q size is 8B. OUT: 5B of &amp;quot;ciao&amp;quot; + 3B of contiguous string &amp;quot;mare&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OUTPUT:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  ciao&lt;br /&gt;
  c  &lt;br /&gt;
  ciao&lt;br /&gt;
  63 69 61 6f 00&lt;br /&gt;
  63 00 00 00  69 00 00 00  61 00 00  00 6f 00 00  00 00 00 00 00 //int are 4B, char 1B, so we have empty bytes. &lt;br /&gt;
  Little endian pattern is evident. %s allow to print untill the first null, so printf prints just 'c' with int declaration of t[]&lt;br /&gt;
  63 &lt;br /&gt;
  63 69 61 6f  00 6d 61 72 // 6d = m, 61 = a, 72 = r.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''Assioma di finite progress.'''&lt;br /&gt;
&amp;quot;Tutto è vero purchè ogni statement abbia durata sconosciuta, ma finita&amp;quot;&lt;br /&gt;
* '''Modalità di concorrenza.'''&lt;br /&gt;
1- Inconsapevolmente concorrenti: ogni processo elabora ciò per cui è stato creato. Il kernel si occupa di coordinazione e comunicazione.&lt;br /&gt;
&lt;br /&gt;
2- Indirettamente concorrenti: processi che accedono a file condivisi.&lt;br /&gt;
&lt;br /&gt;
3- Consapevolmente concorrenti: il programma ha diversi processi in esecuzione.&lt;br /&gt;
*''' Convenzioni usate nel corso (e negli esami) relative alle istruzioni atomiche'''&lt;br /&gt;
Le istruzioni atomiche vengono compiute in modo indivisibile, garantendo che l'azione non interferisca con altri processi durante la sua esecuzione.&lt;br /&gt;
Vengono rappresentate nel seguente modo:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;istruzione atomica&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Anche gli statement sono istruzioni atomiche(escluso il caso long long).&lt;br /&gt;
*''' Proprieta' di Safety e Liveness'''&lt;br /&gt;
-Safety: se il programma evolve, evolve bene(S) &lt;br /&gt;
&lt;br /&gt;
&amp;quot;''Something good'' happens&amp;quot;&lt;br /&gt;
&lt;br /&gt;
-Liveness: il programma va avanti(L)&lt;br /&gt;
&lt;br /&gt;
&amp;quot;''Something eventually'' happens&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* '''Problema del consenso.'''&lt;br /&gt;
Il problema è così definito: ci sono n processi, ognuno propone un valore. Al termine tutti i processi devono accordarsi su di 1 dei valori proposti.Il suddetto problema è risolvibile  se e solo se vengono rispettate le seguenti proprietà (2 di Safety + 1 di Liveness)&lt;br /&gt;
&lt;br /&gt;
Proprietà:&lt;br /&gt;
&lt;br /&gt;
S(1)- Tutti i processi devono ritornare lo stesso valore&lt;br /&gt;
&lt;br /&gt;
S(2)- Ogni processo corretto decide uno tra i valori proposti&lt;br /&gt;
&lt;br /&gt;
L(1)- Ogni processo corretto deve dare una risposta&lt;br /&gt;
&lt;br /&gt;
w/o S(1): f(x) = x&lt;br /&gt;
&lt;br /&gt;
w/o S(2): f(x) = 42&lt;br /&gt;
&lt;br /&gt;
w/o L(1): f(x) = while(1)&lt;br /&gt;
&lt;br /&gt;
*w/o = without&lt;br /&gt;
&lt;br /&gt;
* '''Problemi delle elaborazioni concorrenti. Prima definizione (non formale) di Deadlock e Starvation'''&lt;br /&gt;
Introduzione alla notazione per la rappresentazione dei processi concorrenti&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1.0&lt;br /&gt;
&lt;br /&gt;
  process p {&lt;br /&gt;
    ...&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  process q {&lt;br /&gt;
    ...&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Due processi eseguiti in concorrenza.&lt;br /&gt;
  &lt;br /&gt;
  ...&lt;br /&gt;
  cobegin&lt;br /&gt;
  ...&lt;br /&gt;
  //&lt;br /&gt;
  ...&lt;br /&gt;
  //&lt;br /&gt;
  ...&lt;br /&gt;
  coend&lt;br /&gt;
&lt;br /&gt;
cobegin e coend indicano la sezione di codice con le istruzioni (rappresentate da &amp;quot;...&amp;quot; e separate da &amp;quot;//&amp;quot;)che verranno eseguite in concorrenza.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1.1 - Merge sort&lt;br /&gt;
  cobegin &lt;br /&gt;
    sort(v[1 : N])&lt;br /&gt;
  //&lt;br /&gt;
    sort(v[N : N])&lt;br /&gt;
  //&lt;br /&gt;
    merge(v[1 : N]), v[N : M])&lt;br /&gt;
  coend&lt;br /&gt;
&lt;br /&gt;
Se isoliamo le istruzioni senza considerare cobegin e coend, lo pseudocodice è corretto. &lt;br /&gt;
Se consideriamo anche cobegin e coend il programma esegue la funzione di merge() in concorrenza con le due chiamate a sort(), utilizzando risorse in comune e pertanto cercando di ordinare  elementi non ancora ordinati nelle due metà dell'array.&lt;br /&gt;
Se spostiamo l'invocazione di merge al di fuori della sezione cobegin ... coend, lo pseudocodice (vedi in basso) è corretto.&lt;br /&gt;
&lt;br /&gt;
  cobegin &lt;br /&gt;
    sort(v[1 : N])&lt;br /&gt;
  //&lt;br /&gt;
    sort(v[N : N])&lt;br /&gt;
  coend&lt;br /&gt;
    merge(v[1 : N]), v[N : M])&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In particolare nella programmazione concorrente si devono affrontare due problemi che riguardano la liveness, parliamo di deadlock e starvation.&lt;br /&gt;
&lt;br /&gt;
'''DEADLOCK''': uno o più processi non possono continuare l'esecuzione perchè ognuno di essi è in attesa che gli altri facciano qualcosa&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1.2&lt;br /&gt;
&lt;br /&gt;
  process p {&lt;br /&gt;
  while 1:&lt;br /&gt;
    prendi a&lt;br /&gt;
    prendi b&lt;br /&gt;
    calcola f(a,b)&lt;br /&gt;
    lascia a&lt;br /&gt;
    lascia b&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  process q {&lt;br /&gt;
  while 1:&lt;br /&gt;
    prendi a&lt;br /&gt;
    prendi b&lt;br /&gt;
    calcola f(a,b)&lt;br /&gt;
    lascia a&lt;br /&gt;
    lascia b&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Supponiamo di invertire l'ordine di prendi a e prendi b nel processo p. Dopo che entrambi i processi eseguono la prima istruzione lo scenario che si sviluppa è il seguente: al processo p serve la risorsa a, presa dal processo q, al quale servirebbe la risorsa b, presa dal processo p. I processi aspettano a vicenda che uno rilasci la risorsa che possiede l'altro. Risorsa necessaria ad entrambi i processi per eseguire l'istruzione (f(a,b)) che le richiederebbe entrambe. I processi sono deadlocked.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''STARVATION''': quando un processo attende una risorsa indefinitamente, e questa non gli viene mai allocata&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1.3&lt;br /&gt;
  process p {&lt;br /&gt;
  while 1:&lt;br /&gt;
    prendi a&lt;br /&gt;
    calcola f(a)&lt;br /&gt;
    lascia a&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  process q {&lt;br /&gt;
  while 1:&lt;br /&gt;
    prendi a&lt;br /&gt;
    calcola f(a)&lt;br /&gt;
    lascia a&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Supponiamo che i processi per realizzare &amp;quot;prendi a&amp;quot; verifichino la disponibilit&amp;amp;agrave; di a tramite polling (provando ripetutamente se a &amp;amp;egrave; libero). Il processo q pu&amp;amp;ograve; essere sfortunato e non riuscire mai ad avere accesso ad a (tutte le volte che cerca di &amp;quot;prenderlo&amp;quot; lo trover&amp;amp;agrave; impegnato da p). Il processo q è in starvation.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1.4&lt;br /&gt;
&amp;quot;Supponiamo di avere tre processi (P1, P2, P3) i quali richiedono periodicamente accesso alla risorsa R. Consideriamo la situazione nella quale P1 è in possesso della risorsa, e sia P2 che P3 attendono per quella risorsa. Quando P1 esce dalla sua sezione critica, a P2 o P3 dovrebbe essere permesso l'accesso a R. Assumiamo che il SO garantisca l'accesso a P3 e che P1  richieda nuovamente la risorsa prima che P3 termini la sua sezione critica. Se il SO dovesse garantire l'accesso a P1 dopo che P3 abbia finito, e successivamente dovesse garantire alternativamente a P1 e P3 l'accesso alla risorsa, allora P2 potrebbe vedersi indefinitamente negato l'accesso alla risorsa, sebbene non vi sia una situazione di deadlock&amp;quot; (Stallings W.(2015). ''Operating System: internals and design principles''. (8th ed). Pearson)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Problema: posso creare sezioni critiche, tramite la programmazione, senza aiuti hardware?'''&lt;br /&gt;
&lt;br /&gt;
ESEMPIO&lt;br /&gt;
&lt;br /&gt;
  process p&lt;br /&gt;
    /*enter_cs*/&lt;br /&gt;
    A = A + 1&lt;br /&gt;
    /*exit_cs*/&lt;br /&gt;
&lt;br /&gt;
  process q&lt;br /&gt;
    /*enter_cs*/&lt;br /&gt;
    A = A - 1&lt;br /&gt;
    /*exit_cs*/&lt;br /&gt;
&lt;br /&gt;
'''Le 4 proprietà della critical section(CS)'''&lt;br /&gt;
&lt;br /&gt;
SAFETY(1): una operazione all'interno della CS &lt;br /&gt;
&lt;br /&gt;
ESEMPIO&lt;br /&gt;
  process p&lt;br /&gt;
  while 1:&lt;br /&gt;
    /*enter_cs*/&lt;br /&gt;
    A = A + 1&lt;br /&gt;
    /*exit_cs*/&lt;br /&gt;
    ...  &amp;lt;--- process q deve poter &amp;quot;entrare&amp;quot; qui&lt;br /&gt;
&lt;br /&gt;
LIVENESS(1): no deadlock&lt;br /&gt;
&lt;br /&gt;
LIVENESS(2): no starvation&lt;br /&gt;
&lt;br /&gt;
LIVENESS(3): no useless wait&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''Dekker(1st attempt) //KEY: turno''&lt;br /&gt;
&lt;br /&gt;
  turn = p //var globale condivisa&lt;br /&gt;
&lt;br /&gt;
  process p{&lt;br /&gt;
    while (turn != p)&lt;br /&gt;
      ;&lt;br /&gt;
    /*A = A + 1*/&lt;br /&gt;
    turn = q&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  process q{&lt;br /&gt;
    while(turn != q)&lt;br /&gt;
      ;&lt;br /&gt;
    /*A = A - 1*/&lt;br /&gt;
    turn = p&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Non rispetta la proprietà di liveness(3), infatti uno dei due processi può dover attendere a lungo il suo turno eseguendo ciclicamente l'empty statement all'interno&lt;br /&gt;
del corpo del while. Questa situazione può verificarsi se l'altro processo fallisce dentro o fuori la sua CS (in questo caso l'attesa è infinita). Inoltre visto che i processi si alternano strettamente nell'utilizzo della loro critical section, il ritmo di esecuzione è dettato dal processo più lento dei due.&lt;br /&gt;
&lt;br /&gt;
''Dekker(2nd attempt) //KEY: flag, ma senza turno''&lt;br /&gt;
&lt;br /&gt;
  inp = false&lt;br /&gt;
&lt;br /&gt;
  inq = false&lt;br /&gt;
&lt;br /&gt;
  process p{&lt;br /&gt;
    while 1:&lt;br /&gt;
      while(inq)&lt;br /&gt;
         ;&lt;br /&gt;
      inp = true&lt;br /&gt;
      /*A = A + 1*/&lt;br /&gt;
      inp = false&lt;br /&gt;
      ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  process q{&lt;br /&gt;
    while 1:&lt;br /&gt;
      while(inp)&lt;br /&gt;
         ;&lt;br /&gt;
      inq = true&lt;br /&gt;
      /*A = A - 1*/&lt;br /&gt;
      inq = false&lt;br /&gt;
      ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In questo caso se uno dei processi fallisce fuori dalla CS (dopo aver settato il flag a false) l'altro processo non viene bloccato. Se invece un processo fallisce nella sua CS oppure dopo aver settato il flag a true allora l'altro processo resta bloccato indefinitamente. Non è garantita inoltre la mutua esclusione in quanto entrambi potrebbero essere al contempo nelle loro rispettive CS, ad esempio:&lt;br /&gt;
&lt;br /&gt;
p valuta la condizione del while e trova inq a false&lt;br /&gt;
 &lt;br /&gt;
q valuta la condizione del while e trova inp a false&lt;br /&gt;
&lt;br /&gt;
p assegna true a inp ed entra nella sua CS&lt;br /&gt;
&lt;br /&gt;
q assegna true a inq ed entra nella sua CS &lt;br /&gt;
&lt;br /&gt;
....entrambi sono nella rispettiva CS --&amp;gt; no liveness(1)&lt;br /&gt;
&lt;br /&gt;
''Dekker(3nd attempt) //KEY: come 2nd ma con due istruzioni scambiate di ordine''&lt;br /&gt;
&lt;br /&gt;
  process p{&lt;br /&gt;
    while 1:&lt;br /&gt;
      inp = true&lt;br /&gt;
      while(inq)&lt;br /&gt;
         ;    &lt;br /&gt;
      /*A = A + 1*/&lt;br /&gt;
      inp = false&lt;br /&gt;
      ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  process q{&lt;br /&gt;
    while 1:&lt;br /&gt;
      inq = true&lt;br /&gt;
      while(inp)&lt;br /&gt;
         ;&lt;br /&gt;
      /*A = A - 1*/&lt;br /&gt;
      inq = false&lt;br /&gt;
      ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Scambiando l'ordine di due istruzioni vi è la mutua esclusione, tuttavia se entrambi i processi assegnano true al loro rispettivo flag, prima di valutare la condizione&lt;br /&gt;
del while, entrambi resteranno all'interno del while in attesa che l'altro processo &amp;quot;esca dalla CS&amp;quot; (non vi è mai entrato) --&amp;gt; deadlock&lt;br /&gt;
&lt;br /&gt;
''Dekker(4nd attempt) //KEY: flag + delay''&lt;br /&gt;
&lt;br /&gt;
  process p{&lt;br /&gt;
    while 1:&lt;br /&gt;
      inp = true&lt;br /&gt;
      while(inq){&lt;br /&gt;
        inp = false; &amp;lt; delay &amp;gt;; inp = true;&lt;br /&gt;
      }          &lt;br /&gt;
      /*A = A + 1*/&lt;br /&gt;
      inp = false&lt;br /&gt;
      ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  process q{&lt;br /&gt;
    while 1:&lt;br /&gt;
      inq = true&lt;br /&gt;
      while(inp){&lt;br /&gt;
        inq = false; &amp;lt; delay &amp;gt;; inq = true;&lt;br /&gt;
      }&lt;br /&gt;
      /*A = A - 1*/&lt;br /&gt;
      inq = false&lt;br /&gt;
      ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In questo caso il settare il proprio flag a true è indice del desiderio di entrare nella propria CS. Vi è ancora mutua esclusione. Se però dovesse verificarsi una sequenza del tipo:&lt;br /&gt;
&lt;br /&gt;
p imposta flag inp a true&lt;br /&gt;
&lt;br /&gt;
q imposta falg inq a true&lt;br /&gt;
&lt;br /&gt;
p valuta la condizione del while &lt;br /&gt;
&lt;br /&gt;
q valuta la condizione del while&lt;br /&gt;
&lt;br /&gt;
p imposta flag inp a false&lt;br /&gt;
&lt;br /&gt;
q imposta flag inq a false&lt;br /&gt;
&lt;br /&gt;
p imposta flag inp a true&lt;br /&gt;
&lt;br /&gt;
q imposta flag inq a true&lt;br /&gt;
&lt;br /&gt;
allora c'è la possibilità che si ripeta all'infinito (entrambi i processi potrebbero rimanere nel while). Essendo però una condizione che dipende dalla velocità relativa dei due processi, esiseranno delle situazioni nelle quali uno dei due processi potrà entrare, così come vi saranno delle situazioni nelle quali nessun processo avrà accesso alla propria CS --&amp;gt; livelock&lt;br /&gt;
&lt;br /&gt;
'''Dekker...finally //KEY: flag + turni (&amp;quot;cambio delay con turno)'''&lt;br /&gt;
&lt;br /&gt;
  needp = false &lt;br /&gt;
&lt;br /&gt;
  needq = false&lt;br /&gt;
&lt;br /&gt;
  turn = p&lt;br /&gt;
&lt;br /&gt;
  process p{&lt;br /&gt;
    while 1:&lt;br /&gt;
      needp = true&lt;br /&gt;
      while (needq) { &lt;br /&gt;
        needp = false &lt;br /&gt;
        while(turn == q)&lt;br /&gt;
         ; &lt;br /&gt;
        needp = true&lt;br /&gt;
      }&lt;br /&gt;
     /*A = A + 1*/&lt;br /&gt;
     needp = false&lt;br /&gt;
     turn = q&lt;br /&gt;
     ...&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
  process q{&lt;br /&gt;
   while 1:&lt;br /&gt;
     needq = true&lt;br /&gt;
     while(needp) {&lt;br /&gt;
       needq = false; &lt;br /&gt;
       while(turn == p)&lt;br /&gt;
        ; &lt;br /&gt;
       needq = true&lt;br /&gt;
    }&lt;br /&gt;
    /*A = A - 1*/&lt;br /&gt;
    needq = false&lt;br /&gt;
    turn = p&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
In questo caso il processo che vuole entrare nella sua CS ne manifesta il bisogno settando need a true. Se entrambi i processi entrano nel corpo del while il tie-break è espletato dal while la cui condizione è un turn che sarà uguale soltanto ad uno dei due processi (in questo caso turn è inizializzata a p), e dunque non si verificherànno situazioni di deadlock o livelock.&lt;br /&gt;
*'''Proprietà della sezione critica'''&lt;br /&gt;
Mutua esclusione &amp;lt;br&amp;gt;&lt;br /&gt;
Assenza di deadlock &amp;lt;br&amp;gt;&lt;br /&gt;
Assenza di starvation &amp;lt;br&amp;gt;&lt;br /&gt;
Assenza di delay non necessari&lt;br /&gt;
&lt;br /&gt;
*'''La test&amp;amp;set'''&lt;br /&gt;
E' un istruzione atomica per impostare il valore di una variabile a 1 (lock) ma ritornare il vecchio valore. Questa istruzione può essere utilizzata per gestire sezioni critiche. &amp;lt;br&amp;gt; &lt;br /&gt;
Il valore ritornato dalla test and set viene comparato a 1, se è uguale si aspetta altrimenti si procede.&amp;lt;br&amp;gt;&lt;br /&gt;
In questo modo il primo processo che cercherà di entrare nella sezione critica ci riuscirà e imposterà il lock a 1, gli altri processi si troveranno il lock a 1 e dovranno aspettare. Quando il primo processo avrà finito ripristinerà il vecchio valore di lock e permetterà a uno degli altri processi di eseguire la test and set e di entrare nella sezione critica.&lt;br /&gt;
----&lt;br /&gt;
Altri argomenti trattati che non sono stati qui riportati in forma di appunti:&lt;br /&gt;
Gestione algoritmica della critical section.&lt;br /&gt;
Algoritmo di Dekker.&lt;br /&gt;
Gestione architetturale della critical section.&lt;br /&gt;
&lt;br /&gt;
[[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 10:35, 17 October 2016 (CEST)&lt;br /&gt;
&lt;br /&gt;
== Lezione del 14 ottobre 2016 ==&lt;br /&gt;
Audio Lezione: [https://drive.google.com/open?id=0B8G3NNuJ7MA7UlJadTRVbzhWRFE 14\10\16]&lt;br /&gt;
* Sistemi Operativi per elaboratori multiprocessore SMP/non SMP&lt;br /&gt;
* Macchine di von Neumann (e di Harvard).&lt;br /&gt;
* Il bus.&lt;br /&gt;
* Interrupt e Trap&lt;br /&gt;
* Gestione degli interrupt&lt;br /&gt;
* Mascheramento degli interrupt&lt;br /&gt;
* Interrupt Nidificati&lt;br /&gt;
* DMA&lt;br /&gt;
* Device a carattere e device a blocchi&lt;br /&gt;
* System call come trap.&lt;br /&gt;
* La gerarchia delle memorie&lt;br /&gt;
* cache&lt;br /&gt;
&lt;br /&gt;
== Lezione del 18 ottobre 2016 ==&lt;br /&gt;
Audio Lezione: [https://drive.google.com/open?id=0B8G3NNuJ7MA7eVlFdWR0cUkxd2M 18/10/16]&lt;br /&gt;
&lt;br /&gt;
Argomenti trattati:&lt;br /&gt;
*'''Implementazione in C della Test&amp;amp;Set, degli Algoritmi di Dekker e Peterson.'''&lt;br /&gt;
Codice sorgente disponibile [[Dekker,_Peterson_e_Test%26Set_in_C|qui]].&lt;br /&gt;
*'''Definizione di Semaforo.'''&lt;br /&gt;
Un semaforo è una variabile intera che può essere modificata solo da 2 operazioni atomiche: P(Proberen) e V(Verhogen).&amp;lt;br&amp;gt;&lt;br /&gt;
P aspetta che il valore sia minore o uguale a zero. Quando lo diventa decrementa il valore.&amp;lt;br&amp;gt;&lt;br /&gt;
S incrementa il valore. &amp;lt;br&amp;gt;&lt;br /&gt;
*'''Invariante del Semaforo.'''&lt;br /&gt;
Il numero di Proberen deve essere minore o uguale al numero di Verhogen più il valore d'inizializzazione del semaforo. &amp;lt;br&amp;gt;&lt;br /&gt;
In questo modo si può gestire per esempio l'allocazione di risorse. Il semaforo viene inizializzato al numero di risorse e ogni volta che un processo ha bisogno della risorsa esegue un proberen, quindi decrementa il valore se la risorsa è disponibile. Quando un processo rilascia la risorsa esegue un verhogen.&lt;br /&gt;
*'''Esempio: implementazione di sezione critica e di semplice sincronizzazione con semafori.'''&lt;br /&gt;
&lt;br /&gt;
== Lezione del 21 ottobre 2016 ==&lt;br /&gt;
*'''Make'''&lt;br /&gt;
Make è un sistema per la compilazione intelligente dei programmi. &amp;lt;br&amp;gt;&lt;br /&gt;
Permette invece di compilare tutto il programma tutte le volte di compilarne solo una parte, solo quella che è stata modificata.&lt;br /&gt;
Per capirne il funzionamento è utile un parallelismo con le ricette di cucina. Si specificano dei target che sono gli obbiettivi della ricetta.Per fare una ricetta servono degli ingredienti e magari quegli ingredienti hanno a loro volta delle ricette. Make allo stesso modo definisce dei target che possono avere come dipendenze altri target.  &amp;lt;br&amp;gt;&lt;br /&gt;
Nel installazione del programma il programma ./configure cerca nel sistema se le librerie da cui è dipendente il programma sono presenti. &amp;lt;br&amp;gt;&lt;br /&gt;
*'''Git'''&lt;br /&gt;
Git è un sistema di versioning, serve a tener traccia delle modifiche apportante a un progetto nel tempo. E' utile per collaborare a un progetto condiviso. &amp;lt;br&amp;gt;&lt;br /&gt;
Possibilità per ogni membro del repository di lavorare in maniera indipendente e poi aggiornare il progetto comune. &amp;lt;br&amp;gt;&lt;br /&gt;
Ce ne sono stati tanti di sistemi di versioning tipo SVN, Mercurial ma attualmente Git è il più utilizzato.&amp;lt;br&amp;gt;&lt;br /&gt;
Git non serve solo per scrivere programmi ma per esempio anche libri, in modo collaborativo.&amp;lt;br&amp;gt;&lt;br /&gt;
Git è completamente distribuito, non esiste in un repository una copia principale e delle copie secondarie ma ogni copia può essere principale. &amp;lt;br&amp;gt;&lt;br /&gt;
Per iniziare un repository git si utilizza il comando ''git init''.&amp;lt;br&amp;gt;&lt;br /&gt;
Per aggiungere un file al repository si utilizza il comando ''git add''.&amp;lt;br&amp;gt;&lt;br /&gt;
Ma ad ogni singola modifica git non aggiorna automaticamente il repository ma è l'utente che tramite il comando git commit indica che una certa versione del progetto deve essere salvata.&amp;lt;br&amp;gt;&lt;br /&gt;
Il comando ''git log'' serve a visualizzare la storia del repository.&amp;lt;br&amp;gt;&lt;br /&gt;
Il comando ''git diff'' serve a vedere la differenza tra ciò che è nel repository e le modifiche di cui non si è ancora fatto il commit.&amp;lt;br&amp;gt;&lt;br /&gt;
Dopo aver dato il comando add si dice che i file sono 'on stage' prima del commit.&amp;lt;br&amp;gt;&lt;br /&gt;
Il comando ''git checkout'' serve a spostarsi nella storia del repository.&amp;lt;br&amp;gt;&lt;br /&gt;
Il comando ''git clone'' serve a copiare un repository remoto.&amp;lt;br&amp;gt;&lt;br /&gt;
Il comando ''git push'' serve a inviare le modifiche al repository remoto mentre il comando ''git pull'' serve a ricevere le modifiche più aggiornate dal repository remoto.&amp;lt;br&amp;gt;&lt;br /&gt;
Il ''merge'' è il processo in cui uno degli utenti che crea un conflitto deve &amp;quot;a mano&amp;quot; scegliere quali parti dei due commit in conflitto vuole tenere.&amp;lt;br&amp;gt;&lt;br /&gt;
Git è utile per trovare i bug che non erano presenti in versioni precedenti del progetto. Si può tornare alla versione precedente stabile e avanzare nella storia fino a trovare una versione instabile. A questo punto è probabile che l'origine del bug sia nelle modifiche che hanno creato questa versione instabile.&amp;lt;br&amp;gt;&lt;br /&gt;
Github è una piattaforma di repository dove possono essere mantenuti dei progetti gratuitamente se sono con licenza libera.&amp;lt;br&amp;gt;&lt;br /&gt;
In una directory in cui è presente un repository git e sempre la presente la cartella ''.git'' dove il sistema di versioning mantiene il database della storia. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lezione del 25 ottobre 2016 ==&lt;br /&gt;
Audio Lezione: [https://drive.google.com/open?id=0B8G3NNuJ7MA7WjZJMm11cjhuQXc 25/10/16]&lt;br /&gt;
&lt;br /&gt;
Argomenti trattati:&lt;br /&gt;
* '''Semafori fair'''&lt;br /&gt;
Un semaforo è &amp;quot;fair&amp;quot; quando tende a non sbloccare dallo stato di attesa sempre lo stesso processo ma cerca di distribuire la possibilità di esecuzione a più processi.&lt;br /&gt;
* '''Problemi classici della concorrenza risolti con semafori'''&lt;br /&gt;
** '''Produttore/consumatore'''&lt;br /&gt;
::Due processi: produttore e consumatore. Il produttore genera continuamente dei valori e il consumatore continuamente li legge. Il consumatore deve leggere i valori nello stesso ordine in cui il produttore li genera. &amp;lt;br&amp;gt;&lt;br /&gt;
::Un meccanismo di sincronizzazione e richiesto perchè sorgono immediatamente due problemi:&lt;br /&gt;
::* Produttore troppo veloce: il consumatore non fa in tempo a consumare il valore e c'è la possibilità che i valori non ancora letti vengano sovrascritti.&lt;br /&gt;
::* Consumatore troppo veloce: possibilità che il consumatore legga un valore che ha già letto perchè il produttore non ha fatto in tempo a produrne uno nuovo.&lt;br /&gt;
::Per risolvere il problema utilizziamo due semafori: empty e full. &amp;lt;br&amp;gt;&lt;br /&gt;
::Il produttore aspetta che il segnale del semaforo empty (Proberen), scrive il valore e dà segnale sul semaforo full (Verhogen).&amp;lt;br&amp;gt;&lt;br /&gt;
::Il consumatore aspetta  il segnale del semaforo full (Proberen), legge il valore e dà segnale sul semaforo empty (Verhogen).&amp;lt;br&amp;gt;&lt;br /&gt;
:* '''Buffer-limitato'''&lt;br /&gt;
::Simile al problema produttore/consumatore ma qui lo scambio avviene tramite un buffer limitato. &amp;lt;br&amp;gt;&lt;br /&gt;
::In questo modo nel caso di rallentamento temporaneo del consumer il producer non deve aspettare ma può produrre più unità e viceversa nel caso di rallentamento del producer, se il buffer ha più di un elemento, il consumer può utilizzare il tempo in attesa del producer per consumare più dati. &amp;lt;br&amp;gt;&lt;br /&gt;
::Nel caso il buffer sia completamente pieno/vuoto rimangono comunque i problemi di sincronizzazione tipici del problema: il producer non deve sovrascrivere dei valori che il consumer non ha ancora letto e il consumer non deve leggere più volte lo stesso valore se il producer non è stato abbastanza veloce da cambiarlo. &amp;lt;br&amp;gt;&lt;br /&gt;
::Come soluzione al problema si può utilizzare un coda circolare. Il produttore esegue una enqueue e il consumatore una dequeue. &amp;lt;br&amp;gt;&lt;br /&gt;
::Il problema può essere generalizzato facilmente a produttori/consumatori multipli ma in questo caso dobbiamo creare un semaforo per rendere atomiche la enqueue e la dequeue altrimenti incorriamo in una race condition nel accesso alla coda.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 28 ottobre 2016 ==&lt;br /&gt;
&lt;br /&gt;
'''Implementazione dei semafori''' &amp;lt;br&amp;gt;&lt;br /&gt;
Un processo che esegue una Proberen in teoria dovrebbe aspettare continuando a controllare se può entrare nella sezione critica. Questo meccanismo si chiama &amp;quot;busy waiting&amp;quot; e consuma molte istruzioni della CPU. &amp;lt;br&amp;gt;&lt;br /&gt;
Il busy waiting è diverso dal polling in quando il polling prevede di eseguire altre azioni mentre si aspetta tra un tentativo e l'altro. &amp;lt;br&amp;gt;&lt;br /&gt;
Di solito si utilizza il busy waiting solo quando il tempo di lock di un processo è molto breve. Questo perchè anche se esoso di risorse del processore il busy waiting non comporta un context switch, cioè salvare tutto lo stato del processo in memoria, e quindi permette di risparmiare memoria. &lt;br /&gt;
Se invece il tempo di lock è più lungo allora è preferibile utilizzare un context-switch dove il processo si blocca da solo e chiama un processo di blocking che lo mette in una coda di attesa associata al semaforo che sta aspettando. Il controllo passa poi allo scheduler della CPU che seleziona un altro processo da eseguire.&lt;br /&gt;
Le primitive suspend and wakeup indicano rispettivamente lo spostamento di un processo nella/dalla coda dei processi ready.&lt;br /&gt;
* nei kernel monoprocessore&lt;br /&gt;
: Implementazione dei semafori tramite mutex che possono essere a loro volta implementate con un sistema di mascheramento degli interrupt.&lt;br /&gt;
* nei kernel multiprocessore&lt;br /&gt;
: Utilizzo di soluzioni hardware come test and set. Sono soluzioni che utilizzano il busy waiting ma in questo caso solo per rendere atomico le operazioni del semaforo, che sono poche istruzioni.&lt;br /&gt;
Certe implementazioni dei semafori potrebbero permettere di far andare il valore in negativo. Questo serve per misurare il numero dei processi in attesa di quel semaforo. &amp;lt;br&amp;gt;&lt;br /&gt;
'''Il problema della cena dei filosofi''': &amp;lt;br&amp;gt;&lt;br /&gt;
5 filosofi passano la loro vita pensando e mangiando. Condividono un tavolo circolare con 5 sedie e ogni sedia appartiene a un solo filosofo. Alla sinistra e alla destra di ogni piatto c'è una bacchetta per un totale di 5 bacchette. La bacchetta destra che un filosofo usa per mangiare viene quindi utilizzata come bacchetta sinistra dal filosofo alla sua destra e simmetricamente è uguale per il filosofo che siede a sinistra. &amp;lt;br&amp;gt;&lt;br /&gt;
Ogni tanto un filosofo smette di pensare e cerca di prendere le rispettive bacchette, se ci riesce può mangiare e poi rilasciare le bacchette. &amp;lt;br&amp;gt;&lt;br /&gt;
Questo problema è un modello di problema ad accesso a ''insiemi'' di risorse. Mentre nel produttore/consumatore e buffer limitato gli attori in gioco avevano la necessità di avere l'accesso esclusivo a una sola risorsa in questo problema è richiesto l'accesso a insiemi di risorse (le bacchette) le cui parti possono essere desiderate da altri richiedenti.&lt;br /&gt;
* soluzione con contesa sulle posate e casi di deadlock&lt;br /&gt;
:Poniamo un semaforo per ogni bacchetta. Può succedere però, se nel implementazione i filosofi &amp;quot;prendono in mano&amp;quot; prima la bacchetta destra e poi quella sinistra, che tutti prendono in mano contemporaneamente la bacchetta destra e rimangono in un'attesa infinita della bacchetta sinistra che non gli verrà mai rilasciata perché anche il rispettivo filosofo alla propria sinistra è in attesa. &amp;lt;br&amp;gt; Questo è un caso di deadlock. &amp;lt;br&amp;gt;&lt;br /&gt;
:Una soluzione può essere quella di imporre che un filosofo sia &amp;quot;mancino&amp;quot; cioè prenda prima la bacchetta a sinistra e poi la destra. &amp;lt;br&amp;gt;&lt;br /&gt;
* soluzione con acquisizione atomica di entrambe le posate, casi di starvation&lt;br /&gt;
:Un'altra soluzione è quella di rendere atomico l'acquisizione di entrambe le posate. &amp;lt;br&amp;gt;&lt;br /&gt;
:Può succede che 4 filosofi compiano una &amp;quot;congiura&amp;quot;, cioè siano d'accordo a non lasciar risorse a un quinto filosofo. Questo è un caso di starvation e non è irreale in quando i 4 filosofi potrebbero essere processi malevoli che siano stati programmati di proposito per questo.&lt;br /&gt;
'''System Call''':&lt;br /&gt;
* POSIX.&lt;br /&gt;
:Il POSIX è uno standard (IEEE 1003) che definisce le signature delle system call. Un sistema operativo deve rispettare il POSIX per essere definito &amp;quot;Unix-like&amp;quot;. &amp;lt;br&amp;gt;&lt;br /&gt;
:Di solito i parametri delle system call sono pensati per essere contenuti in un registro. Quasi sempre le system call ritornano un intero: 0 se la procedura è stata eseguita correttamente, -1 se c'è un errore. Quando una system call ritorna -1 la variabile &amp;quot;globale&amp;quot; errno (error number) contiene il codice di errore. La variabile errno non è realmente globale perché ne esiste una per ogni processo. &amp;lt;br&amp;gt;&lt;br /&gt;
:Unix è un sistema operativo file-system centrico quindi quando facciamo input/output anche da periferiche come stampanti e lettori CD usiamo le system call per la scrittura/lettura dei file. &amp;lt;br&amp;gt;&lt;br /&gt;
:Un catalogo delle system call principali può essere trovato [[Il ''catalogo'' delle System Call | qui]].&lt;br /&gt;
* fork/exec&lt;br /&gt;
:fork e exec sono system call importanti e servono per creare un nuovo processo: &amp;lt;br&amp;gt;&lt;br /&gt;
:fork: genera un processo clone di quello che ha chiamato fork. &amp;lt;br&amp;gt;&lt;br /&gt;
:exec: &amp;quot;cambia&amp;quot; il codice eseguito dal processo corrente. &amp;lt;br&amp;gt;&lt;br /&gt;
:Quindi per &amp;quot;aprire un nuovo programma&amp;quot; si utilizza una fork seguita da un exec.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 4 novembre 2016 ==&lt;br /&gt;
Audio Lezione: [https://drive.google.com/open?id=0B8G3NNuJ7MA7blhMT0tSNDRGSUU 4/11/16]&lt;br /&gt;
&lt;br /&gt;
* '''Problema della Cena dei filosofi'''. Soluzione con acquisizione atomica delle bacchette (con starvation)&lt;br /&gt;
* '''Tecnica del passaggio del testimone''' (Passing the Baton).&lt;br /&gt;
&lt;br /&gt;
: Non uscire dalla mutua esclusione se c'è qualcun altro che vuole entrarci e semplicemente fare in modo che lui l'acquisisca. La mutua esclusione termina solo se non c'è nessuno che più la richiede.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO IMPLEMENAZIONE BATON&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
  void baton(int i) {&lt;br /&gt;
    int j;&lt;br /&gt;
    for (j=1; j&amp;lt;5; j++) {&lt;br /&gt;
       if(philo_status[(i+j)%5] == 'W' &amp;amp;&amp;amp; philo_status[(i+j+1)%5] != 'E' &amp;amp;&amp;amp; philo_status[(i+j+4)%5] != 'E') {&lt;br /&gt;
          semaphore_V(philowait[(i+j)%5]);&lt;br /&gt;
          return;&lt;br /&gt;
       }&lt;br /&gt;
    }&lt;br /&gt;
    semaphore_V(mutex);&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* '''Cena dei filosofi, acquisizione atomica delle bacchette con passaggio del testimone'''.&lt;br /&gt;
* '''Implementazione della congiura dei filosofi'''&lt;br /&gt;
&lt;br /&gt;
* '''Le system call di gestione file (open, read, write, close, lseek)'''&lt;br /&gt;
Quando lavoriamo con dei file dobbiamo sempre specificare se vogliamo solo leggerli oppure anche scrivere, magari in append mode. Ci sono quindi varie modalità con cui possiamo interagire con i file. &amp;lt;br&amp;gt;&lt;br /&gt;
Un utente potrebbe essere autorizzato a gestire il file in certe modalità oppure no. &amp;lt;br&amp;gt;&lt;br /&gt;
Il descrittore di un file è un intero. &amp;lt;br&amp;gt;&lt;br /&gt;
Le system call, a differenza delle chiamate di funzioni di libreria, non sono bufferizzate. &lt;br /&gt;
&lt;br /&gt;
ESEMPIO&lt;br /&gt;
&lt;br /&gt;
Se si decommenta printf stampa 2 volte hello world perchè la stringa resta nel buffer. &lt;br /&gt;
&lt;br /&gt;
Se si decommenta write stampa 1 volta hello world.&lt;br /&gt;
&lt;br /&gt;
Per vedere le chiamate di sistema effettuate in fase di esecuzione, compilare il codice sorgente e poi utilizzare strace -f &amp;quot;eseguibile&amp;quot;.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
   #include&amp;lt;stdio.h&amp;gt;&lt;br /&gt;
   #include&amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  int main(int argc, char* argv[]){&lt;br /&gt;
    //printf(&amp;quot;hello world&amp;quot;); //if you ends the strings with \n is like to &amp;quot;fflushing&amp;quot; the buffer &lt;br /&gt;
    //write(1, &amp;quot;hello world&amp;quot;, 11); // 1 and 11 are file descriptors&lt;br /&gt;
    switch(fork()) {&lt;br /&gt;
      case 0: //child&lt;br /&gt;
         break;&lt;br /&gt;
      default: //parent&lt;br /&gt;
         wait(NULL);&lt;br /&gt;
         break;&lt;br /&gt;
      case -1: //error&lt;br /&gt;
         break;&lt;br /&gt;
    }&lt;br /&gt;
    printf(&amp;quot;\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Copia il file passato come primo argomento (argv[1]) nel file passato come secondo argomento (argv[2]), se quest'ultimo non esiste, allora lo crea.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
  #include&amp;lt;stdio.h&amp;gt;&lt;br /&gt;
  #define BUFSIZE 2048&lt;br /&gt;
  char buffer[BUFSIZE];&lt;br /&gt;
  int main(int argc, char *argv[]){&lt;br /&gt;
     int fdin = open(argv[1], O_RDONLY);&lt;br /&gt;
     int fdout = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666); //0666 is the umask used to modify the permissions of new files&lt;br /&gt;
&lt;br /&gt;
     int n;&lt;br /&gt;
     while((n = read(fdin, buffer, BUFSIZE)) &amp;gt; 0)&lt;br /&gt;
        write(fdout, buffer, n);&lt;br /&gt;
    &lt;br /&gt;
     close(fdin);&lt;br /&gt;
     close(fdout);&lt;br /&gt;
     return 0;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''umask''' &amp;lt;br&amp;gt;&lt;br /&gt;
Con la open possiamo usare dei permessi al massimo per quelli garantiti dal umask. &amp;lt;br&amp;gt;&lt;br /&gt;
Un numero composto da quattro cifre ottali. Controlla la maschera di creazione dei file. I bit di permesso contenuti nella maschera NON vengono settati nel nuovo file creato. Corrisponde all'opereazione:&lt;br /&gt;
&lt;br /&gt;
  R:(D &amp;amp;(~M)) &lt;br /&gt;
&lt;br /&gt;
Può essere vista come una sottrazione, i bit accesi nella umask vengono spenti nel file creato. &lt;br /&gt;
&lt;br /&gt;
e.g. se i permessi di default corrispondono a 0666(D) e la maschera corrisponde a 0022(M), R è uguale a 0666-0022 = 0644. Infatti&lt;br /&gt;
&lt;br /&gt;
   0022 =        |000|000|010|010|&lt;br /&gt;
  ~0022 =        |111|111|101|101|(7755)&lt;br /&gt;
   0666 =        |000|110|110|110|&lt;br /&gt;
  ~0022 &amp;amp; 0666 = |000|110|100|100|(0644)&lt;br /&gt;
&lt;br /&gt;
Tabella significato cifre umask&lt;br /&gt;
&lt;br /&gt;
  OTTALE  BINARIO  SIGNIFICATO&lt;br /&gt;
   0       000      Nessun permesso&lt;br /&gt;
   1       001      Solo esecuzione&lt;br /&gt;
   2       010      Solo scrittura&lt;br /&gt;
   3       011      Scrittura ed esecuzione&lt;br /&gt;
   4       100      Solo lettura&lt;br /&gt;
   5       101      Lettura ed esecuzione&lt;br /&gt;
   6       110      Lettura e scrittura&lt;br /&gt;
   7       111      Lettura, scrittura ed esecuzione&lt;br /&gt;
&lt;br /&gt;
I permessi sono rappresentati in simboli come segue:&lt;br /&gt;
&lt;br /&gt;
  r: read&lt;br /&gt;
  w: write&lt;br /&gt;
  x: execute&lt;br /&gt;
&lt;br /&gt;
'''umask - S''' da shell per visualizzare la umask attuale con rappresentazione simbolica&lt;br /&gt;
&lt;br /&gt;
'''ls -l &amp;quot;nomefile&amp;quot;''' da shell per visualizzare i permessi del file in notazione simbolica: '-' seguito da 3 triple che indicano i permessi di accesso per '''ugo''',  ovvero, rispettivamente, per user,  per group, per other.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO&lt;br /&gt;
  -rw-r--r--&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''File aperti durante fork/exec'''https://www.kernel.org/&lt;br /&gt;
&lt;br /&gt;
I file aperti vengono ereditati dai processi figli.&lt;br /&gt;
Processo padre e processo figlio condividono lo stesso offset nei file comuni. In questo modo si evita di sovrascriversi a vicenda. Ogni volta che il padre o il figlio scrivono sul file aggiornano l'offset comune.In questo modo se l'altro processo vuole anche lui scrivere in quel file partirà dal offset aggiornato e non sovrascriverà i dati scritti dal altro processo. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''La system call dup e la ridirezione'''&lt;br /&gt;
E' possibile fare in modo che più file descriptor siano associati a un unico file (per la precisione a un unica file table, che serve per gestire un file). &amp;lt;br&amp;gt;&lt;br /&gt;
La system call dup permette di fare ciò. Ritorna un nuovo file descriptor che punta a una file table già esistente.&amp;lt;br&amp;gt;&lt;br /&gt;
Possiamo quindi &amp;quot;ridirezionare&amp;quot; l'output o l'input di un processo figlio modificando i file descriptor prima di creare il processo figlio. Per esempio possiamo modificare un file descriptor che puntava a un normale file in modo che punti a un file di standard output e quindi anche se al processo figlio sembrerà di scrivere in un normale file starà scrivendo nel terminale.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
  #include&amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
  #include&amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
  #include&amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
  #include&amp;lt;stdio.h&amp;gt;&lt;br /&gt;
  #include&amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  int main(int argc, char * argv[]){&lt;br /&gt;
    int fdout;&lt;br /&gt;
    if(argc &amp;gt; 1);&lt;br /&gt;
    fdout = open (argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0666);&lt;br /&gt;
    switch(fork()) {&lt;br /&gt;
        case 0: //child&lt;br /&gt;
          dup2(fdout, 1); // duplica descrittore file, posso ridirezionare l'output. E' come fare '&amp;gt;' dalla shell &lt;br /&gt;
          execlp(”ls”, “-l”, 0); &lt;br /&gt;
          close(fdout);&lt;br /&gt;
          break;&lt;br /&gt;
        default: // parent&lt;br /&gt;
          wait(NULL);&lt;br /&gt;
          break;&lt;br /&gt;
        case -1: // error&lt;br /&gt;
          break;&lt;br /&gt;
    }&lt;br /&gt;
    printf(“\n”);&lt;br /&gt;
    return 0;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lezione del 8 novembre 2016 ==&lt;br /&gt;
'''Codice per la congiura dei filosofi'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;quot;semaphore.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
semaphore philowait[5];&lt;br /&gt;
semaphore mutex;&lt;br /&gt;
semaphore cospsem;&lt;br /&gt;
char philo_status[]=&amp;quot;TTTTT&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
void baton(int i){&lt;br /&gt;
    int j = 0;&lt;br /&gt;
    //iterate through remaining philosophers&lt;br /&gt;
    for(j = 1; j &amp;lt; 5;j++){&lt;br /&gt;
        //if there's any waiting philosopher, and no other philosophers(on his left and right side) are eating&lt;br /&gt;
        if(philo_status[(i+j)%5]=='W' &amp;amp;&amp;amp; philo_status[(i+j+1)%5]!= 'E' &amp;amp;&amp;amp; philo_status[(i+j+4)%5]!='E'){&lt;br /&gt;
            semaphore_V(philowait[(i+j)%5]);&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    semaphore_V(mutex);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ok2eat(int i){&lt;br /&gt;
    semaphore_P(mutex);&lt;br /&gt;
    philo_status[i]='W';&lt;br /&gt;
    if(philo_status[(i+1)%5] == 'E' || philo_status[(i+4)%5] == 'E'){&lt;br /&gt;
        semaphore_V(mutex);&lt;br /&gt;
        semaphore_P(philowait[i]); //wait for baton&lt;br /&gt;
    }&lt;br /&gt;
    philo_status[i] = 'E';&lt;br /&gt;
    printf(&amp;quot;philo eating:   %d |%s|\n&amp;quot;,i,philo_status);&lt;br /&gt;
    baton(i);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void ok2think(int i){&lt;br /&gt;
    semaphore_P(mutex);&lt;br /&gt;
    philo_status[i] = 'T';&lt;br /&gt;
    printf(&amp;quot;philo thinking: %d |%s|\n&amp;quot;,i,philo_status);&lt;br /&gt;
    baton(i);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void conspiracy_in(){&lt;br /&gt;
    static int first_time = 1; //this variable's value is preserved between calls to this function&lt;br /&gt;
    semaphore_P(mutex);&lt;br /&gt;
    if(first_time){&lt;br /&gt;
        //first conspirator entering here will act like nothing happened&lt;br /&gt;
        first_time = 0;&lt;br /&gt;
    }&lt;br /&gt;
    else{&lt;br /&gt;
        //conspire&lt;br /&gt;
        semaphore_V(cospsem);&lt;br /&gt;
    }&lt;br /&gt;
    semaphore_V(mutex);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void conspiracy_out(){&lt;br /&gt;
    semaphore_P(cospsem);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void *philo(void *arg) {&lt;br /&gt;
    int i = (uintptr_t)arg;&lt;br /&gt;
    while (1) {&lt;br /&gt;
        //thinking/still thinking&lt;br /&gt;
        usleep(random() % 200000);&lt;br /&gt;
        ok2eat(i);&lt;br /&gt;
        //eating&lt;br /&gt;
&lt;br /&gt;
        //conspirators need to be synchronized to conspire&lt;br /&gt;
        if(i==1 || i==3) conspiracy_in();&lt;br /&gt;
        usleep(random() % 200000);&lt;br /&gt;
        if(i==1 || i==3) conspiracy_out();&lt;br /&gt;
        ok2think(i);&lt;br /&gt;
        //thinking again&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[]) {&lt;br /&gt;
    int i;&lt;br /&gt;
    pthread_t philo_t[5];&lt;br /&gt;
    srandom(time(NULL));&lt;br /&gt;
    mutex = semaphore_create(1);&lt;br /&gt;
    cospsem = semaphore_create(0);&lt;br /&gt;
    for (i=0; i&amp;lt;5; i++)&lt;br /&gt;
        philowait[i]=semaphore_create(0);&lt;br /&gt;
    for (i=0; i&amp;lt;5; i++)&lt;br /&gt;
        pthread_create(&amp;amp;philo_t[i], NULL, philo, (void *)(uintptr_t) i);&lt;br /&gt;
    for (i=0; i&amp;lt;5; i++)&lt;br /&gt;
        pthread_join(philo_t[i], NULL);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* '''Problema dei lettori e scrittori'''&lt;br /&gt;
:Spesso quando gestiamo la concorrenza all’accesso a strutture dati separiamo i ruoli di solo lettori con anche quello di scrittori (che per fare ciò possono dover anche leggere).&lt;br /&gt;
:Mentre che per i lettori non c’è un limite a quanti contemporaneamente possono avere accesso alle informazioni, gli scrittori devono avere accesso esclusivo alla struttura dati.&lt;br /&gt;
:Questo perché incorriamo in una race-condition solo quando modifichiamo la struttura dati.&lt;br /&gt;
:Dare precedenza di accesso a una categoria o all’altra porta starvation nella categoria con meno priorità.&lt;br /&gt;
:Una soluzione che abbiamo dato è quella di dare esclusività ai ruoli a turno:&lt;br /&gt;
:dopo che uno scrittore ha finito di scrivere garantisce l’accesso prima a tutti i lettori in attesa e se non sono presenti agli scrittori in attesa. Per i lettori è il contrario: dopo che l’ultimo lettore ha finito di leggere garantisce l’accesso a uno dei possibili scrittori in attesa e se non ce ne sono ai lettori.&lt;br /&gt;
:La soluzione in C al problema lettori e scrittori può essere trovata [[Zibaldone|qui]].&lt;br /&gt;
* '''Semafori Binari(Introduzione)'''&lt;br /&gt;
: Un semaforo binario è un semaforo ordinario che rispetta la seguente invariante: '''nP&amp;lt;=nV+Init&amp;lt;=nP+1''', ovvero è un semaforo che può assumere solo valori 0 o 1.&lt;br /&gt;
: Si può dimostrare che i semafori ordinari sono espressivi tanto quanto quelli binari(e viceversa) fornendo una implementazione di semafori binari tramite i semafori ordinari e viceversa una implementazione di semafori ordinari tramite semafori binari. (l'invariante dei semafori binari si pu&amp;amp;ograve; anche scrivere '''0 &amp;lt;= nV + Init - nP &amp;lt;= 1''' dove '''nV + Init - nP''' rappresenta proprio il valore del semaforo).&lt;br /&gt;
&lt;br /&gt;
== Lezione del 11 novembre 2016 ==&lt;br /&gt;
* '''Valore di ritorno di un processo'''&lt;br /&gt;
:Un processo ritorna un valore che è un numero intero. &amp;lt;br&amp;gt;&lt;br /&gt;
:Questo può essere deciso come valore di ritorno del main, oppure con la funzione ''exit''.&lt;br /&gt;
:La system call wait aspetta che lo stato di un processo figlio cambi e quando cambia scrive il nuovo in una variabile. &amp;lt;br&amp;gt;&lt;br /&gt;
:Quando un programma termina il suo valore di ritorno viene salvato nel descrittore del processo. Se il padre non esegue la wait il descrittore del processo figlio non verrà mai eliminato e il processo figlio rimarrà uno &amp;quot;zombie&amp;quot;.&lt;br /&gt;
:Un esempio del uso si queste system call si può trovare [[2016-17_Programmi_C#Print_child_exit_status|qui]].&lt;br /&gt;
* '''Pipe'''&lt;br /&gt;
:La system call pipe crea un canale monodirezionale che può essere usato per la comunicazione tra processi. Dimostrazione del uso di pipe si può trovare [[2016-17_Programmi_C#Demonstration_of_the_use_of_pipe_system_call|qui]].&lt;br /&gt;
* '''Lo zoo delle exec'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang='C'&amp;gt;&lt;br /&gt;
int execl(const char *path, const char *arg, ...); &lt;br /&gt;
int execlp(const char *file, const char *arg, ...);&lt;br /&gt;
int execle(const char *path, const char *arg, ..., char * const envp[]);&lt;br /&gt;
int execv(const char *path, char *const argv[]);&lt;br /&gt;
int execvp(const char *file, char *const argv[]);&lt;br /&gt;
int execvpe(const char *file, char *const argv[],char *const envp[]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Spiegazione dei postfissi:&lt;br /&gt;
* l = elenco dei parametri passato tramite parametri a cardinalità variabile (variadic function)&lt;br /&gt;
* v = elenco dei parametri passato come array&lt;br /&gt;
* p = bisogna specificare il path completo dell'eseguibile&lt;br /&gt;
* e = bisogna specificare anche l'enviroment con cui verrà eseguito il programma. L'enviroment è una serie di variabili globali, tra cui il path. Il path è l'elenco dei percorsi dei programmi più usati.&lt;br /&gt;
Tutte queste funzioni alla fine sono solo delle interfacce per execve.&lt;br /&gt;
* '''Programmazione ad eventi'''&lt;br /&gt;
:E' un paradigma di programmazione dove il flusso del programma è determinato dal avverarsi di eventi. Il programmatore specifica il codice (Handlers) che deve essere eseguito al verificarsi di un certo evento. &amp;lt;br&amp;gt;&lt;br /&gt;
:System call che sono utili per far questo sono select,pselect,poll,ppoll,epoll.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 15 novembre 2016 ==&lt;br /&gt;
* '''Differenza tra select,poll,pselect,ppoll e epoll'''&lt;br /&gt;
:La poll a differenza della select richiede un array di strutture pollfd invece che una bitmask. Permette di specificare precisi eventi per cui stare in ascolto. &amp;lt;br&amp;gt;&lt;br /&gt;
:Le versioni con prefisso &amp;quot;p&amp;quot; implementano anche la gestione dei segnali. &amp;lt;br&amp;gt;&lt;br /&gt;
:L'epoll è linux only. E' moderna (2002) e fornisce un file dove leggere direttamente quali file descriptor sono stati modificati in modo da non dover scorrere tutta la lista.&lt;br /&gt;
* '''Dimostrazione della equivalenza di potere espressivo fra semafori e semafori binari.'''&lt;br /&gt;
:I semafori implementati per dimostrare l'equivalenza possono essere trovati a [[Esperimenti_con_semafori_e_monitor_in_C | questa pagina]].&lt;br /&gt;
* '''Definizione di Monitor.'''&lt;br /&gt;
Un monitor è un costrutto ad alto livello per la sincronizzazione pensato ispirandosi alla programmazione ad oggetti. &amp;lt;br&amp;gt;&lt;br /&gt;
E' una classe che incapsula la risorsa condivisa, la rende privata e permette di accedere a questa solo tramite metodi. Questi metodi implementano i meccanismi di sincronizzazione. &amp;lt;br&amp;gt;&lt;br /&gt;
Uno solo processo può essere al interno del monitor e avere accesso ai dati protetti da mutua esclusione. &amp;lt;br&amp;gt;&lt;br /&gt;
E' presente una coda di attesa per l'accesso al monitor. &amp;lt;br&amp;gt;&lt;br /&gt;
Definiamo inoltre delle variabili ''condition''. &amp;lt;br&amp;gt;&lt;br /&gt;
Gli unici metodi che possono essere chiamati su una condition sono wait e signal. Ogni condition ha una propria coda di attesa &amp;lt;br&amp;gt;&lt;br /&gt;
L'operazione wait sospende il chiamante e lo sposta nella coda di attesa della condition.&lt;br /&gt;
L'operazione signal risveglia un processo in attesa. Se non c'è nessun processo in attesa all'interno del monitor la signal fa entrare un eventuale processo in attesa di entrare nel monitor. Il chiamante di signal dopo aver risvegliato l'altro possibile processo deve mettersi in attesa per evitare che ci siano due processi in concorrenza al interno del monitor, ma invece di andare nella coda di attesa della condition viene messo in un ''urgent stack'' che ha più priorità rispetto alla coda esterna dei processi in attesa di entrare nel monitor. &amp;lt;br&amp;gt;&lt;br /&gt;
Ricapitolando le strutture dati per un monitor sono una coda esterna per l'accesso al monitor, una coda interna (non accessibile da fuori ma non dentro la mutua esclusione) per ogni variabile condizionale e l'urgent stack. &amp;lt;br&amp;gt;&lt;br /&gt;
* '''Implementazione dei Semafori tramite monitor.'''&lt;br /&gt;
&lt;br /&gt;
== Lezione del 18 novembre 2016 ==&lt;br /&gt;
* '''File speciali a blocchi o a caratteri'''&lt;br /&gt;
: In Unix molte cose sono rappresentate come file. Per esempio i device come i terminali e le partizioni del disco sono file. Si chiamano ''file speciali'' ma non sono tanto differenti dai file comuni. Utilizzano anche gli stessi sistemi di protezione all'accesso. Si dividono i due categori:&lt;br /&gt;
:* File speciali a blocchi: utilizzati maggiormente dai sistemi di storage tipo hard-disk e CD-ROMs. Se per esempio il blocco è di 1024 bytes allora possiamo scrivere e leggere con tale granularità, cioè non potremo mai scrivere soltato 500 bytes ma dovremo scrivere, appunto a &amp;quot;blocchi&amp;quot;.&lt;br /&gt;
:* File speciali a caratteri: utilizzati maggiormente per dispositivi di input-output tipo mouse,tastiera,terminali ecc.. permettono di lavorare con una granularità di 1 byte ma hanno un accesso sequenziale invece che diretto come i file a blocchi.&lt;br /&gt;
:Inoltre nei file speciali l'ampiezza è sostituita da 2 numeri, uno che indica il driver del dispositivo e l'altro le possibili parti del dispositivo, per esempio le partizioni.&lt;br /&gt;
* '''System call ioctl'''&lt;br /&gt;
: Ioctl serve per controllare dei device. Per esempio permette di fermare la testina di lettura del disco oppure far partire il motore del lettore CD. E' necessaria in quando si è voluto uniformare la lettura e scrittura di stream per tutti i device tramite le system call read/write ma chiaramente ogni dispositivo ha anche le sue diverse necessità e queste vengono soddisfatte da ioctl.&lt;br /&gt;
* '''Syscall'''&lt;br /&gt;
: Syscall è una funzione di libreria C che serve a chiamare system calls che non hanno una loro interfaccia per il linguaggi C. Per esempio getdents.&lt;br /&gt;
* '''System call e funzioni di libreria per directory'''&lt;br /&gt;
:Le directories sono sempre file ma si può fare l'accesso solo in lettura, per esempio per ottenere la lista di tutti i file e directory all'interno di quella directory. &amp;lt;br&amp;gt;&lt;br /&gt;
:getdents legge un file directory ed è la system call principale, tutte le funzioni di libreria C tipo opendir,readdir,readdir_r(per multithread) chiamano getdents. &amp;lt;br&amp;gt;&lt;br /&gt;
:Tutte queste funzioni lavorano con una struct dirent che rappresenta una directory entry. &amp;lt;br&amp;gt;&lt;br /&gt;
:Si può usare anche scandir che è reetrant (nessun problema di race condition) e invece di leggere un elemento della directory per volta ritorna direttamente un array di tutti gli element allocato dinamicamente (occorre ricordarsi di deallocare con free tutti gli elementi e l'array stesso).&lt;br /&gt;
&lt;br /&gt;
== Lezione del 22 novembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
Problema dei filosofi a cena risolto con i monitor. Soluzione con le condizioni associate alle &amp;quot;bacchette&amp;quot; e con le condizioni associate ai filosofi.&lt;br /&gt;
discussione dei problemi di deadlock e starvation nell'implementazione tramite monitor.&lt;br /&gt;
&lt;br /&gt;
Problema dei lettori e scrittori tramite monitor. Soluzione con privilegio ai lettori, agli scrittori e modifiche necessarie per la soluzione priva di starvation.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 25 novembre 2016 ==&lt;br /&gt;
[[Coding Contest]]&lt;br /&gt;
&lt;br /&gt;
== Lezione del 29 novembre 2016 ==&lt;br /&gt;
* '''Implementazione dei monitor tramite semafori'''&lt;br /&gt;
:Un soluzione simile a quella vista a lezione può essere trovata [[Esperimenti_con_semafori_e_monitor_in_C#Monitor implementati con semafori | qui]].&lt;br /&gt;
* '''Introduzione al message passing'''&lt;br /&gt;
:E' un altro modo per fare programmazione concorrente alternativo alla memoria condivisa che abbiamo visto fin ora.&lt;br /&gt;
:Se i programmi non condividono memoria ma devono sincronizzarsi per lavorare assieme hanno bisogno di scambiarsi messaggi.&lt;br /&gt;
:Per l'indirizzamento serve un sistema di naming, come il pid dei processi.&lt;br /&gt;
:Due primitive: send e receive. Nella receive come indirizzo da chi ricevere si può mettere una wildcard in modo da riceve da tutti.&lt;br /&gt;
:Nella send invece occorre indicare l'identificativo del destinatario (se si ammettesse di poter inserire una wildcard si spedirebbero messaggi in broadcast/multicast. E' una problematica pi&amp;amp;ugrave; complessa che viene trattata nei corsi di sistemi distribuiti).&lt;br /&gt;
:Lo scambio di messaggi può essere:&lt;br /&gt;
:* Sincrono: send bloccante e receive bloccante, senza memoria di supporto.&lt;br /&gt;
:* Asincrono: send non bloccante e receive bloccante, con memoria di supporto. I messaggi spediti rimangono in una coda (di ampiezza massima illimitata nel modello).&lt;br /&gt;
:* Completamente asincrono: send non bloccante e receive non bloccante. Poco utilizzato, il destinatario deve fare un polling per controllare se il messaggio è arrivato o no.&lt;br /&gt;
:Per dimostrare la stessa espressività dei metodi dobbiamo sempre creare delle librerie e non dobbiamo aggiungere nessun processo.&lt;br /&gt;
:La metodologia asincrona è espressiva almeno quanto quella sincrona mentre non è vero il contrario.&lt;br /&gt;
:Per creare un metodo sincrono con chiamate asincrone basta bloccare il programma finché non si riceve un acknowledgement.&lt;br /&gt;
:Invece per creare un metodo asincrono con chiamate sincrone bisogna aggiungere un processo e quindi questo dimostra che non sono equivalentemente espressive.&lt;br /&gt;
* '''Soluzioni di esercizi d'esame'''&lt;br /&gt;
** Problema della lavatrice&lt;br /&gt;
** Problema del ponte a senso unico&lt;br /&gt;
&lt;br /&gt;
== Lezione del 2 dicembre 2016 ==&lt;br /&gt;
* '''Implementazione della libreria per message passing'''&lt;br /&gt;
:I sorgenti della implementazione possono essere trovati [[Esperimenti con message passing in C|qui]]&lt;br /&gt;
* '''I segnali'''&lt;br /&gt;
:I segnali sono un metodo di comunicazione tra processi utilizzato in UNIX.&lt;br /&gt;
:Ogni segnale è rappresentato da un numero. Per esempio &amp;quot;segmentation fault&amp;quot; è il numero 11. &lt;br /&gt;
:La system call kill() serve per inviare un segnale. signal() serve per definire l'handler, una funzione che viene eseguita quando il processo riceve uno specifico segnale.&lt;br /&gt;
:Ogni segnale ha il suo handler di default e tramite signal() noi sovrascriviamo il default handler. &lt;br /&gt;
* '''Permesso setuid'''&lt;br /&gt;
:Permette a dei programmi di eseguire operazioni che hanno bisogno dei permessi di root. L'attributo setUserId attivato vuol dire che il programma verrà eseguito con i privilegi del possessore del file piuttosto di quelli di chi lo esegue.&lt;br /&gt;
* '''Proposta di soluzione esercizio 4 del [[Coding Contest 25 novembre 2016]]'''&lt;br /&gt;
&lt;br /&gt;
== Lezione del 6 dicembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
* Implementazione di un servizio di Message Passing Asincrono dato uno sincrono (con processo server).&lt;br /&gt;
* Implementazione di un servizio di Message Passing completamente asincrono dato uno sincrono.&lt;br /&gt;
* Analisi del potere espressivo dei servizi di message passing.&lt;br /&gt;
&lt;br /&gt;
* kernel monolitici e microkernel&lt;br /&gt;
* macchine virtuali a livello di processo e a livello di sistema&lt;br /&gt;
&lt;br /&gt;
I sorgenti degli esperimenti con il message passing sono in [http://www.cs.unibo.it/~renzo/so/tools/mp.extra.tgz questo file].&lt;br /&gt;
&lt;br /&gt;
== Lezione del 9 dicembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
* Il problema della Cena dei filosofi con il message passing (asincrono)&lt;br /&gt;
&lt;br /&gt;
* Buffer Limitato con message passing (asincrono).&lt;br /&gt;
&lt;br /&gt;
== Lezione del 13 dicembre 2016 ==&lt;br /&gt;
&lt;br /&gt;
La lezione TACE per malattia (influenza) del docente.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 16 dicembre 2016 ==&lt;br /&gt;
*'''Specifiche del progetto fase uno'''&lt;br /&gt;
Il documento delle specifiche può essere trovato [http://www.cs.unibo.it/~renzo/so/mikaboo/phase1.pdf qui].&lt;br /&gt;
:*List.h&lt;br /&gt;
:*Lista dei processi&lt;br /&gt;
:*Coda dei thread&lt;br /&gt;
:*Code dei messaggi&lt;br /&gt;
*'''Autotools'''&lt;br /&gt;
:Insieme di strumenti per la generazione automatica di Makefiles. &amp;lt;br&amp;gt;&lt;br /&gt;
:comando ''autoreconf'' per generale il file ''configure'' &amp;lt;br&amp;gt;&lt;br /&gt;
:''./configure'' per generare makefile.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 21 febbraio 2017 ==&lt;br /&gt;
'''Argomenti trattati:'''&lt;br /&gt;
* '''Visione a strati di un elaboratore'''&lt;br /&gt;
:Gli strati sono livelli di astrazione. Ogni strato deve &amp;quot;parlare&amp;quot; il linguaggio del livello sottostante e deve fornire un proprio linguaggio ai livelli superiori.&lt;br /&gt;
:Per esempio il sistema operativo parla l'ISA del livello hardware e fornisce alle applicazioni le system call. Il sistema operativo maschera il livello hardware in quanto fornisce la possibilità alle applicazioni di utilizzare istruzioni ISA ma non tutte, per ragioni di sicurezza.&lt;br /&gt;
* '''Componenti fondamentali di un sistema operativo'''&lt;br /&gt;
:A sua volta il sistema operativo può essere visto come un insieme di strati. Esistono sistemi operativi a singolo strato (come MS-DOS) e più strati (layered).&lt;br /&gt;
:Le parti principali in cui possiamo suddividere un sistema operativo sono:&lt;br /&gt;
:*&amp;lt;u&amp;gt;Lo scheduler&amp;lt;/u&amp;gt;&lt;br /&gt;
:*&amp;lt;u&amp;gt;Il memory manager&amp;lt;/u&amp;gt;&lt;br /&gt;
:*&amp;lt;u&amp;gt;Il file system&amp;lt;/u&amp;gt;&lt;br /&gt;
:*&amp;lt;u&amp;gt;L'I/O manager&amp;lt;/u&amp;gt;&lt;br /&gt;
:*&amp;lt;u&amp;gt;Il networking manager&amp;lt;/u&amp;gt;&lt;br /&gt;
:Di questa lista, gli elementi assolutamente fondamentali per considera un programma un sistema operativo sono lo scheduler e una parte essenziale del memory e I/O manager.&lt;br /&gt;
:Il networking e file manager si appoggiano al I/O manager per svolgere i loro compiti.&lt;br /&gt;
*'''Bootstrap'''&lt;br /&gt;
:Letteralmente &amp;quot;mettersi in piedi tirandosi dai lacci degli stivali&amp;quot;, nel senso di attivarsi senza aiuto esterno. All'accensione il computer legge da una ROM le prime istruzioni necessarie. Viene sempre letto dai primi settori della memoria di massa il bootloader. :Il bootloader (per esempio GRUB) conosce già molti filesystem, ma non abbastanza. Il bootloader carica un file system temporaneo apposito chiamato initrd per caricare i moduli necessari al kernel.&lt;br /&gt;
*'''Come rappresentiamo i processi'''&lt;br /&gt;
:Dobbiamo sia tener conto a che punto siamo arrivati nell'esecuzione del processo sia tener conto delle risorse di cui il processo è detentore.&lt;br /&gt;
:Utilizziamo la struttura ''Process Control Block'' (task_struct in linux) per i processi e &amp;quot;Thread Control Block&amp;quot; (task_struct in linux) per i thread.&lt;br /&gt;
:Process control block:&lt;br /&gt;
:*PID (Process ID) &lt;br /&gt;
:*PPID (Parent Process ID) per poter creare una gerarchia di processi&lt;br /&gt;
:*punti di accesso alla memoria (codice,dati,tabella di rilocazione,paginazione)&lt;br /&gt;
:*lista dei thread&lt;br /&gt;
:*owner (se il sistema è multiuser)&lt;br /&gt;
:*Risorse del processo&lt;br /&gt;
:*Coda dei messaggi per IPC (Inter-Process Communication)&lt;br /&gt;
:*accounting (tempo di esecuzione sia in modalità kernel sia in modalità user)&lt;br /&gt;
:*valore di ritorno del processo&lt;br /&gt;
:Thread control block:&lt;br /&gt;
:*TID (Thread ID)&lt;br /&gt;
:*stato del processore&lt;br /&gt;
:*call stack&lt;br /&gt;
:*ready/running/waiting&lt;br /&gt;
*'''Avvicendamento dei processi'''&lt;br /&gt;
:Definizione di avvicendamento: ''Successione prevista o regolata nel tempo, alternanza.''&lt;br /&gt;
:I context switch avvengono a seguito di trap/interrupt. &lt;br /&gt;
:Il ciclo di vita di un processo passa tra gli stati ''ready, running, waiting'' e infine ''term''.  &lt;br /&gt;
:Lo scheduling (processo decisionale) può venire applicato nelle seguenti 4 situazioni:&lt;br /&gt;
:# Quando un processo passa dallo stato di running a quello di waiting&lt;br /&gt;
:# Quando un processo passa dallo stato di running a quello di ready&lt;br /&gt;
:# Quando un processo passa dallo stato di waiting a quello di ready&lt;br /&gt;
:# Quando un processo passa dallo stato di running a quello di term&lt;br /&gt;
:Se lo scheduling avviene solo nella situazione 1 e 4 allora si utilizza un metodo non preemptive. Dal momento che la CPU è allocata a un processo questo la detiene finchè non termina o entra di sua spontanea volontà in stato di waiting. La CPU non può decidere di far aspettare un processo spostandolo temporaneamente nella coda dei processi ready. Questo sistema era utilizzato da vecchi sistemi operativi come Windows 3.x e dalle versione di Mac OS prima della versione X.&lt;br /&gt;
:Nel preemptive scheduling invece la CPU può decidere di sospendere un processo, questo può creare race condition ma anche un sistema più equo di distribuzione delle risorse. Le race condition vengono risolte con i metodi che abbiamo visto nel primo semestre.&lt;br /&gt;
'&lt;br /&gt;
&lt;br /&gt;
== Lezione del 23 febbraio 2017 ==&lt;br /&gt;
&lt;br /&gt;
'''System call'''&lt;br /&gt;
&lt;br /&gt;
Abbiamo analizzato le system call che erano già presenti nella [http://man.cat-v.org/unix-6th/2/ versione 6 di Unix] (1975). &amp;lt;br&amp;gt;&lt;br /&gt;
Le abbiamo poi confrontate con quelle attuali del [[Il_%27%27catalogo%27%27_delle_System_Call]].&lt;br /&gt;
&lt;br /&gt;
'''Shell Scripting'''&lt;br /&gt;
&lt;br /&gt;
Gli script vengono utilizzati per lanciare un demone, guardare i processi, etc.&lt;br /&gt;
Anche l'amministrazione di sistema avviene tramite scripting. Ad esempio, dopo il boot, il processo init avvia le funzionalità di sistema tramite script.&lt;br /&gt;
&lt;br /&gt;
Unix unifica l'idea di script con quella di shell.&lt;br /&gt;
La shell è un processo che interagisce con l'utente. In Unix la novità introdotta è rappresentata dalla presenza di un linguaggio che riguarda i comandi, utile per scrivere gli script per la shell.&lt;br /&gt;
&lt;br /&gt;
Come indichiamo quale shell usare? Scrivendo nella prima riga #! seguito dal path e dal nome della shell. Il kernel prende ciò che è scritto nella prima riga e lo esegue per interpretare lo script. &lt;br /&gt;
Nell'esempio sottostante Script è un commento perchè è preceduto da #.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO&lt;br /&gt;
&lt;br /&gt;
   #!/bin/bash&lt;br /&gt;
   #Script&lt;br /&gt;
&lt;br /&gt;
   echo &amp;quot;Hello World!&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Per scrivere script abbiamo bisogno di variabili. Nelle shell queste possono essere di 2 tipi:&lt;br /&gt;
&lt;br /&gt;
- Locali: viste solo dalla shell, non perturbano comportamento dei processi.&lt;br /&gt;
&lt;br /&gt;
- D'ambiente: recepita dai comandi come ambiente.&lt;br /&gt;
&lt;br /&gt;
'''ASSEGNAMENTO E VARIABILI'''&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1&lt;br /&gt;
&lt;br /&gt;
  a=2  /* Non devono esserci spazi!!! a = 2 doesn't work */&lt;br /&gt;
  echo $a  /* Stampa 2 */&lt;br /&gt;
  echo ${a}a  /* Stampa 2a */&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 2 - Nuova shell&lt;br /&gt;
&lt;br /&gt;
  bash  /* Apro nuova shell */&lt;br /&gt;
  echo $a /* Stamperà una riga vuota perchè la var. a non è locale della nuova shell */&lt;br /&gt;
  exit  /* chiudo nuova shell */&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 3 - Esportare variabile locale&lt;br /&gt;
&lt;br /&gt;
  export a /* La var. a diventa variabile d'ambiente */&lt;br /&gt;
  bash &lt;br /&gt;
  echo $a /* Ora possiamo chiamare la var. a perchè è diventata d'ambiente */&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 4 - csh &lt;br /&gt;
&lt;br /&gt;
csh in molti casi dovrebbe già essere installato di default. &lt;br /&gt;
Se non è così è possibile installarlo eseguendo&lt;br /&gt;
&lt;br /&gt;
  sudo apt-get install csh&lt;br /&gt;
&lt;br /&gt;
  csh /* Apro cshell, noterete il simbolo di percentuale % a inizio riga di comando */&lt;br /&gt;
  set a=42 /* assegnamento */&lt;br /&gt;
  csh /* apro nuova shell */&lt;br /&gt;
  echo $a /* non possiamo chiamare a(42) perchè locale alla prima shell, viene chiamata a d'ambiente se presente */&lt;br /&gt;
  exit&lt;br /&gt;
  setenv a 42 /* Per creare una variabile d'ambiente, da notare la mancanza di = */&lt;br /&gt;
  csh&lt;br /&gt;
  echo $a&lt;br /&gt;
&lt;br /&gt;
'''ARGOMENTI'''&lt;br /&gt;
&lt;br /&gt;
Negli script possiamo avere degli argomenti. &lt;br /&gt;
Vengono visti come $1, ..., $9, ${10}, etc.&lt;br /&gt;
$* indica tutti gli argomenti&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1&lt;br /&gt;
  #!/bin/bash&lt;br /&gt;
  echo $*  /* Richiama tutti gli argomenti */&lt;br /&gt;
  echo $1 $2 /* Richiama solo i primi due argomenti */&lt;br /&gt;
  shift /* Sposta la riga di comando degli argomenti di una posizione verso sx */&lt;br /&gt;
  echo $* &lt;br /&gt;
&lt;br /&gt;
Una volta salvato il file con estensione .sh, eseguire da terminale:&lt;br /&gt;
  chmod +x nomefile.sh&lt;br /&gt;
  ./nomefile.sh  arg1 arg2 ...&lt;br /&gt;
&lt;br /&gt;
Se passiamo come argomenti: uno due tre quattro,&lt;br /&gt;
l'output sarà: &lt;br /&gt;
  uno due tre quattro&lt;br /&gt;
  uno due&lt;br /&gt;
  due tre quattro&lt;br /&gt;
&lt;br /&gt;
A volte dobbiamo proteggere dei caratteri. Alcuni di essi possono essere delle wildcard. Ci sono 3 modi per proteggerli:&lt;br /&gt;
  \*&lt;br /&gt;
  '*'&lt;br /&gt;
  &amp;quot;*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Analizziamone le differenze tramite un esempio.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 1 &lt;br /&gt;
&lt;br /&gt;
  echo \*  // OUTPUT: *&lt;br /&gt;
  echo '*$a' // OUTPUT: *$a&lt;br /&gt;
  echo &amp;quot;*$a&amp;quot; // OUTPUT: *2 &lt;br /&gt;
&lt;br /&gt;
Nel primo caso l'escape vale solo per *. Nel secondo caso vale per tutta la stringa. Nel terzo caso vale per * e richiama la variabile.&lt;br /&gt;
&lt;br /&gt;
ESEMPIO 2&lt;br /&gt;
  echo ab // OUTPUT: ab&lt;br /&gt;
  echo &amp;quot;ab&amp;quot; //OUTPUT: ab&lt;br /&gt;
  &lt;br /&gt;
  echo a b //OUTPUT: ab &amp;lt;-- non rileva gli spazi&lt;br /&gt;
  echo &amp;quot;a b&amp;quot; //OUTPUT: a b&lt;br /&gt;
&lt;br /&gt;
== Lezione del 28 febbraio 2017 ==&lt;br /&gt;
'''Argomenti trattati''':&lt;br /&gt;
* '''Ciclo di vita di un kernel'''&lt;br /&gt;
: Dopo l'inizializzazione (Creazione di code, rilevazione di device, ecc..) si crea a &amp;quot;mano&amp;quot; il primo processo. &lt;br /&gt;
: L'esecuzione dello scheduler si alterna a quella dei processi. Quando un processo utente viene eseguito il kernel non ha più il controllo del flusso e gli viene restituito solo quando avviene un interrupt/trap.&lt;br /&gt;
* '''Parametri di valutazione di un algoritmo di scheduling'''&lt;br /&gt;
** % Utilizzo CPU: Bisogna cercare di tener utilizzata il processore il più possibile riducendo i momenti di inutilizzo (idle).&lt;br /&gt;
** Produttività (Throughput) Numero di processi completati per unità di tempo&lt;br /&gt;
** Tempo totale (turnaround time) Tempo totale impiegato dall'esecuzione del processo al completamente. Tiene conto del tempo speso in attesa oltre al tempo in esecuzione.&lt;br /&gt;
** Tempo di risposta: Tempo prima che un primo output venga generato dal momento in cui il processo viene eseguito. In un sistema interattivo è importante per non dare all'utente l'idea di malfunzionamento.&lt;br /&gt;
** Tempo di attesa. Non aspettando l'I/O ma bensì tempo speso nella ready queue.&lt;br /&gt;
* '''Processi CPU Bound e I/O Bound'''&lt;br /&gt;
:: I processi si possono dividere in queste due categorie. I processi che utilizzano il processore per tempi più lunghi (per esempio quelli di calcolo scientifico) si chiamano CPU Bound mentre quelli che richiedono spesso interazioni e si fermano ad aspettare l'I/O si chiamano I/O Bound. Algoritmi di scheduling differenti possono favorire o sfavorire una delle due categorie. L'obbiettivo e trovare un algoritmo che sia ottimale per tutti i processi.&lt;br /&gt;
* '''Algoritmi di scheduling'''&lt;br /&gt;
** First come first served&lt;br /&gt;
:: Lo scheduler implementa una semplice coda dei processi che hanno richiesto l'esecuzione. E' semplice da capire e creare ma il tempo medio di attesa per un processo può essere lungo. Dipende molto dall'ordine in cui vengono avvicendati processi CPU bound e I/O Bound. Un processo CPU bound può creare un ''effetto convoglio'' e far dipendere il tempo di attesa di altri processi che sono I/O bound al suo tempo di esecuzione. Questo algoritmo non è quindi equo.&lt;br /&gt;
:* Shortest job first&lt;br /&gt;
:: Forse più correttamente nominato algoritmo ''shortest next CPU burst''. Ad ogni processo viene associato il suo tempo di esecuzione e viene scelto dalla ready queue sempre il processo con il tempo più basso. Per determinare il tempo di esecuzione a priori si utilizza una media esponenziale dei tempi di esecuzione dello stesso processo nel passato. [[File:Media_esponenziale.png]] ''t'' sono le misurazioni recenti mentre 'tau' sono la storia più ''remota''. Il parametro alfa determina quanto la media esponenziale sia basata sulla storia recente o su quella passata.&lt;br /&gt;
:* Shortest remaining time first&lt;br /&gt;
:: SJF può essere preemptive oppure no. Nel caso preemptive prende il nome di ''Shortest remaining time first''. Se un processo A è in esecuzione e arriva un'altro processo B con tempo di esecuzione minore allora A viene fermato e viene eseguito B. La priorità di A nella coda è ora il tempo stimato rimanente.&lt;br /&gt;
:* Round robin&lt;br /&gt;
:: E' definita un unità di tempo, di solito tra i 10 e i 100 millisecondi, e ogni processo ha un tempo massimo di esecuzione pari a questa unità. I processi vengono avvicendati a turno in una coda. Un processo termina l'esecuzione o per sua volontà (il tempo rimasto del quanto di tempo non è considerato) oppure perchè è scattato l'interval timer. Nell'ultimo caso il processo viene inserito in coda come se fosse un processo appena arrivato. La performance di questo algoritmo dipendono molto da che tipi di processi si gestiscono e dalla dimensione dell'unita di tempo. Se questa è larga allora l'algoritmo è più simile a FCFS mentre un'unita di tempo piccola porta a più context switch e quindi a più overhead. L'unità di tempo andrebbe decisa in proporzione al tempo di context switch.&lt;br /&gt;
:* Scheduling a priorità&lt;br /&gt;
:: Ad ogni processo è assegnata una priorità che si riflette nella priorità che ha il processo nella ready queue.&lt;br /&gt;
:: La priorità di un processo può essere influenzata da fattori interni (come i requisiti di memoria) o esterni al sistema operativo (come ''l'importanza'' di un processo). L'algoritmo può essere non preemptive o premtive, nel caso in cui interrompa il processo attuale se arriva un processo con più priorità.&lt;br /&gt;
:: L'SJF può essere visto come un caso particolare di cheduling a priorità, in cui la priorità e appunto inversamente proporzionale al tempo di esecuzione.&lt;br /&gt;
:: Un problema dello scheduling a priorità è la starvation, che può essere risolta con il meccanismo di ''aging'' in cui un processo aumenta di priorità proporzionalmente al tempo cui rimane nella ready queue.&lt;br /&gt;
:* Scheduler multilivello&lt;br /&gt;
:: Questo è lo scheduler più completo in quanto divide i processi in gruppi e utilizza differenti algoritmi per ogni gruppo.&lt;br /&gt;
:: Una tipica suddivisione è quella di processi foreground e background, rispettivamente I/O bound e CPU bound. Per ogni gruppo viene creata una coda apposita.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 2 marzo 2017 ==&lt;br /&gt;
&lt;br /&gt;
'''Shell Scripting'''&lt;br /&gt;
&lt;br /&gt;
'$' seguito da caratteri esegue una sostituzione, questa può essere:&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 1 (contenuto)'''&lt;br /&gt;
&lt;br /&gt;
  echo $SHELL&lt;br /&gt;
&lt;br /&gt;
  echo $USER&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 2 (comando)'''&lt;br /&gt;
&lt;br /&gt;
  echo $ls&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 2.1'''&lt;br /&gt;
&lt;br /&gt;
  du -s $(ls -d *es) //du -s prende come input l'output del comando compreso tra parentesi tonde&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 3 (wildcard)'''&lt;br /&gt;
&lt;br /&gt;
  echo *       // tutti file/dir&lt;br /&gt;
  echo *es     // file/dir che terminanon con es&lt;br /&gt;
  echo *[lg]es // file/dir che terminanon con les/ges&lt;br /&gt;
  echo ?????   // file/dir con 5 caratteri &lt;br /&gt;
&lt;br /&gt;
Queste wildcard servono per poter individuare insiemi di file in un colpo solo.&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 4 ($ come sintassi riconosciuta da grep)'''&lt;br /&gt;
&lt;br /&gt;
  grep '&amp;gt;$' *.c // trova tutte le linee che terminano con '&amp;gt;' all'interno dei file .c&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 5 (PID)'''&lt;br /&gt;
&lt;br /&gt;
  echo $$ // restituisce PID dello script nel quale viene eseguito&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 6a'''&lt;br /&gt;
&lt;br /&gt;
  echo $1 $2 $3 &amp;gt;/tmp/tmpscript //scrive i 3 argomenti in tmpscript&lt;br /&gt;
 &lt;br /&gt;
  cat /tmp/tmpscript  //lettura tmpscript&lt;br /&gt;
  rm -f /tmp/tmpscript  //rimozione tmpscript&lt;br /&gt;
&lt;br /&gt;
Se cerchiamo di lanciare quasi contemporaneamente lo script dell'esempio 6 in due shell differenti, lo script non funziona perchè lavorano sullo stesso file. Possiamo quindi ricorrere all'utilizzo di $$.&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 6b'''&lt;br /&gt;
&lt;br /&gt;
  echo $1 $2 $3 &amp;gt;/tmp/tmpscript$$&lt;br /&gt;
  &lt;br /&gt;
  sleep 3 //in modo da poter lanciare lo script in un'altra shell &lt;br /&gt;
  cat /tmp/tmpscript$$  &lt;br /&gt;
  rm -f /tmp/tmpscript$$&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 7'''&lt;br /&gt;
  grep bash myscript.sh &amp;gt;&amp;amp; dev/null //ridireziono l'output di grep sul &amp;quot;null device&amp;quot;&lt;br /&gt;
  echo $? //restituisce 0 se il comando precedente è stato eseguito correttamente, 1 altrimenti&lt;br /&gt;
&lt;br /&gt;
'''Gli operatori &amp;gt;&amp;gt; e &amp;lt;&amp;lt;'''&lt;br /&gt;
&lt;br /&gt;
&amp;gt;&amp;gt; è simile a &amp;gt; ma scrive alla fine del file invece che all'inizio. &amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;&amp;lt; serve invece per inserire l'input di un comando direttamente nello script, come mostrato nel successivo esempio della mail.&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 8 (script per inviare mail)'''&lt;br /&gt;
  &lt;br /&gt;
  #!/bin/bash&lt;br /&gt;
   &lt;br /&gt;
  mail $1 &amp;lt;&amp;lt; ENDOFMESSAGE&lt;br /&gt;
  ciao $1 questa è una mail di prova&lt;br /&gt;
  ENDOFMESSAGE //etichetta usata per indicare il termine del messaggio&lt;br /&gt;
&lt;br /&gt;
Con&lt;br /&gt;
&lt;br /&gt;
  echo $1; echo $2; ls &lt;br /&gt;
&lt;br /&gt;
raggruppiamo una sequenza di comandi sulla stessa riga&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 9((|| &amp;amp;&amp;amp;)'''&lt;br /&gt;
&lt;br /&gt;
Gli operatori || e &amp;amp;&amp;amp; possono essere utilizzati per processare più comandi consecutivamente.&lt;br /&gt;
&lt;br /&gt;
  command || die&lt;br /&gt;
&lt;br /&gt;
  gcc -o hwx hw.c &amp;amp;&amp;amp; ./hwx&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 10 (sottoshell)'''&lt;br /&gt;
&lt;br /&gt;
Una lista di comandi all'interno di () viene eseguita in una sottoshell. Le variabili all'interno della sottoshell non sono visibili al di fuori del blocco di codice nella sottoshell. Sono variabili locali.&lt;br /&gt;
&lt;br /&gt;
  (cd subdir; ls) //eseguo un comando in un'altra directory&lt;br /&gt;
&lt;br /&gt;
'''Pushd e popd'''&lt;br /&gt;
&lt;br /&gt;
Simili al comando ''cd'' ma pushd inserisce i percorsi visitati in una pila e tramite popd e possibile ripercorrere a ritroso le directory visitate.&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 11 (background jobs)&lt;br /&gt;
  ping google.com &amp;amp; //esegue ping in background&lt;br /&gt;
  jobs //mostra la lista dei jobs in esecuzione&lt;br /&gt;
  //poniamo che l'output di jobs sia &amp;quot;[1]+  In esecuzione           ping google.com &amp;amp;&amp;quot;&lt;br /&gt;
  kill -9 %1 //termina il processo con identificatore 1&lt;br /&gt;
''bg'' repristina un processo fermato con CTRL+Z. &amp;lt;br&amp;gt;&lt;br /&gt;
''fg'' riporta in primo piano un processo mandato in background.&lt;br /&gt;
&lt;br /&gt;
'''test e type'''&lt;br /&gt;
&lt;br /&gt;
''test'' serve a comparare valori e controllare il tipo dei file. &amp;lt;br&amp;gt;&lt;br /&gt;
la sintassi ''[]'' è un alias di test.&lt;br /&gt;
  if [ $$ -eq '10853' ] ; then echo &amp;quot;il processo corrente ha pid 10853&amp;quot;; else echo &amp;quot;il processo corrente non ha pid 10853&amp;quot;; fi;&lt;br /&gt;
  //che è equivalente a &lt;br /&gt;
  if test $$ -eq '10853'; then echo &amp;quot;il processo corrente ha pid 10853&amp;quot;; else echo &amp;quot;il processo corrente non ha pid 10853&amp;quot;; fi;&lt;br /&gt;
''type'' invece mostra l'origine di un comando, se sono integrati nella shell oppure è un programma a parte.&lt;br /&gt;
&lt;br /&gt;
'''Switch'''&lt;br /&gt;
Sintassi degli switch in bash.  &lt;br /&gt;
  case expression in&lt;br /&gt;
    pattern1 )&lt;br /&gt;
        statements ;;&lt;br /&gt;
    pattern2 )&lt;br /&gt;
        statements ;;&lt;br /&gt;
    ...&lt;br /&gt;
    esac&lt;br /&gt;
'''Comando cron''' &amp;lt;br&amp;gt;&lt;br /&gt;
''cron'' serve per eseguire operazioni pianificate. Gli script in /etc/init.d vengono eseguiti automaticamente da cron.&lt;br /&gt;
&lt;br /&gt;
'''xarg''' &amp;lt;br&amp;gt;&lt;br /&gt;
xarg permette di eseguire un comando passandogli come parametri i valori letti da standard input. &lt;br /&gt;
&lt;br /&gt;
'''uniq''' &amp;lt;br&amp;gt;&lt;br /&gt;
uniq può essere usato per trovare e eliminare righe duplicate.&lt;br /&gt;
&lt;br /&gt;
'''wc''' &amp;lt;br&amp;gt;&lt;br /&gt;
''wc'' crea il ''word count'' di un file. Non conta solo le parole ma anche i caratteri e le righe.&lt;br /&gt;
&lt;br /&gt;
'''diff e patch''' &amp;lt;br&amp;gt;&lt;br /&gt;
Il comando ''diff'' può essere utilizzato per mostrare le differenze tra due file testuali. E' utilizzato internamente da git. &amp;lt;br&amp;gt;&lt;br /&gt;
Patch serve per applicare un ''diff file'' a un altro file in modo da aggiornarlo.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 7 marzo 2017 ==&lt;br /&gt;
Una risorsa è ogni elemento utile all'elaborazione.&lt;br /&gt;
* '''Classificazione di risorse'''&lt;br /&gt;
** '''Fisiche e logiche'''&lt;br /&gt;
::Esempi:&lt;br /&gt;
::CPU, memoria e dispositivi sono risorse ''fisiche''.&lt;br /&gt;
::file, strutture dati, critical section sono risorse ''logiche''.&lt;br /&gt;
:* '''Fungibili'''&lt;br /&gt;
::Una risorsa è fungibile se è uguale prendere un bene o un'altro se sono dello stesso tipo. Richiedere un area di memoria o un altra è indifferente se tutte e due soddisfano i requisiti del processo. Richiedere file o dispositivi differenti invece cambia.&lt;br /&gt;
:* '''Assegnazione statica o dinamica'''&lt;br /&gt;
::L'assegnazione statica prevede che una risorsa sia monopolio di un processo.&lt;br /&gt;
:* '''Accetta richieste multiple o solo singole'''&lt;br /&gt;
:: Se può ricevere richieste d'uso da più processi e memorizzare/servire contemporaneamente le richieste oppure no.&lt;br /&gt;
:* '''condivisibili e non condivisibili'''&lt;br /&gt;
:: Un file in lettura è una risorsa condivisibile, più processi possono accedere contemporaneamente. Un file in scrittura invece è una risorsa non condivisibile perché potrebbe capitare una race condition.&lt;br /&gt;
:* '''bloccante e non bloccante'''&lt;br /&gt;
::Se l'accesso alla risorsa implica dei tempi di attesa relativamente lunghi al processo, la risorsa è da considerarsi bloccante. &lt;br /&gt;
:* '''prelasciabile o non prelasciabile'''&lt;br /&gt;
::Il processo può rilasciare in qualsiasi momento la risorsa e permetterne l'uso ad un altro processo? Una stampante e un esempio di rirosa non prelasciabile. &lt;br /&gt;
* '''Deadlock''' &lt;br /&gt;
:Un deadlock può avvenire in presenza di risorse bloccanti, non condivisibili, non prerilasciabili e con una situazione di attesa circolare. &lt;br /&gt;
:* '''detection: grafo di Holt ''' &lt;br /&gt;
::I grafi di allocazione delle risorse, introdotti da Holt nel 1972 sono un buon modo per descrivere lo stato di un sistema e prevedere possibili deadlock. Ogni nodo rappresenta una risorsa o un processo. Di solito le risorse sono rappresentate da quadrati e i processi da cerchi. Un arco da un processo a una risorsa significa che quel processo ha richiesto un'istanza della risorsa. Un arco da una risorsa a un processo vuol dire che un'istanza di quella risorsa è stata assegnata al processo. &amp;lt;br&amp;gt;&lt;br /&gt;
::* ''' teorema dei cicli in grafi di Holt con un solo tipo di risorsa&lt;br /&gt;
:::Se il grafo non contiene cicli allora di sicuro non possono esserci deadlock. &lt;br /&gt;
:::Se il grafo contiene un ciclo allora &amp;lt;u&amp;gt;può&amp;lt;/u&amp;gt; esserci un deadlock.&lt;br /&gt;
:::Se le risorse coinvolte in un ciclo hanno solo un'istanza o ogni loro istanza e coinvolta in un ciclo allora c'è di sicuro un deadlock.&lt;br /&gt;
:::Questo tecnica di permette di analizzare uno stato ''attuale'' di un sistema, ma dobbiamo trovare un modo per prevedere lo stato ''futuro'' del sistema e quindi prevedere i deadlock.&lt;br /&gt;
::* '''processo di riduzione&lt;br /&gt;
:::Per vedere lo stato di un sistema futuro possiamo usare un processo di riduzione del grafo di allocazione delle risorse.&lt;br /&gt;
:::Se una risorsa allocata a un processo può essere data dopo un certo periodo a un altro processo che l'ha richiesta allora si può fare una riduzione. Una riduzione consiste nell'eliminare un arco risorsa-processo perchè quel processo ha finito di utilizzare la risorsa oppure è stata prerilasciata e invertire un arco processo-risorsa di un processo che aveva richiesto la risorsa in un arco risorsa-processo.&lt;br /&gt;
:::Se un grafo può essere ridotto per tutti i suoi processi allora non può esserci deadlock.&lt;br /&gt;
:::Il problema di questo metodo/algoritmo è che nel momento che deallochiamo una risorsa a un processo e più processi avevano richiesto quella risorsa come scegliamo deterministicamente a quale processo assegnare la risorsa? Dovremmo provare tutti i casi per accertarsi che non si crei deadlock, ciò significa fare backtrack e perdere la polinomialità dell'algoritmo.&lt;br /&gt;
::* '''definizione di Knot&lt;br /&gt;
:::Il Knot è una caratteristica di un grafo che si verifica velocemente e permette di determinare possibili deadlock.&lt;br /&gt;
:::Un knot è un insieme di nodi in cui, per ogni nodo del knot, tutti e solo i nodi del knot sono raggiungibili da quel nodo.&lt;br /&gt;
:::Un knot è una condizione sufficiente ma non necessaria per i deadlock. Ciò vuol dire che possono capitare deadlock anche senza la presenta di un knot.&lt;br /&gt;
::* '''teorema dei knot nei grafi di Holt con più tipi di risorse&lt;br /&gt;
:* '''prevention'''&lt;br /&gt;
::Se il sistema cerca di non far verificare almeno una delle condizioni necessarie al deadlock allora questo non si verificherà mai.&lt;br /&gt;
::Questo però comporta anche sottoutilizzo delle risorse del sistema.&lt;br /&gt;
:* '''avoidance''' &lt;br /&gt;
::*'''Condizione di safety'''&lt;br /&gt;
:::Un sistema è in una condizione di safety se esiste una safe sequence.&lt;br /&gt;
:::Una safe sequence è una sequenza di processi dove per ogni richiesta di risorse di un processo questa richiesta può essere soddisfatta con le richieste disponibili al momento più tutte le risorse allocate precedentemente ai precedenti processi della sequenza. &lt;br /&gt;
:::Finchè c'è safety non possono verificarsi deadlock. In una situazione unsafe non è detto che capiti deadlock, ma è possibile.&lt;br /&gt;
::*'''algoritmo del Banchiere (mono e multivaluta).&lt;br /&gt;
:::L'algoritmo del banchiere si basa sul fatto che una banca non dovrebbe mai rimanere senza denaro da prestare ai clienti.&lt;br /&gt;
:::Vengono mantenute varie strutture dati come il capitale iniziale della banca, quanto è disponibile in cassa, quanto è stato prestato ad ogni cliente e quanto si può ancora prestare ad ogni cliente (in base al ''credito'' che ogni cliente ha).&lt;br /&gt;
:::La condizione di safety è data dalla condizione che bisogna mantenere almeno uno dei clienti soddisfacibile cioè che il denaro in cassa sia maggiore del possibile richiesta di prestito di almeno un cliente.&lt;br /&gt;
:::Se questa condizione non si verificasse alloca il banchiere deve aspettare che un cliente restituisca il prestito.&lt;br /&gt;
:::In caso di più valute (yen, dollari, euro, ...) che rappresentano i tipi diversi di risorse del sistema. Tutte le strutture dati aumentano di una dimensione, per esempio il denaro iniziale diventa un vettore.&lt;br /&gt;
::Teorema: il processo di controllo di safety non ha necessita' di backtrack.&lt;br /&gt;
:* '''Tecnica dell'ostrica/struzzo'''&lt;br /&gt;
::Un altro modo per gestire i deadlock e quello di ignorarli completamente.&lt;br /&gt;
::La maggior parte dei sistemi operativi utilizza questo metodo.&lt;br /&gt;
::Soprattuto in sistemi in cui è statisticamente determinato che i deadlock si verificano poco frequentemente i sistemi di prevenzione e avoidance costituirebbero un overhead troppo elevato.&lt;br /&gt;
::Sta allora alle singole applicazioni evitare di crearli.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 9 marzo 2017 ==&lt;br /&gt;
&lt;br /&gt;
* awk&lt;br /&gt;
* phase2: prime fasi del boot&lt;br /&gt;
* python: presentazione del linguaggio ed analisi delle caratteristiche&lt;br /&gt;
&lt;br /&gt;
== Lezione del 14 marzo 2017 ==&lt;br /&gt;
'''Gestione della memoria'''&lt;br /&gt;
* Binding a tempo di esecuzione, compilazione o esecuzione&lt;br /&gt;
* Memory Management Unit (MMU)&lt;br /&gt;
* Allocazione statica o dinamica&lt;br /&gt;
* Allocazione continua o non contigua&lt;br /&gt;
* Metodo delle partizioni fisse&lt;br /&gt;
: Problema della frammentazione interna.&lt;br /&gt;
* Metodo delle partizioni variabili&lt;br /&gt;
:Problema della frammentazione esterna.&lt;br /&gt;
:* Algoritmi di scelta dell'area di allocazione&lt;br /&gt;
:** First fit&lt;br /&gt;
:** Best fit&lt;br /&gt;
:** Worst fit&lt;br /&gt;
:** Next fit&lt;br /&gt;
:* Paginazione&lt;br /&gt;
:* Segmentazione&lt;br /&gt;
:* Memoria virtuale&lt;br /&gt;
&lt;br /&gt;
== Lezione del 16 marzo 2017 ==&lt;br /&gt;
* '''Progetto phase2''': analisi del manuale di uARM&lt;br /&gt;
* '''Python''': funzioni, oop, @memoize, @trace&lt;br /&gt;
&lt;br /&gt;
== Lezione del 21 marzo 2017 ==&lt;br /&gt;
&lt;br /&gt;
La lezione del 21 marzo tace per impegno del docente.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 23 marzo 2017 ==&lt;br /&gt;
'''Memoria virtuale''': &amp;lt;br&amp;gt;&lt;br /&gt;
'''Algoritmi di paginazione:'''&lt;br /&gt;
Quando avviene un page fault serve un algoritmo per trovare la pagina &amp;quot;meno utile&amp;quot; in memoria a cui sostituire la pagina richiesta.&lt;br /&gt;
* '''FIFO''' viene sostituita la pagina che da più tempo è in memoria&lt;br /&gt;
* '''LFU''' ''Least Frequently Used'' contando il numero di richieste alla pagina possiamo calcolare quali sono le pagine più richieste e tenere solo quelle in memoria. Il problema è che una pagina potrebbe essere richiesta molto in una fase iniziale di un processo, quindi avere un rank elevato, però poi non essere più utilizzata. Una soluzione a questo problema è quella di shiftare a destra di 1 i bit del contatore degli utilizzi a intervalli regolari, ponendo questo in un decadimento esponenziale. &lt;br /&gt;
* '''LRU''' ''Least Recently Used'' si sostituisce la pagina che non è stata utilizzata da più tempo. &lt;br /&gt;
* '''Algoritmo ottimo''' algoritmo teorico utile per fare ''benchmark'' degli altri algoritmi. Se conosciamo a priori la ''reference string'' una sequenza che indica quali saranno le richieste delle pagine nel futuro, possiamo sostituire le pagine che per più tempo non saranno richieste, riducendo al minimo i page fault.&lt;br /&gt;
'''Anomalia di Belady'''&lt;br /&gt;
Abbiamo dimostrato che l'algoritmo FIFO all'aumentare della memoria può peggiorare di performance.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 28 marzo 2017 ==&lt;br /&gt;
'''Algoritmo second chance o dell'orologio''' &amp;lt;br&amp;gt;&lt;br /&gt;
'''Linking statico'''&amp;lt;br&amp;gt;&lt;br /&gt;
'''Loading dinamico'''&amp;lt;br&amp;gt;&lt;br /&gt;
'''Memoria secondaria'''&amp;lt;br&amp;gt;&lt;br /&gt;
'''Algoritmi di scheduling delle seek'''&amp;lt;br&amp;gt;&lt;br /&gt;
*'''FIFO'''&lt;br /&gt;
*'''SSF'''&lt;br /&gt;
*'''c-look'''&lt;br /&gt;
&lt;br /&gt;
== Lezione del 30 marzo 2017 ==&lt;br /&gt;
'''Discussione sulle specifiche del progetto'''&amp;lt;br&amp;gt;&lt;br /&gt;
:Creazione pagina [[Specifiche_phase_2_2016/17]]&lt;br /&gt;
'''Python standard library'''&lt;br /&gt;
* libreria sys: Permette di prendere parametri da linea di comando. Si utilizza ''sys.argv''.&lt;br /&gt;
* libreria os: Permette di navigare nella gerarchia di directory con ''os.walk''.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 4 aprile 2017 ==&lt;br /&gt;
'''RAID''' &amp;lt;br&amp;gt;&lt;br /&gt;
(Redundant Array Inexpensive/Independent Disks)&lt;br /&gt;
&lt;br /&gt;
l’idea principale nasce per ottenere maggiori performance (raid 0 ed 1), non maggiore affidabilità (raid 4 e 5).&lt;br /&gt;
&lt;br /&gt;
* RAID0 (Striping): i dischi sono posti in sequenza.Vengono create le stripe, sequenze di bit che sono posti su n dischi sequenzialmente. Lo striping può essere a vari livelli, a livello di bit, di byte, di settore o di blocco. Quello più utilizzato e il livello di blocco. Cioè per scrivere un file che è composto da più blocchi si suddividono i blocchi in più dischi. Attenzione, file piccoli potrebbero sostare su una sola stripe, quindi su un solo disco.&lt;br /&gt;
:Il miglioramento di performance si paga in maggiore faultness. dipende dal costo dei dati salvati.&lt;br /&gt;
&lt;br /&gt;
* RAID1 (mirroring): ogni disco ospita la copia dei dati. doppia velocità in lettura (teoricamente). Stessa velocita di scrittura.&lt;br /&gt;
: Size = ½ readSpeed = N wSpeed = ½ N FT = 1&lt;br /&gt;
* RAID10 = 1+0 striping su 2 mirror.&lt;br /&gt;
&lt;br /&gt;
* RAID 2-3 (deprecated):Agisce sui singoli blocchi i dati vengono salvati in maniera sincrona con meccanismi di parità (hoffman per correzione di errori nel RAID2, ciclica per RAID3).&lt;br /&gt;
&lt;br /&gt;
* RAID4: usa dischi normali, non agisce su singoli blocchi ma su stripe ampi. &lt;br /&gt;
&lt;br /&gt;
parityMap = S0 XOR S1 XOR S2 XOR S3 (in caso di 4+1 dischi).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
nel caso si rompa S4, i dati non avranno problemi. Se si rompesse un qualunque dei primi 4 dischi, basterà effettuare lo XOR degli altri e copiare tali dati su un nuovo HD.&lt;br /&gt;
&lt;br /&gt;
S1 rotto.&lt;br /&gt;
new_S1 = S0 XOR S2 XOR S3 XOR P0123&lt;br /&gt;
Sopporta una sola rottura.&lt;br /&gt;
&lt;br /&gt;
Per aggiornare il disco di parità in caso di modifica a S2 (chiamato ora S2’)&lt;br /&gt;
&lt;br /&gt;
P0123 = P0123 XOR S2 XOR S2’.&lt;br /&gt;
il primo xor toglie il valore da P0123 di S2, il secondo XOR aggiunge il valore S2’.&lt;br /&gt;
in questo modo, aggiorno solo 2 dischi, e uso gli stessi in lettura per ricalcolare la parità.&lt;br /&gt;
&lt;br /&gt;
Le parità ciclano su tutti i dischi. Esistono controller che effettuano RAID5, oppure e’ possibile ottenere le funzionalità raid via sistema operativo.&lt;br /&gt;
&lt;br /&gt;
* RAID5: &lt;br /&gt;
:Senza fault&lt;br /&gt;
:Size = N-1/N, rspeed=(N-1), wspeed=(N-2), FT=1&lt;br /&gt;
&lt;br /&gt;
I dischi RAID soffrono alquanto in caso di terremoti.&lt;br /&gt;
&lt;br /&gt;
'''File System:''' &amp;lt;br&amp;gt;&lt;br /&gt;
Servizio per dare una interfaccia più comoda e generale per l’accesso alla memoria secondaria.&lt;br /&gt;
&lt;br /&gt;
Nasce come informatizzazione delle pratiche di ufficio. le pratiche sono salvate in folders, fascicoli, poste poi nelle directories. come visualizzo questa struttura per l’utente? E ad i programmatori? come viene implementato?&lt;br /&gt;
&lt;br /&gt;
Nasce l’idea di “aprire” i file. Nell'accedere da un programma ad il file-system (operazione non banale) si preferisce chiamare un’operazione di apertura, e utilizzare un descrittore al file per agire sul file richiesto.&lt;br /&gt;
&lt;br /&gt;
La struttura di file/folder/directories possono inoltre supportare le astrazioni di condivisione e proprieta’.&lt;br /&gt;
Tutti i controlli sono effettuati sono in fase di apertura (in modo da migliorare le performance). &lt;br /&gt;
&lt;br /&gt;
Quali attributi caratterizzano un file?&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Name,&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Type,&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Location,&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Size,&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Ownership,&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Protection,&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Time stamp&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
UNIX opta per una scelta minimalista. Tutti i file sono stringhe di byte.&lt;br /&gt;
AFS (Apple) da un tag di tipo e di creator per ogni file&lt;br /&gt;
&lt;br /&gt;
maggiore e’ il numero di tipi di file, maggiore supporto per essi, ma piu’ codice nel kernel.&lt;br /&gt;
I suffissi possono indicare ad altri programmi il tipo.&lt;br /&gt;
&lt;br /&gt;
Semantica della coerenza vs Semantica delle sessioni (Andrew File System)&lt;br /&gt;
&lt;br /&gt;
Per quali motivo vengono create le partizioni?&lt;br /&gt;
/var andrebbe in una nuova partizione per un fattore di sicurezza.&lt;br /&gt;
&lt;br /&gt;
Cosa c’e’ effettivamente sul disco? Una sequenza di blocchi, il cui primo (boot block) ha spazio per il bootloader e la tabella delle partizioni (MBR), con 4 elementi. 4 record che indicano dove ogni partizione inizia e finisce. Si possono usare partizioni estese, usando le prime 3 e nella quarta tutto il resto.&lt;br /&gt;
&lt;br /&gt;
Ogni partizione ha all'inizio un BootRecord.&lt;br /&gt;
L’ultima partizione contiene nel BootRecord le altre partizioni. &lt;br /&gt;
GBT consente di avere un numero arbitrario di partizioni.&lt;br /&gt;
&lt;br /&gt;
Raizer FS,&lt;br /&gt;
&lt;br /&gt;
Read only file systems.&lt;br /&gt;
&lt;br /&gt;
Initrd, ISO9660.&lt;br /&gt;
&lt;br /&gt;
Quando si costruisce l’immagine, in realtà si sta creando il file-system ISO9660.&lt;br /&gt;
&lt;br /&gt;
Come gestire invece file che possono cambiare?&lt;br /&gt;
Allocazione indicizzata (a lista): creo un (o vari) blocco/i degli indici che punta ad i blocchi dati.&lt;br /&gt;
l’accesso diretto cresce come il logaritmo dell’ampiezza. &lt;br /&gt;
Allocazione concatenata (ad albero): (in fondo al blocco indico il successivo). Gestisce bene la dinamicità. Non piace perché’ non é per nulla efficiente l’accesso diretto. &lt;br /&gt;
&lt;br /&gt;
Quali allocazioni usano i filesystem reali oggi?&lt;br /&gt;
&lt;br /&gt;
fat: concatenata. I blocchi tengono solo i dati, esiste una file allocation table (vettore) che indica il prossimo blocco. I puntatori sono tutti vicini e quindi ponendo il vettore in cache, abbiamo disponibili diversi puntatori.&lt;br /&gt;
&lt;br /&gt;
BerkleyFastFileSystem (progenitore dei EXT4…) usano allocazione indicizzata. Nasce per garantire performance ad i file piccoli. Nel descrittore del file (iNode) avra’ N (13 attualmente) puntatori. Uno, il numero 10, ad i blocchi, uno (11) indiretto alla tabella, uno (12) come duplice indiretto e il 13esimo come triplice indiretto.&lt;br /&gt;
&lt;br /&gt;
I file hanno quindi una lunghezza massima. EX4 pero’ risolve indicizzando non i singoli blocchi ma le serie.&lt;br /&gt;
&lt;br /&gt;
Gestione spazio libero&lt;br /&gt;
&lt;br /&gt;
nella struttura indicizzata serve una struttura per tenere conto degli spazi liberi. Si puo’ usare una bitmap per tenere la contabilità degli spazi liberi e quelli utilizzati.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 6 aprile 2017 ==&lt;br /&gt;
&lt;br /&gt;
File System. Ext2 e VFAT. Sicurezza, principi generali.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 11 aprile 2017 ==&lt;br /&gt;
&lt;br /&gt;
Sicurezza: crittografia (chiave pubblica/chiave segreta) autorizzazione autenticazione firma elettronica.&lt;br /&gt;
Virus Worm Cavalli di Troia.&lt;br /&gt;
&lt;br /&gt;
Attacchi buffer overflow e toctou.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 20 aprile 2017 ==&lt;br /&gt;
&lt;br /&gt;
(tace per impossibilita' del docente)&lt;br /&gt;
&lt;br /&gt;
== Lezione del 27 aprile 2017 ==&lt;br /&gt;
&lt;br /&gt;
P2test (prestest2).&lt;br /&gt;
&lt;br /&gt;
DIscussione specifiche.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 2 maggio 2017 ==&lt;br /&gt;
== Lezione del 4 maggio 2017 ==&lt;br /&gt;
* Discussione su proposta da parte degli studenti di modifiche al test di progetto&lt;br /&gt;
* Soluzione all'esercizio C1 del 2014-06-16 (Monitor)&lt;br /&gt;
* Soluzione all'esercizio 1 del [[http://www.cs.unibo.it/~renzo/so/pratiche/2015.05.29.pdf 2016-05-20]] ( C )&lt;br /&gt;
* Soluzione all'esercizio 2 del 2008-06-13 (Dimostrazione algoritmo di rimpiazzamento a stack)&lt;br /&gt;
* Soluzione all'esercizio 1 del [[http://www.cs.unibo.it/~renzo/so/compiti/2013.07.19.tot.pdf 2013-07-19]] (Monitor)&lt;br /&gt;
&lt;br /&gt;
== Lezione del 9 maggio 2017 ==&lt;br /&gt;
*Esercizio 2 prova teorica 2014.09.24&lt;br /&gt;
*Esercizio g.1 prova teorica 2013.07.19&lt;br /&gt;
*Esercizio c1 e c2 prova teorica 2015.01.20(2014.01.20)&lt;br /&gt;
*Esercizio c2 2013.05.30&lt;br /&gt;
&lt;br /&gt;
== Lezione del 11 maggio 2017 ==&lt;br /&gt;
== Lezione del 16 maggio 2017 ==&lt;br /&gt;
* Chiarimenti su svolgimento esame&lt;br /&gt;
* Commenti su recenti eventi (WannaCry)&lt;br /&gt;
* Visione di programma python per costruzione di stringhe palindrome con monitor&lt;br /&gt;
* Correzione esercizio g2 prova teorica 20170209&lt;br /&gt;
* Correzione esercizio 1 [[Prova pratica 2016.09.13]]&lt;br /&gt;
* Esercizio c2 prova teorica 20140122&lt;br /&gt;
&lt;br /&gt;
== Lezione del 18 maggio 2017 ==&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2016.09.13&amp;diff=1883</id>
		<title>Prova pratica 2016.09.13</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2016.09.13&amp;diff=1883"/>
		<updated>2017-05-16T13:24:53Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: /* Proposta di FedericoB */ Soluzione corretta con lock Posix funzionanti&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/pratiche/2016.09.13.pdf Link al testo] (è sbagliata la data nel titolo ma il testo è quello giusto)&lt;br /&gt;
==Esercizio 1==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Scrivere un programma che mostri il funzionamento del file locking.&lt;br /&gt;
In particolare il programma cplk avra’ due argomenti che I pathname di due file. Il contenuto del primo file deve&lt;br /&gt;
essere copiato nel secondo usando la system call sendfile.&lt;br /&gt;
Prima di iniziare la copia occorre usare la system call fcntl per bloccare il file di output (write lock).&lt;br /&gt;
Completata la copia occorre chiamare nuovamente la fcntl per sbloccare il file.&lt;br /&gt;
Inserire un ritardo (sleep) con valore casuale da 1 a 10 secondi sia prima sia dopo aver sbloccato il file e&lt;br /&gt;
stampe di controllo che indichino quando il file viene bloccato e sbloccato.&lt;br /&gt;
In questo modo lanciando piu’ esecuzioni concorrenti con output sullo stesso file deve apparire che l’accesso in&lt;br /&gt;
scrittura e’ mutualmente esclusivo.&lt;br /&gt;
Es. di output:&lt;br /&gt;
$ cplk cplk.c out&lt;br /&gt;
locked attende da 1 a 10 sec&lt;br /&gt;
unlocked attende da 1 a 10 sec&lt;br /&gt;
$&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define _GNU_SOURCE&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/sendfile.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;time.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]) {&lt;br /&gt;
    if (argc == 3) {&lt;br /&gt;
        //get file descriptors&lt;br /&gt;
        int fdFile2 = open(argv[2], O_WRONLY);&lt;br /&gt;
        int fdFile1 = open(argv[1], O_RDONLY);&lt;br /&gt;
        //create structure for file locking&lt;br /&gt;
        struct flock flock;&lt;br /&gt;
        struct stat stat_buf;&lt;br /&gt;
        fstat(fdFile1, &amp;amp;stat_buf);&lt;br /&gt;
        //type = write lock&lt;br /&gt;
        flock.l_type = F_WRLCK;&lt;br /&gt;
        flock.l_start = 0;&lt;br /&gt;
        //set l_start relative of beginning of file&lt;br /&gt;
        flock.l_whence = SEEK_SET;&lt;br /&gt;
        flock.l_len = stat_buf.st_size;&lt;br /&gt;
        flock.l_pid = getpid();&lt;br /&gt;
        fcntl(fdFile2, F_SETLKW, &amp;amp;flock);&lt;br /&gt;
        //print &amp;quot;locked&amp;quot;&lt;br /&gt;
        printf(&amp;quot;locked\n&amp;quot;);&lt;br /&gt;
        //use sendfile for copy, use fdFile1 offset and copy all file 1&lt;br /&gt;
        sendfile(fdFile2, fdFile1, NULL, stat_buf.st_size);&lt;br /&gt;
        //wait for a random time between 1 and 10 seconds&lt;br /&gt;
        srand(time(NULL));&lt;br /&gt;
        int r = rand() % 10 + 1;&lt;br /&gt;
        sleep(r);&lt;br /&gt;
        //unclock second file&lt;br /&gt;
        flock.l_type = F_UNLCK;&lt;br /&gt;
        fcntl(fdFile2, F_SETLKW, &amp;amp;flock);&lt;br /&gt;
        //print &amp;quot;unlocked&amp;quot;&lt;br /&gt;
        printf(&amp;quot;unlocked\n&amp;quot;);&lt;br /&gt;
        //wait for a random time between 1 and 10 second&lt;br /&gt;
        r = rand() % 10 + 1;&lt;br /&gt;
        sleep(r);&lt;br /&gt;
        return 0;&lt;br /&gt;
    } else {&lt;br /&gt;
        printf(&amp;quot;wrong number of arguments!&amp;quot;);&lt;br /&gt;
        return -1;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prove_pratiche&amp;diff=1882</id>
		<title>Prove pratiche</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prove_pratiche&amp;diff=1882"/>
		<updated>2017-05-16T12:33:39Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Aggiunto link alla Prova pratica 2016.09.13&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;''Qui troverete alcune prove teoriche pratiche (laboratorio), in modo da poter discutere sull'elaborato''&lt;br /&gt;
&lt;br /&gt;
''Le prove sono svolte da studenti e servono come base per la discussione. Le soluzioni possono essere errate''&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2017.01.17]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2016.05.31]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2016.09.13]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2015.05.20]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2015.01.21]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2014.09.25]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2014.07.17]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Pratica 2014.07.02]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Pratica 2014.06.17]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2014.05.29]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Pratica 2014.02.20]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2014.01.23]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.09.13]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.07.18]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.06.21]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.05.29]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.02.15]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.01.25]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2012.09.19]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2012.07.17]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2012.06.20]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Pratica 2012.05.30]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2011.09.12]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Pratica 2011.06.22]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2011.05.30]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2011.01.19]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2010.07.19]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica_2010.07.12]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2010.02.03]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2009.09.23]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2009.06.23]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2009.02.12]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2009.01.15]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2005.02.10]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2002.01.24]]&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2016.09.13&amp;diff=1880</id>
		<title>Prova pratica 2016.09.13</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2016.09.13&amp;diff=1880"/>
		<updated>2017-05-16T12:31:11Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Creata pagina con soluzione da sistemare&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/pratiche/2016.09.13.pdf Link al testo] (è sbagliata la data nel titolo ma il testo è quello giusto)&lt;br /&gt;
==Esercizio 1==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Scrivere un programma che mostri il funzionamento del file locking.&lt;br /&gt;
In particolare il programma cplk avra’ due argomenti che I pathname di due file. Il contenuto del primo file deve&lt;br /&gt;
essere copiato nel secondo usando la system call sendfile.&lt;br /&gt;
Prima di iniziare la copia occorre usare la system call fcntl per bloccare il file di output (write lock).&lt;br /&gt;
Completata la copia occorre chiamare nuovamente la fcntl per sbloccare il file.&lt;br /&gt;
Inserire un ritardo (sleep) con valore casuale da 1 a 10 secondi sia prima sia dopo aver sbloccato il file e&lt;br /&gt;
stampe di controllo che indichino quando il file viene bloccato e sbloccato.&lt;br /&gt;
In questo modo lanciando piu’ esecuzioni concorrenti con output sullo stesso file deve apparire che l’accesso in&lt;br /&gt;
scrittura e’ mutualmente esclusivo.&lt;br /&gt;
Es. di output:&lt;br /&gt;
$ cplk cplk.c out&lt;br /&gt;
locked attende da 1 a 10 sec&lt;br /&gt;
unlocked attende da 1 a 10 sec&lt;br /&gt;
$&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Proposta di FedericoB===&lt;br /&gt;
Non sono sicuro che funzioni. --[[User:FedericoB|FedericoB]] ([[User talk:FedericoB|talk]]) 14:31, 16 May 2017 (CEST)&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define _GNU_SOURCE&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/sendfile.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;time.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]) {&lt;br /&gt;
    if (argc == 3) {&lt;br /&gt;
        //get file descriptors&lt;br /&gt;
        int fdFile2 = open(argv[2], O_WRONLY);&lt;br /&gt;
        int fdFile1 = open(argv[1], O_RDONLY);&lt;br /&gt;
        //create structure for file locking&lt;br /&gt;
        struct flock flock;&lt;br /&gt;
        struct stat stat_buf;&lt;br /&gt;
        fstat(fdFile1, &amp;amp;stat_buf);&lt;br /&gt;
        //type = write lock&lt;br /&gt;
        flock.l_type = F_WRLCK;&lt;br /&gt;
        flock.l_start = 0;&lt;br /&gt;
        //set l_start relative of beginning of file&lt;br /&gt;
        flock.l_whence = SEEK_SET;&lt;br /&gt;
        flock.l_len = stat_buf.st_size;&lt;br /&gt;
        flock.l_pid = getpid();&lt;br /&gt;
        fcntl(fdFile2, F_OFD_SETLKW, &amp;amp;flock);&lt;br /&gt;
        //print &amp;quot;locked&amp;quot;&lt;br /&gt;
        printf(&amp;quot;locked\n&amp;quot;);&lt;br /&gt;
        //use sendfile for copy, use fdFile1 offset and copy all file 1&lt;br /&gt;
        sendfile(fdFile2, fdFile1, NULL, stat_buf.st_size);&lt;br /&gt;
        //wait for a random time between 1 and 10 seconds&lt;br /&gt;
        srand(time(NULL));&lt;br /&gt;
        int r = rand() % 10 + 1;&lt;br /&gt;
        sleep(r);&lt;br /&gt;
        //unclock second file&lt;br /&gt;
        flock.l_type = F_UNLCK;&lt;br /&gt;
        fcntl(fdFile2, F_OFD_SETLKW, &amp;amp;flock);&lt;br /&gt;
        //print &amp;quot;unlocked&amp;quot;&lt;br /&gt;
        printf(&amp;quot;unlocked\n&amp;quot;);&lt;br /&gt;
        //wait for a random time between 1 and 10 second&lt;br /&gt;
        r = rand() % 10 + 1;&lt;br /&gt;
        sleep(r);&lt;br /&gt;
        return 0;&lt;br /&gt;
    } else {&lt;br /&gt;
        printf(&amp;quot;wrong number of arguments!&amp;quot;);&lt;br /&gt;
        return -1;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2017.01.17&amp;diff=1878</id>
		<title>Prova pratica 2017.01.17</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2017.01.17&amp;diff=1878"/>
		<updated>2017-05-15T14:55:59Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Aggiunti testi esercizi&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/pratiche/2017.01.17.pdf link al testo]&lt;br /&gt;
==Esercizio 1==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Scrivere un programma che conti I file ordinari e le directory presenti nel sottoalbero della directory passata come&lt;br /&gt;
parametro (o della directory corrente se non viene passato alcun parametro).&lt;br /&gt;
Ogni file o directory deve venir contato una sola volta anche se e’ presente con diversi nomi a causa dei link fisici.&lt;br /&gt;
Ogni altro tipo di file che non sia file ordinario o directory (e.g. socket, named pipe, file speciali, link simbolici)&lt;br /&gt;
devono essere ignorati.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione del prof===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dirent.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct fileelem {&lt;br /&gt;
	dev_t dev; &lt;br /&gt;
	ino_t ino;&lt;br /&gt;
	struct fileelem *next;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
int addfile(struct fileelem **head, dev_t dev, ino_t ino) {&lt;br /&gt;
	if (*head == NULL) {&lt;br /&gt;
		*head = malloc(sizeof(struct fileelem));&lt;br /&gt;
		(*head)-&amp;gt;dev = dev;&lt;br /&gt;
		(*head)-&amp;gt;ino = ino;&lt;br /&gt;
		(*head)-&amp;gt;next = NULL;&lt;br /&gt;
		return 1;&lt;br /&gt;
	} else if ((*head)-&amp;gt;dev == dev &amp;amp;&amp;amp; (*head)-&amp;gt;ino == ino) {&lt;br /&gt;
		return 0;&lt;br /&gt;
	} else {&lt;br /&gt;
		return addfile(&amp;amp;((*head)-&amp;gt;next), dev, ino);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int filt(const struct dirent *elem) {&lt;br /&gt;
	if (strcmp(elem-&amp;gt;d_name,&amp;quot;.&amp;quot;) == 0 ||&lt;br /&gt;
			strcmp(elem-&amp;gt;d_name,&amp;quot;..&amp;quot;) == 0)&lt;br /&gt;
		return 0;&lt;br /&gt;
	else {&lt;br /&gt;
		return 1;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int recscan(char *path, struct fileelem **head) {&lt;br /&gt;
	struct dirent **list;&lt;br /&gt;
	int count = 0;&lt;br /&gt;
	int n = scandir(path, &amp;amp;list, filt, alphasort);&lt;br /&gt;
	int i;&lt;br /&gt;
	for (i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
		struct stat buf;&lt;br /&gt;
		char *filepath;&lt;br /&gt;
		asprintf(&amp;amp;filepath,&amp;quot;%s/%s&amp;quot;,path,list[i]-&amp;gt;d_name);&lt;br /&gt;
		stat(filepath, &amp;amp;buf);&lt;br /&gt;
		switch (buf.st_mode &amp;amp; S_IFMT) {&lt;br /&gt;
			case S_IFREG:&lt;br /&gt;
				count += addfile(head, buf.st_dev, buf.st_ino);&lt;br /&gt;
				printf(&amp;quot;file %s %ld %ld ... %d\n&amp;quot;,filepath,buf.st_dev,buf.st_ino,count);&lt;br /&gt;
				break;&lt;br /&gt;
			case S_IFDIR:&lt;br /&gt;
				count = count + recscan(filepath, head);&lt;br /&gt;
				break;&lt;br /&gt;
		}&lt;br /&gt;
		free(filepath);&lt;br /&gt;
		free(list[i]);&lt;br /&gt;
	}&lt;br /&gt;
	free(list);&lt;br /&gt;
	return count;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[]) {&lt;br /&gt;
	struct fileelem *head = NULL;&lt;br /&gt;
	int result;&lt;br /&gt;
	if (argc &amp;gt; 1)&lt;br /&gt;
		chdir(argv[1]);&lt;br /&gt;
	result = recscan(&amp;quot;.&amp;quot;, &amp;amp;head);&lt;br /&gt;
	printf(&amp;quot;Num of independent files: %d\n&amp;quot;, result);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Esercizio 3:==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Scrivere uno script che faccia il merge dei contenuti due directory:&lt;br /&gt;
merge a b c&lt;br /&gt;
Tutti i file (non le directory) esistenti in a e in b devono essere spostati in c. Nel caso due file con lo stesso nome&lt;br /&gt;
compaiano sia in a sia in b, il file spostato in c deve essere quello piu' recentementente modificato, l’altro deve&lt;br /&gt;
rimanere nella directory di origine.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Python===&lt;br /&gt;
====Soluzione di Ossigeno====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=Python&amp;gt;&lt;br /&gt;
def merge():  # dati due path di due directory, fa la merge in una terza directory&lt;br /&gt;
    # 1=pathprimo 2=pathsecondo 3=pathdestinazione&lt;br /&gt;
    if (len(sys.argv) &amp;gt; 3):&lt;br /&gt;
        for d, _, fl in os.walk(sys.argv[1]):&lt;br /&gt;
            for f in fl:&lt;br /&gt;
                path = os.path.join(d, f)&lt;br /&gt;
                if (os.path.isfile(path)):  # e' un file e non una directory allora copio tutto&lt;br /&gt;
                    shutil.copy2(path, sys.argv[3])&lt;br /&gt;
        for d, _, fl in os.walk(sys.argv[2]):&lt;br /&gt;
            for f in fl:&lt;br /&gt;
                path = os.path.join(d, f)&lt;br /&gt;
                if (os.path.isfile(path)):  # e' un file e non una directory in path2&lt;br /&gt;
                    esistenza = os.path.join(sys.argv[3], f)&lt;br /&gt;
                    if (not os.path.isfile(esistenza)):  # se non esiste un file con quel nome&lt;br /&gt;
                        shutil.copy2(path, sys.argv[3])&lt;br /&gt;
                    else:&lt;br /&gt;
                        if (os.stat(path).st_mtime &amp;gt; os.stat(&lt;br /&gt;
                                esistenza).st_mtime):  # e' piu recente tempo del file in path2 rispetto a path3&lt;br /&gt;
                            shutil.copy2(path, sys.argv[3])&lt;br /&gt;
    else:&lt;br /&gt;
        print(&amp;quot;Mancano degli argomenti&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
===Bash===&lt;br /&gt;
====Soluzione di Gabriele Calarota====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=Bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
if [ $# -lt 3 ] ; then&lt;br /&gt;
	echo &amp;quot;ERROR: example merge a b c&amp;quot;&lt;br /&gt;
	exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
a=$1&lt;br /&gt;
b=$2&lt;br /&gt;
c=$3&lt;br /&gt;
tmp_file=file_tmp&lt;br /&gt;
&lt;br /&gt;
touch $tmp_file&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
find $a -type f -print0 | xargs -0 basename -a &amp;gt;&amp;gt; $tmp_file&lt;br /&gt;
find $b -type f -print0 | xargs -0 basename -a &amp;gt;&amp;gt; $tmp_file&lt;br /&gt;
&lt;br /&gt;
#univoci&lt;br /&gt;
#echo &amp;quot;UNIQ&amp;quot;&lt;br /&gt;
sort $tmp_file | uniq -u | while read line&lt;br /&gt;
do&lt;br /&gt;
	&lt;br /&gt;
	#now $line contains the basename of the file&lt;br /&gt;
	ls &amp;quot;$a/$line&amp;quot; &amp;amp;&amp;gt;/dev/null &amp;amp;&amp;amp; mv -v &amp;quot;$a/$line&amp;quot; &amp;quot;$c/$line&amp;quot;&lt;br /&gt;
	ls &amp;quot;$b/$line&amp;quot; &amp;amp;&amp;gt;/dev/null &amp;amp;&amp;amp; mv -v &amp;quot;$b/$line&amp;quot; &amp;quot;$c/$line&amp;quot;&lt;br /&gt;
done&lt;br /&gt;
&lt;br /&gt;
#echo &amp;quot;DOUBLE&amp;quot;&lt;br /&gt;
sort $tmp_file | uniq -d | while read line&lt;br /&gt;
do&lt;br /&gt;
	#now $line contains the basename of both of the filename&lt;br /&gt;
	file1=&amp;quot;$(stat -c %Y $a/$line)&amp;quot;&lt;br /&gt;
	file2=&amp;quot;$(stat -c %Y $b/$line)&amp;quot;&lt;br /&gt;
	if [ $file1 -lt $file2 ] ; then&lt;br /&gt;
		#echo &amp;quot;$line chose from $b&amp;quot;&lt;br /&gt;
		mv -v &amp;quot;$b/$line&amp;quot; &amp;quot;$c/$line&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		#echo &amp;quot;$line chose from $a&amp;quot;&lt;br /&gt;
		mv -v &amp;quot;$a/$line&amp;quot; &amp;quot;$c/$line&amp;quot;&lt;br /&gt;
	fi&lt;br /&gt;
done&lt;br /&gt;
&lt;br /&gt;
rm $tmp_file  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
--[[User:GabrieleCalarota|GabrieleCalarota]] ([[User talk:GabrieleCalarota|talk]]) 12:17, 15 May 2017 (CEST)&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2015.05.20&amp;diff=1873</id>
		<title>Prova pratica 2015.05.20</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2015.05.20&amp;diff=1873"/>
		<updated>2017-05-12T08:10:36Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Aggiunta soluzione esercizio 2&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/pratiche/2015.05.29.pdf Link al testo]&lt;br /&gt;
&lt;br /&gt;
==Esercizio 1==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Scrivere un programma catsig che copi lo standard input nello standard output (come il comando cat senza&lt;br /&gt;
parametri, per intenderci) e che stampi la scritta “ho ricevuto un segnale” quando riceve un segnale SIGUSR1.&lt;br /&gt;
La particolarita' di questo programma e' che per la gestione dei segnali deve usare la chiamata di sistema&lt;br /&gt;
signalfd (e non la signal o la sigaction)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Soluzione di FedericoB===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/signalfd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;poll.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]) {&lt;br /&gt;
    //declare a mask for signal&lt;br /&gt;
    sigset_t mask;&lt;br /&gt;
    //initialize signal mask&lt;br /&gt;
    sigemptyset(&amp;amp;mask);&lt;br /&gt;
    sigaddset(&amp;amp;mask, SIGUSR1);&lt;br /&gt;
    //create a file descriptor for signal USR1&lt;br /&gt;
    int signalFileDescriptor = signalfd(-1, &amp;amp;mask, 0);&lt;br /&gt;
    //remove default handler for SIGURS1&lt;br /&gt;
    sigprocmask(SIG_BLOCK, &amp;amp;mask, NULL);&lt;br /&gt;
    struct pollfd pollstruct[2];&lt;br /&gt;
    pollstruct[0].fd = signalFileDescriptor;&lt;br /&gt;
    pollstruct[0].events = POLLIN;&lt;br /&gt;
    pollstruct[1].fd = STDIN_FILENO;&lt;br /&gt;
    pollstruct[1].events = POLLIN;&lt;br /&gt;
    while (1) {&lt;br /&gt;
        poll(pollstruct, 2, -1);&lt;br /&gt;
        //is signal USR1 is received&lt;br /&gt;
        if (pollstruct[0].revents &amp;amp; POLLIN) {&lt;br /&gt;
            struct signalfd_siginfo siginfo;&lt;br /&gt;
            //read signal information from signal file descriptor&lt;br /&gt;
            read(signalFileDescriptor, &amp;amp;siginfo, sizeof(struct signalfd_siginfo));&lt;br /&gt;
            printf(&amp;quot;ho ricevuto un segnale\n&amp;quot;);&lt;br /&gt;
            //set event as handled&lt;br /&gt;
            pollstruct[0].revents = 0;&lt;br /&gt;
        }&lt;br /&gt;
        //if there is something to read in standard input&lt;br /&gt;
        if (pollstruct[1].revents &amp;amp; POLLIN) {&lt;br /&gt;
            char character;&lt;br /&gt;
            //read one character until there is a character to read&lt;br /&gt;
            while (read(fileno(stdin), &amp;amp;character, 1) &amp;gt; 0) {&lt;br /&gt;
                //print that character to standard output&lt;br /&gt;
                write(fileno(stdout), &amp;amp;character, 1);&lt;br /&gt;
            }&lt;br /&gt;
            //set event as handled&lt;br /&gt;
            pollstruct[1].revents = 0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Esercizio 2==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Ora, prendendospunto dall'esercizio 1 occorre scrivere due programmi:&lt;br /&gt;
sigsend e sigreceive.&lt;br /&gt;
Sigreceive non ha come parametri. Per prima cosa stampa il suo pid poi entra in un ciclo in cui:&lt;br /&gt;
• aspetta un segnale SIGUSR1&lt;br /&gt;
• stampa su stdout (visualizza) il file con nome /tmp/giroXXXX (dove XXXX e' il proprio pid)&lt;br /&gt;
• cancella il file /tmp/giroXXXX&lt;br /&gt;
• Manda un segnale SIGUSR1 al processo dal quale lo ha ricevuto nella operazione due righe sopra&lt;br /&gt;
questa.&lt;br /&gt;
Sigreceive termina quando riceve in SIGUSR2.&lt;br /&gt;
Sigsend ha come parametro il pid del processo ricevente. Per ogni riga posta in input da stdin fa le seguenti azioni:&lt;br /&gt;
• crea un file /tmp/giroXXXX (dove XXXX e' il pid del processo ricevente), vi scrive la riga letta e lo chiude.&lt;br /&gt;
• Spedisce un segnale SIGUSR1 al ricevente&lt;br /&gt;
• Aspetta dallo stesso un SIGUSR1.&lt;br /&gt;
Quando lo stdin termina (^D da tastiera) manda un SIGUSR2 al ricevente.&lt;br /&gt;
All'attivazione scrive nel file il proprio pid e aspetta un segnale sigusr1.Quando riceve il segnale legge&lt;br /&gt;
Anche in questo esercizio occorre usare solo la signalfd e non la signal o la sigaction.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
===Soluzione di FedericoB===&lt;br /&gt;
sigreceive&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
#define _GNU_SOURCE&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/signalfd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;poll.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]) {&lt;br /&gt;
    int pid = getpid();&lt;br /&gt;
    printf(&amp;quot;pid: %d\n&amp;quot;,pid);&lt;br /&gt;
    char* path;&lt;br /&gt;
    //create path with format giroXXXX&lt;br /&gt;
    asprintf(&amp;amp;path, &amp;quot;/tmp/giro%d&amp;quot;, pid);&lt;br /&gt;
    sigset_t mask;&lt;br /&gt;
    //initialize signal mask&lt;br /&gt;
    sigemptyset(&amp;amp;mask);&lt;br /&gt;
    sigaddset(&amp;amp;mask, SIGUSR1);&lt;br /&gt;
    sigaddset(&amp;amp;mask, SIGUSR2);&lt;br /&gt;
    //block default handler for SIGUSR1 and SIGUSR2&lt;br /&gt;
    sigprocmask(SIG_BLOCK, &amp;amp;mask, NULL);&lt;br /&gt;
    int fd;&lt;br /&gt;
    //get a file descript for signal SIGUSR2 and SIGUS2&lt;br /&gt;
    fd = signalfd(-1,&amp;amp;mask,0);&lt;br /&gt;
    //create a poll structur for waiting signal USR1 and USR2&lt;br /&gt;
    struct pollfd polls = {.fd = fd,.events=POLLIN};&lt;br /&gt;
    //bool for checking is a sigsr2 arrived&lt;br /&gt;
    int sigusr2 = 0;&lt;br /&gt;
    while (!sigusr2) {&lt;br /&gt;
        //wait for signal&lt;br /&gt;
        poll(&amp;amp;polls,1,-1);&lt;br /&gt;
        //if a signal arrived&lt;br /&gt;
        if (polls.revents &amp;amp; POLLIN) {&lt;br /&gt;
            struct signalfd_siginfo siginfo;&lt;br /&gt;
            //read signal information from signal file descriptor&lt;br /&gt;
            read(fd, &amp;amp;siginfo, sizeof(struct signalfd_siginfo));&lt;br /&gt;
            if (siginfo.ssi_signo == SIGUSR1) {&lt;br /&gt;
                FILE* file = fopen(path, &amp;quot;r&amp;quot;);&lt;br /&gt;
                if (file!=NULL) {&lt;br /&gt;
                    char* buffer = malloc(50);&lt;br /&gt;
                    //read from file&lt;br /&gt;
                    fgets(buffer, 50, file);&lt;br /&gt;
                    //print to standard output&lt;br /&gt;
                    printf(&amp;quot;%s&amp;quot;, buffer);&lt;br /&gt;
                    free(buffer);&lt;br /&gt;
                    fclose(file);&lt;br /&gt;
                    //remove file&lt;br /&gt;
                    remove(path);&lt;br /&gt;
                    //send a USR2 signal to sender of signal&lt;br /&gt;
                    kill(siginfo.ssi_pid, SIGUSR2);&lt;br /&gt;
                } else {&lt;br /&gt;
                    printf(&amp;quot;Error opening the file %s \n&amp;quot;,path);&lt;br /&gt;
                }&lt;br /&gt;
            } else if (siginfo.ssi_signo == SIGUSR2 ) {&lt;br /&gt;
                sigusr2 = 1;&lt;br /&gt;
            }&lt;br /&gt;
            polls.revents = 0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
sigsend&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
#define _GNU_SOURCE&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/signalfd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]) {&lt;br /&gt;
    if (argc == 2) {&lt;br /&gt;
        //get pid from program arguments&lt;br /&gt;
        int receiverPid = atoi(argv[1]);&lt;br /&gt;
        char* path;&lt;br /&gt;
        //create path with format giroXXXX&lt;br /&gt;
        asprintf(&amp;amp;path, &amp;quot;/tmp/giro%d&amp;quot;, receiverPid);&lt;br /&gt;
        char* buffer;&lt;br /&gt;
        do {&lt;br /&gt;
            buffer = malloc(200);&lt;br /&gt;
            //read standard input&lt;br /&gt;
            buffer = fgets(buffer, 200, stdin);&lt;br /&gt;
            //fgets return NULL on ^D&lt;br /&gt;
            if (buffer != NULL) {&lt;br /&gt;
                FILE* file = fopen(path, &amp;quot;w&amp;quot;);&lt;br /&gt;
                if (file != NULL) {&lt;br /&gt;
                    fputs(buffer, file);&lt;br /&gt;
                    free(buffer);&lt;br /&gt;
                    fclose(file);&lt;br /&gt;
                    //send signal SIGURS1 to receiver&lt;br /&gt;
                    kill(receiverPid, SIGUSR1);&lt;br /&gt;
                    sigset_t mask;&lt;br /&gt;
                    //initialize signal mask&lt;br /&gt;
                    sigemptyset(&amp;amp;mask);&lt;br /&gt;
                    sigaddset(&amp;amp;mask, SIGUSR2);&lt;br /&gt;
                    //block default handler for SIGUSR2&lt;br /&gt;
                    sigprocmask(SIG_BLOCK, &amp;amp;mask, NULL);&lt;br /&gt;
                    int sig;&lt;br /&gt;
                    //wait for a SIGURS2&lt;br /&gt;
                    sigwait(&amp;amp;mask, &amp;amp;sig);&lt;br /&gt;
                } else {&lt;br /&gt;
                    printf(&amp;quot;Error opening the file %s \n&amp;quot;, path);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        } while (buffer != NULL);&lt;br /&gt;
        //send a SIGUSR2 to receiver&lt;br /&gt;
        kill(receiverPid, SIGUSR2);&lt;br /&gt;
    } else {&lt;br /&gt;
        printf(&amp;quot;Parameters error: pid required\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prove_pratiche&amp;diff=1872</id>
		<title>Prove pratiche</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prove_pratiche&amp;diff=1872"/>
		<updated>2017-05-11T12:36:33Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Aggiunto link Prova pratica 2015.05.20&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;''Qui troverete alcune prove teoriche pratiche (laboratorio), in modo da poter discutere sull'elaborato''&lt;br /&gt;
&lt;br /&gt;
''Le prove sono svolte da studenti e servono come base per la discussione. Le soluzioni possono essere errate''&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2017.01.17]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2015.05.20]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2015.01.21]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2014.09.25]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2014.07.17]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Pratica 2014.07.02]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Pratica 2014.06.17]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2014.05.29]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Pratica 2014.02.20]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2014.01.23]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.09.13]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.07.18]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.06.21]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.05.29]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.02.15]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2013.01.25]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2012.09.19]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2012.07.17]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2012.06.20]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Pratica 2012.05.30]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2011.09.12]]&lt;br /&gt;
&lt;br /&gt;
[[Prova Pratica 2011.06.22]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2011.05.30]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2011.01.19]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2010.07.19]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica_2010.07.12]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2010.02.03]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2009.09.23]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2009.06.23]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2009.02.12]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2009.01.15]]&lt;br /&gt;
&lt;br /&gt;
[[ProvaPratica 2005.02.10]]&lt;br /&gt;
&lt;br /&gt;
[[Prova pratica 2002.01.24]]&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2015.05.20&amp;diff=1871</id>
		<title>Prova pratica 2015.05.20</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2015.05.20&amp;diff=1871"/>
		<updated>2017-05-11T12:14:29Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Aggiornata soluzione es 1 dopo discussione in aula&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/pratiche/2015.05.29.pdf Link al testo]&lt;br /&gt;
&lt;br /&gt;
==Esercizio 1==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Scrivere un programma catsig che copi lo standard input nello standard output (come il comando cat senza&lt;br /&gt;
parametri, per intenderci) e che stampi la scritta “ho ricevuto un segnale” quando riceve un segnale SIGUSR1.&lt;br /&gt;
La particolarita' di questo programma e' che per la gestione dei segnali deve usare la chiamata di sistema&lt;br /&gt;
signalfd (e non la signal o la sigaction)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Soluzione di FedericoB===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/signalfd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;poll.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]) {&lt;br /&gt;
    //declare a mask for signal&lt;br /&gt;
    sigset_t mask;&lt;br /&gt;
    //initialize signal mask&lt;br /&gt;
    sigemptyset(&amp;amp;mask);&lt;br /&gt;
    sigaddset(&amp;amp;mask, SIGUSR1);&lt;br /&gt;
    //create a file descriptor for signal USR1&lt;br /&gt;
    int signalFileDescriptor = signalfd(-1, &amp;amp;mask, 0);&lt;br /&gt;
    //remove default handler for SIGURS1&lt;br /&gt;
    sigprocmask(SIG_BLOCK, &amp;amp;mask, NULL);&lt;br /&gt;
    struct pollfd pollstruct[2];&lt;br /&gt;
    pollstruct[0].fd = signalFileDescriptor;&lt;br /&gt;
    pollstruct[0].events = POLLIN;&lt;br /&gt;
    pollstruct[1].fd = STDIN_FILENO;&lt;br /&gt;
    pollstruct[1].events = POLLIN;&lt;br /&gt;
    while (1) {&lt;br /&gt;
        poll(pollstruct, 2, -1);&lt;br /&gt;
        //is signal USR1 is received&lt;br /&gt;
        if (pollstruct[0].revents &amp;amp; POLLIN) {&lt;br /&gt;
            struct signalfd_siginfo siginfo;&lt;br /&gt;
            //read signal information from signal file descriptor&lt;br /&gt;
            read(signalFileDescriptor, &amp;amp;siginfo, sizeof(struct signalfd_siginfo));&lt;br /&gt;
            printf(&amp;quot;ho ricevuto un segnale\n&amp;quot;);&lt;br /&gt;
            //set event as handled&lt;br /&gt;
            pollstruct[0].revents = 0;&lt;br /&gt;
        }&lt;br /&gt;
        //if there is something to read in standard input&lt;br /&gt;
        if (pollstruct[1].revents &amp;amp; POLLIN) {&lt;br /&gt;
            char character;&lt;br /&gt;
            //read one character until there is a character to read&lt;br /&gt;
            while (read(fileno(stdin), &amp;amp;character, 1) &amp;gt; 0) {&lt;br /&gt;
                //print that character to standard output&lt;br /&gt;
                write(fileno(stdout), &amp;amp;character, 1);&lt;br /&gt;
            }&lt;br /&gt;
            //set event as handled&lt;br /&gt;
            pollstruct[1].revents = 0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2015.05.20&amp;diff=1870</id>
		<title>Prova pratica 2015.05.20</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2015.05.20&amp;diff=1870"/>
		<updated>2017-05-11T07:58:29Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Creata pagina con proposta di soluzione esercizio 1&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/pratiche/2015.05.29.pdf Link al testo]&lt;br /&gt;
&lt;br /&gt;
==Esercizio 1==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Scrivere un programma catsig che copi lo standard input nello standard output (come il comando cat senza&lt;br /&gt;
parametri, per intenderci) e che stampi la scritta “ho ricevuto un segnale” quando riceve un segnale SIGUSR1.&lt;br /&gt;
La particolarita' di questo programma e' che per la gestione dei segnali deve usare la chiamata di sistema&lt;br /&gt;
signalfd (e non la signal o la sigaction)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Soluzione di FedericoB===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/signalfd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;poll.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]) {&lt;br /&gt;
    /*&lt;br /&gt;
    int fileDescriptor[2];&lt;br /&gt;
    pipe(fileDescriptor);&lt;br /&gt;
    dup2(fileDescriptor[1], STDIN_FILENO);&lt;br /&gt;
    dup2(fileDescriptor[0], STDOUT_FILENO);&lt;br /&gt;
    */&lt;br /&gt;
&lt;br /&gt;
    //declare a mask for signal&lt;br /&gt;
    sigset_t mask;&lt;br /&gt;
    //initialize signal mask&lt;br /&gt;
    sigemptyset(&amp;amp;mask);&lt;br /&gt;
    sigaddset(&amp;amp;mask, SIGUSR1);&lt;br /&gt;
    //create a file descriptor for signal USR1&lt;br /&gt;
    int signalFileDescriptor = signalfd(-1, &amp;amp;mask, 0);&lt;br /&gt;
    //remove default handled for SIGURS1&lt;br /&gt;
    sigprocmask(SIG_BLOCK, &amp;amp;mask, NULL);&lt;br /&gt;
    struct pollfd pollstruct[2];&lt;br /&gt;
    pollstruct[0].fd = signalFileDescriptor;&lt;br /&gt;
    pollstruct[0].events = POLLIN;&lt;br /&gt;
    pollstruct[1].fd = STDIN_FILENO;&lt;br /&gt;
    pollstruct[1].events = POLLIN;&lt;br /&gt;
    while (1) {&lt;br /&gt;
        poll(pollstruct, 2, -1);&lt;br /&gt;
        //is signal USR1 is received&lt;br /&gt;
        if (pollstruct[0].revents &amp;amp; POLLIN) {&lt;br /&gt;
            struct signalfd_siginfo siginfo;&lt;br /&gt;
            //read signal information from signal file descriptor&lt;br /&gt;
            read(signalFileDescriptor, &amp;amp;siginfo, sizeof(struct signalfd_siginfo));&lt;br /&gt;
            printf(&amp;quot;ho ricevuto un segnale\n&amp;quot;);&lt;br /&gt;
            //set event as handled&lt;br /&gt;
            pollstruct[0].revents = 0;&lt;br /&gt;
            //send signal acknowledge&lt;br /&gt;
            kill(siginfo.ssi_pid, SIGUSR1);&lt;br /&gt;
        }&lt;br /&gt;
        //if there is something to read in standard input&lt;br /&gt;
        if (pollstruct[1].revents &amp;amp; POLLIN) {&lt;br /&gt;
            char character;&lt;br /&gt;
            //read one character until there is a character to read&lt;br /&gt;
            while (read(fileno(stdin), &amp;amp;character, 1) &amp;gt; 0) {&lt;br /&gt;
                //print that character to standard output&lt;br /&gt;
                write(fileno(stdout), &amp;amp;character, 1);&lt;br /&gt;
            }&lt;br /&gt;
            //set event as handled&lt;br /&gt;
            pollstruct[1].revents = 0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ho provato a usare la pipe per la ridirezione dello stdin nollo stdout ma non funziona. Qualcuno ha idea del perchè? --[[User:FedericoB|FedericoB]] ([[User talk:FedericoB|talk]]) 09:58, 11 May 2017 (CEST)&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaPratica_2013.05.29&amp;diff=1869</id>
		<title>ProvaPratica 2013.05.29</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaPratica_2013.05.29&amp;diff=1869"/>
		<updated>2017-05-10T16:16:03Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Aggiunto link e testo del es 1&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/pratiche/2013.05.29.pdf Link al compito]&lt;br /&gt;
==Esercizio 1==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Scrivere un programma testeventfd che faccia uso della system call eventfd.&lt;br /&gt;
In particolare il programma deve eseguire una fork, quando l'utente digita un numero letto dal processo padre, il processo&lt;br /&gt;
figlio deve stampare un numero uguale di x.&lt;br /&gt;
$ testeventfd&lt;br /&gt;
3&lt;br /&gt;
x&lt;br /&gt;
x&lt;br /&gt;
x&lt;br /&gt;
2&lt;br /&gt;
x&lt;br /&gt;
x&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Soluzione di GiuliaN.===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/eventfd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define BUFFSIZE 10&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[]){&lt;br /&gt;
	int efd,n,i;&lt;br /&gt;
	char buf1[BUFFSIZE];&lt;br /&gt;
	char buf2[BUFFSIZE];&lt;br /&gt;
	&lt;br /&gt;
	do{&lt;br /&gt;
		efd = eventfd(0,0);&lt;br /&gt;
	}while(efd&amp;lt;0);&lt;br /&gt;
	&lt;br /&gt;
	while(1){&lt;br /&gt;
		if(fork()){ /*padre*/&lt;br /&gt;
				read(0,buf1,sizeof(buf1)); /*0 indica lo standard input*/&lt;br /&gt;
				write(efd, buf1, sizeof(buf1));&lt;br /&gt;
				}&lt;br /&gt;
			&lt;br /&gt;
		else{ /*figlio*/&lt;br /&gt;
			read(efd, buf2, sizeof(buf2));&lt;br /&gt;
			n=atoi(buf2);&lt;br /&gt;
			for(i=0; i&amp;lt;n; i++) printf(&amp;quot;x\n&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
GiuliaN. con grande aiuto da parte dei colleghi&lt;br /&gt;
&lt;br /&gt;
===Soluzione del Prof. Davoli===&lt;br /&gt;
(visto a lezione , soluzione del Prof. Davoli ricopiata da Pirata_20131203)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/eventfd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
#define BUFFSIZE 10&lt;br /&gt;
 &lt;br /&gt;
int main(int argc, char *argv[]){&lt;br /&gt;
	int efd , n , i;&lt;br /&gt;
	uint64_t a , b;&lt;br /&gt;
	if ( ( efd = eventfd( 0 , EFD_SEMAPHORE ) ) &amp;lt; 0 )&lt;br /&gt;
		exit( 1 );&lt;br /&gt;
	if ( fork() &amp;gt; 0 ){&lt;br /&gt;
		while ( 1 ) {&lt;br /&gt;
		uint64_t a;&lt;br /&gt;
		scanf( &amp;quot;%lld&amp;quot; , &amp;amp;a );&lt;br /&gt;
		write( efd , &amp;amp;a , sizeof( a ) );&lt;br /&gt;
				}&lt;br /&gt;
		}&lt;br /&gt;
	else {&lt;br /&gt;
		while ( 1 ) {&lt;br /&gt;
		uint64_t b;&lt;br /&gt;
		read( efd , &amp;amp;b , sizeof( b ) );&lt;br /&gt;
		printf( &amp;quot;x\n&amp;quot; );&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Pierg===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/eventfd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int main (int argc, char *argv[]) {&lt;br /&gt;
	&lt;br /&gt;
	int efd, i; &lt;br /&gt;
	long long unsigned int val;&lt;br /&gt;
    	ssize_t s;&lt;br /&gt;
	&lt;br /&gt;
	/* Check for eventfd error */&lt;br /&gt;
	efd = eventfd(0, 0);&lt;br /&gt;
	if (efd == -1) {&lt;br /&gt;
		perror(&amp;quot;Eventfd Error&amp;quot;); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* Use fork to move eventfd form parent to child */&lt;br /&gt;
	switch (fork()) {&lt;br /&gt;
		&lt;br /&gt;
		case 0:&lt;br /&gt;
			/* Read parent event */&lt;br /&gt;
			s = read(efd, &amp;amp;val, sizeof(long long unsigned int));&lt;br /&gt;
&lt;br /&gt;
            		if (s != sizeof(long long unsigned int)) {&lt;br /&gt;
                		perror(&amp;quot;Read Error&amp;quot;);&lt;br /&gt;
				exit(EXIT_FAILURE);&lt;br /&gt;
        		}&lt;br /&gt;
&lt;br /&gt;
			/* For is used to print the 'x' of the argument passed by terminal */&lt;br /&gt;
			for (i = (int)val; i &amp;gt; 0; i--) {&lt;br /&gt;
				printf (&amp;quot;x\n&amp;quot;);&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			exit(EXIT_SUCCESS);&lt;br /&gt;
		&lt;br /&gt;
		/* Check for error */&lt;br /&gt;
		case -1:&lt;br /&gt;
			perror(&amp;quot;Fork Error&amp;quot;); &lt;br /&gt;
			exit(EXIT_FAILURE);&lt;br /&gt;
&lt;br /&gt;
		default:&lt;br /&gt;
			/* Wait for number */&lt;br /&gt;
			printf (&amp;quot;Inserisci un intero diverso da 0: \n&amp;quot;);&lt;br /&gt;
			scanf (&amp;quot;%llu&amp;quot;, &amp;amp;val);&lt;br /&gt;
&lt;br /&gt;
			/* Write parent event */&lt;br /&gt;
           		s = write(efd, &amp;amp;val, sizeof(long long unsigned int));&lt;br /&gt;
&lt;br /&gt;
            		if (s != sizeof(long long unsigned int)) {&lt;br /&gt;
                		perror(&amp;quot;Write Error&amp;quot;);&lt;br /&gt;
				exit(EXIT_FAILURE);&lt;br /&gt;
        		}&lt;br /&gt;
	&lt;br /&gt;
			exit(EXIT_SUCCESS);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Maldus===&lt;br /&gt;
Semaforo implementato usando un altro eventfd.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/eventfd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;inttypes.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
	pid_t son ;&lt;br /&gt;
	int ed1 , ed2 ,  i;&lt;br /&gt;
	uint64_t x = 1;&lt;br /&gt;
	int y = 1 ;&lt;br /&gt;
	ed1 = eventfd(0,0) ;	/*eventfd usato per la comunicazione effettiva del numero*/&lt;br /&gt;
	ed2 = eventfd( 1 , EFD_SEMAPHORE ) ;	/*eventfd usato come semaforo*/&lt;br /&gt;
	switch( son = fork() ){&lt;br /&gt;
		case 0:&lt;br /&gt;
			while( 1 ){&lt;br /&gt;
				read( ed1 , &amp;amp;x , 8) ;	/*se ci sono dei dati li legge*/&lt;br /&gt;
				for(i = x ; i &amp;gt; 0 ; i--) printf( &amp;quot;x&amp;quot; ) ;&lt;br /&gt;
				printf(&amp;quot;\n&amp;quot;);&lt;br /&gt;
				write(ed2 , &amp;amp;y , 8 ) ;	/*dopo la lettura dei dati, dà il via libera sul semaforo per scrivere di nuovo*/&lt;br /&gt;
				}&lt;br /&gt;
			return 1;&lt;br /&gt;
		default:&lt;br /&gt;
			while( x ){&lt;br /&gt;
				read( ed2 , &amp;amp;y , 8 ) ;	/*si blocca sul semaforo se i dati non sono ancora stati letti*/&lt;br /&gt;
				scanf(&amp;quot;%&amp;quot; PRIu64 , &amp;amp;x) ;&lt;br /&gt;
				write(ed1 , &amp;amp;x , 8 ) ;	/*scrittura dei dati*/&lt;br /&gt;
			}&lt;br /&gt;
			kill(son ,SIGTERM);&lt;br /&gt;
			return 0 ;		&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Esercizio 2==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Eduardo Santarelli===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/eventfd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define SIZE 64&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char **argv){&lt;br /&gt;
	int efd_parent, efd_child;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
&lt;br /&gt;
	efd_parent = eventfd(0, 0);&lt;br /&gt;
	efd_child = eventfd(0, 0);&lt;br /&gt;
&lt;br /&gt;
	pid = fork();&lt;br /&gt;
	if(pid &amp;lt; 0){&lt;br /&gt;
		perror(&amp;quot;Fork error. &amp;quot;);&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	/* parent */&lt;br /&gt;
	if(pid){&lt;br /&gt;
		int okToWrite = 1;&lt;br /&gt;
		ssize_t n;&lt;br /&gt;
		char buf[SIZE];&lt;br /&gt;
&lt;br /&gt;
		for(;;){&lt;br /&gt;
			if(okToWrite){&lt;br /&gt;
				scanf(&amp;quot;%s&amp;quot;, buf);&lt;br /&gt;
			&lt;br /&gt;
				n = write(efd_parent, buf, SIZE);&lt;br /&gt;
				if(n &amp;lt; 0){&lt;br /&gt;
					perror(&amp;quot;Parent: write error.&amp;quot;);&lt;br /&gt;
					exit(EXIT_FAILURE);&lt;br /&gt;
				}&lt;br /&gt;
			okToWrite = 1-okToWrite;&lt;br /&gt;
			}&lt;br /&gt;
			else{&lt;br /&gt;
				n = read(efd_child, buf, SIZE);&lt;br /&gt;
				if( n &amp;lt; sizeof(uint64_t) ){&lt;br /&gt;
					printf(&amp;quot;Parent: something bad happened&amp;quot;);&lt;br /&gt;
					exit(EXIT_FAILURE);&lt;br /&gt;
				}&lt;br /&gt;
&lt;br /&gt;
				printf(&amp;quot;padre: %s\n&amp;quot;, buf);&lt;br /&gt;
				okToWrite = 1-okToWrite;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		close(efd_parent);&lt;br /&gt;
	}&lt;br /&gt;
	/* child */&lt;br /&gt;
	else{&lt;br /&gt;
		int okToWrite = 0;&lt;br /&gt;
		char buf[SIZE];&lt;br /&gt;
		int n;&lt;br /&gt;
	&lt;br /&gt;
		for(;;){&lt;br /&gt;
			if(okToWrite){&lt;br /&gt;
				scanf(&amp;quot;%s&amp;quot;, buf);&lt;br /&gt;
			&lt;br /&gt;
				n = write(efd_child, buf, SIZE);&lt;br /&gt;
				if(n &amp;lt; 0){&lt;br /&gt;
					perror(&amp;quot;child: write error.&amp;quot;);&lt;br /&gt;
					exit(EXIT_FAILURE);&lt;br /&gt;
				}&lt;br /&gt;
				okToWrite = 1-okToWrite;&lt;br /&gt;
			}&lt;br /&gt;
			else{&lt;br /&gt;
				n = read(efd_parent, buf, SIZE);&lt;br /&gt;
				if( n &amp;lt; sizeof(uint64_t) ){&lt;br /&gt;
					printf(&amp;quot;Child: something bad happened&amp;quot;);&lt;br /&gt;
					exit(EXIT_FAILURE);&lt;br /&gt;
				}&lt;br /&gt;
				printf(&amp;quot;figlio: %s\n&amp;quot;, buf);&lt;br /&gt;
				okToWrite = 1-okToWrite;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		close(efd_child);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
- Eduardo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Esercizio 3==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Tommaso Ognibene(Python 3)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Python&amp;quot;&amp;gt;&lt;br /&gt;
'''&lt;br /&gt;
Prova Pratica di Laboratorio di Sistemi Operativi&lt;br /&gt;
29 maggio 2013&lt;br /&gt;
Esercizio 3&lt;br /&gt;
&lt;br /&gt;
URL: http://www.cs.unibo.it/~renzo/so/pratiche/2013.05.29.pdf&lt;br /&gt;
&lt;br /&gt;
@author: Tommaso Ognibene&lt;br /&gt;
'''&lt;br /&gt;
&lt;br /&gt;
import os, sys&lt;br /&gt;
&lt;br /&gt;
def Main(argv):&lt;br /&gt;
    # Check number of arguments&lt;br /&gt;
    if len(argv) != 2:&lt;br /&gt;
        print(&amp;quot;The function requires one argument to be passed in.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # Check parameters&lt;br /&gt;
    topDir = str(sys.argv[1])&lt;br /&gt;
    if not os.path.isdir(topDir):&lt;br /&gt;
        print(&amp;quot;The parameter should be an existing directory.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    &lt;br /&gt;
    # Build a dictionary with key-value pair {file extension - total size}&lt;br /&gt;
    extensionSize = { }&lt;br /&gt;
    GetSize(topDir, extensionSize)&lt;br /&gt;
    &lt;br /&gt;
    # Print results&lt;br /&gt;
    PrintResults(extensionSize)&lt;br /&gt;
&lt;br /&gt;
def GetSize(topDir, extensionSize):&lt;br /&gt;
    for dirPath, dirNames, files in os.walk(topDir):&lt;br /&gt;
        for file in files:&lt;br /&gt;
            # 'example.mp3' -&amp;gt; ['example', 'mp3']&lt;br /&gt;
            # 'example.tar.gz' -&amp;gt; ['example', 'tar', 'gz']&lt;br /&gt;
            parts = file.split('.')&lt;br /&gt;
            # ['example', 'mp3'] -&amp;gt; ['mp3']&lt;br /&gt;
            # ['example', 'tar', 'gz'] -&amp;gt; ['tar', 'gz']&lt;br /&gt;
            parts = parts[1:]&lt;br /&gt;
            # ['mp3'] -&amp;gt; '.mp3'&lt;br /&gt;
            # ['tar', 'gz'] -&amp;gt; '.tar.gz'&lt;br /&gt;
            fileExtension = &amp;quot;.{0}&amp;quot;.format(&amp;quot;.&amp;quot;.join(str(part) for part in parts))&lt;br /&gt;
            &lt;br /&gt;
            # Compute the size in Bytes and update the dictionary&lt;br /&gt;
            filePath = os.path.join(dirPath, file)   &lt;br /&gt;
            fileSize = os.path.getsize(filePath)&lt;br /&gt;
            extensionSize[fileExtension] = extensionSize.get(fileExtension, 0) + fileSize&lt;br /&gt;
&lt;br /&gt;
# Print results&lt;br /&gt;
def PrintResults(extensionSize):&lt;br /&gt;
    for key, value in sorted(extensionSize.items()):&lt;br /&gt;
        print('{0}: {1} Bytes.'.format(key, value))&lt;br /&gt;
        &lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    sys.exit(Main(sys.argv))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Fede(python 3)===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Python&amp;quot;&amp;gt;&lt;br /&gt;
import os, sys, copy&lt;br /&gt;
&lt;br /&gt;
def dotsubstr(a):#restituisce la sottostringa .suffisso&lt;br /&gt;
	#fcd = os.listdir('{0}'.format(arg1))&lt;br /&gt;
	i=0&lt;br /&gt;
	try:&lt;br /&gt;
		while a[i]!='.':&lt;br /&gt;
			i=i+1&lt;br /&gt;
		return a[i:]&lt;br /&gt;
	except IndexError:&lt;br /&gt;
		return -1&lt;br /&gt;
&lt;br /&gt;
def compliarg(li,arg):#restituisce una lista di tutti gli elementi contenenti la sottostringa arg come suffisso&lt;br /&gt;
	res=[]&lt;br /&gt;
	while li != []:&lt;br /&gt;
		a=li.pop()&lt;br /&gt;
		if a.endswith(arg): res.append(a)&lt;br /&gt;
	return res&lt;br /&gt;
&lt;br /&gt;
def listremintsect(l2,l1):#restituisce una lista res = l1 - intersezione di l1 ed l2&lt;br /&gt;
	res=[]&lt;br /&gt;
	while l1 != []:&lt;br /&gt;
		a=l1.pop()&lt;br /&gt;
		if not a in l2: res.append(a)&lt;br /&gt;
	return res&lt;br /&gt;
&lt;br /&gt;
def createpathlist(c,path):#restituisce una lista di path 'path' relativi ai file contenuti in c.&lt;br /&gt;
	res = []&lt;br /&gt;
	while c != []:&lt;br /&gt;
		res.append('{0}/{1}'.format(path,c.pop()))&lt;br /&gt;
	return res&lt;br /&gt;
&lt;br /&gt;
def totsizes(d): #data una lista d di path restituisce la somma di tutti i size di d&lt;br /&gt;
	res = 0&lt;br /&gt;
	while d != []:&lt;br /&gt;
		a = d.pop()&lt;br /&gt;
		if os.path.isfile('{0}'.format(a)):&lt;br /&gt;
			res = res + os.path.getsize('{0}'.format(a))&lt;br /&gt;
	return res&lt;br /&gt;
&lt;br /&gt;
def listsubstr(arg): #ritorna un dizionario del tipo diz[str(suffisso)] = int(size relativo al suffisso)&lt;br /&gt;
	res = {}&lt;br /&gt;
	fcd = os.listdir('{0}'.format(arg))&lt;br /&gt;
	while fcd != []:&lt;br /&gt;
		fcdtmp=copy.deepcopy(fcd) #BUGGONE SENZA COPY!!!!!!&lt;br /&gt;
		a = fcd.pop()&lt;br /&gt;
		b = dotsubstr(a)&lt;br /&gt;
		if b == -1: continue&lt;br /&gt;
		else: pass&lt;br /&gt;
		c = compliarg(fcdtmp,b)&lt;br /&gt;
		s=copy.deepcopy(c) #!!!!!!!!!!!!!!!!!!!!!!!!!&lt;br /&gt;
		d = createpathlist(c,arg)&lt;br /&gt;
		res[b] = totsizes(d)&lt;br /&gt;
&lt;br /&gt;
		fcd = listremintsect(s,fcd)&lt;br /&gt;
&lt;br /&gt;
	return res&lt;br /&gt;
		&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
	res = listsubstr(sys.argv[1])&lt;br /&gt;
	a=list(res.keys())&lt;br /&gt;
	while a != []:&lt;br /&gt;
		b = a.pop()&lt;br /&gt;
		print('{0}:\t{1}'.format(b,res[b]))&lt;br /&gt;
except OSError:&lt;br /&gt;
	print(&amp;quot;Could not solve path&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Soluzione in bash(Pirata &amp;amp; Fede)===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#! /bin/bash&lt;br /&gt;
&lt;br /&gt;
for f in &amp;quot;$1&amp;quot;/*.* ; do&lt;br /&gt;
	somma=0&lt;br /&gt;
	ext=${f##*.}&lt;br /&gt;
	for file in *.&amp;quot;$ext&amp;quot;&lt;br /&gt;
	do&lt;br /&gt;
		somma=$(expr $somma + $(stat -c%s &amp;quot;$file&amp;quot;))&lt;br /&gt;
	done&lt;br /&gt;
	echo -n &amp;quot;size of file with extension &amp;quot;$ext&amp;quot; : &amp;quot;&lt;br /&gt;
	echo &amp;quot;$somma&amp;quot;&lt;br /&gt;
done&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Per (Pirata &amp;amp; Fede) -Bash-: ci siamo quasi...però forse rifate gli stessi calcoli un po' di volte in più del necessario ;-) (am_20131127).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Eduardo Santarelli (python)===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Python&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env python3&lt;br /&gt;
#&lt;br /&gt;
# Prova Pratica di Lab SO 2013.05.29&lt;br /&gt;
# Es. 3&lt;br /&gt;
# Eduardo Santarelli&lt;br /&gt;
#####################################&lt;br /&gt;
&lt;br /&gt;
import os, sys&lt;br /&gt;
&lt;br /&gt;
# return the suffix of file named fname, if present.&lt;br /&gt;
# return boolean value False otherwise&lt;br /&gt;
def get_suffix(fname):&lt;br /&gt;
	if '.' in fname:&lt;br /&gt;
		separator = fname.find('.')&lt;br /&gt;
		return fname[separator : ]&lt;br /&gt;
	else:&lt;br /&gt;
		return False	&lt;br /&gt;
&lt;br /&gt;
# Place a file in a dictionary. Each key corresponds&lt;br /&gt;
# to a specific suffix, its value is a list of all the&lt;br /&gt;
# file with that suffix&lt;br /&gt;
def build_dict(item, suffix):&lt;br /&gt;
	if suffix in all_files:&lt;br /&gt;
		all_files[suffix].append(item)&lt;br /&gt;
	else:&lt;br /&gt;
		all_files[suffix] = []&lt;br /&gt;
		all_files[suffix].append(item)&lt;br /&gt;
&lt;br /&gt;
# For each suffix key in the dictionary,&lt;br /&gt;
# calculates the total size of the files&lt;br /&gt;
# in the corresponding list&lt;br /&gt;
def size_of_entry(file_list):&lt;br /&gt;
		size = 0&lt;br /&gt;
		for item in file_list:&lt;br /&gt;
			size += os.path.getsize(item)&lt;br /&gt;
		return size&lt;br /&gt;
&lt;br /&gt;
# This dictionary will contain a key for each suffix,&lt;br /&gt;
# associated with a list of all the files ending with&lt;br /&gt;
# that suffix&lt;br /&gt;
all_files = dict()&lt;br /&gt;
&lt;br /&gt;
# Go to target directory and build a list containing all filenames&lt;br /&gt;
try:&lt;br /&gt;
	target_dir = sys.argv[1]&lt;br /&gt;
except:&lt;br /&gt;
	target_dir = '.'&lt;br /&gt;
os.chdir(target_dir)&lt;br /&gt;
dir_list = os.listdir('.')&lt;br /&gt;
&lt;br /&gt;
# For each file call get_suffix and then build_dict&lt;br /&gt;
# (unless get_suffix returns False)&lt;br /&gt;
for item in dir_list:&lt;br /&gt;
	suffix = get_suffix(item)&lt;br /&gt;
	if not suffix:&lt;br /&gt;
		pass&lt;br /&gt;
	else:&lt;br /&gt;
		build_dict(item, suffix)&lt;br /&gt;
&lt;br /&gt;
for key in all_files :&lt;br /&gt;
	print(&amp;quot;{0}: {1} Bytes&amp;quot;.format(key, size_of_entry(all_files[key])))&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
-Eduardo&lt;br /&gt;
--edit: dir_list = os.listdir(target_dir) non funzionava bene coi path relativi.&lt;br /&gt;
Ora os.listdir('.') dovrebbe andare bene.&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaPratica_2009.02.12&amp;diff=1868</id>
		<title>ProvaPratica 2009.02.12</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaPratica_2009.02.12&amp;diff=1868"/>
		<updated>2017-05-10T07:44:36Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Aggiunte soluzioni di altri studenti&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/pratiche/2009.02.12.pdf Link al testo]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Esercizio 1 ==&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Stefano92===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Scrivere un programma C denominato “invarg” che esegua il programma passato&lt;br /&gt;
come parametro invertendo gli argomenti.&lt;br /&gt;
Esempio:&lt;br /&gt;
invarg cat a b c&lt;br /&gt;
deve avere l'effetto di&lt;br /&gt;
cat c b a&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void usage()&lt;br /&gt;
{&lt;br /&gt;
	printf(&amp;quot;usage: ./invarg.out EXEC_NAME [ARG1] [ARG2] ... [ARGN]\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc,char* argv[])&lt;br /&gt;
{&lt;br /&gt;
   char* tmp_i,*tmp_x;&lt;br /&gt;
   int x=(argc-1),i=2;&lt;br /&gt;
   if (argc&amp;lt;2)&lt;br /&gt;
   {&lt;br /&gt;
   		usage();&lt;br /&gt;
   		return(1);&lt;br /&gt;
   }&lt;br /&gt;
   while(i&amp;lt;x)&lt;br /&gt;
   {&lt;br /&gt;
       tmp_i=argv[i];&lt;br /&gt;
       tmp_x=argv[x];&lt;br /&gt;
       argv[i]=tmp_x;&lt;br /&gt;
       argv[x]=tmp_i;&lt;br /&gt;
       i++;&lt;br /&gt;
       x--;&lt;br /&gt;
   }&lt;br /&gt;
	for(i=0;i&amp;lt;argc-1;i++)&lt;br /&gt;
		argv[i]=argv[i+1];&lt;br /&gt;
	&lt;br /&gt;
	argv[argc-1]=NULL;&lt;br /&gt;
	argc--;&lt;br /&gt;
	execvp(argv[0],argv);&lt;br /&gt;
        //equivalentemente avrei potuto lanciare execvp(argv[1],argv+1) evitando di modificare argv[]&lt;br /&gt;
	return 0;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
stefano92&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Maldus===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc , char* argv[]){&lt;br /&gt;
	char *nargv[argc-1] ;	/*nargv è il vettore degli argomenti da passare al programma da eseguire*/&lt;br /&gt;
	int i , j ;		/*puntatori che scorrono, rispettivamente, argv ed nargv per riempire correttamente nargv*/&lt;br /&gt;
	nargv[0] = argv[1] ;	/*il primo valore di nargv deve essere il programma chiamato, l'ultimo deve essere NULL*/&lt;br /&gt;
	nargv[argc-1] = NULL ;&lt;br /&gt;
	for( i = argc - 1 , j = 1 ; i &amp;gt; 1 , j &amp;lt; (argc - 1) ; i-- , j++ ) nargv[j] = argv[i] ;	/*metto in nargv (dal secondo elemento in poi) gli argomenti presenti &lt;br /&gt;
in argv in ordine invertito*/&lt;br /&gt;
	execvp( nargv[0] , nargv  ) ;	/*eseguo il programma (il cui nome è contenuto in nargv[0]) passandogli nargv come vettore di parametri*/&lt;br /&gt;
	fprintf( stderr , &amp;quot;programma errato\n&amp;quot; ) ;	/*se ci troviamo in questo punto significa che la chiamata a execvp è fallita, probabilmente&lt;br /&gt;
 perchè il programma indicato non è stato trovato*/&lt;br /&gt;
	exit(1) ;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Eddy===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void scambia_ordine(int argc, char **argv, int j);&lt;br /&gt;
&lt;br /&gt;
int main(int argc , char* argv[])&lt;br /&gt;
{&lt;br /&gt;
	if (argc &amp;lt; 2) return 2;&lt;br /&gt;
	argv++;	argc--;&lt;br /&gt;
	scambia_ordine(argc, argv, 1);&lt;br /&gt;
	execvp(argv[0], argv);&lt;br /&gt;
	perror(&amp;quot;execvp&amp;quot;);&lt;br /&gt;
	return 3;	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* piccola funzione ricorsiva che:&lt;br /&gt;
 * -memorizza in tmp gli argomenti dalla fine&lt;br /&gt;
 * -assegna ad argv in ordine inverso&lt;br /&gt;
 *&lt;br /&gt;
 *  non scambia il primo argomento&lt;br /&gt;
 */&lt;br /&gt;
void scambia_ordine(int argc, char **argv, int j)&lt;br /&gt;
{&lt;br /&gt;
	char *tmp;&lt;br /&gt;
	if (argc &amp;gt; 1){&lt;br /&gt;
		tmp = argv[argc-1];&lt;br /&gt;
		scambia_ordine (argc-1, argv, j+1);&lt;br /&gt;
		argv[j]=tmp;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
[[User:Eddy|Eddy]] ([[User talk:Eddy|talk]]) 21:00 Saturday, 22 November 2014 (CET)&lt;br /&gt;
&lt;br /&gt;
===Soluzione di GFede===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void scambia(int offset, int argc, char* argv[]);&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[])&lt;br /&gt;
{&lt;br /&gt;
   if(argc &amp;lt;= 1)&lt;br /&gt;
   {&lt;br /&gt;
      printf(&amp;quot;Usage: invarg.exe command [arguments...]\n&amp;quot;);&lt;br /&gt;
      return 2;&lt;br /&gt;
   }&lt;br /&gt;
   scambia(2, argc - 1, argv);&lt;br /&gt;
&lt;br /&gt;
   execvp(argv[1], argv + 1);&lt;br /&gt;
   perror(&amp;quot;execvp&amp;quot;);&lt;br /&gt;
   return 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Scambia le stringhe all'interno dell'array&lt;br /&gt;
 * void scambia&lt;br /&gt;
 * int offset     Indice da cui iniziare a scambiare&lt;br /&gt;
 * int argc       Numero di valori nell'array (Compreso l'offset)&lt;br /&gt;
 * char* argv[]   array di stringhe da scambiare&lt;br /&gt;
 */&lt;br /&gt;
void scambia(int offset, int argc, char* argv[])&lt;br /&gt;
{&lt;br /&gt;
   int i;&lt;br /&gt;
   char* tmp;&lt;br /&gt;
   for( i = offset; i &amp;lt; argc - i + offset; i++ )&lt;br /&gt;
   {&lt;br /&gt;
      tmp = argv[i];&lt;br /&gt;
      argv[i] = argv[argc - i + offset];&lt;br /&gt;
      argv[argc - i + offset] = tmp;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Soluzione di LorenzoV===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void swap(char **p, char **q){		// La swap modifica solo i puntatori&lt;br /&gt;
	char *tmp;&lt;br /&gt;
	tmp=*p;&lt;br /&gt;
	*p=*q;&lt;br /&gt;
	*q=tmp;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]){	&lt;br /&gt;
	char **begin, **end;&lt;br /&gt;
	if (argc&amp;lt;3){&lt;br /&gt;
		fprintf(stderr, &amp;quot;Usage:\n\t%s command ...arguments...\n&amp;quot;, argv[0]);&lt;br /&gt;
		exit(1);	&lt;br /&gt;
	}	&lt;br /&gt;
	begin=argv+2;&lt;br /&gt;
	end=argv+argc-1;&lt;br /&gt;
	while (begin&amp;lt;end){		// Inverte i parametri passati al comando&lt;br /&gt;
		swap(begin, end);&lt;br /&gt;
		begin++;&lt;br /&gt;
		end--;&lt;br /&gt;
	}&lt;br /&gt;
	execvp(argv[1], argv+1);&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
--[[User:LorenzoV|LorenzoV]] ([[User talk:LorenzoV|talk]]) 18:35, 25 November 2014 (CET)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Soluzione di BoldrinD===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void invarg(int Qparametri, char* Rargv[], char* argv[]);&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]){&lt;br /&gt;
	int Qparametri=argc-1;	&lt;br /&gt;
	char *Rargv[Qparametri];&lt;br /&gt;
	invarg(Qparametri, Rargv, argv);&lt;br /&gt;
	printf(&amp;quot;%s\n&amp;quot;,&amp;quot;il nuovo vettore dei parametri è stato creato con successo&amp;quot;);&lt;br /&gt;
   	execvp(Rargv[0], Rargv);&lt;br /&gt;
  	 perror(&amp;quot;execvp&amp;quot;);&lt;br /&gt;
   return 1;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void invarg(int Qparametri, char* Rargv[], char* argv[]){&lt;br /&gt;
	Rargv[0]=argv[1];&lt;br /&gt;
	Rargv[Qparametri]=NULL;&lt;br /&gt;
	int i=2;&lt;br /&gt;
		while(argv[i]!=NULL){&lt;br /&gt;
		Rargv[Qparametri-1]=(argv[i]);&lt;br /&gt;
		i++;&lt;br /&gt;
		Qparametri--;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Soluzione di AleZ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
int main (int argc, char* argv[]) {&lt;br /&gt;
	if (argc == 1) {	// Non è stato passato alcun comando per la execvp&lt;br /&gt;
		fprintf(stderr, &amp;quot;Error: Segmentation fault\nUsage: [command] [arguments...]\n&amp;quot;);&lt;br /&gt;
		return(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	char* tmp[argc];	// vettore che conterrà gli argomenti scambiati&lt;br /&gt;
	int i = 0;&lt;br /&gt;
	tmp[0] = argv[1];	// per convenzione il primo argomento è il programma chiamato&lt;br /&gt;
	tmp[argc-1] = (char*)NULL;	// come da manuale&lt;br /&gt;
	for (i = argc-2; i &amp;gt; 0; i--) tmp[i] = argv[(argc-i)];	// inverto gli argomenti &lt;br /&gt;
	execvp(tmp[0], tmp);&lt;br /&gt;
	fprintf(stderr, &amp;quot;%s: command not found\n&amp;quot;, argv[1]);	//execvp ha fallito&lt;br /&gt;
	return(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
--[[User:Ale|Ale]] ([[User talk:Ale|talk]]) 20:39, 1 December 2014 (CET)&lt;br /&gt;
&lt;br /&gt;
==Esercizio 2==&lt;br /&gt;
===Soluzione di Dado e Pierg===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Scrivere un programma in linguaggio C che crei due named pipe (passate per argomento), le apra in lettura e&lt;br /&gt;
e copi in standard output i dati via via disponibili da ogni pipe.&lt;br /&gt;
Test di funzionamento: aprire tre finestre di emulazione terminale. Nella prima lanciare: mergepipe p1 p2, nella seconda:&lt;br /&gt;
cat &amp;gt;p1, nella terza cat&amp;gt;p2.&lt;br /&gt;
Tutto cio' che scriverete o nella seconda o nella terza finestra deve comparire nella prima.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;poll.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define MAX 2048&lt;br /&gt;
&lt;br /&gt;
int main (int argc, char *argv[]) {&lt;br /&gt;
    if (argc &amp;lt; 3) {&lt;br /&gt;
        perror(&amp;quot;Argomenti sbagliati&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        struct pollfd pino [2];&lt;br /&gt;
        nfds_t n = 2;&lt;br /&gt;
        char buf[MAX];&lt;br /&gt;
        &lt;br /&gt;
        mkfifo(argv[1], 0666);&lt;br /&gt;
        mkfifo(argv[2], 0666);&lt;br /&gt;
        pino[0].fd = open(argv[1], O_RDONLY | O_NONBLOCK);&lt;br /&gt;
        pino[1].fd = open(argv[2], O_RDONLY | O_NONBLOCK);&lt;br /&gt;
        pino[0].events = POLLIN;&lt;br /&gt;
        pino[1].events = POLLIN;&lt;br /&gt;
        &lt;br /&gt;
        while(1) {&lt;br /&gt;
            int ret = poll(pino, n, 100);&lt;br /&gt;
            if (ret == 1) {&lt;br /&gt;
                if (pino[0].revents == POLLIN) {&lt;br /&gt;
                    ssize_t x = read(pino[0].fd, buf, MAX);&lt;br /&gt;
                    buf[x+1] = '\0';&lt;br /&gt;
                    printf(&amp;quot;%s&amp;quot;, buf);&lt;br /&gt;
                    pino[0].revents = 0;&lt;br /&gt;
                }&lt;br /&gt;
                else {&lt;br /&gt;
                    ssize_t x = read(pino[1].fd, buf, MAX);&lt;br /&gt;
                    buf[x+1] = '\0';&lt;br /&gt;
                    printf(&amp;quot;%s&amp;quot;, buf);&lt;br /&gt;
                    pino[1].revents = 0;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            else if (ret == 2) {&lt;br /&gt;
                ssize_t x = read(pino[0].fd, buf, MAX);&lt;br /&gt;
                buf[x+1] = '\0';&lt;br /&gt;
                printf(&amp;quot;%s&amp;quot;, buf);&lt;br /&gt;
                pino[0].revents = 0;&lt;br /&gt;
                x = read(pino[1].fd, buf, MAX);&lt;br /&gt;
                buf[x+1] = '\0';&lt;br /&gt;
                printf(&amp;quot;%s&amp;quot;, buf);&lt;br /&gt;
                pino[1].revents = 0;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaPratica_2013.09.13&amp;diff=1867</id>
		<title>ProvaPratica 2013.09.13</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaPratica_2013.09.13&amp;diff=1867"/>
		<updated>2017-05-10T07:42:09Z</updated>

		<summary type="html">&lt;p&gt;FedericoB: Aggiunte soluzione di un'altro studente&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Esercizio 1 e 2==&lt;br /&gt;
===Soluzione di Edu San===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 /***********************************************************************&lt;br /&gt;
 * Prova Pratica di Laboratorio di Sistemi Operativi			*&lt;br /&gt;
 * 13 settembre 2013							*&lt;br /&gt;
 * Esercizio 1+2							*&lt;br /&gt;
 * 									*&lt;br /&gt;
 * URL: http://www.cs.unibo.it/~renzo/so/pratiche/2013.09.13.pdf 	*&lt;br /&gt;
 * Autore: Eduardo Santarelli						*&lt;br /&gt;
 ************************************************************************/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dirent.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* An entry in the hash table. */&lt;br /&gt;
typedef struct fileEntry{&lt;br /&gt;
&lt;br /&gt;
	const char *filename;&lt;br /&gt;
	struct fileEntry *next;&lt;br /&gt;
&lt;br /&gt;
} fileEntry;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Global variables */&lt;br /&gt;
fileEntry **table = NULL;&lt;br /&gt;
int table_size;&lt;br /&gt;
&lt;br /&gt;
/* Hash function. Calculates XOR of 8-bytes blocks		*&lt;br /&gt;
 * (actually, sizeof(long int)) read from the input file	*/&lt;br /&gt;
unsigned int hash(const char *fname){&lt;br /&gt;
&lt;br /&gt;
	FILE *f;&lt;br /&gt;
	unsigned long int hash_val = 0;&lt;br /&gt;
	unsigned long int tmp = 0;&lt;br /&gt;
&lt;br /&gt;
	f = fopen(fname, &amp;quot;r&amp;quot;);&lt;br /&gt;
	fread(&amp;amp;hash_val, sizeof(unsigned long int), 1, f);&lt;br /&gt;
&lt;br /&gt;
	while((fread(&amp;amp;tmp, sizeof(unsigned long int), 1, f) == 1))&lt;br /&gt;
		hash_val = hash_val^tmp;&lt;br /&gt;
&lt;br /&gt;
	fclose(f);&lt;br /&gt;
	return hash_val;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Creates a table of struct fileEntry elements, based on the hash values of the files	*&lt;br /&gt;
 * detected by scandir. If a collision is detected, the new entry is pointed by the	*&lt;br /&gt;
 * 'next' field of the current last element*/&lt;br /&gt;
void populate_table(struct dirent *dir_list[], int n){&lt;br /&gt;
&lt;br /&gt;
	int i;&lt;br /&gt;
	&lt;br /&gt;
	table_size = 2*n;	&lt;br /&gt;
	table = (fileEntry**)calloc(table_size, sizeof(fileEntry));&lt;br /&gt;
	if(!table) abort();&lt;br /&gt;
&lt;br /&gt;
	for(i=0; i&amp;lt;n; i++){&lt;br /&gt;
		unsigned int index;&lt;br /&gt;
		fileEntry *entry = NULL;&lt;br /&gt;
&lt;br /&gt;
		entry = (fileEntry*)malloc(sizeof(fileEntry));&lt;br /&gt;
		if(!entry) abort();&lt;br /&gt;
&lt;br /&gt;
		index = hash(dir_list[i]-&amp;gt;d_name)%table_size;&lt;br /&gt;
		if(table[index] == NULL){&lt;br /&gt;
			entry-&amp;gt;filename = dir_list[i]-&amp;gt;d_name;&lt;br /&gt;
			entry-&amp;gt;next = NULL;&lt;br /&gt;
			table[index] = entry;&lt;br /&gt;
		}&lt;br /&gt;
		else{&lt;br /&gt;
			entry-&amp;gt;filename = dir_list[i]-&amp;gt;d_name;&lt;br /&gt;
			entry-&amp;gt;next = table[index];&lt;br /&gt;
			table[index] = entry;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Compares two files */&lt;br /&gt;
int fcompare(const char *f1, const char *f2){&lt;br /&gt;
&lt;br /&gt;
	FILE *f, *g;&lt;br /&gt;
	int file1, file2;	&lt;br /&gt;
&lt;br /&gt;
	f = fopen(f1, &amp;quot;r&amp;quot;);&lt;br /&gt;
	g = fopen(f2, &amp;quot;r&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	while(fread(&amp;amp;file1, sizeof(int), 1, f) &amp;amp;&amp;amp; fread(&amp;amp;file2, sizeof(int), 1, g)){&lt;br /&gt;
		if(file1!=file2){&lt;br /&gt;
			fclose(f);&lt;br /&gt;
			fclose(g);&lt;br /&gt;
			return 0;&lt;br /&gt;
 		}&lt;br /&gt;
	}&lt;br /&gt;
	fclose(f);&lt;br /&gt;
	fclose(g);&lt;br /&gt;
	return 1;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
		&lt;br /&gt;
&lt;br /&gt;
/* This function scans the file table. If an entry contains more than one file,	*&lt;br /&gt;
 * it compares all them. If a duplicate is found, the first file is removed,  	*&lt;br /&gt;
 * and a hard link is created with the deleted file's name, pointing to the	*&lt;br /&gt;
 * second, identical, file.							*/&lt;br /&gt;
void deduplicate(){&lt;br /&gt;
&lt;br /&gt;
	int i;&lt;br /&gt;
&lt;br /&gt;
	for(i=0; i&amp;lt;table_size; i++){&lt;br /&gt;
		while(table[i] != NULL &amp;amp;&amp;amp; table[i]-&amp;gt;next != NULL){&lt;br /&gt;
&lt;br /&gt;
			fileEntry *file1, *file2;&lt;br /&gt;
			file1 = table[i];&lt;br /&gt;
			file2 = table[i]-&amp;gt;next;&lt;br /&gt;
			while(file2 != NULL){&lt;br /&gt;
				if(fcompare(file1-&amp;gt;filename, file2-&amp;gt;filename)){&lt;br /&gt;
					unlink(file1-&amp;gt;filename);&lt;br /&gt;
					link(file2-&amp;gt;filename, file1-&amp;gt;filename);&lt;br /&gt;
					printf(&amp;quot;%s -&amp;gt; %s\n&amp;quot;, file1-&amp;gt;filename, file2-&amp;gt;filename);&lt;br /&gt;
					break;&lt;br /&gt;
				}&lt;br /&gt;
				file2 = file2-&amp;gt;next;&lt;br /&gt;
			}&lt;br /&gt;
			table[i] = table[i]-&amp;gt;next;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*selector function for scandir() */&lt;br /&gt;
int exclude(const struct dirent *dir){&lt;br /&gt;
&lt;br /&gt;
	if(dir-&amp;gt;d_type != DT_REG)&lt;br /&gt;
		return 0;&lt;br /&gt;
	else&lt;br /&gt;
		return 1;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[]){&lt;br /&gt;
&lt;br /&gt;
	struct dirent **entries = NULL;&lt;br /&gt;
	int num_files;&lt;br /&gt;
&lt;br /&gt;
	num_files = scandir(&amp;quot;.&amp;quot;, &amp;amp;entries, exclude, alphasort);&lt;br /&gt;
&lt;br /&gt;
	populate_table(entries, num_files);&lt;br /&gt;
	deduplicate();&lt;br /&gt;
&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Maldus (ora con tabella hash!)===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dirent.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define SIZE 1024 &lt;br /&gt;
&lt;br /&gt;
char BUFF1[SIZE] ;	/*buffer usati per leggere i file*/&lt;br /&gt;
char BUFF2[SIZE] ;&lt;br /&gt;
&lt;br /&gt;
struct list{	/*struttura che uso per implementare la lista di file che hanno uguale somma crittografica*/&lt;br /&gt;
	struct dirent* ent ;&lt;br /&gt;
	struct list *next ;&lt;br /&gt;
	struct list *prev ;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
inline void del(struct list* p){	/*rimuove p dalla lista in cui si trova*/&lt;br /&gt;
	(p-&amp;gt;next)-&amp;gt;prev = p-&amp;gt;prev ;&lt;br /&gt;
	(p-&amp;gt;prev)-&amp;gt;next = p-&amp;gt;next ;&lt;br /&gt;
	free(p) ;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int critsum( int f ){	/*somma crittografica per la tabella hash*/&lt;br /&gt;
	int i , n ;&lt;br /&gt;
	int x = 0 ;&lt;br /&gt;
	while(( n = read( f , BUFF1 , SIZE ) )&amp;gt; 0 ){&lt;br /&gt;
		for( i = 0 ; i &amp;lt; n ; i++ ){&lt;br /&gt;
			x += ((int) BUFF1[i])*((i%8)+1) ;	/*aggiungo ogni volta il valore numerico corrispondente al byte letto moltiplicato per un numero da 1 a 8*/&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if( x &amp;lt; 0) return -x ;/*restituisco sempre un valore positivo, perchè possa essere usato come indice di un vettore*/&lt;br /&gt;
	else return x ;&lt;br /&gt;
}&lt;br /&gt;
		&lt;br /&gt;
&lt;br /&gt;
int buffcmp( char* buf1 , char* buf2 , int s1 , int s2 ){/*funzione che compara due buffer*/&lt;br /&gt;
	int i ;&lt;br /&gt;
	if( s1 != s2 ) return 1 ; /*se hanno lunghezza diversa sono diversi*/&lt;br /&gt;
	for( i = 0 ; i &amp;lt; s1 ; i++ ){&lt;br /&gt;
		if( buf1[i] != buf2[i] ) return 1 ;&lt;br /&gt;
	}&lt;br /&gt;
	return 0 ;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int filecmp( int f1 , int f2 ){	/*funzione ausiliaria che confronta due file. Si comporta come strcmp*/&lt;br /&gt;
	int n1 , n2 ;&lt;br /&gt;
	while(( n1 = read( f1 , BUFF1 , SIZE ) )&amp;gt; 0 ){	/*ho provato a inserire entrambe le read nel controllo del while con un &amp;amp;&amp;amp;; mi creava dei problemi, come se la seconda read venisse eseguita nel giro seguente del ciclo*/&lt;br /&gt;
		n2 = read( f2 , BUFF2 , SIZE ) ;&lt;br /&gt;
		if( buffcmp( BUFF1 , BUFF2 , n1 , n2 ) != 0 ) return 1 ;	/*ritorna 1 se i due file sono diversi*/&lt;br /&gt;
	}&lt;br /&gt;
	return 0 ;	/*ritorna 0 se sono uguali*/&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void del_clones( struct list* p ){	/*cerca nella lista il cui primo elemento è p tutti i file con lo stesso contenuto*/&lt;br /&gt;
	struct list *aux1 , *aux2 , *tmp;&lt;br /&gt;
	int fd1, fd2 ;&lt;br /&gt;
	struct stat s1 , s2 ;&lt;br /&gt;
	aux1 = p ;&lt;br /&gt;
	aux2 = aux1-&amp;gt;next ;&lt;br /&gt;
	while( aux1-&amp;gt;next != p ){&lt;br /&gt;
		printf( &amp;quot;%s:\n&amp;quot; , (aux1-&amp;gt;ent)-&amp;gt;d_name ) ;&lt;br /&gt;
		if( stat( (aux1-&amp;gt;ent)-&amp;gt;d_name , &amp;amp;s1 ) != 0 ){&lt;br /&gt;
			perror(&amp;quot;stat&amp;quot;) ;&lt;br /&gt;
			exit(1) ;&lt;br /&gt;
		}&lt;br /&gt;
		if ( (fd1 = open((aux1-&amp;gt;ent)-&amp;gt;d_name, O_RDONLY ) ) == -1){	/*apro la entry*/&lt;br /&gt;
			perror(&amp;quot;open&amp;quot;) ;&lt;br /&gt;
			exit(1) ;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		while( (aux2-&amp;gt;next) != p &amp;amp;&amp;amp; (aux2 != p) ){&lt;br /&gt;
			tmp = aux2-&amp;gt;next ;&lt;br /&gt;
			if( stat( (aux2-&amp;gt;ent)-&amp;gt;d_name , &amp;amp;s2 ) != 0 ){&lt;br /&gt;
				perror(&amp;quot;stat&amp;quot;) ;&lt;br /&gt;
				exit(1) ;&lt;br /&gt;
			}&lt;br /&gt;
			if ( (fd2 = open((aux2-&amp;gt;ent)-&amp;gt;d_name, O_RDONLY ) ) == -1){	/*apro la entry*/&lt;br /&gt;
				perror(&amp;quot;open&amp;quot;) ;&lt;br /&gt;
				exit(1) ;&lt;br /&gt;
			}&lt;br /&gt;
			printf(&amp;quot;\t%s&amp;quot; , (aux2-&amp;gt;ent)-&amp;gt;d_name) ;&lt;br /&gt;
			if( s1.st_ino != s2.st_ino){&lt;br /&gt;
				if( filecmp( fd1 , fd2 ) == 0 ){	/*controllo se i due file hanno lo stesso contenuto*/&lt;br /&gt;
						printf(&amp;quot;&amp;lt;-uguale\t&amp;quot;) ;&lt;br /&gt;
						unlink((aux2-&amp;gt;ent)-&amp;gt;d_name) ;	/*rimuovo il file uguale*/&lt;br /&gt;
						if( link( (aux1-&amp;gt;ent)-&amp;gt;d_name , (aux2-&amp;gt;ent)-&amp;gt;d_name) == -1 ){	/*creo un link fisico*/&lt;br /&gt;
							perror(&amp;quot;link&amp;quot;) ;&lt;br /&gt;
							exit(1) ;&lt;br /&gt;
						}&lt;br /&gt;
						del(aux2) ;&lt;br /&gt;
					}&lt;br /&gt;
			}&lt;br /&gt;
			else del(aux2) ;	/*se hanno lo stesso i-node toglili dalla lista*/&lt;br /&gt;
			aux2 = tmp;	/*tmp = aux2-&amp;gt;next. Lo salvo in precedenza nel caso in cui aux2 sia stato cancellato*/&lt;br /&gt;
			close(fd2) ;&lt;br /&gt;
		}&lt;br /&gt;
		printf(&amp;quot;\n&amp;quot;) ;&lt;br /&gt;
		close(fd1) ;&lt;br /&gt;
		aux1 = aux1-&amp;gt;next ;&lt;br /&gt;
		&lt;br /&gt;
	} &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main( int argc, char* argv[]){&lt;br /&gt;
	struct stat s1 , s2;	/*struttura di informazioni su un file*/&lt;br /&gt;
	DIR *dir  ;	/*puntatore a directory*/&lt;br /&gt;
	struct dirent *entry ;	/*struttura di informazioni sulle entry di una directory*/&lt;br /&gt;
	int fd1;	/*descrittore di file*/&lt;br /&gt;
	char *name ;&lt;br /&gt;
	int n = 0 ; /*contatore di file all'interno della directory*/&lt;br /&gt;
	int cs , i ; /*cs è somma crittografica del file*/&lt;br /&gt;
	struct list *hash ;&lt;br /&gt;
	struct list *tmp ;&lt;br /&gt;
	if( stat( argv[1] , &amp;amp;s1 ) != 0 ){&lt;br /&gt;
		perror(&amp;quot;stat&amp;quot;) ;&lt;br /&gt;
		return 1 ;&lt;br /&gt;
	}&lt;br /&gt;
	if( !S_ISDIR(s1.st_mode) ){&lt;br /&gt;
		printf( &amp;quot;argument is not a valid directory\n&amp;quot; ) ;&lt;br /&gt;
		return 0 ;&lt;br /&gt;
	}&lt;br /&gt;
	if( (dir = opendir( argv[1] )) == NULL ){&lt;br /&gt;
		perror(&amp;quot;opendir&amp;quot;) ;&lt;br /&gt;
		return 1 ;&lt;br /&gt;
	}&lt;br /&gt;
	if( chdir( argv[1] ) == -1 ){	/*sposto la directory corrente in quella di esecuzione per poter aprire i file*/&lt;br /&gt;
		perror(&amp;quot;chdir&amp;quot;) ;&lt;br /&gt;
		return 1 ;&lt;br /&gt;
	}&lt;br /&gt;
	while( (entry = readdir(dir) ) != NULL){	/*conto i file contenuti nella directory*/&lt;br /&gt;
		stat( entry-&amp;gt;d_name , &amp;amp;s1 ) ;&lt;br /&gt;
		if( S_ISREG(s1.st_mode) ) n++ ;	/*se si tratta di un file normale contalo*/&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;La directory contiene %i file\n&amp;quot; , n ) ;&lt;br /&gt;
	n = 2*n ;	/*da questo momento n è il numero di indici nella mia hashtable*/&lt;br /&gt;
	hash = (struct list*) malloc( sizeof(struct list)*n) ;	/*alloco una hash table con n = 2n indici*/&lt;br /&gt;
	rewinddir(dir) ;&lt;br /&gt;
	for( i = 0 ; i &amp;lt; n ; i++ ){	/*inizializzo le liste*/&lt;br /&gt;
		hash[i].next = &amp;amp;hash[i] ;&lt;br /&gt;
		hash[i].prev = &amp;amp;hash[i] ;&lt;br /&gt;
		hash[i].ent = NULL ;&lt;br /&gt;
	}&lt;br /&gt;
	while( (entry = readdir(dir) ) != NULL){	/*con questo ciclo riempo la hashtable*/&lt;br /&gt;
		stat( entry-&amp;gt;d_name , &amp;amp;s1 ) ;&lt;br /&gt;
		if( S_ISREG(s1.st_mode) ){	/*se si tratta di un file normale consideralo*/&lt;br /&gt;
			if ( (fd1 = open(entry-&amp;gt;d_name, O_RDONLY ) ) == -1){	/*apro la entry*/&lt;br /&gt;
				perror(&amp;quot;open&amp;quot;) ;&lt;br /&gt;
				exit(1) ;&lt;br /&gt;
			}&lt;br /&gt;
			cs = critsum( fd1 ) ;&lt;br /&gt;
			tmp = (struct list*) malloc( sizeof(struct list) ) ;&lt;br /&gt;
			tmp-&amp;gt;ent = entry ;&lt;br /&gt;
			(hash[cs%n].next)-&amp;gt;prev = tmp ;	/*inserisco il file in testa alla lista di indice cs%n*/&lt;br /&gt;
			tmp-&amp;gt;prev = &amp;amp;hash[cs%n] ;&lt;br /&gt;
			tmp-&amp;gt;next = hash[cs%n].next ;&lt;br /&gt;
			hash[cs%n].next = tmp ;&lt;br /&gt;
			close(fd1) ;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	for( i = 0 ; i &amp;lt; n ; i++ ){	/*con questo ciclo cancello effettivamente i file uguali*/&lt;br /&gt;
		printf(&amp;quot;%i: &amp;quot; , i ) ;&lt;br /&gt;
		if( hash[i].next != &amp;amp;hash[i] ){&lt;br /&gt;
			del_clones( hash[i].next ) ;&lt;br /&gt;
		}&lt;br /&gt;
		else printf(&amp;quot;\n&amp;quot;) ;&lt;br /&gt;
	}&lt;br /&gt;
	return 0 ;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Esercizio 3==&lt;br /&gt;
===Soluzione di Edu San===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Python&amp;quot;&amp;gt;&lt;br /&gt;
#!/usr/bin/env python3&lt;br /&gt;
 &lt;br /&gt;
# Prova Pratica di Laboratorio di Sistemi Operativi&lt;br /&gt;
# 13 settembre 2013&lt;br /&gt;
# Esercizio 3&lt;br /&gt;
#&lt;br /&gt;
# URL: http://www.cs.unibo.it/~renzo/so/pratiche/2013.09.13.pdf 	*&lt;br /&gt;
# Autore: Eduardo Santarelli&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class LineCounter:&lt;br /&gt;
&lt;br /&gt;
    def __init__(self, fileList):&lt;br /&gt;
        # A list containing the char-per-line&lt;br /&gt;
        # count for the specified files.&lt;br /&gt;
        # Value for line 1 is in result[0] and so on.&lt;br /&gt;
        self.result = []&lt;br /&gt;
        self.files = fileList # List of files to elaborate.&lt;br /&gt;
        self.getLinesCount()&lt;br /&gt;
&lt;br /&gt;
    # Count the chars for each lines of specified file,&lt;br /&gt;
    # add the result to the relevant field in self.result,&lt;br /&gt;
    # if already present, append the value to the&lt;br /&gt;
    # end of the list if not.&lt;br /&gt;
    def countLines(self, file):&lt;br /&gt;
        i = 0&lt;br /&gt;
        fd = open(file)&lt;br /&gt;
        for line in fd:&lt;br /&gt;
            lineLength = len(line)&lt;br /&gt;
            # If an entry for the current line does&lt;br /&gt;
            # not exist, append the value to the end of the list&lt;br /&gt;
            try:&lt;br /&gt;
                self.result[i] += lineLength&lt;br /&gt;
            except IndexError:&lt;br /&gt;
                self.result.append(lineLength)&lt;br /&gt;
            i = i+1&lt;br /&gt;
        fd.close()&lt;br /&gt;
&lt;br /&gt;
    # Call self.countLines() for each file in self.files&lt;br /&gt;
    # in order to build the self.result list&lt;br /&gt;
    def getLinesCount(self):&lt;br /&gt;
        for entry in self.files:&lt;br /&gt;
            self.countLines(entry)&lt;br /&gt;
&lt;br /&gt;
    # Print the items in self.result, each preceded by the&lt;br /&gt;
    # corresponding line number.&lt;br /&gt;
    def printLines(self):&lt;br /&gt;
        i = 1&lt;br /&gt;
        for value in self.result:&lt;br /&gt;
            print(&amp;quot;{:d}: {:d}&amp;quot;.format(i, value))&lt;br /&gt;
            i = i+1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
import os&lt;br /&gt;
&lt;br /&gt;
lc = LineCounter(os.listdir(os.getcwd()))&lt;br /&gt;
lc.printLines()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Pirata===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
#Esercizio 3: Script bash o Python: (10 punti):&lt;br /&gt;
#Sia data una directory che contiene file di testo.&lt;br /&gt;
#Scopo dell'esercizio e' di contare i caratteri delle corrispondenti righe di testo di tutti i file della directory, si vuole cioe' sapere&lt;br /&gt;
#il numero totale di caratteri presenti nelle prime righe di tutti i file, nelle seconde linee, ecc.&lt;br /&gt;
#$ ccpl mydir&lt;br /&gt;
#1 234&lt;br /&gt;
#2 21&lt;br /&gt;
#3 333&lt;br /&gt;
# .....&lt;br /&gt;
#l'ouput significa che se contiamo tutti i caratteri contenuti nella prima riga di tutti i file in mydir otteniamo 234 (mydir/file1&lt;br /&gt;
#puo' avere 40 caratteri nella prima riga, mydir/file2 ne puo' avere 20, ecc... procedendo per tutti i file di mydir la somma fa 234)&lt;br /&gt;
&lt;br /&gt;
#! bin/bash&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
nline=1&lt;br /&gt;
touch fileresult&lt;br /&gt;
paste $1/* &amp;gt; fileresult             #merges every file's line from the directory passed to the file fileresult&lt;br /&gt;
while read line; do&lt;br /&gt;
	count=$(echo $line | wc -m)      #counts the number of char in each line of fileresult&lt;br /&gt;
	echo  $nline $count&lt;br /&gt;
	nline=$(($nline + 1))	&lt;br /&gt;
done &amp;lt; fileresult&lt;br /&gt;
rm fileresult&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Maldus===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#! /bin/bash&lt;br /&gt;
&lt;br /&gt;
count=1		#conta il numero di riga che sto considerando&lt;br /&gt;
bool=1		#resta 1 finchè ci sono righe da contare in qualche file&lt;br /&gt;
if [[ -n $1 ]]; then&lt;br /&gt;
	cd $1&lt;br /&gt;
else&lt;br /&gt;
	exit 1&lt;br /&gt;
fi&lt;br /&gt;
file=`ls`&lt;br /&gt;
&lt;br /&gt;
while [[ $bool -eq 1 ]]; do&lt;br /&gt;
	tot=0&lt;br /&gt;
	bool=0&lt;br /&gt;
	for word in $file; do&lt;br /&gt;
		if [[ (-r $word) &amp;amp;&amp;amp; (-s $word) &amp;amp;&amp;amp; ( -f $word) &amp;amp;&amp;amp; (! $word = *~ ) ]]; then&lt;br /&gt;
			x=`awk 'NR==c' c=$count $word | wc -m`	#considero solo la riga contata da count&lt;br /&gt;
			if [[ $x -ne 0 ]]; then			#se la riga non è vuota&lt;br /&gt;
				tot=`expr $tot + $x - 1`	#con -1 tolgo il carattere \n che era stato contato da awk&lt;br /&gt;
				bool=1				#bool=1 finchè c'è qualche riga da contare&lt;br /&gt;
			fi&lt;br /&gt;
		fi&lt;br /&gt;
	done&lt;br /&gt;
	if [[ $bool -eq 1 ]]; then &lt;br /&gt;
		echo &amp;quot;$count: $tot&amp;quot;&lt;br /&gt;
	fi&lt;br /&gt;
	count=`expr $count + 1`&lt;br /&gt;
done&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Davide Quadrelli===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
if [[ -n $1 ]]; then&lt;br /&gt;
	cd $1&lt;br /&gt;
	files=`ls -p | grep -v /`	#elenco file&lt;br /&gt;
else&lt;br /&gt;
	echo&amp;quot;Inserire cartella come parametro&amp;quot;&lt;br /&gt;
	exit&lt;br /&gt;
fi&lt;br /&gt;
array=()&lt;br /&gt;
#per ogni file nella cartella&lt;br /&gt;
for file in $files; do&lt;br /&gt;
	i=0&lt;br /&gt;
	while read line; do&lt;br /&gt;
		#conto la lunghezza di ogni riga e la aggiungo nella corrispondente cella dell'array&lt;br /&gt;
		len=`expr length &amp;quot;${line}&amp;quot;`&lt;br /&gt;
		array[$i]=`expr ${array[$i]} + $len`&lt;br /&gt;
		((i++))&lt;br /&gt;
	done &amp;lt; $file&lt;br /&gt;
done&lt;br /&gt;
#a questo punto l'i-esima cella contiene la somma delle lunghezze della i-esima riga di ogni file&lt;br /&gt;
i=1&lt;br /&gt;
for n in ${array[@]}; do&lt;br /&gt;
	echo &amp;quot;$i $n&amp;quot;&lt;br /&gt;
	((i++))&lt;br /&gt;
done&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>FedericoB</name></author>
	</entry>
</feed>