<?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=Mak</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=Mak"/>
	<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php/Special:Contributions/Mak"/>
	<updated>2026-05-04T05:31:15Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.35.5</generator>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaTeorica_2013.01.24&amp;diff=1955</id>
		<title>ProvaTeorica 2013.01.24</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaTeorica_2013.01.24&amp;diff=1955"/>
		<updated>2017-07-08T09:32:45Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* Esercizio g1 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;testo: http://www.cs.unibo.it/~renzo/so/compiti/2013-01-24.tot.pdf&lt;br /&gt;
&lt;br /&gt;
Esercizio C1:&lt;br /&gt;
(a) Scrivere un monitor nmbb che realizzi un buffer limitato (di ampiezza BUFSIZE) che consenta alle&lt;br /&gt;
chiamate write (inserimento nel buffer) e read (lettura da buffer) di operare su vettori di piu' elementi. In particolare&lt;br /&gt;
l'interfaccia delle procedure entry da implementare e' la seguente:&lt;br /&gt;
procedure entry write(int n, struct elem *v);&lt;br /&gt;
procedure entry read(int m, struct elem *w);&lt;br /&gt;
se n o m sono maggiori di BUFSIZE le funzioni non devono fare nulla (caso di errore).&lt;br /&gt;
La funzione write deve attendere che ci sia spazio nel buffer per inserire n elementi (il vettore v conterra' n elementi).&lt;br /&gt;
Solo quando e' possibile completare l'operazione vengono inseriti tutti gli elementi di v nel buffer.&lt;br /&gt;
La funzione read attende che vi siano almeno m elementi nel buffer quindi estrae dal buffer (in ordine FIFO) m elementi e li copia nel vettore w .&lt;br /&gt;
(b) sono possibili casi di deadlock? (motivare dettagliatamente la risposta)&lt;br /&gt;
&lt;br /&gt;
La mia Soluzione:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
read(): legge dalla coda un elemento senza rimuoverlo&lt;br /&gt;
queue(elem v,int n): inserisce nella coda n elementi v;&lt;br /&gt;
dequeue(int n): legge n elementi dalla coda e li rimuove;&lt;br /&gt;
&lt;br /&gt;
monitor  mnbb{&lt;br /&gt;
	conditon oktowrite, oktoread;&lt;br /&gt;
	queue qwrite,qread;&lt;br /&gt;
&lt;br /&gt;
	procedure entry write(int n, struct elem *v){&lt;br /&gt;
		if(n &amp;gt; BUFSIZE){&lt;br /&gt;
			return(ERROR);&lt;br /&gt;
		}&lt;br /&gt;
		if( n &amp;gt; (BUFSIZE - buff.lengh)){&lt;br /&gt;
			qwrite.queue( n, 1 );&lt;br /&gt;
			oktowrite.wait();&lt;br /&gt;
		}&lt;br /&gt;
		buff.queue( v, n );&lt;br /&gt;
		if(qread.read() &amp;lt;= buff.lengh){&lt;br /&gt;
			qread.dequeue(1);&lt;br /&gt;
			oktoread.signal();&lt;br /&gt;
		}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
	procedure entry read(int m, struct elem *w){&lt;br /&gt;
		if(m &amp;gt; BUFSIZE){&lt;br /&gt;
			return(ERROR);&lt;br /&gt;
		} &lt;br /&gt;
		if(m &amp;gt; buff.lengh){&lt;br /&gt;
			qread.queue( m, 1 );&lt;br /&gt;
			oktoread.wait();&lt;br /&gt;
		}&lt;br /&gt;
		w = buff.dequeue(m);&lt;br /&gt;
		if(qwrite.read() &amp;lt;= (BUFSIZE - buff.lengh)){&lt;br /&gt;
			qwrite.dequeue(1);&lt;br /&gt;
			oktowirte.signal();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
- Midolo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
monitor nmbb{&lt;br /&gt;
	queue buffer;&lt;br /&gt;
	condition oktowrite;&lt;br /&gt;
	condition oktoread;&lt;br /&gt;
	&lt;br /&gt;
	/* assumiamo che essendo un buffer limitato ci sia un solo processo che vuole scrivere e un solo processo che vuole leggere */&lt;br /&gt;
	int N, M;&lt;br /&gt;
	&lt;br /&gt;
	procedure entry write(int n, struct elem *v){&lt;br /&gt;
		if(n&amp;gt;BUFSIZE) return;&lt;br /&gt;
		N=n;&lt;br /&gt;
		if((BUFSIZE - buffer.len) &amp;lt; N)&lt;br /&gt;
			oktowrite.wait();&lt;br /&gt;
		&lt;br /&gt;
		for(i=0; i&amp;lt;N; i++)&lt;br /&gt;
			buffer.enqueue(v[i]);&lt;br /&gt;
			&lt;br /&gt;
		if(buffer.len &amp;gt;= M)&lt;br /&gt;
			oktoread.signal();&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
	procedure entry read(int m, struct elem *w){&lt;br /&gt;
		if(m&amp;gt;BUFSIZE) return;&lt;br /&gt;
		M=m;&lt;br /&gt;
		if((BUFSIZE - buffer.len) &amp;lt; M)&lt;br /&gt;
			oktoread.wait();&lt;br /&gt;
		&lt;br /&gt;
		for(i=0; i&amp;lt;M; i++)&lt;br /&gt;
			w[i] = buffer.dequeue;&lt;br /&gt;
		&lt;br /&gt;
		if((BUFSIZE - buffer.len) &amp;gt;= N)&lt;br /&gt;
			oktowrite.signal();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Si può verificare deadlock. Esempio: un lettore vuole leggere un tot di elementi, ma quelli presenti non sono sufficienti. Uno scrittore a sua volta non può più scrivere un certo numero di elementi in quanto il buffer è parzialmente occupato. Lo scrittore aspetterà che il lettore legga; il lettore aspetterà invece lo scrittore: deadlock.&lt;br /&gt;
&lt;br /&gt;
L'eventualità che si verifichi deadlock tuttavia è contemplata dalla traccia, come fa capire il punto b)&lt;br /&gt;
&lt;br /&gt;
Gabriele e Giulia&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Esercizio g1 ==&lt;br /&gt;
                                                                                                               &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|TEMPO&lt;br /&gt;
|1&lt;br /&gt;
|2&lt;br /&gt;
|3&lt;br /&gt;
|4&lt;br /&gt;
|5&lt;br /&gt;
|6&lt;br /&gt;
|7&lt;br /&gt;
|8&lt;br /&gt;
|9&lt;br /&gt;
|10&lt;br /&gt;
|11&lt;br /&gt;
|12&lt;br /&gt;
|13&lt;br /&gt;
|14&lt;br /&gt;
|15&lt;br /&gt;
|16&lt;br /&gt;
|17&lt;br /&gt;
|18&lt;br /&gt;
|19&lt;br /&gt;
|20&lt;br /&gt;
|21&lt;br /&gt;
|22&lt;br /&gt;
|23&lt;br /&gt;
|24&lt;br /&gt;
|-&lt;br /&gt;
|CPU 1&lt;br /&gt;
|P1&lt;br /&gt;
|P1&lt;br /&gt;
|P1&lt;br /&gt;
|P4&lt;br /&gt;
|P4&lt;br /&gt;
|P4&lt;br /&gt;
|P3&lt;br /&gt;
|P2&lt;br /&gt;
|P2&lt;br /&gt;
|&amp;lt;br&amp;gt;&lt;br /&gt;
|&amp;lt;br&amp;gt;&lt;br /&gt;
|P1&lt;br /&gt;
|P1&lt;br /&gt;
|&amp;lt;br&amp;gt;&lt;br /&gt;
|P3&lt;br /&gt;
|P3&lt;br /&gt;
|P3&lt;br /&gt;
|P4&lt;br /&gt;
|&amp;lt;br&amp;gt;&lt;br /&gt;
|P1&lt;br /&gt;
|P1&lt;br /&gt;
|P1&lt;br /&gt;
|P1&lt;br /&gt;
|P1&lt;br /&gt;
|-&lt;br /&gt;
|CPU 2&lt;br /&gt;
|P2&lt;br /&gt;
|P2&lt;br /&gt;
|P3&lt;br /&gt;
|P3&lt;br /&gt;
|P3&lt;br /&gt;
|P1&lt;br /&gt;
|P1&lt;br /&gt;
|P4&lt;br /&gt;
|P4&lt;br /&gt;
|P4&lt;br /&gt;
|P4&lt;br /&gt;
|P4&lt;br /&gt;
|P4&lt;br /&gt;
|P4&lt;br /&gt;
|P4&lt;br /&gt;
|P4&lt;br /&gt;
|P2&lt;br /&gt;
|P2&lt;br /&gt;
|P2&lt;br /&gt;
|P2&lt;br /&gt;
|P2&lt;br /&gt;
|P3&lt;br /&gt;
|P3&lt;br /&gt;
|P3&lt;br /&gt;
|-&lt;br /&gt;
|I/O&lt;br /&gt;
|&amp;lt;br&amp;gt;&lt;br /&gt;
|&amp;lt;br&amp;gt;&lt;br /&gt;
|P2&lt;br /&gt;
|P2&lt;br /&gt;
|P2&lt;br /&gt;
|P2&lt;br /&gt;
|&amp;lt;br&amp;gt;&lt;br /&gt;
|P1&lt;br /&gt;
|P1&lt;br /&gt;
|P1&lt;br /&gt;
|P1&lt;br /&gt;
|P3&lt;br /&gt;
|P3&lt;br /&gt;
|P3&lt;br /&gt;
|P2&lt;br /&gt;
|P1&lt;br /&gt;
|P1&lt;br /&gt;
|P1&lt;br /&gt;
|P1&lt;br /&gt;
|P3&lt;br /&gt;
|P3&lt;br /&gt;
|P4&lt;br /&gt;
|&amp;lt;br&amp;gt;&lt;br /&gt;
|&amp;lt;br&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
GiuliaN.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Mak&lt;br /&gt;
&lt;br /&gt;
   SMP |1|1|1|4|4|4|3|2|2| | |1|1| |3|3|3|4| |1|1|1|1|1|&lt;br /&gt;
       |2|2|3|3|3|1|1|4|4|4|4|4|4|4|4|4|2|2|2|2|2|3|3|3|&lt;br /&gt;
   RQ  |3|3| |1|1|3|2| | | | | | | | | | | | | | | | | |&lt;br /&gt;
       |4|4|4| | | |4| | | | | | | |3|2|4| | | | | | | |&lt;br /&gt;
   IO  | | |2|2|2|2| |1|1|1|1|3|3|3|2|1|1|1|1|3|3|4| | |&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_Teorica_2013.06.21&amp;diff=1954</id>
		<title>Prova Teorica 2013.06.21</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_Teorica_2013.06.21&amp;diff=1954"/>
		<updated>2017-07-07T16:02:14Z</updated>

		<summary type="html">&lt;p&gt;Mak: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;h1&amp;gt;[http://www.cs.unibo.it/~renzo/so/compiti/2013.06.21.tot.pdf Testo del compito]&amp;lt;/h1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2&amp;gt; Esercizio c.1 &amp;lt;/h2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
monitor nvie&lt;br /&gt;
{&lt;br /&gt;
	condition oktoread;&lt;br /&gt;
	queue buffer[N]; &lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
	procedure entry put(int n, generic object)&lt;br /&gt;
	{&lt;br /&gt;
		&lt;br /&gt;
                           if (buffer[n].length &amp;lt; NELEM)&lt;br /&gt;
				buffer[n].enqueue(object);&lt;br /&gt;
 &lt;br /&gt;
		           oktoread.signal();&lt;br /&gt;
 &lt;br /&gt;
	}&lt;br /&gt;
 &lt;br /&gt;
	procedure entry generic get(generic *object)&lt;br /&gt;
	{&lt;br /&gt;
&lt;br /&gt;
               int count=0;&lt;br /&gt;
&lt;br /&gt;
			for (int i=0;i&amp;lt;N;i++)&lt;br /&gt;
                        {   &lt;br /&gt;
                            if (buffer[i].length&amp;gt;0)&lt;br /&gt;
                                  count++;&lt;br /&gt;
                            return None ;&lt;br /&gt;
                        }&lt;br /&gt;
			for (int j=0;j&amp;lt;N;j++)&lt;br /&gt;
                        {   if (count&amp;gt;=N/2)&lt;br /&gt;
                                (buffer[j].dequeue(*object) ;&lt;br /&gt;
                            oktoread.wait();&lt;br /&gt;
                        }&lt;br /&gt;
       }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Save&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2&amp;gt; Esercizio g.1 &amp;lt;/h2&amp;gt;&lt;br /&gt;
[[http://so.v2.cs.unibo.it/wiki/images/0/02/G1paginazione.jpg soluzione]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2&amp;gt; Esercizio g.1 &amp;lt;/h2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   P1 2 2 2 2 2&lt;br /&gt;
   P2 3 2 3 2 3&lt;br /&gt;
   P3 1 2 1 2 1&lt;br /&gt;
&lt;br /&gt;
NON-PREEMPTIVE&lt;br /&gt;
   &lt;br /&gt;
   CPU |1|1|2|2|2|1|1|2|2|2|1|1|2|2|2|3| | |3| | |3|&lt;br /&gt;
   RQ  |2|2|1|1|1|2|2|1|1|1|2|2|3|3|3| | | | | | | |&lt;br /&gt;
       |3|3|3|3|3|3|3|3|3|3|3|3| | | | | | | | | | |&lt;br /&gt;
   IO1 | | |1|1| | | |1|1| | | | | | | | | | | | | |&lt;br /&gt;
   IO2 | | | | | |2|2| | | |2|2| | | | | | | | | | |           &lt;br /&gt;
   IO3 | | | | | | | | | | | | | | | | |3|3| |3|3| |&lt;br /&gt;
&lt;br /&gt;
PREEMPTIVE&lt;br /&gt;
&lt;br /&gt;
   CPU |1|1|2|2|1|1|2|3|1|1|2|2|2|3|2|2|2|3|&lt;br /&gt;
   RQ  |2|2| | |2|2| | | |2|3|3|3| | | |3| |&lt;br /&gt;
       |3|3|3|3|3|3|3| | | | | | | | | | | |&lt;br /&gt;
   IO1 | | |1|1| | |1|1| | | | | | | | | | |&lt;br /&gt;
   IO2 | | | | | | | |2|2| | | | |2|2| | | |&lt;br /&gt;
   IO3 | | | | | | | | |3|3| | | | |3|3| | |&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2&amp;gt; Esercizio g.2 &amp;lt;/h2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
a) quali sono le differenze fra la paginazione e la segmentazione nella gestione della memoria?&lt;br /&gt;
&lt;br /&gt;
PAGINAZIONE&lt;br /&gt;
Dimensioni pagina fissa; suddivisione automatica; le pagine possono contenere informazioni disomogenee (codice, dati); una pagina ha un indirizzo; dim tipiche: 1-4KB&lt;br /&gt;
&lt;br /&gt;
SEGMENTAZIONE&lt;br /&gt;
Dimensioni variabili; la suddivisione in segmenti e' compito del programmatore; i segmenti contengono dati omogenei; un segmento ha un nome; dim tipica: 64KB-1MB&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
b) quali sono le differenze fra le operazioni P e V dei semafori e le operazioni wait/signal delle variabili di condizione?&lt;br /&gt;
&lt;br /&gt;
Signal() non ha alcun effetto se nessun processo sta attendendo la condizione. V memorizza il verificarsi degli eventi.&lt;br /&gt;
Wait() e' sempre bloccante. P, se il semaforo ha valore positivo, no. &lt;br /&gt;
Il processo risvegliato dalla signal viene eseguito per primo.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
c) allocazione contigua, concatenata e indicizzata nei file system, quali sono le differenze e i campi di applicazione?&lt;br /&gt;
&lt;br /&gt;
Nell'allocazione contigua ogni file viene allocato in blocchi di memoria adiacenti. &lt;br /&gt;
&lt;br /&gt;
PRO: non sono presenti strutture di collegamento, l'accesso sequenziale è efficiente. Anche l'accesso diretto risulta efficiente.&lt;br /&gt;
&lt;br /&gt;
CON: frammentazione esterna, politica scelta blocchi liberi da usare; i file non possono crescere&lt;br /&gt;
&lt;br /&gt;
APP: e'utilizzato nell'ISO 9660 (dischi ottici) e nel file system NTFS. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nell'allocazione concatenata ogni file è una lista concatenata di blocchi ed ogni blocco contiene un puntatore al blocco successivo.&lt;br /&gt;
&lt;br /&gt;
PRO: accesso sequenziale o in append mode efficiente, risolve la frammentazione esterna&lt;br /&gt;
&lt;br /&gt;
CON: l'accesso diretto è inefficiente, se il blocco è piccolo l'overhead per i puntatori può essere rilevante.&lt;br /&gt;
&lt;br /&gt;
APP: utile per i log, dato l'accesso efficiente in append mode. Usata in Xerox alto, DEC TOPS-10&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nell'allocazione indicizzata l'elenco dei blocchi che compongono un file viene memorizzato in un'area indice, quindi per accedere ad un file si carica in memoria la sua area indice e si utilizzano i puntatori contenuti.&lt;br /&gt;
&lt;br /&gt;
PRO: risolve frammentazione esterna; efficiente per accesso diretto. Il blocco indice viene caricato in memoria solo quando il file è aperto.&lt;br /&gt;
&lt;br /&gt;
CON: la dimensinoe del blocco indice determina ampiezza massima file&lt;br /&gt;
&lt;br /&gt;
APP: file grosse dimensioni in quanto e' facile accedere alla parte intermedia del file. Utile per i file system di uso generale.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2&amp;gt; Esercizio c.2 &amp;lt;/h2&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#Casi possibili delle iterazioni&lt;br /&gt;
int val = 0&lt;br /&gt;
&lt;br /&gt;
PQPQQ (((val +1) *2) +1) * 2 * 2	→ 	12&lt;br /&gt;
PQQPQ (( val +1 ) * 2 * 2) +1 * 2	→	12&lt;br /&gt;
QQPQP (((val * 2 * 2) + 1 )* 2) + 1	→	3&lt;br /&gt;
QPQQP (((val * 2) + 1) * 2 * 2) + 1	→	5&lt;br /&gt;
QPQPQ ((((val * 2) + 1) * 2) + 1) * 2	→	6&lt;br /&gt;
&lt;br /&gt;
a) 	#Quindi i valori possibili sono → (3, 5, 6, 12)&lt;br /&gt;
b)	#&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
- Midolo e Marangoni&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_Teorica_2013.06.21&amp;diff=1953</id>
		<title>Prova Teorica 2013.06.21</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_Teorica_2013.06.21&amp;diff=1953"/>
		<updated>2017-07-07T16:00:34Z</updated>

		<summary type="html">&lt;p&gt;Mak: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;h1&amp;gt;[http://www.cs.unibo.it/~renzo/so/compiti/2013.06.21.tot.pdf Testo del compito]&amp;lt;/h1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2&amp;gt; Esercizio c.1 &amp;lt;/h2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
monitor nvie&lt;br /&gt;
{&lt;br /&gt;
	condition oktoread;&lt;br /&gt;
	queue buffer[N]; &lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
	procedure entry put(int n, generic object)&lt;br /&gt;
	{&lt;br /&gt;
		&lt;br /&gt;
                           if (buffer[n].length &amp;lt; NELEM)&lt;br /&gt;
				buffer[n].enqueue(object);&lt;br /&gt;
 &lt;br /&gt;
		           oktoread.signal();&lt;br /&gt;
 &lt;br /&gt;
	}&lt;br /&gt;
 &lt;br /&gt;
	procedure entry generic get(generic *object)&lt;br /&gt;
	{&lt;br /&gt;
&lt;br /&gt;
               int count=0;&lt;br /&gt;
&lt;br /&gt;
			for (int i=0;i&amp;lt;N;i++)&lt;br /&gt;
                        {   &lt;br /&gt;
                            if (buffer[i].length&amp;gt;0)&lt;br /&gt;
                                  count++;&lt;br /&gt;
                            return None ;&lt;br /&gt;
                        }&lt;br /&gt;
			for (int j=0;j&amp;lt;N;j++)&lt;br /&gt;
                        {   if (count&amp;gt;=N/2)&lt;br /&gt;
                                (buffer[j].dequeue(*object) ;&lt;br /&gt;
                            oktoread.wait();&lt;br /&gt;
                        }&lt;br /&gt;
       }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Save&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2&amp;gt; Esercizio g.1 &amp;lt;/h2&amp;gt;&lt;br /&gt;
[[http://so.v2.cs.unibo.it/wiki/images/0/02/G1paginazione.jpg soluzione]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2&amp;gt; Esercizio g.1 &amp;lt;/h2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   P1 2 2 2 2 2&lt;br /&gt;
   P2 3 2 3 2 3&lt;br /&gt;
   P3 1 2 1 2 1&lt;br /&gt;
&lt;br /&gt;
NON-PREEMPTIVE&lt;br /&gt;
   &lt;br /&gt;
   CPU |1|1|2|2|2|1|1|2|2|2|1|1|2|2|2|3| | |3| | |3|&lt;br /&gt;
   RQ  |2|2|1|1|1|2|2|1|1|1|2|2|3|3|3| | | | | | | |&lt;br /&gt;
       |3|3|3|3|3|3|3|3|3|3|3|3| | | | | | | | | | |&lt;br /&gt;
   IO1 | | |1|1| | | |1|1| | | | | | | | | | | | | |&lt;br /&gt;
   IO2 | | | | | |2|2| | | |2|2| | | | | | | | | | |           &lt;br /&gt;
   IO3 | | | | | | | | | | | | | | | | |3|3| |3|3| |&lt;br /&gt;
&lt;br /&gt;
PREEMPTIVE&lt;br /&gt;
&lt;br /&gt;
   CPU |1|1|2|2|1|1|2|3|1|1|2|2|2|3|2|2|2|3|&lt;br /&gt;
   RQ  |2|2| | |2|2| | | |2|3|3|3| | | |3| |&lt;br /&gt;
       |3|3|3|3|3|3|3| | | | | | | | | | | |&lt;br /&gt;
   IO1 | | |1|1| | |1|1| | | | | | | | | | |&lt;br /&gt;
   IO2 | | | | | | | |2|2| | | | |2|2| | | |&lt;br /&gt;
   IO3 | | | | | | | | |3|3| | | | |3|3| | |&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2&amp;gt; Esercizio g.2 &amp;lt;/h2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
a) quali sono le differenze fra la paginazione e la segmentazione nella gestione della memoria?&lt;br /&gt;
&lt;br /&gt;
PAGINAZIONE&lt;br /&gt;
Dimensioni pagina fissa&lt;br /&gt;
Suddivisione automatica&lt;br /&gt;
Le pagine possono contenere informazioni disomogenee (codice, dati) &lt;br /&gt;
Una pagina ha un indirizzo&lt;br /&gt;
Dim tipiche: 1-4KB&lt;br /&gt;
&lt;br /&gt;
SEGMENTAZIONE&lt;br /&gt;
Dimensioni variabili&lt;br /&gt;
La suddivisione in segmenti e' compito del programmatore&lt;br /&gt;
I segmenti contengono dati omogenei&lt;br /&gt;
Un segmento ha un nome&lt;br /&gt;
Dim tipica: 64KB-1MB&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
b) quali sono le differenze fra le operazioni P e V dei semafori e le operazioni wait/signal delle variabili di condizione?&lt;br /&gt;
&lt;br /&gt;
Signal() non ha alcun effetto se nessun processo sta attendendo la condizione. V memorizza il verificarsi degli eventi.&lt;br /&gt;
Wait() e' sempre bloccante. P, se il semaforo ha valore positivo, no. &lt;br /&gt;
Il processo risvegliato dalla signal viene eseguito per primo.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
c) allocazione contigua, concatenata e indicizzata nei file system, quali sono le differenze e i campi di applicazione?&lt;br /&gt;
&lt;br /&gt;
Nell'allocazione contigua ogni file viene allocato in blocchi di memoria adiacenti. &lt;br /&gt;
&lt;br /&gt;
PRO: non sono presenti strutture di collegamento, l'accesso sequenziale è efficiente. Anche l'accesso diretto risulta efficiente.&lt;br /&gt;
&lt;br /&gt;
CON: frammentazione esterna, politica scelta blocchi liberi da usare; i file non possono crescere&lt;br /&gt;
&lt;br /&gt;
APP: e'utilizzato nell'ISO 9660 (dischi ottici) e nel file system NTFS. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nell'allocazione concatenata ogni file è una lista concatenata di blocchi ed ogni blocco contiene un puntatore al blocco successivo.&lt;br /&gt;
&lt;br /&gt;
PRO: accesso sequenziale o in append mode efficiente, risolve la frammentazione esterna&lt;br /&gt;
&lt;br /&gt;
CON: l'accesso diretto è inefficiente, se il blocco è piccolo l'overhead per i puntatori può essere rilevante.&lt;br /&gt;
&lt;br /&gt;
APP: utile per i log, dato l'accesso efficiente in append mode. Usata in Xerox alto, DEC TOPS-10&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nell'allocazione indicizzata l'elenco dei blocchi che compongono un file viene memorizzato in un'area indice, quindi per accedere ad un file si carica in memoria la sua area indice e si utilizzano i puntatori contenuti.&lt;br /&gt;
&lt;br /&gt;
PRO: risolve frammentazione esterna; efficiente per accesso diretto. Il blocco indice viene caricato in memoria solo quando il file è aperto.&lt;br /&gt;
&lt;br /&gt;
CON: la dimensinoe del blocco indice determina ampiezza massima file&lt;br /&gt;
&lt;br /&gt;
APP: file grosse dimensioni in quanto e' facile accedere alla parte intermedia del file. Utile per i file system di uso generale.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2&amp;gt; Esercizio c.2 &amp;lt;/h2&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#Casi possibili delle iterazioni&lt;br /&gt;
int val = 0&lt;br /&gt;
&lt;br /&gt;
PQPQQ (((val +1) *2) +1) * 2 * 2	→ 	12&lt;br /&gt;
PQQPQ (( val +1 ) * 2 * 2) +1 * 2	→	12&lt;br /&gt;
QQPQP (((val * 2 * 2) + 1 )* 2) + 1	→	3&lt;br /&gt;
QPQQP (((val * 2) + 1) * 2 * 2) + 1	→	5&lt;br /&gt;
QPQPQ ((((val * 2) + 1) * 2) + 1) * 2	→	6&lt;br /&gt;
&lt;br /&gt;
a) 	#Quindi i valori possibili sono → (3, 5, 6, 12)&lt;br /&gt;
b)	#&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
- Midolo e Marangoni&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_Teorica_2013.09.12&amp;diff=1952</id>
		<title>Prova Teorica 2013.09.12</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_Teorica_2013.09.12&amp;diff=1952"/>
		<updated>2017-07-05T09:53:49Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* Esercizio g.2 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&amp;lt;h1&amp;gt;[http://www.cs.unibo.it/~renzo/so/compiti/2013.09.12.tot.pdf Testo del compito]&amp;lt;/h1&amp;gt;&lt;br /&gt;
== Esercizio g.2 ==&lt;br /&gt;
&lt;br /&gt;
Q: Il meccanismo RAID livello 0 aumenta la tolleranza ai guasti.&lt;br /&gt;
A: FALSO &lt;br /&gt;
&lt;br /&gt;
RAID 0 sebbene utilizzi l'acronimo RAID, in realtà non presenta meccanismi&lt;br /&gt;
di ridondanza. &lt;br /&gt;
I dati infatti sono suddivisi in strip consecutivi su dischi diversi, &lt;br /&gt;
aumentando le performance di lettura di dati sequenziali.&lt;br /&gt;
E' più indicato per applicazioni nelle quali l'affidabilità &lt;br /&gt;
non è un grosso problema.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Q: I microkernel consentono maggiori performance rispetto ai kernel&lt;br /&gt;
monolitici&lt;br /&gt;
A: FALSO &lt;br /&gt;
&lt;br /&gt;
C'è un overhead dovuto alla comunicazione mediata tramite kernel&lt;br /&gt;
del sistema operativo, anche se parzialmente superata con i s.o. recenti.&lt;br /&gt;
La comunicazione ha un overhead perchè è basata su message passing.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Q: L'uso di librerie dinamiche consente una minor occupazione di memoria &lt;br /&gt;
principale e secondaria.&lt;br /&gt;
A: VERO&lt;br /&gt;
&lt;br /&gt;
Mentre nel linking statico le routine di libreria vengono copiate in OGNI&lt;br /&gt;
programma che le usa (es: printf), con il linking dinamico le librerie&lt;br /&gt;
vengono implementate come codice reentrant, ovvero ne è presente una sola&lt;br /&gt;
istanza in memoria e tutti i programmi eseguono il codice di questa istanza.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Q: Esistono stringhe di riferimenti per le quali LRU e MIN causano lo stesso numero di page fault.&lt;br /&gt;
A: VERO&lt;br /&gt;
&lt;br /&gt;
LRU&lt;br /&gt;
&lt;br /&gt;
   0 1 2 3 4 1 2 3 0 2 3 0 1 3 0 1 &lt;br /&gt;
   0 0 0 0 4 4 4 4 4 4 4 4 4 4 4 4 &lt;br /&gt;
     1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 &lt;br /&gt;
       2 2 2 2 2 2 2 2 2 2 2 1 1 1 &lt;br /&gt;
         3 3 3 3 3 3 3 3 3 3 3 3 3  &lt;br /&gt;
           ^       ^       ^        3PF&lt;br /&gt;
MIN&lt;br /&gt;
&lt;br /&gt;
   0 1 2 3 4 1 2 3 0 2 3 0 1 3 0 1 &lt;br /&gt;
   0 0 0 0 4 4 4 4 4 4 4 4 4 4 4 4 &lt;br /&gt;
     1 1 1 1 1 1 1 0 0 0 0 0 0 0 0&lt;br /&gt;
       2 2 2 2 2 2 2 2 2 2 1 1 1 1&lt;br /&gt;
         3 3 3 3 3 3 3 3 3 3 3 3 3&lt;br /&gt;
           ^       ^       ^        3PF&lt;br /&gt;
&lt;br /&gt;
== Esercizio c.2 ==&lt;br /&gt;
&lt;br /&gt;
[[File:Esemafori.png]]&lt;br /&gt;
&lt;br /&gt;
by Mario&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_Teorica_2013.09.12&amp;diff=1951</id>
		<title>Prova Teorica 2013.09.12</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_Teorica_2013.09.12&amp;diff=1951"/>
		<updated>2017-07-05T09:51:01Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* Esercizio c.2 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&amp;lt;h1&amp;gt;[http://www.cs.unibo.it/~renzo/so/compiti/2013.09.12.tot.pdf Testo del compito]&amp;lt;/h1&amp;gt;&lt;br /&gt;
== Esercizio g.2 ==&lt;br /&gt;
&lt;br /&gt;
Q: Il meccanismo RAID livello 0 aumenta la tolleranza ai guasti.&lt;br /&gt;
A: F &lt;br /&gt;
&lt;br /&gt;
RAID 0 sebbene utilizzi l'acronimo RAID, in realtà non presenta meccanismi&lt;br /&gt;
di ridondanza. &lt;br /&gt;
I dati infatti sono suddivisi in strip consecutivi su dischi diversi, &lt;br /&gt;
aumentando le performance di lettura di dati sequenziali.&lt;br /&gt;
E' più indicato per applicazioni nelle quali l'affidabilità &lt;br /&gt;
non è un grosso problema.&lt;br /&gt;
&lt;br /&gt;
Q: I microkernel consentono maggiori performance rispetto ai kernel&lt;br /&gt;
monolitici&lt;br /&gt;
A: F &lt;br /&gt;
&lt;br /&gt;
C'è un overhead dovuto alla comunicazione mediata tramite kernel&lt;br /&gt;
del sistema operativo, anche se parzialmente superata con i s.o. recenti.&lt;br /&gt;
La comunicazione ha un overhead perchè è basata su message passing.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Q: L'uso di librerie dinamiche consente una minor occupazione di memoria &lt;br /&gt;
principale e secondaria.&lt;br /&gt;
A: V&lt;br /&gt;
&lt;br /&gt;
Mentre nel linking statico le routine di libreria vengono copiate in OGNI&lt;br /&gt;
programma che le usa (es: printf), con il linking dinamico le librerie&lt;br /&gt;
vengono implementate come codice reentrant, ovvero ne è presente una sola&lt;br /&gt;
istanza in memoria e tutti i programmi eseguono il codice di questa istanza.&lt;br /&gt;
&lt;br /&gt;
Q: Esistono stringhe di riferimenti per le quali LRU e MIN causano lo stesso&lt;br /&gt;
 numero di page fault.&lt;br /&gt;
A: v&lt;br /&gt;
&lt;br /&gt;
LRU&lt;br /&gt;
&lt;br /&gt;
0 1 2 3 4 1 2 3 0 2 3 0 1 3 0 1 &lt;br /&gt;
0 0 0 0 4 4 4 4 4 4 4 4 4 4 4 4 &lt;br /&gt;
  1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 &lt;br /&gt;
    2 2 2 2 2 2 2 2 2 2 2 1 1 1 &lt;br /&gt;
      3 3 3 3 3 3 3 3 3 3 3 3 3  &lt;br /&gt;
        ^       ^       ^        3PF&lt;br /&gt;
MIN&lt;br /&gt;
&lt;br /&gt;
0 1 2 3 4 1 2 3 0 2 3 0 1 3 0 1 &lt;br /&gt;
0 0 0 0 4 4 4 4 4 4 4 4 4 4 4 4 &lt;br /&gt;
  1 1 1 1 1 1 1 0 0 0 0 0 0 0 0&lt;br /&gt;
    2 2 2 2 2 2 2 2 2 2 1 1 1 1&lt;br /&gt;
      3 3 3 3 3 3 3 3 3 3 3 3 3&lt;br /&gt;
        ^       ^       ^        3PF&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Esercizio c.2 ==&lt;br /&gt;
&lt;br /&gt;
[[File:Esemafori.png]]&lt;br /&gt;
&lt;br /&gt;
by Mario&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaTeoria_2012.01.12&amp;diff=1949</id>
		<title>ProvaTeoria 2012.01.12</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaTeoria_2012.01.12&amp;diff=1949"/>
		<updated>2017-06-23T16:42:45Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* Esercizio 2 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=[http://www.cs.unibo.it/~renzo/so/compiti/2012-01-12.tot.pdf TESTO COMPITO]=&lt;br /&gt;
&lt;br /&gt;
== Esercizio 1 ==&lt;br /&gt;
   P1: 5 6 2 4 5&lt;br /&gt;
   P2: 2 4 2 2 5&lt;br /&gt;
   P3: 4 3 3 2 3&lt;br /&gt;
       C I C I C&lt;br /&gt;
 &lt;br /&gt;
P1 ready queue&lt;br /&gt;
 &lt;br /&gt;
   SMP|2|2|1|1|1|1|1| | |3|3|3| | | |1|1| | | | | | |1|1|1|1|1|   28 ms&lt;br /&gt;
      |3|3|3|3| |2|2| | | | | | | | | | | | | | | | | | | | | |&lt;br /&gt;
   RQ |1| | | | | | | | | | | | | | | | | | | | | | | | | | | | &lt;br /&gt;
   I/O| | |2|2|2|2|3|3|3|1|1|1|1|1|1|2|2|3|3|1|1|1|1| | | | | |  &lt;br /&gt;
 &lt;br /&gt;
P2 ready queue&lt;br /&gt;
 &lt;br /&gt;
   SMP|1|1|1|1|1| | | | | | | | | | | | | | | | | | | | |2|2|2|2|2|   30ms&lt;br /&gt;
      |3|3|3|3|2|2| |3|3|3| | | |1|1| | |2|2|3|3|3| |1|1|1|1|1| | |&lt;br /&gt;
   RQ |2|2|2|2| | | | | | | | | | | | | | | | | | | | | | | | | | |&lt;br /&gt;
   I/O| | | | |3|3|3|1|1|1|1|1|1|2|2|2|2|3|3|1|1|1|1|2|2|2| | | | |&lt;br /&gt;
 &lt;br /&gt;
P3 ready queue&lt;br /&gt;
&lt;br /&gt;
   SMP|1|1|1|1|1||2|2|| | | |1|1| |3|3|3| | | |1|1|1|1|1|   26ms&lt;br /&gt;
      |2|2|3|3|3|3| | | | | | | | | | |2|2|2|2|2| |3|3|3|&lt;br /&gt;
   RQ |3| | | | | | | | | | | | | | | | | | | | | | | | |&lt;br /&gt;
   I/O| | |2|2|2|2|1|1|1|1|1|1|3|3|3|2|2|1|1|1|1|3|3| | |&lt;br /&gt;
&lt;br /&gt;
== Esercizio 2 ==&lt;br /&gt;
&lt;br /&gt;
MIN&lt;br /&gt;
 &lt;br /&gt;
   0 1 2 3 4 5 6 0 1 2 3 4 5 0 1 2 3 4 6 0 1 2&lt;br /&gt;
 &lt;br /&gt;
   0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 &lt;br /&gt;
     1 1 1 1 1 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 2 2 2 2 2  &lt;br /&gt;
         3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 &lt;br /&gt;
           4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 &lt;br /&gt;
             5 6 6 6 6 6 6 5 5 5 5 5 5 6 6 6 6 &lt;br /&gt;
               ^           ^           ^         3 PF&lt;br /&gt;
&lt;br /&gt;
FIFO&lt;br /&gt;
 &lt;br /&gt;
   0 1 2 3 4 5 6 0 1 2 3 4 5 0 1 2 3 4 6 0 1 2&lt;br /&gt;
 &lt;br /&gt;
   0 0 0 0 0 0 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5     &lt;br /&gt;
     1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 6 6 6 6 &lt;br /&gt;
       2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 0 0 0&lt;br /&gt;
         3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 1 1&lt;br /&gt;
           4 4 4 4 4 4 3 3 3 3 3 3 3 3 3 3 3 2&lt;br /&gt;
             5 5 5 5 5 5 4 4 4 4 4 4 4 4 4 4 4 &lt;br /&gt;
               ^ ^ ^ ^ ^               ^ ^ ^ ^    9 PF&lt;br /&gt;
&lt;br /&gt;
==Esercizio 1==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
struct proc{&lt;br /&gt;
	int id;&lt;br /&gt;
	condition oktoexit;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
monitor seq{&lt;br /&gt;
	stack of struct proc inside; /*pila di proc*/&lt;br /&gt;
	&lt;br /&gt;
	procedure entry enter(){&lt;br /&gt;
		this=malloc(sizeof(struct proc)); //or this=new(struct proc);&lt;br /&gt;
		this-&amp;gt;id = getpid();&lt;br /&gt;
		inside.push(this);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure entry exit(){&lt;br /&gt;
		struct proc *mine = inside.search(getpid()); //cerca il mio elemento nella pila&lt;br /&gt;
		if(inside.first()!= mine) /*first restituisce il primo elemento in cima alla pila*/&lt;br /&gt;
			mine-&amp;gt;oktoexit.wait(); /*se l'elemento corrente non è il primo nella pila, allora deve aspettare*/&lt;br /&gt;
		inside.pop(); /*rimuovo l'elemento corrente dalla pila*/&lt;br /&gt;
		free(mine); //butto via il risultato perché è certamente == mine&lt;br /&gt;
		next=inside.first();&lt;br /&gt;
		inside.first()-&amp;gt;oktoexit.signal(); /*risvegliamo il primo elemento sulla pila*/&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
GiuliaN. (con le correzioni del professore)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
monitor seq&lt;br /&gt;
{&lt;br /&gt;
	condition* c=List() /*lista di condizioni, una per ogni processo arrivato*/&lt;br /&gt;
	int* procstack=Stack(); /*stack di processi per l'uscita LIFO*/&lt;br /&gt;
	&lt;br /&gt;
	procedure entry enter()&lt;br /&gt;
	{&lt;br /&gt;
		procstack.push(this-&amp;gt;p_id);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure entry exit()&lt;br /&gt;
	{&lt;br /&gt;
		if (procstack.Top()!=this-&amp;gt;p_id){ /*cioè se il processo non è in cima allo stack*/&lt;br /&gt;
			List.insert(this-&amp;gt;p_id);&lt;br /&gt;
			(List.search(this-&amp;gt;p_id)).wait(); /*aggiungo il mio p_id alla lista di condizioni e mi metto in wait*/&lt;br /&gt;
			}&lt;br /&gt;
		List.remove(this-&amp;gt;p_id);&lt;br /&gt;
		procstack.Pop();&lt;br /&gt;
		List.search(procstack.Top()).signal();&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NOTA: Con questa soluzione se l'ultimo processo chiama la exit e ci sono altri processi in coda, gli altri processi usciranno prima che l'ultimo esca dallo urgent stack per uscire a sua volta, quindi non credo che rispetti la traccia.&lt;br /&gt;
Però non ho trovato un'altra soluzione&lt;br /&gt;
&lt;br /&gt;
==Esercizio 2==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
pid index[N] /*index contiene gli id dei vari server*/&lt;br /&gt;
&lt;br /&gt;
process server[0]&lt;br /&gt;
{&lt;br /&gt;
	while(1)&lt;br /&gt;
	{&lt;br /&gt;
		tmp msg=arecv(*);&lt;br /&gt;
		for (i=1;i&amp;lt;N;i++)&lt;br /&gt;
			asend(index[i],tmp);&lt;br /&gt;
		print(tmp);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
process server[1...N-1]&lt;br /&gt;
{&lt;br /&gt;
	while(1)&lt;br /&gt;
	{&lt;br /&gt;
		msg tmp =arecv(*)&lt;br /&gt;
		if (tmp.sender==index[0])&lt;br /&gt;
			print(tmp);&lt;br /&gt;
		else&lt;br /&gt;
			asend(index[0],tmp);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
-stefano92&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
process server[0]{ //il primo processo server, che gestisce tutti gli altri&lt;br /&gt;
	while(1){&lt;br /&gt;
		(sender, m) = areceive(*);&lt;br /&gt;
		//chiunque mi abbia spedito il msg io lo stampo&lt;br /&gt;
		printf(m);&lt;br /&gt;
		//lo mando a tutti gli altri server (me escluso) in modo che lo stampino&lt;br /&gt;
		for(i=1; i&amp;lt;N; i++)&lt;br /&gt;
			asend((server0, m), server[i]);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
process server[i]{ //per i che va da 1 a N, dove lo 0-esimo è il primo server&lt;br /&gt;
	while(1){&lt;br /&gt;
		(sender, m) = areceive(*);&lt;br /&gt;
		if(sender == server0)&lt;br /&gt;
			printf(m);&lt;br /&gt;
		else //messaggio dal client&lt;br /&gt;
			asend(m, server0); //lo mando al server0, senza prima stamparlo&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
GiuliaN.&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaTeoria_2012.01.12&amp;diff=1948</id>
		<title>ProvaTeoria 2012.01.12</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaTeoria_2012.01.12&amp;diff=1948"/>
		<updated>2017-06-23T16:40:01Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* Esercizio 2 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=[http://www.cs.unibo.it/~renzo/so/compiti/2012-01-12.tot.pdf TESTO COMPITO]=&lt;br /&gt;
&lt;br /&gt;
== Esercizio 2 ==&lt;br /&gt;
&lt;br /&gt;
MIN&lt;br /&gt;
 &lt;br /&gt;
   0 1 2 3 4 5 6 0 1 2 3 4 5 0 1 2 3 4 6 0 1 2&lt;br /&gt;
 &lt;br /&gt;
   0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 &lt;br /&gt;
     1 1 1 1 1 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 2 2 2 2 2  &lt;br /&gt;
         3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 &lt;br /&gt;
           4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 &lt;br /&gt;
             5 6 6 6 6 6 6 5 5 5 5 5 5 6 6 6 6 &lt;br /&gt;
               ^           ^           ^         3 PF&lt;br /&gt;
&lt;br /&gt;
FIFO&lt;br /&gt;
 &lt;br /&gt;
   0 1 2 3 4 5 6 0 1 2 3 4 5 0 1 2 3 4 6 0 1 2&lt;br /&gt;
 &lt;br /&gt;
   0 0 0 0 0 0 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5     &lt;br /&gt;
     1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 6 6 6 6 &lt;br /&gt;
       2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 0 0 0&lt;br /&gt;
         3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 1 1&lt;br /&gt;
           4 4 4 4 4 4 3 3 3 3 3 3 3 3 3 3 3 2&lt;br /&gt;
             5 5 5 5 5 5 4 4 4 4 4 4 4 4 4 4 4 &lt;br /&gt;
               ^ ^ ^ ^ ^               ^ ^ ^ ^    9 PF&lt;br /&gt;
&lt;br /&gt;
==Esercizio 1==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
struct proc{&lt;br /&gt;
	int id;&lt;br /&gt;
	condition oktoexit;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
monitor seq{&lt;br /&gt;
	stack of struct proc inside; /*pila di proc*/&lt;br /&gt;
	&lt;br /&gt;
	procedure entry enter(){&lt;br /&gt;
		this=malloc(sizeof(struct proc)); //or this=new(struct proc);&lt;br /&gt;
		this-&amp;gt;id = getpid();&lt;br /&gt;
		inside.push(this);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure entry exit(){&lt;br /&gt;
		struct proc *mine = inside.search(getpid()); //cerca il mio elemento nella pila&lt;br /&gt;
		if(inside.first()!= mine) /*first restituisce il primo elemento in cima alla pila*/&lt;br /&gt;
			mine-&amp;gt;oktoexit.wait(); /*se l'elemento corrente non è il primo nella pila, allora deve aspettare*/&lt;br /&gt;
		inside.pop(); /*rimuovo l'elemento corrente dalla pila*/&lt;br /&gt;
		free(mine); //butto via il risultato perché è certamente == mine&lt;br /&gt;
		next=inside.first();&lt;br /&gt;
		inside.first()-&amp;gt;oktoexit.signal(); /*risvegliamo il primo elemento sulla pila*/&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
GiuliaN. (con le correzioni del professore)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
monitor seq&lt;br /&gt;
{&lt;br /&gt;
	condition* c=List() /*lista di condizioni, una per ogni processo arrivato*/&lt;br /&gt;
	int* procstack=Stack(); /*stack di processi per l'uscita LIFO*/&lt;br /&gt;
	&lt;br /&gt;
	procedure entry enter()&lt;br /&gt;
	{&lt;br /&gt;
		procstack.push(this-&amp;gt;p_id);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure entry exit()&lt;br /&gt;
	{&lt;br /&gt;
		if (procstack.Top()!=this-&amp;gt;p_id){ /*cioè se il processo non è in cima allo stack*/&lt;br /&gt;
			List.insert(this-&amp;gt;p_id);&lt;br /&gt;
			(List.search(this-&amp;gt;p_id)).wait(); /*aggiungo il mio p_id alla lista di condizioni e mi metto in wait*/&lt;br /&gt;
			}&lt;br /&gt;
		List.remove(this-&amp;gt;p_id);&lt;br /&gt;
		procstack.Pop();&lt;br /&gt;
		List.search(procstack.Top()).signal();&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NOTA: Con questa soluzione se l'ultimo processo chiama la exit e ci sono altri processi in coda, gli altri processi usciranno prima che l'ultimo esca dallo urgent stack per uscire a sua volta, quindi non credo che rispetti la traccia.&lt;br /&gt;
Però non ho trovato un'altra soluzione&lt;br /&gt;
&lt;br /&gt;
==Esercizio 2==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
pid index[N] /*index contiene gli id dei vari server*/&lt;br /&gt;
&lt;br /&gt;
process server[0]&lt;br /&gt;
{&lt;br /&gt;
	while(1)&lt;br /&gt;
	{&lt;br /&gt;
		tmp msg=arecv(*);&lt;br /&gt;
		for (i=1;i&amp;lt;N;i++)&lt;br /&gt;
			asend(index[i],tmp);&lt;br /&gt;
		print(tmp);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
process server[1...N-1]&lt;br /&gt;
{&lt;br /&gt;
	while(1)&lt;br /&gt;
	{&lt;br /&gt;
		msg tmp =arecv(*)&lt;br /&gt;
		if (tmp.sender==index[0])&lt;br /&gt;
			print(tmp);&lt;br /&gt;
		else&lt;br /&gt;
			asend(index[0],tmp);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
-stefano92&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
process server[0]{ //il primo processo server, che gestisce tutti gli altri&lt;br /&gt;
	while(1){&lt;br /&gt;
		(sender, m) = areceive(*);&lt;br /&gt;
		//chiunque mi abbia spedito il msg io lo stampo&lt;br /&gt;
		printf(m);&lt;br /&gt;
		//lo mando a tutti gli altri server (me escluso) in modo che lo stampino&lt;br /&gt;
		for(i=1; i&amp;lt;N; i++)&lt;br /&gt;
			asend((server0, m), server[i]);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
process server[i]{ //per i che va da 1 a N, dove lo 0-esimo è il primo server&lt;br /&gt;
	while(1){&lt;br /&gt;
		(sender, m) = areceive(*);&lt;br /&gt;
		if(sender == server0)&lt;br /&gt;
			printf(m);&lt;br /&gt;
		else //messaggio dal client&lt;br /&gt;
			asend(m, server0); //lo mando al server0, senza prima stamparlo&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
GiuliaN.&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaTeoria_2012.01.12&amp;diff=1947</id>
		<title>ProvaTeoria 2012.01.12</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaTeoria_2012.01.12&amp;diff=1947"/>
		<updated>2017-06-23T16:39:09Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* Esercizio 1 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=[http://www.cs.unibo.it/~renzo/so/compiti/2012-01-12.tot.pdf TESTO COMPITO]=&lt;br /&gt;
&lt;br /&gt;
== Esercizio 2 ==&lt;br /&gt;
&lt;br /&gt;
MIN&lt;br /&gt;
 &lt;br /&gt;
0 1 2 3 4 5 6 0 1 2 3 4 5 0 1 2 3 4 6 0 1 2&lt;br /&gt;
 &lt;br /&gt;
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 &lt;br /&gt;
  1 1 1 1 1 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 2 2 2 2 2  &lt;br /&gt;
      3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 &lt;br /&gt;
        4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 &lt;br /&gt;
          5 6 6 6 6 6 6 5 5 5 5 5 5 6 6 6 6 &lt;br /&gt;
            ^           ^           ^         3 PF&lt;br /&gt;
&lt;br /&gt;
FIFO&lt;br /&gt;
 &lt;br /&gt;
0 1 2 3 4 5 6 0 1 2 3 4 5 0 1 2 3 4 6 0 1 2&lt;br /&gt;
 &lt;br /&gt;
0 0 0 0 0 0 6 6 6 6 6 6 5 5 5 5 5 5 5 5 5 5     &lt;br /&gt;
  1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 6 6 6 6 &lt;br /&gt;
    2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 0 0 0&lt;br /&gt;
      3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 1 1&lt;br /&gt;
        4 4 4 4 4 4 3 3 3 3 3 3 3 3 3 3 3 2&lt;br /&gt;
          5 5 5 5 5 5 4 4 4 4 4 4 4 4 4 4 4 &lt;br /&gt;
            ^ ^ ^ ^ ^               ^ ^ ^ ^    9 PF&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Esercizio 1==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
struct proc{&lt;br /&gt;
	int id;&lt;br /&gt;
	condition oktoexit;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
monitor seq{&lt;br /&gt;
	stack of struct proc inside; /*pila di proc*/&lt;br /&gt;
	&lt;br /&gt;
	procedure entry enter(){&lt;br /&gt;
		this=malloc(sizeof(struct proc)); //or this=new(struct proc);&lt;br /&gt;
		this-&amp;gt;id = getpid();&lt;br /&gt;
		inside.push(this);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure entry exit(){&lt;br /&gt;
		struct proc *mine = inside.search(getpid()); //cerca il mio elemento nella pila&lt;br /&gt;
		if(inside.first()!= mine) /*first restituisce il primo elemento in cima alla pila*/&lt;br /&gt;
			mine-&amp;gt;oktoexit.wait(); /*se l'elemento corrente non è il primo nella pila, allora deve aspettare*/&lt;br /&gt;
		inside.pop(); /*rimuovo l'elemento corrente dalla pila*/&lt;br /&gt;
		free(mine); //butto via il risultato perché è certamente == mine&lt;br /&gt;
		next=inside.first();&lt;br /&gt;
		inside.first()-&amp;gt;oktoexit.signal(); /*risvegliamo il primo elemento sulla pila*/&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
GiuliaN. (con le correzioni del professore)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
monitor seq&lt;br /&gt;
{&lt;br /&gt;
	condition* c=List() /*lista di condizioni, una per ogni processo arrivato*/&lt;br /&gt;
	int* procstack=Stack(); /*stack di processi per l'uscita LIFO*/&lt;br /&gt;
	&lt;br /&gt;
	procedure entry enter()&lt;br /&gt;
	{&lt;br /&gt;
		procstack.push(this-&amp;gt;p_id);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure entry exit()&lt;br /&gt;
	{&lt;br /&gt;
		if (procstack.Top()!=this-&amp;gt;p_id){ /*cioè se il processo non è in cima allo stack*/&lt;br /&gt;
			List.insert(this-&amp;gt;p_id);&lt;br /&gt;
			(List.search(this-&amp;gt;p_id)).wait(); /*aggiungo il mio p_id alla lista di condizioni e mi metto in wait*/&lt;br /&gt;
			}&lt;br /&gt;
		List.remove(this-&amp;gt;p_id);&lt;br /&gt;
		procstack.Pop();&lt;br /&gt;
		List.search(procstack.Top()).signal();&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NOTA: Con questa soluzione se l'ultimo processo chiama la exit e ci sono altri processi in coda, gli altri processi usciranno prima che l'ultimo esca dallo urgent stack per uscire a sua volta, quindi non credo che rispetti la traccia.&lt;br /&gt;
Però non ho trovato un'altra soluzione&lt;br /&gt;
&lt;br /&gt;
==Esercizio 2==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
pid index[N] /*index contiene gli id dei vari server*/&lt;br /&gt;
&lt;br /&gt;
process server[0]&lt;br /&gt;
{&lt;br /&gt;
	while(1)&lt;br /&gt;
	{&lt;br /&gt;
		tmp msg=arecv(*);&lt;br /&gt;
		for (i=1;i&amp;lt;N;i++)&lt;br /&gt;
			asend(index[i],tmp);&lt;br /&gt;
		print(tmp);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
process server[1...N-1]&lt;br /&gt;
{&lt;br /&gt;
	while(1)&lt;br /&gt;
	{&lt;br /&gt;
		msg tmp =arecv(*)&lt;br /&gt;
		if (tmp.sender==index[0])&lt;br /&gt;
			print(tmp);&lt;br /&gt;
		else&lt;br /&gt;
			asend(index[0],tmp);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
-stefano92&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
process server[0]{ //il primo processo server, che gestisce tutti gli altri&lt;br /&gt;
	while(1){&lt;br /&gt;
		(sender, m) = areceive(*);&lt;br /&gt;
		//chiunque mi abbia spedito il msg io lo stampo&lt;br /&gt;
		printf(m);&lt;br /&gt;
		//lo mando a tutti gli altri server (me escluso) in modo che lo stampino&lt;br /&gt;
		for(i=1; i&amp;lt;N; i++)&lt;br /&gt;
			asend((server0, m), server[i]);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
process server[i]{ //per i che va da 1 a N, dove lo 0-esimo è il primo server&lt;br /&gt;
	while(1){&lt;br /&gt;
		(sender, m) = areceive(*);&lt;br /&gt;
		if(sender == server0)&lt;br /&gt;
			printf(m);&lt;br /&gt;
		else //messaggio dal client&lt;br /&gt;
			asend(m, server0); //lo mando al server0, senza prima stamparlo&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
GiuliaN.&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaTeorica_2014.01.22&amp;diff=1941</id>
		<title>ProvaTeorica 2014.01.22</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=ProvaTeorica_2014.01.22&amp;diff=1941"/>
		<updated>2017-06-16T14:24:43Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* Esercizio g.1 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;h1&amp;gt;[http://www.cs.unibo.it/~renzo/so/compiti/2014.01.22.tot.pdf Testo del compito]&amp;lt;/h1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
URL: http://www.cs.unibo.it/~renzo/so/compiti/2014.01.22.tot.pdf&lt;br /&gt;
&lt;br /&gt;
@author: Alessandro&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
monitor bridge {&lt;br /&gt;
condition oktomove;	/* auto attraversa il ponte */&lt;br /&gt;
int turn=0;		/*indica il senso delle auto */&lt;br /&gt;
int est=0,ovest=0;      /*contatori delle auto da est e da ovest */&lt;br /&gt;
Queue q;		/*coda delle senso delle macchine in attesa */&lt;br /&gt;
&lt;br /&gt;
procedure entry enter (char EoW)&lt;br /&gt;
{&lt;br /&gt;
	if(EoW == &amp;quot;E&amp;quot;)		/* viene da est */&lt;br /&gt;
	{&lt;br /&gt;
		if(turn == 2 || est &amp;gt;=N || !empty(q))  &lt;br /&gt;
		{&lt;br /&gt;
			q=enqueue(&amp;quot;E&amp;quot;);		/*inserisco nella coda dei sensi*/	&lt;br /&gt;
			oktomove.wait();   	/*aspetta*/&lt;br /&gt;
									&lt;br /&gt;
		}&lt;br /&gt;
	turn = 1;	/*impongo il senso delle auto in circolo nel ponte */&lt;br /&gt;
	est++;&lt;br /&gt;
	}&lt;br /&gt;
	else		/* viene da ovest */&lt;br /&gt;
	{&lt;br /&gt;
		if(turn ==1 || ovest &amp;gt;=N || !empty(q))&lt;br /&gt;
		{&lt;br /&gt;
		        q=enqueue(&amp;quot;O&amp;quot;);		/*inserisco nella coda dei sensi*/&lt;br /&gt;
			oktomove.wait();	/*aspetta*/&lt;br /&gt;
									&lt;br /&gt;
		}&lt;br /&gt;
	turn = 2;	/*impongo il senso delle auto in circolo nel ponte */&lt;br /&gt;
	ovest++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
procedure entry exit(char EoW)&lt;br /&gt;
{&lt;br /&gt;
	char r;&lt;br /&gt;
	if(EoW == &amp;quot;O&amp;quot;)&lt;br /&gt;
	{&lt;br /&gt;
	est--;&lt;br /&gt;
	r=head(q);                          /* leggo la prossima auto che aspetta di entrare */&lt;br /&gt;
		if(est == 0 || r == &amp;quot;E&amp;quot;)     /*nessuna auto in transito nel ponte o il prossimo va nella stessa direzione*/&lt;br /&gt;
		{&lt;br /&gt;
		est--;&lt;br /&gt;
			if(est == 0)	/*nessuna auto in transito nel ponte */&lt;br /&gt;
			{&lt;br /&gt;
			turn = 0;	/*avanti un altro */&lt;br /&gt;
			}&lt;br /&gt;
			if(!empty(q))&lt;br /&gt;
			{&lt;br /&gt;
			q.dequeue();&lt;br /&gt;
			oktomove.signal();	/*segnale*/&lt;br /&gt;
			}&lt;br /&gt;
                }&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
	ovest--;&lt;br /&gt;
	r=head(q);                          /* leggo la prossima auto che aspetta di entrare */&lt;br /&gt;
		if(ovest == 0 || r == &amp;quot;O&amp;quot;)	 /*nessuna auto in transito nel ponte o il prossimo va nella stessa direzione*/&lt;br /&gt;
		{&lt;br /&gt;
		turn = 0;	/*avanti un altro */&lt;br /&gt;
		}&lt;br /&gt;
		if(!empty(q))&lt;br /&gt;
		{&lt;br /&gt;
		q.dequeue();		&lt;br /&gt;
		oktomove.signal();	/*segnale*/&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;
------&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * URL: http://www.cs.unibo.it/~renzo/so/compiti/2014.01.22.tot.pdf&lt;br /&gt;
 * author: Tommaso Ognibene&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
monitor bridge&lt;br /&gt;
{&lt;br /&gt;
	condition okE;    // attraversare in direzione Est&lt;br /&gt;
	condition okW;    // attraversare in direzione Ovest&lt;br /&gt;
	int n = 0;        // numero di veicoli sul ponte&lt;br /&gt;
	int waitingE = 0; // numero di veicoli che attendono di attraversare in direzione Est&lt;br /&gt;
	int waitingW = 0; // numero di veicoli che attendono di attraversare in direzione Ovest&lt;br /&gt;
	bool toE = true;  // direzione di attraversamento&lt;br /&gt;
&lt;br /&gt;
	procedure entry enter(Vehicle vehicle)&lt;br /&gt;
	{&lt;br /&gt;
		// [Case 1] Il veicolo vuole attraversare in direzione Ovest&lt;br /&gt;
		if (vehicle.To == 'W')&lt;br /&gt;
		{&lt;br /&gt;
			/* Se - il numero di veicoli sul ponte ha raggiunto il massimo; oppure&lt;br /&gt;
			      - qualcuno sta attraversando in direzione opposta; oppure&lt;br /&gt;
			      - qualcuno sta attendendo di attraversare in direzione opposta */&lt;br /&gt;
			if (n == N || (toE &amp;amp;&amp;amp; n &amp;gt; 0) || (!toE &amp;amp;&amp;amp; waitingE &amp;gt; 0))&lt;br /&gt;
			{&lt;br /&gt;
				// Mi fermo e attendo di essere sbloccato&lt;br /&gt;
				waitingW++;&lt;br /&gt;
				okW.wait();&lt;br /&gt;
				waitingW--;&lt;br /&gt;
			}&lt;br /&gt;
			toE = false;&lt;br /&gt;
			n++;&lt;br /&gt;
			&lt;br /&gt;
			/* Se possibile, sblocco eventuali altri veicoli in attesa per la stessa direzione.&lt;br /&gt;
			 * Questo non crea starvation in quanto sono sicuramente un numero limitato. */&lt;br /&gt;
			if (n &amp;lt; N &amp;amp;&amp;amp; waitingE == 0)&lt;br /&gt;
				okW.signal();&lt;br /&gt;
		}&lt;br /&gt;
		// [Case 2] Il veicolo vuole attraversare in direzione Est&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			/* Se - il numero di veicoli sul ponte ha raggiunto il massimo; oppure&lt;br /&gt;
			      - qualcuno sta attraversando in direzione opposta; oppure&lt;br /&gt;
			      - qualcuno sta attendendo di attraversare in direzione opposta */&lt;br /&gt;
			if (n == N || (!toE &amp;amp;&amp;amp; n &amp;gt; 0) || (toE &amp;amp;&amp;amp; waitingW &amp;gt; 0))&lt;br /&gt;
			{&lt;br /&gt;
			        // Mi fermo e attendo di essere sbloccato&lt;br /&gt;
				waitingE++;&lt;br /&gt;
				okE.wait();&lt;br /&gt;
				waitingE--;&lt;br /&gt;
			}&lt;br /&gt;
			toE = true;&lt;br /&gt;
			n++;&lt;br /&gt;
&lt;br /&gt;
			/* Se possibile, sblocco eventuali altri veicoli in attesa per la stessa direzione.&lt;br /&gt;
			 * Questo non crea starvation in quanto sono sicuramente un numero limitato. */&lt;br /&gt;
			if (n &amp;lt; N &amp;amp;&amp;amp; waitingW == 0)&lt;br /&gt;
				okE.signal();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	procedure entry exit(Vehicle vehicle)&lt;br /&gt;
	{&lt;br /&gt;
		n--;&lt;br /&gt;
		/* Se nessuno sta attraversando il ponte */&lt;br /&gt;
		if (n == 0)&lt;br /&gt;
		{&lt;br /&gt;
			// [Case 1] L'ultimo ad attraversare andava in direzione Ovest&lt;br /&gt;
			if (vehicle.To == 'W')&lt;br /&gt;
			{&lt;br /&gt;
			   /* Se esiste, attivo il veicolo che per primo si era messo&lt;br /&gt;
			      in attesa per la direzione opposta */&lt;br /&gt;
			   if (waitingE &amp;gt; 0)&lt;br /&gt;
				okE.signal();&lt;br /&gt;
			   else&lt;br /&gt;
				okW.signal();&lt;br /&gt;
			}&lt;br /&gt;
			// [Case 2] L'ultimo ad attraversare andava in direzione Est&lt;br /&gt;
			else&lt;br /&gt;
			{&lt;br /&gt;
			   /* Se esiste, attivo il veicolo che per primo si era messo&lt;br /&gt;
		              in attesa per la direzione opposta */&lt;br /&gt;
			   if (waitingW &amp;gt; 0)&lt;br /&gt;
				okW.signal();&lt;br /&gt;
			   else&lt;br /&gt;
				okE.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;
La Mia soluzione:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * author: Midolo&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
Monitor Ponte {&lt;br /&gt;
#define O 0&lt;br /&gt;
#define E 1&lt;br /&gt;
condition oktocross[2];&lt;br /&gt;
int countwaiting[2], crossing;&lt;br /&gt;
&lt;br /&gt;
	bridge.enter(dir){&lt;br /&gt;
		if(crossing != 0 || countwaiting[1-dir] != 0){&lt;br /&gt;
			countwaiting[dir]++;&lt;br /&gt;
			oktocross[dir].wait();&lt;br /&gt;
			countwaiting[dir]--;            &lt;br /&gt;
		}&lt;br /&gt;
		crossing++;&lt;br /&gt;
		if(crossing &amp;lt; N){ &lt;br /&gt;
			oktocross[dir].signal();&lt;br /&gt;
		}    &lt;br /&gt;
	}&lt;br /&gt;
    &lt;br /&gt;
	bridge.exit(dir){&lt;br /&gt;
		crossing--;&lt;br /&gt;
		if(crossing == 0){&lt;br /&gt;
			if(countwaiting[dir] != 0){&lt;br /&gt;
				oktocross[dir].signal();&lt;br /&gt;
			}else{&lt;br /&gt;
				oktocross[1-dir].signal();&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;
== Esercizio g.1==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;Punto a&amp;quot;&lt;br /&gt;
                123456789123456789123456789...&lt;br /&gt;
                 &lt;br /&gt;
                111111111111111111111111111&lt;br /&gt;
                 22222222222222222222222222          MIN e MAXNUM (infiniti page fault)&lt;br /&gt;
                  3456789993456789993456789&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Punto b&amp;quot;&lt;br /&gt;
                123453453453453453453453453...&lt;br /&gt;
                 &lt;br /&gt;
                111111111111111111111111111&lt;br /&gt;
                 22222222222222222222222222          MIN e MAXNUM (page fault ad ogni accesso)&lt;br /&gt;
                  3453453453453453453453453&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Esercizio g.1 ==&lt;br /&gt;
a)&lt;br /&gt;
                 1234123124123124123...&lt;br /&gt;
                 111111111111111111 ...&lt;br /&gt;
                  22222222222222222 ...&lt;br /&gt;
                   3444333444333444 ...&lt;br /&gt;
&lt;br /&gt;
b)&lt;br /&gt;
                 12343434343...&lt;br /&gt;
                 11144444444...&lt;br /&gt;
                  1222222222...&lt;br /&gt;
                   333333333...&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;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
message database=Queue():&lt;br /&gt;
&lt;br /&gt;
message msgxchange(pid_t pid, message msg)&lt;br /&gt;
{&lt;br /&gt;
	message ris;&lt;br /&gt;
	asendx(pid,msg);&lt;br /&gt;
	ris=database.Search(pid,getpid());&lt;br /&gt;
	if (ris!=NULL)&lt;br /&gt;
		return ris;&lt;br /&gt;
	do&lt;br /&gt;
	{&lt;br /&gt;
		ris=arecvx();&lt;br /&gt;
		if (ris.sender!=pid)&lt;br /&gt;
			database.Add(ris)&lt;br /&gt;
	}&lt;br /&gt;
	while (ris.sender!=pid);&lt;br /&gt;
	return ris;&lt;br /&gt;
}&lt;br /&gt;
/*	&lt;br /&gt;
Sì, può provocare deadlock. Es:&lt;br /&gt;
&lt;br /&gt;
A: msgxchange(B,msg);&lt;br /&gt;
B: msgxchange(C,msg);&lt;br /&gt;
C: msgxchange(A,msg);&lt;br /&gt;
*/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1734</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=1734"/>
		<updated>2017-03-03T22:00:20Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* Lezione del 2 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 = 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&amp;quot;, s);&lt;br /&gt;
  printf(&amp;quot;%s&amp;quot;, t);&lt;br /&gt;
  printf(&amp;quot;%s&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;
** Shortest job first&lt;br /&gt;
** Shortest remaining time first&lt;br /&gt;
** Round robin&lt;br /&gt;
** Scheduling a priorità&lt;br /&gt;
** Scheduler multilivello&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;
'''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;
== Lezione del 7 marzo 2017 ==&lt;br /&gt;
== Lezione del 8 marzo 2017 ==&lt;br /&gt;
== Lezione del 14 marzo 2017 ==&lt;br /&gt;
== Lezione del 16 marzo 2017 ==&lt;br /&gt;
== Lezione del 21 marzo 2017 ==&lt;br /&gt;
== Lezione del 23 marzo 2017 ==&lt;br /&gt;
== Lezione del 28 marzo 2017 ==&lt;br /&gt;
== Lezione del 30 marzo 2017 ==&lt;br /&gt;
== Lezione del 4 aprile 2017 ==&lt;br /&gt;
== Lezione del 6 aprile 2017 ==&lt;br /&gt;
== Lezione del 11 aprile 2017 ==&lt;br /&gt;
== Lezione del 20 aprile 2017 ==&lt;br /&gt;
== Lezione del 27 aprile 2017 ==&lt;br /&gt;
== Lezione del 2 maggio 2017 ==&lt;br /&gt;
== Lezione del 4 maggio 2017 ==&lt;br /&gt;
== Lezione del 9 maggio 2017 ==&lt;br /&gt;
== Lezione del 11 maggio 2017 ==&lt;br /&gt;
== Lezione del 16 maggio 2017 ==&lt;br /&gt;
== Lezione del 18 maggio 2017 ==&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1733</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=1733"/>
		<updated>2017-03-03T21:57:07Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* Lezione del 2 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 = 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&amp;quot;, s);&lt;br /&gt;
  printf(&amp;quot;%s&amp;quot;, t);&lt;br /&gt;
  printf(&amp;quot;%s&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;
** Shortest job first&lt;br /&gt;
** Shortest remaining time first&lt;br /&gt;
** Round robin&lt;br /&gt;
** Scheduling a priorità&lt;br /&gt;
** Scheduler multilivello&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 &amp;amp;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;
'''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;
Gli operatori || e &amp;amp;&amp;amp; possono essere utilizzati per processare più comandi consecutivamente.&lt;br /&gt;
&lt;br /&gt;
'''ESEMPIO 9((|| &amp;amp;&amp;amp;)'''&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 n. 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;
== Lezione del 7 marzo 2017 ==&lt;br /&gt;
== Lezione del 8 marzo 2017 ==&lt;br /&gt;
== Lezione del 14 marzo 2017 ==&lt;br /&gt;
== Lezione del 16 marzo 2017 ==&lt;br /&gt;
== Lezione del 21 marzo 2017 ==&lt;br /&gt;
== Lezione del 23 marzo 2017 ==&lt;br /&gt;
== Lezione del 28 marzo 2017 ==&lt;br /&gt;
== Lezione del 30 marzo 2017 ==&lt;br /&gt;
== Lezione del 4 aprile 2017 ==&lt;br /&gt;
== Lezione del 6 aprile 2017 ==&lt;br /&gt;
== Lezione del 11 aprile 2017 ==&lt;br /&gt;
== Lezione del 20 aprile 2017 ==&lt;br /&gt;
== Lezione del 27 aprile 2017 ==&lt;br /&gt;
== Lezione del 2 maggio 2017 ==&lt;br /&gt;
== Lezione del 4 maggio 2017 ==&lt;br /&gt;
== Lezione del 9 maggio 2017 ==&lt;br /&gt;
== Lezione del 11 maggio 2017 ==&lt;br /&gt;
== Lezione del 16 maggio 2017 ==&lt;br /&gt;
== Lezione del 18 maggio 2017 ==&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1732</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=1732"/>
		<updated>2017-03-03T20:52:28Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* Lezione del 2 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 = 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&amp;quot;, s);&lt;br /&gt;
  printf(&amp;quot;%s&amp;quot;, t);&lt;br /&gt;
  printf(&amp;quot;%s&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;
** Shortest job first&lt;br /&gt;
** Shortest remaining time first&lt;br /&gt;
** Round robin&lt;br /&gt;
** Scheduling a priorità&lt;br /&gt;
** Scheduler multilivello&lt;br /&gt;
&lt;br /&gt;
== Lezione del 2 marzo 2017 ==&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 &amp;amp;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;
== Lezione del 7 marzo 2017 ==&lt;br /&gt;
== Lezione del 8 marzo 2017 ==&lt;br /&gt;
== Lezione del 14 marzo 2017 ==&lt;br /&gt;
== Lezione del 16 marzo 2017 ==&lt;br /&gt;
== Lezione del 21 marzo 2017 ==&lt;br /&gt;
== Lezione del 23 marzo 2017 ==&lt;br /&gt;
== Lezione del 28 marzo 2017 ==&lt;br /&gt;
== Lezione del 30 marzo 2017 ==&lt;br /&gt;
== Lezione del 4 aprile 2017 ==&lt;br /&gt;
== Lezione del 6 aprile 2017 ==&lt;br /&gt;
== Lezione del 11 aprile 2017 ==&lt;br /&gt;
== Lezione del 20 aprile 2017 ==&lt;br /&gt;
== Lezione del 27 aprile 2017 ==&lt;br /&gt;
== Lezione del 2 maggio 2017 ==&lt;br /&gt;
== Lezione del 4 maggio 2017 ==&lt;br /&gt;
== Lezione del 9 maggio 2017 ==&lt;br /&gt;
== Lezione del 11 maggio 2017 ==&lt;br /&gt;
== Lezione del 16 maggio 2017 ==&lt;br /&gt;
== Lezione del 18 maggio 2017 ==&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1727</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=1727"/>
		<updated>2017-02-24T10:21:54Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* Lezione del 23 febbraio 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 = 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&amp;quot;, s);&lt;br /&gt;
  printf(&amp;quot;%s&amp;quot;, t);&lt;br /&gt;
  printf(&amp;quot;%s&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;
'''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;
== Lezione del 2 marzo 2017 ==&lt;br /&gt;
== Lezione del 7 marzo 2017 ==&lt;br /&gt;
== Lezione del 8 marzo 2017 ==&lt;br /&gt;
== Lezione del 14 marzo 2017 ==&lt;br /&gt;
== Lezione del 16 marzo 2017 ==&lt;br /&gt;
== Lezione del 21 marzo 2017 ==&lt;br /&gt;
== Lezione del 23 marzo 2017 ==&lt;br /&gt;
== Lezione del 28 marzo 2017 ==&lt;br /&gt;
== Lezione del 30 marzo 2017 ==&lt;br /&gt;
== Lezione del 4 aprile 2017 ==&lt;br /&gt;
== Lezione del 6 aprile 2017 ==&lt;br /&gt;
== Lezione del 11 aprile 2017 ==&lt;br /&gt;
== Lezione del 20 aprile 2017 ==&lt;br /&gt;
== Lezione del 27 aprile 2017 ==&lt;br /&gt;
== Lezione del 2 maggio 2017 ==&lt;br /&gt;
== Lezione del 4 maggio 2017 ==&lt;br /&gt;
== Lezione del 9 maggio 2017 ==&lt;br /&gt;
== Lezione del 11 maggio 2017 ==&lt;br /&gt;
== Lezione del 16 maggio 2017 ==&lt;br /&gt;
== Lezione del 18 maggio 2017 ==&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1565</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=1565"/>
		<updated>2016-11-08T23:22:08Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* + implementazione corretta (+j) baton */&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 = 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&amp;quot;, s);&lt;br /&gt;
  printf(&amp;quot;%s&amp;quot;, t);&lt;br /&gt;
  printf(&amp;quot;%s&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;
&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;
        if(i==1 || i==3) conspiracy_out();&lt;br /&gt;
        usleep(random() % 200000);&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;
* '''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.&lt;br /&gt;
&lt;br /&gt;
== Lezione del 11 novembre 2016 ==&lt;br /&gt;
== Lezione del 15 novembre 2016 ==&lt;br /&gt;
== Lezione del 18 novembre 2016 ==&lt;br /&gt;
== Lezione del 22 novembre 2016 ==&lt;br /&gt;
== Lezione del 25 novembre 2016 ==&lt;br /&gt;
== Lezione del 29 novembre 2016 ==&lt;br /&gt;
== Lezione del 2 dicembre 2016 ==&lt;br /&gt;
== Lezione del 6 dicembre 2016 ==&lt;br /&gt;
== Lezione del 9 dicembre 2016 ==&lt;br /&gt;
== Lezione del 13 dicembre 2016 ==&lt;br /&gt;
== Lezione del 16 dicembre 2016 ==&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1562</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=1562"/>
		<updated>2016-11-08T23:08:00Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* syntaxhighlight */&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 = 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&amp;quot;, s);&lt;br /&gt;
  printf(&amp;quot;%s&amp;quot;, t);&lt;br /&gt;
  printf(&amp;quot;%s&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;
&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;
: 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;
* '''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;
== Lezione del 11 novembre 2016 ==&lt;br /&gt;
== Lezione del 15 novembre 2016 ==&lt;br /&gt;
== Lezione del 18 novembre 2016 ==&lt;br /&gt;
== Lezione del 22 novembre 2016 ==&lt;br /&gt;
== Lezione del 25 novembre 2016 ==&lt;br /&gt;
== Lezione del 29 novembre 2016 ==&lt;br /&gt;
== Lezione del 2 dicembre 2016 ==&lt;br /&gt;
== Lezione del 6 dicembre 2016 ==&lt;br /&gt;
== Lezione del 9 dicembre 2016 ==&lt;br /&gt;
== Lezione del 13 dicembre 2016 ==&lt;br /&gt;
== Lezione del 16 dicembre 2016 ==&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1561</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=1561"/>
		<updated>2016-11-08T23:05:20Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* + umask simbolica, esempio ridirezione con dup2() */&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 = 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&amp;quot;, s);&lt;br /&gt;
  printf(&amp;quot;%s&amp;quot;, t);&lt;br /&gt;
  printf(&amp;quot;%s&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;
&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;
: 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;
* '''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;
  #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;
* '''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;
== Lezione del 8 novembre 2016 ==&lt;br /&gt;
== Lezione del 11 novembre 2016 ==&lt;br /&gt;
== Lezione del 15 novembre 2016 ==&lt;br /&gt;
== Lezione del 18 novembre 2016 ==&lt;br /&gt;
== Lezione del 22 novembre 2016 ==&lt;br /&gt;
== Lezione del 25 novembre 2016 ==&lt;br /&gt;
== Lezione del 29 novembre 2016 ==&lt;br /&gt;
== Lezione del 2 dicembre 2016 ==&lt;br /&gt;
== Lezione del 6 dicembre 2016 ==&lt;br /&gt;
== Lezione del 9 dicembre 2016 ==&lt;br /&gt;
== Lezione del 13 dicembre 2016 ==&lt;br /&gt;
== Lezione del 16 dicembre 2016 ==&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1554</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=1554"/>
		<updated>2016-11-05T21:44:47Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* Lezione del 4 novembre 2016 */&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 = 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&amp;quot;, s);&lt;br /&gt;
  printf(&amp;quot;%s&amp;quot;, t);&lt;br /&gt;
  printf(&amp;quot;%s&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;
&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;
* 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;
&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;
&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;
&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;
&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;
&lt;br /&gt;
&lt;br /&gt;
'''umask'''&lt;br /&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;
* File aperti durante fork/exec&lt;br /&gt;
* La system dup e la ridirezione&lt;br /&gt;
&lt;br /&gt;
== Lezione del 8 novembre 2016 ==&lt;br /&gt;
== Lezione del 11 novembre 2016 ==&lt;br /&gt;
== Lezione del 15 novembre 2016 ==&lt;br /&gt;
== Lezione del 18 novembre 2016 ==&lt;br /&gt;
== Lezione del 22 novembre 2016 ==&lt;br /&gt;
== Lezione del 25 novembre 2016 ==&lt;br /&gt;
== Lezione del 29 novembre 2016 ==&lt;br /&gt;
== Lezione del 2 dicembre 2016 ==&lt;br /&gt;
== Lezione del 6 dicembre 2016 ==&lt;br /&gt;
== Lezione del 9 dicembre 2016 ==&lt;br /&gt;
== Lezione del 13 dicembre 2016 ==&lt;br /&gt;
== Lezione del 16 dicembre 2016 ==&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1486</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=1486"/>
		<updated>2016-10-18T21:23:42Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* Lezione del 11 ottobre 2016 */&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=0B8G3NNuJ7MA7ZENCMThCUHJSaHM 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 = 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&amp;quot;, s);&lt;br /&gt;
  printf(&amp;quot;%s&amp;quot;, t);&lt;br /&gt;
  printf(&amp;quot;%s&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;
*''' 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;
----&lt;br /&gt;
Altri argomenti trattati che non sono stati qui riportati in forma di appunti:&lt;br /&gt;
Proprieta' della sezione critica.&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;
La test&amp;amp;set.&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=0B8G3NNuJ7MA7VzZicTNuU1N4aVE 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;
== Lezione del 21 ottobre 2016 ==&lt;br /&gt;
== Lezione del 25 ottobre 2016 ==&lt;br /&gt;
== Lezione del 28 ottobre 2016 ==&lt;br /&gt;
== Lezione del 28 ottobre 2016 ==&lt;br /&gt;
== Lezione del 4 novembre 2016 ==&lt;br /&gt;
== Lezione del 8 novembre 2016 ==&lt;br /&gt;
== Lezione del 11 novembre 2016 ==&lt;br /&gt;
== Lezione del 15 novembre 2016 ==&lt;br /&gt;
== Lezione del 18 novembre 2016 ==&lt;br /&gt;
== Lezione del 22 novembre 2016 ==&lt;br /&gt;
== Lezione del 25 novembre 2016 ==&lt;br /&gt;
== Lezione del 29 novembre 2016 ==&lt;br /&gt;
== Lezione del 2 dicembre 2016 ==&lt;br /&gt;
== Lezione del 6 dicembre 2016 ==&lt;br /&gt;
== Lezione del 9 dicembre 2016 ==&lt;br /&gt;
== Lezione del 13 dicembre 2016 ==&lt;br /&gt;
== Lezione del 16 dicembre 2016 ==&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1438</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=1438"/>
		<updated>2016-10-11T23:43:14Z</updated>

		<summary type="html">&lt;p&gt;Mak: &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;
&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;
  #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 = 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&amp;quot;, s);&lt;br /&gt;
  printf(&amp;quot;%s&amp;quot;, t);&lt;br /&gt;
  printf(&amp;quot;%s&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;
&lt;br /&gt;
  OUTPUT:&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;
&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;
* '''Sintassi per la concorrenza.'''&lt;br /&gt;
1° Metodo:&lt;br /&gt;
&lt;br /&gt;
Process p{&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
Process q{&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*Con questa scrittura si intende che i tuoi processi vengono eseguiti contemporaneamente*/&lt;br /&gt;
&lt;br /&gt;
2° Metodo:&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;
/*Con questa scrittura si intende che i processi definiti tra il &amp;quot;cobegin&amp;quot; e il &amp;quot;coend&amp;quot; vengono eseguiti contemporaneamente*/&lt;br /&gt;
* Convenzioni usate nel corso (e negli esami) relative alle istruzioni atomiche&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 bene(L)&lt;br /&gt;
&lt;br /&gt;
&amp;quot;''Something'' happens&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[[/*Se qualcuno ha scritto/trovato una definizione migliore, sostituisca quella scritta sopra */]]&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 lesecuzione del processo p sia rapidissima, allora il processo q non riuscirà mai ad avere accesso ad a. 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;
* (SMP e non SMP)&lt;br /&gt;
&lt;br /&gt;
== Lezione del 14 ottobre 2016 ==&lt;br /&gt;
== Lezione del 18 ottobre 2016 ==&lt;br /&gt;
== Lezione del 21 ottobre 2016 ==&lt;br /&gt;
== Lezione del 25 ottobre 2016 ==&lt;br /&gt;
== Lezione del 28 ottobre 2016 ==&lt;br /&gt;
== Lezione del 28 ottobre 2016 ==&lt;br /&gt;
== Lezione del 4 novembre 2016 ==&lt;br /&gt;
== Lezione del 8 novembre 2016 ==&lt;br /&gt;
== Lezione del 11 novembre 2016 ==&lt;br /&gt;
== Lezione del 15 novembre 2016 ==&lt;br /&gt;
== Lezione del 18 novembre 2016 ==&lt;br /&gt;
== Lezione del 22 novembre 2016 ==&lt;br /&gt;
== Lezione del 25 novembre 2016 ==&lt;br /&gt;
== Lezione del 29 novembre 2016 ==&lt;br /&gt;
== Lezione del 2 dicembre 2016 ==&lt;br /&gt;
== Lezione del 6 dicembre 2016 ==&lt;br /&gt;
== Lezione del 9 dicembre 2016 ==&lt;br /&gt;
== Lezione del 13 dicembre 2016 ==&lt;br /&gt;
== Lezione del 16 dicembre 2016 ==&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1437</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=1437"/>
		<updated>2016-10-11T23:36:51Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* Lezione del 11 ottobre 2016 */&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;
&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;
  #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 = 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&amp;quot;, s);&lt;br /&gt;
  printf(&amp;quot;%s&amp;quot;, t);&lt;br /&gt;
  printf(&amp;quot;%s&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;
&lt;br /&gt;
  OUTPUT:&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;
&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;
* '''Sintassi per la concorrenza.'''&lt;br /&gt;
1° Metodo:&lt;br /&gt;
&lt;br /&gt;
Process p{&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
Process q{&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*Con questa scrittura si intende che i tuoi processi vengono eseguiti contemporaneamente*/&lt;br /&gt;
&lt;br /&gt;
2° Metodo:&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;
/*Con questa scrittura si intende che i processi definiti tra il &amp;quot;cobegin&amp;quot; e il &amp;quot;coend&amp;quot; vengono eseguiti contemporaneamente*/&lt;br /&gt;
* Convenzioni usate nel corso (e negli esami) relative alle istruzioni atomiche&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 bene(L)&lt;br /&gt;
&lt;br /&gt;
&amp;quot;''Something'' happens&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[[/*Se qualcuno ha scritto/trovato una definizione migliore, sostituisca quella scritta sopra */]]&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 lesecuzione del processo p sia rapidissima, allora il processo q non riuscirà mai ad avere accesso ad a. 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 garantisse l'accesso a P1 dopo che P3 finisca, e successivamente garantisca 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: interndals and design principles. (8th ed). Pearson)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* (SMP e non SMP)&lt;br /&gt;
&lt;br /&gt;
== Lezione del 14 ottobre 2016 ==&lt;br /&gt;
== Lezione del 18 ottobre 2016 ==&lt;br /&gt;
== Lezione del 21 ottobre 2016 ==&lt;br /&gt;
== Lezione del 25 ottobre 2016 ==&lt;br /&gt;
== Lezione del 28 ottobre 2016 ==&lt;br /&gt;
== Lezione del 28 ottobre 2016 ==&lt;br /&gt;
== Lezione del 4 novembre 2016 ==&lt;br /&gt;
== Lezione del 8 novembre 2016 ==&lt;br /&gt;
== Lezione del 11 novembre 2016 ==&lt;br /&gt;
== Lezione del 15 novembre 2016 ==&lt;br /&gt;
== Lezione del 18 novembre 2016 ==&lt;br /&gt;
== Lezione del 22 novembre 2016 ==&lt;br /&gt;
== Lezione del 25 novembre 2016 ==&lt;br /&gt;
== Lezione del 29 novembre 2016 ==&lt;br /&gt;
== Lezione del 2 dicembre 2016 ==&lt;br /&gt;
== Lezione del 6 dicembre 2016 ==&lt;br /&gt;
== Lezione del 9 dicembre 2016 ==&lt;br /&gt;
== Lezione del 13 dicembre 2016 ==&lt;br /&gt;
== Lezione del 16 dicembre 2016 ==&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1436</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=1436"/>
		<updated>2016-10-11T21:54:39Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* Lezione del 11 ottobre 2016 */&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;
&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;
* '''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;
* '''Sintassi per la concorrenza.'''&lt;br /&gt;
1° Metodo:&lt;br /&gt;
&lt;br /&gt;
Process p{&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
Process q{&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*Con questa scrittura si intende che i tuoi processi vengono eseguiti contemporaneamente*/&lt;br /&gt;
&lt;br /&gt;
2° Metodo:&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;
/*Con questa scrittura si intende che i processi definiti tra il &amp;quot;cobegin&amp;quot; e il &amp;quot;coend&amp;quot; vengono eseguiti contemporaneamente*/&lt;br /&gt;
* Convenzioni usate nel corso (e negli esami) relative alle istruzioni atomiche&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 bene(L)&lt;br /&gt;
&lt;br /&gt;
&amp;quot;''Something'' happens&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[[/*Se qualcuno ha scritto/trovato una definizione migliore, sostituisca quella scritta sopra */]]&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 lesecuzione del processo p sia rapidissima, allora il processo q non riuscirà mai ad avere accesso ad a. 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 garantisse l'accesso a P1 dopo che P3 finisca, e successivamente garantisca 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: interndals and design principles. (8th ed). Pearson)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* (SMP e non SMP)&lt;br /&gt;
&lt;br /&gt;
== Lezione del 14 ottobre 2016 ==&lt;br /&gt;
== Lezione del 18 ottobre 2016 ==&lt;br /&gt;
== Lezione del 21 ottobre 2016 ==&lt;br /&gt;
== Lezione del 25 ottobre 2016 ==&lt;br /&gt;
== Lezione del 28 ottobre 2016 ==&lt;br /&gt;
== Lezione del 28 ottobre 2016 ==&lt;br /&gt;
== Lezione del 4 novembre 2016 ==&lt;br /&gt;
== Lezione del 8 novembre 2016 ==&lt;br /&gt;
== Lezione del 11 novembre 2016 ==&lt;br /&gt;
== Lezione del 15 novembre 2016 ==&lt;br /&gt;
== Lezione del 18 novembre 2016 ==&lt;br /&gt;
== Lezione del 22 novembre 2016 ==&lt;br /&gt;
== Lezione del 25 novembre 2016 ==&lt;br /&gt;
== Lezione del 29 novembre 2016 ==&lt;br /&gt;
== Lezione del 2 dicembre 2016 ==&lt;br /&gt;
== Lezione del 6 dicembre 2016 ==&lt;br /&gt;
== Lezione del 9 dicembre 2016 ==&lt;br /&gt;
== Lezione del 13 dicembre 2016 ==&lt;br /&gt;
== Lezione del 16 dicembre 2016 ==&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1435</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=1435"/>
		<updated>2016-10-11T21:48:34Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* Lezione del 11 ottobre 2016 */&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;
&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;
* '''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&lt;br /&gt;
&lt;br /&gt;
2- Indirettamente concorrenti&lt;br /&gt;
&lt;br /&gt;
3- Consapevolmente concorrenti&lt;br /&gt;
* '''Sintassi per la concorrenza.'''&lt;br /&gt;
1° Metodo:&lt;br /&gt;
&lt;br /&gt;
Process p{&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
Process q{&lt;br /&gt;
...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*Con questa scrittura si intende che i tuoi processi vengono eseguiti contemporaneamente*/&lt;br /&gt;
&lt;br /&gt;
2° Metodo:&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;
/*Con questa scrittura si intende che i processi definiti tra il &amp;quot;cobegin&amp;quot; e il &amp;quot;coend&amp;quot; vengono eseguiti contemporaneamente*/&lt;br /&gt;
* Convenzioni usate nel corso (e negli esami) relative alle istruzioni atomiche&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;
-Liveness: il programma va avanti bene(L)&lt;br /&gt;
&lt;br /&gt;
[[/*Se qualcuno ha scritto/trovato una definizione migliore, sostituisca quella scritta sopra */]]&lt;br /&gt;
&lt;br /&gt;
* '''Problema del consenso.'''&lt;br /&gt;
Proprietà:&lt;br /&gt;
&lt;br /&gt;
1S- Tutti i processi devono ritornare lo stesso valore&lt;br /&gt;
&lt;br /&gt;
2S- Ogni processo corretto decide uno tra i valori proposti&lt;br /&gt;
&lt;br /&gt;
1L- Ogni processo corretto deve dare una risposta&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 lesecuzione del processo p sia rapidissima, allora il processo q non riuscirà mai ad avere accesso ad a. 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 garantisse l'accesso a P1 dopo che P3 finisca, e successivamente garantisca 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: interndals and design principles. (8th ed). Pearson)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* (SMP e non SMP)&lt;br /&gt;
&lt;br /&gt;
== Lezione del 14 ottobre 2016 ==&lt;br /&gt;
== Lezione del 18 ottobre 2016 ==&lt;br /&gt;
== Lezione del 21 ottobre 2016 ==&lt;br /&gt;
== Lezione del 25 ottobre 2016 ==&lt;br /&gt;
== Lezione del 28 ottobre 2016 ==&lt;br /&gt;
== Lezione del 28 ottobre 2016 ==&lt;br /&gt;
== Lezione del 4 novembre 2016 ==&lt;br /&gt;
== Lezione del 8 novembre 2016 ==&lt;br /&gt;
== Lezione del 11 novembre 2016 ==&lt;br /&gt;
== Lezione del 15 novembre 2016 ==&lt;br /&gt;
== Lezione del 18 novembre 2016 ==&lt;br /&gt;
== Lezione del 22 novembre 2016 ==&lt;br /&gt;
== Lezione del 25 novembre 2016 ==&lt;br /&gt;
== Lezione del 29 novembre 2016 ==&lt;br /&gt;
== Lezione del 2 dicembre 2016 ==&lt;br /&gt;
== Lezione del 6 dicembre 2016 ==&lt;br /&gt;
== Lezione del 9 dicembre 2016 ==&lt;br /&gt;
== Lezione del 13 dicembre 2016 ==&lt;br /&gt;
== Lezione del 16 dicembre 2016 ==&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1385</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=1385"/>
		<updated>2016-09-27T21:09:39Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* Lezione del 27 settembre 2016 */&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;
* '''Universit&amp;amp;agrave;'''&lt;br /&gt;
Università = studenti + docenti &lt;br /&gt;
* Digitale/Analogico&lt;br /&gt;
* '''Binario/Decimale'''&lt;br /&gt;
Nei calcolatori l'informazione è memorizzata utilizzando il sistema binario in quanto in passato i relè consentivano di avere due stati: &amp;quot; 1 e 0 &amp;quot;&lt;br /&gt;
* I linguaggi e i problemi dell'Informatica&lt;br /&gt;
* '''Conoscenze a lungo, medio e breve termine nell'Informatica.'''&lt;br /&gt;
BREVE: tecnologia, determinati programmi di sviluppo.&lt;br /&gt;
&lt;br /&gt;
MEDIO: singoli linguaggi, singoli OS, protocolli di comunicazione.&lt;br /&gt;
&lt;br /&gt;
LUNGO: algoritmi, informatica teorica, fondamenti costruttivi di reti e di OS.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Sistema Operativo: perch&amp;amp;eacute;'''&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     L'&lt;br /&gt;
  ---------------&lt;br /&gt;
        hw     L&lt;br /&gt;
  ---------------&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 sitruzioni 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 read().&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;
== Lezione del 30 settembre 2016 ==&lt;br /&gt;
Argomenti da approfondire:&lt;br /&gt;
* (Economia Push/Economia Pull, Distribuzione)&lt;br /&gt;
* Storia dell'Informatica dopo l'avvento dei transistor&lt;br /&gt;
Argomenti da trattare:&lt;br /&gt;
== Lezione del 7 ottobre 2016 ==&lt;br /&gt;
== Lezione del 11 ottobre 2016 ==&lt;br /&gt;
== Lezione del 14 ottobre 2016 ==&lt;br /&gt;
== Lezione del 18 ottobre 2016 ==&lt;br /&gt;
== Lezione del 21 ottobre 2016 ==&lt;br /&gt;
== Lezione del 25 ottobre 2016 ==&lt;br /&gt;
== Lezione del 28 ottobre 2016 ==&lt;br /&gt;
== Lezione del 28 ottobre 2016 ==&lt;br /&gt;
== Lezione del 4 novembre 2016 ==&lt;br /&gt;
== Lezione del 8 novembre 2016 ==&lt;br /&gt;
== Lezione del 11 novembre 2016 ==&lt;br /&gt;
== Lezione del 15 novembre 2016 ==&lt;br /&gt;
== Lezione del 18 novembre 2016 ==&lt;br /&gt;
== Lezione del 22 novembre 2016 ==&lt;br /&gt;
== Lezione del 25 novembre 2016 ==&lt;br /&gt;
== Lezione del 29 novembre 2016 ==&lt;br /&gt;
== Lezione del 2 dicembre 2016 ==&lt;br /&gt;
== Lezione del 6 dicembre 2016 ==&lt;br /&gt;
== Lezione del 9 dicembre 2016 ==&lt;br /&gt;
== Lezione del 13 dicembre 2016 ==&lt;br /&gt;
== Lezione del 16 dicembre 2016 ==&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1384</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=1384"/>
		<updated>2016-09-27T21:06:04Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* Lezione del 27 settembre 2016 */&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;
* '''Universit&amp;amp;agrave;'''&lt;br /&gt;
Università = studenti + docenti &lt;br /&gt;
* Digitale/Analogico&lt;br /&gt;
* '''Binario/Decimale'''&lt;br /&gt;
Nei calcolatori l'informazione è memorizzata utilizzando il sistema binario in quanto in passato i relè consentivano di avere due stati: &amp;quot; 1 e 0 &amp;quot;&lt;br /&gt;
* I linguaggi e i problemi dell'Informatica&lt;br /&gt;
* '''Conoscenze a lungo, medio e breve termine nell'Informatica.'''&lt;br /&gt;
BREVE: tecnologia, determinati programmi di sviluppo.&lt;br /&gt;
&lt;br /&gt;
MEDIO: singoli linguaggi, singoli OS, protocolli di comunicazione.&lt;br /&gt;
&lt;br /&gt;
LUNGO: algoritmi, informatica teorica, fondamenti costruttivi di reti e di OS.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* '''Sistema Operativo: perch&amp;amp;eacute;'''&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     L'&lt;br /&gt;
  ---------------&lt;br /&gt;
        hw     L&lt;br /&gt;
  ---------------&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 sitruzioni non ambigue, atta a risolvere un problema.&lt;br /&gt;
Programma: traduzione degli algoritmi in un linguaggio di programmazione.&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;
System call: richieste di processi al OS. e.g. printf (visto nel codice compilato in assembly tramite gcc -s), invoca read().&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;
== Lezione del 30 settembre 2016 ==&lt;br /&gt;
Argomenti da approfondire:&lt;br /&gt;
* (Economia Push/Economia Pull, Distribuzione)&lt;br /&gt;
* Storia dell'Informatica dopo l'avvento dei transistor&lt;br /&gt;
Argomenti da trattare:&lt;br /&gt;
== Lezione del 7 ottobre 2016 ==&lt;br /&gt;
== Lezione del 11 ottobre 2016 ==&lt;br /&gt;
== Lezione del 14 ottobre 2016 ==&lt;br /&gt;
== Lezione del 18 ottobre 2016 ==&lt;br /&gt;
== Lezione del 21 ottobre 2016 ==&lt;br /&gt;
== Lezione del 25 ottobre 2016 ==&lt;br /&gt;
== Lezione del 28 ottobre 2016 ==&lt;br /&gt;
== Lezione del 28 ottobre 2016 ==&lt;br /&gt;
== Lezione del 4 novembre 2016 ==&lt;br /&gt;
== Lezione del 8 novembre 2016 ==&lt;br /&gt;
== Lezione del 11 novembre 2016 ==&lt;br /&gt;
== Lezione del 15 novembre 2016 ==&lt;br /&gt;
== Lezione del 18 novembre 2016 ==&lt;br /&gt;
== Lezione del 22 novembre 2016 ==&lt;br /&gt;
== Lezione del 25 novembre 2016 ==&lt;br /&gt;
== Lezione del 29 novembre 2016 ==&lt;br /&gt;
== Lezione del 2 dicembre 2016 ==&lt;br /&gt;
== Lezione del 6 dicembre 2016 ==&lt;br /&gt;
== Lezione del 9 dicembre 2016 ==&lt;br /&gt;
== Lezione del 13 dicembre 2016 ==&lt;br /&gt;
== Lezione del 16 dicembre 2016 ==&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1383</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=1383"/>
		<updated>2016-09-27T20:56:33Z</updated>

		<summary type="html">&lt;p&gt;Mak: /* Lezione del 27 settembre 2016 */&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;
* '''Universit&amp;amp;agrave;'''&lt;br /&gt;
  Università = studenti + docenti &lt;br /&gt;
* Digitale/Analogico&lt;br /&gt;
* '''Binario/Decimale'''&lt;br /&gt;
  Nei calcolatori l'informazione è memorizzata utilizzando il sistema binario in quanto in passato i relè consentivano di avere due stati: &amp;quot; 1 e 0 &amp;quot;&lt;br /&gt;
* I linguaggi e i problemi dell'Informatica&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;
&lt;br /&gt;
* '''Sistema Operativo: perch&amp;amp;eacute;'''&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     L'&lt;br /&gt;
  ---------------&lt;br /&gt;
        hw     L&lt;br /&gt;
  ---------------&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 sitruzioni non ambigue, atta a risolvere un problema.&lt;br /&gt;
  Programma: traduzione degli algoritmi in un linguaggio di programmazione.&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;
  System call: richieste di processi al OS. e.g. printf (visto nel codice compilato in assembly tramite gcc -s), invoca read().&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;
== Lezione del 30 settembre 2016 ==&lt;br /&gt;
Argomenti da approfondire:&lt;br /&gt;
* (Economia Push/Economia Pull, Distribuzione)&lt;br /&gt;
* Storia dell'Informatica dopo l'avvento dei transistor&lt;br /&gt;
Argomenti da trattare:&lt;br /&gt;
== Lezione del 7 ottobre 2016 ==&lt;br /&gt;
== Lezione del 11 ottobre 2016 ==&lt;br /&gt;
== Lezione del 14 ottobre 2016 ==&lt;br /&gt;
== Lezione del 18 ottobre 2016 ==&lt;br /&gt;
== Lezione del 21 ottobre 2016 ==&lt;br /&gt;
== Lezione del 25 ottobre 2016 ==&lt;br /&gt;
== Lezione del 28 ottobre 2016 ==&lt;br /&gt;
== Lezione del 28 ottobre 2016 ==&lt;br /&gt;
== Lezione del 4 novembre 2016 ==&lt;br /&gt;
== Lezione del 8 novembre 2016 ==&lt;br /&gt;
== Lezione del 11 novembre 2016 ==&lt;br /&gt;
== Lezione del 15 novembre 2016 ==&lt;br /&gt;
== Lezione del 18 novembre 2016 ==&lt;br /&gt;
== Lezione del 22 novembre 2016 ==&lt;br /&gt;
== Lezione del 25 novembre 2016 ==&lt;br /&gt;
== Lezione del 29 novembre 2016 ==&lt;br /&gt;
== Lezione del 2 dicembre 2016 ==&lt;br /&gt;
== Lezione del 6 dicembre 2016 ==&lt;br /&gt;
== Lezione del 9 dicembre 2016 ==&lt;br /&gt;
== Lezione del 13 dicembre 2016 ==&lt;br /&gt;
== Lezione del 16 dicembre 2016 ==&lt;/div&gt;</summary>
		<author><name>Mak</name></author>
	</entry>
</feed>