<?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=Alexp</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=Alexp"/>
	<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php/Special:Contributions/Alexp"/>
	<updated>2026-05-03T12:12:40Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.35.5</generator>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2016.05.31&amp;diff=1876</id>
		<title>Prova pratica 2016.05.31</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prova_pratica_2016.05.31&amp;diff=1876"/>
		<updated>2017-05-13T18:40:03Z</updated>

		<summary type="html">&lt;p&gt;Alexp: Aggiunte le soluzioni degli es1 e es2 della prova pratica del 2016.05.31.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://www.cs.unibo.it/~renzo/so/pratiche/2016.05.31.pdf Link al testo]&lt;br /&gt;
&lt;br /&gt;
==Esercizio 1==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Scrivere un programma che preso come parametro a linea comando il path di una directory elenchi solamente I file&lt;br /&gt;
che hanno un nome che ha come suffisso un numero (es. Prova.10). I file devono essere ordinati in ordine&lt;br /&gt;
numerico.&lt;br /&gt;
Esempio se la directory &lt;br /&gt;
test contiene I file prova5, giovanni, aaa.1000, bb.2000, ccc.dd.500 l'output del programma deve essere:&lt;br /&gt;
ccc.dd.500&lt;br /&gt;
aaa.1000&lt;br /&gt;
bb.2000&lt;br /&gt;
(in quanto 500 numericamente e' minore di 1000, prova5 non si considera: manca il punto prima del numero).&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Soluzione di Alexp ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dirent.h&amp;gt;&lt;br /&gt;
#include &amp;lt;ctype.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
Assumes that filename contains a digit in the suffix&lt;br /&gt;
*/&lt;br /&gt;
uint64_t getSuffixNumber(const char* filename){&lt;br /&gt;
    size_t len = strlen(filename)-1;&lt;br /&gt;
    size_t pos = len;&lt;br /&gt;
    uint64_t number = 0;&lt;br /&gt;
    uint64_t decimal = 1;&lt;br /&gt;
    //while there are digits to read&lt;br /&gt;
    while(pos&amp;gt;=0 &amp;amp;&amp;amp; isdigit(filename[pos])){&lt;br /&gt;
        //number+=digit converted to int, multiplied by decimal position&lt;br /&gt;
        number+=(filename[pos] - '0')*decimal;&lt;br /&gt;
        decimal*=10;&lt;br /&gt;
        pos--;&lt;br /&gt;
    }&lt;br /&gt;
    return number;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
Returns 1 if the given filename ends with a number, 0 otherwise&lt;br /&gt;
*/&lt;br /&gt;
int endsWithNumber(const char* filename){&lt;br /&gt;
    size_t len = strlen(filename)-1;&lt;br /&gt;
    size_t pos = len;&lt;br /&gt;
    //stating from last character, go backwards until there are digits&lt;br /&gt;
    while(pos&amp;gt;=0 &amp;amp;&amp;amp; isdigit(filename[pos])) pos--;&lt;br /&gt;
    //if the suffix is actually a number, preceeded by a '.'&lt;br /&gt;
    if(pos&amp;gt;=0 &amp;amp;&amp;amp; pos != len &amp;amp;&amp;amp; filename[pos] == '.') return 1;&lt;br /&gt;
    else return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int sortbysuffix(const struct dirent **e1, const struct dirent **e2) {&lt;br /&gt;
    const char *a = (*e1)-&amp;gt;d_name;&lt;br /&gt;
    const char *b = (*e2)-&amp;gt;d_name;&lt;br /&gt;
    uint64_t suffixA = getSuffixNumber(a);&lt;br /&gt;
    uint64_t suffixB = getSuffixNumber(b);&lt;br /&gt;
    //compare two suffixes&lt;br /&gt;
    return suffixA &amp;lt; suffixB;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int filter(const struct dirent* ent){&lt;br /&gt;
    //exclude &amp;quot;.&amp;quot; and &amp;quot;..&amp;quot; and files not ending with a &amp;quot;.(number)+&amp;quot;&lt;br /&gt;
    if(strcmp(ent-&amp;gt;d_name,&amp;quot;.&amp;quot;) &amp;amp;&amp;amp; strcmp(ent-&amp;gt;d_name,&amp;quot;..&amp;quot;) &amp;amp;&amp;amp; endsWithNumber(ent-&amp;gt;d_name)){&lt;br /&gt;
        return 1;&lt;br /&gt;
    }&lt;br /&gt;
    else return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void printFilenames(char* path){&lt;br /&gt;
    struct dirent **namelist;&lt;br /&gt;
    int n;&lt;br /&gt;
    n = scandir(path, &amp;amp;namelist, filter, sortbysuffix);&lt;br /&gt;
    if (n &amp;lt; 0)&lt;br /&gt;
        perror(&amp;quot;scandir&amp;quot;);&lt;br /&gt;
    else {&lt;br /&gt;
        while (n--) {&lt;br /&gt;
            printf(&amp;quot;%s\n&amp;quot;, namelist[n]-&amp;gt;d_name);&lt;br /&gt;
            free(namelist[n]);&lt;br /&gt;
        }&lt;br /&gt;
        free(namelist);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int isDirectory(const char *path) {&lt;br /&gt;
    struct stat statbuf;&lt;br /&gt;
    //if unable to get file status&lt;br /&gt;
    if (stat(path, &amp;amp;statbuf) != 0) return 0;&lt;br /&gt;
    //otherwise if we got the file status, check if it's a directory&lt;br /&gt;
    return S_ISDIR(statbuf.st_mode);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]){&lt;br /&gt;
    if(argc == 2 &amp;amp;&amp;amp; isDirectory(argv[1])){&lt;br /&gt;
        printFilenames(argv[1]);&lt;br /&gt;
    }&lt;br /&gt;
    else{&lt;br /&gt;
        printf(&amp;quot;Error, input a directory name.\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Esercizio 2==&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Come nell'esercizio 1 occorre cercare in una directory (passata come parametro) I file che hanno come suffisso un&lt;br /&gt;
numero. Nell'esercizio 2 I file sono eseguibili e l numero indica il numero di millisecondi da attendere a partire dalla attivazione del programma prima di attivarli.&lt;br /&gt;
Nell'esempio dell'esercizio precedente occorre aspettare mezzo secondo e lanciare ccc.dd.500, poi a 1 secondo&lt;br /&gt;
dall'attivazione (cioe' dopo approssimativamente ulteriori 0.5 secondi) si lancia aaa.1000 e allo scadere del&lt;br /&gt;
secondo secondo bbb.2000.&lt;br /&gt;
I file eseguibili nella directory vengono eseguiti in background (non si attende la fine dell'esecuzione per&lt;br /&gt;
continuare). Quindi se due file hanno lo stesso prefisso numerico vengono eseguiti in modo concorrente.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
=== Soluzione di Alexp ===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dirent.h&amp;gt;&lt;br /&gt;
#include &amp;lt;ctype.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
Assumes that filename contains a digit in the suffix&lt;br /&gt;
*/&lt;br /&gt;
uint64_t getSuffixNumber(const char* filename){&lt;br /&gt;
    size_t len = strlen(filename)-1;&lt;br /&gt;
    size_t pos = len;&lt;br /&gt;
    uint64_t number = 0;&lt;br /&gt;
    uint64_t decimal = 1;&lt;br /&gt;
    //while there are digits to read&lt;br /&gt;
    while(pos&amp;gt;=0 &amp;amp;&amp;amp; isdigit(filename[pos])){&lt;br /&gt;
        //number+=digit converted to int, multiplied by decimal position&lt;br /&gt;
        number+=(filename[pos] - '0')*decimal;&lt;br /&gt;
        decimal*=10;&lt;br /&gt;
        pos--;&lt;br /&gt;
    }&lt;br /&gt;
    return number;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int endsWithNumber(const char* filename){&lt;br /&gt;
    size_t len = strlen(filename)-1;&lt;br /&gt;
    size_t pos = len;&lt;br /&gt;
    //stating from last character, go backwards until there are digits&lt;br /&gt;
    while(pos&amp;gt;=0 &amp;amp;&amp;amp; isdigit(filename[pos])) pos--;&lt;br /&gt;
    //if the suffix is actually a number, preceeded by a '.'&lt;br /&gt;
    if(pos&amp;gt;=0 &amp;amp;&amp;amp; pos != len &amp;amp;&amp;amp; filename[pos] == '.') return 1;&lt;br /&gt;
    else return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int sortbysuffix(const struct dirent **e1, const struct dirent **e2) {&lt;br /&gt;
    const char *a = (*e1)-&amp;gt;d_name;&lt;br /&gt;
    const char *b = (*e2)-&amp;gt;d_name;&lt;br /&gt;
    uint64_t suffixA = getSuffixNumber(a);&lt;br /&gt;
    uint64_t suffixB = getSuffixNumber(b);&lt;br /&gt;
    //compare two suffixes&lt;br /&gt;
    return suffixA &amp;lt; suffixB;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
Returns 1 if the file is an executable file, 0 otherwise&lt;br /&gt;
*/&lt;br /&gt;
int isExecutable(const char* path){&lt;br /&gt;
    struct stat buf;&lt;br /&gt;
    //if unable to get file status&lt;br /&gt;
    if (stat(path, &amp;amp;buf) != 0) return 0;&lt;br /&gt;
    //if the file is executable by user&lt;br /&gt;
    if (buf.st_mode &amp;amp; S_IXUSR) return 1;&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int filter(const struct dirent* ent){&lt;br /&gt;
    //exclude &amp;quot;.&amp;quot; and &amp;quot;..&amp;quot; and files not ending with a &amp;quot;.(number)+&amp;quot;&lt;br /&gt;
    if(strcmp(ent-&amp;gt;d_name,&amp;quot;.&amp;quot;) &amp;amp;&amp;amp; strcmp(ent-&amp;gt;d_name,&amp;quot;..&amp;quot;) &amp;amp;&amp;amp; endsWithNumber(ent-&amp;gt;d_name)){&lt;br /&gt;
        return 1;&lt;br /&gt;
    }&lt;br /&gt;
    else return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void runExecutables(char* path){&lt;br /&gt;
    struct dirent **namelist;&lt;br /&gt;
    int n;&lt;br /&gt;
    //delay time in ms&lt;br /&gt;
    uint64_t waitingMS;&lt;br /&gt;
    char* fullpath;&lt;br /&gt;
    //forked child's pid&lt;br /&gt;
    pid_t pid;&lt;br /&gt;
    //used to wait all the children&lt;br /&gt;
    int status = 0;&lt;br /&gt;
    n = scandir(path, &amp;amp;namelist, filter, sortbysuffix);&lt;br /&gt;
    if (n &amp;lt; 0) perror(&amp;quot;scandir&amp;quot;);&lt;br /&gt;
    else {&lt;br /&gt;
        while (n--) {&lt;br /&gt;
            waitingMS = getSuffixNumber(namelist[n]-&amp;gt;d_name);&lt;br /&gt;
            //get the full dirname+filename path&lt;br /&gt;
            asprintf(&amp;amp;fullpath, &amp;quot;%s%s&amp;quot;, path, namelist[n]-&amp;gt;d_name);&lt;br /&gt;
            if(isExecutable(fullpath)){&lt;br /&gt;
                printf(&amp;quot;Calling %s\n&amp;quot;,fullpath);&lt;br /&gt;
                if( pid != fork() ){&lt;br /&gt;
                    //child&lt;br /&gt;
                    //wait for the required time(in microseconds)&lt;br /&gt;
          	    usleep(waitingMS*1000);&lt;br /&gt;
                    char *args[] = {fullpath,(char *)0};&lt;br /&gt;
                    printf(&amp;quot;Starting%s\n&amp;quot;, args[0]);&lt;br /&gt;
                    execvp(fullpath,args);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            free(fullpath);&lt;br /&gt;
            free(namelist[n]);&lt;br /&gt;
        }&lt;br /&gt;
        //wait till all the children terminate&lt;br /&gt;
        while ((pid = wait(&amp;amp;status)) &amp;gt; 0); &lt;br /&gt;
        free(namelist);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int isDirectory(const char *path) {&lt;br /&gt;
    struct stat statbuf;&lt;br /&gt;
    //if unable to get file status&lt;br /&gt;
    if (stat(path, &amp;amp;statbuf) != 0) return 0;&lt;br /&gt;
    //otherwise if we got the file status, check if it's a directory&lt;br /&gt;
    return S_ISDIR(statbuf.st_mode);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]){&lt;br /&gt;
    if(argc == 2 &amp;amp;&amp;amp; isDirectory(argv[1])){&lt;br /&gt;
        runExecutables(argv[1]);&lt;br /&gt;
    }&lt;br /&gt;
    else{&lt;br /&gt;
        printf(&amp;quot;Error, input a directory name.\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Alexp</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Prove_pratiche&amp;diff=1875</id>
		<title>Prove pratiche</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Prove_pratiche&amp;diff=1875"/>
		<updated>2017-05-13T18:19:18Z</updated>

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

		<summary type="html">&lt;p&gt;Alexp: /* Esercizio c.2 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Testo: [http://www.cs.unibo.it/~renzo/so/compiti/2014.07.16.tot.pdf]&lt;br /&gt;
&lt;br /&gt;
== Esercizio c.1 ==&lt;br /&gt;
===Soluzione di S.G===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Esercizio c.1: scrivere il monitor mMbb che realizzi un meccanismo di un minmax bounded buffer.&lt;br /&gt;
 * Dopo le prime MIN operazioni di scrittura, un minmax bounded buffer deve sempre avere almeno MIN elementi e mai piu' di MAX&lt;br /&gt;
 * elementi (e' quindi limitato sia nell'ampiezza massima, sia nell'ampiezza minima).&lt;br /&gt;
 * Le funzioni (procedure entry) read e write del minmax bounded buffer hanno gli stessi argomenti e valori di ritorno di quelle del&lt;br /&gt;
 * producer/consumer o del bounded buffer ordinario.&lt;br /&gt;
 */&lt;br /&gt;
#define MIN&lt;br /&gt;
#define MAX&lt;br /&gt;
#define SIZE // con SIZE &amp;gt;= MAX&lt;br /&gt;
&lt;br /&gt;
monitor mMbb{&lt;br /&gt;
	&lt;br /&gt;
	generic_type bb[];&lt;br /&gt;
	int front, rear, count;&lt;br /&gt;
	condition R, W;&lt;br /&gt;
	&lt;br /&gt;
	void mMbb(void){&lt;br /&gt;
		bb = new generic_type[SIZE];&lt;br /&gt;
		front = rear = count = 0;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure_entry generic_type read(){&lt;br /&gt;
		while(count &amp;lt; MIN)	// Controllo che il buffer abbia MIN elementi per poter leggere&lt;br /&gt;
			R.wait();&lt;br /&gt;
		&lt;br /&gt;
		generic_type ret = bb[rear];&lt;br /&gt;
		count--;&lt;br /&gt;
		rear = (rear + 1) % SIZE;&lt;br /&gt;
		W.signal();&lt;br /&gt;
		return ret;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure_entry write(generic_type c){&lt;br /&gt;
		while(count &amp;gt;= MAX)	// Controllo che il buffer non abbia MAX elementi per poter scrivere&lt;br /&gt;
			W.wait();&lt;br /&gt;
		&lt;br /&gt;
		bb[front] = c;&lt;br /&gt;
		count++;&lt;br /&gt;
		front = (front + 1) % SIZE;&lt;br /&gt;
		R.signal();	&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[User:S.G|S.G]] ([[User talk:S.G|talk]]) 17:21, 2 December 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
Non &amp;amp;egrave; fifo. Perch&amp;amp;eacute;? [[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 07:43, 3 December 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
Nel caso in cui MIN vale 3 e arrivino tre processi lettori nel seguente ordine, L1, L2, L3, avremmo che la coda di R è la seguente:&lt;br /&gt;
&lt;br /&gt;
R: L1, L2, L3&lt;br /&gt;
&lt;br /&gt;
Ora arriva un processo scrittore S1, viene eseguito liberando un lettore (R.signal) cioè L1. A questo punto L1 riprende la sua esecuzione, riesegue il ciclo e rientra nello stato di attesa. Quindi ora la coda di R è la seguente:&lt;br /&gt;
&lt;br /&gt;
R: L2, L3, L1&lt;br /&gt;
&lt;br /&gt;
Si potrebbe risolvere il problema modificando le funzioni write e read nel seguente modo:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
procedure_entry generic_type read(){&lt;br /&gt;
		if(count &amp;lt; MIN)	// Controllo che il buffer abbia MIN elementi per poter leggere&lt;br /&gt;
			R.wait();&lt;br /&gt;
		&lt;br /&gt;
		generic_type ret = bb[rear];&lt;br /&gt;
		count--;&lt;br /&gt;
		rear = (rear + 1) % SIZE;&lt;br /&gt;
		if(count &amp;lt; MAX)&lt;br /&gt;
			W.signal();&lt;br /&gt;
		return ret;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	procedure_entry write(generic_type c){&lt;br /&gt;
		if(count &amp;gt;= MAX)	// Controllo che il buffer non abbia MAX elementi per poter scrivere&lt;br /&gt;
			W.wait();&lt;br /&gt;
		&lt;br /&gt;
		bb[front] = c;&lt;br /&gt;
		count++;&lt;br /&gt;
		front = (front + 1) % SIZE;&lt;br /&gt;
		if(count &amp;gt; MIN)&lt;br /&gt;
			R.signal();	&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[User:S.G|S.G]] ([[User talk:S.G|talk]]) 10:37, 3 December 2016 (CET)&lt;br /&gt;
===Soluzione di Stefano Zaniboni===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
/*esercizio 1*/&lt;br /&gt;
&lt;br /&gt;
monitor mMbb{&lt;br /&gt;
	object[] buffer; //spazio di memorizzazione&lt;br /&gt;
	&lt;br /&gt;
	final int MAXELEM; // numero massimo di elementi che ci possono essere nel buffer&lt;br /&gt;
	final int MINELEM; //numero minimo di elementi --&amp;gt; numero di scritture minime&lt;br /&gt;
	&lt;br /&gt;
	condition okW; // dove mi fermo se non posso scrivere&lt;br /&gt;
	condition okR; // dove mi fermo se non posso leggere&lt;br /&gt;
&lt;br /&gt;
	int count; // elementi presenti nel buffer&lt;br /&gt;
	int front;   // contatore al elemento prodotto&lt;br /&gt;
	int rear;   // contatore al elemento consumato	&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
	mMbb(){&lt;br /&gt;
		 buffer = new Object[MAXELEM];&lt;br /&gt;
    	count = rear = front = 0;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	procedure entry object read(){&lt;br /&gt;
		if ((count &amp;lt; MINELEM))  //se il buffer e' pieno oppure non ci sono min elem chiamo la wait&lt;br /&gt;
    		     okR.wait();&lt;br /&gt;
  		eltype val = buffer[rear]; // assegno al val l'elemento che ho letto e poi aggiorno rear&lt;br /&gt;
		rear = ((rear + 1) % (MAXELEM -1 ));  // aggiorno rear;&lt;br /&gt;
		count--; // decremento count per dire che ho consumato un elemento&lt;br /&gt;
  		okW.signal(); // qui faccio signal sui produttori che adesso possono scrivere&lt;br /&gt;
  		return retval;&lt;br /&gt;
	}&lt;br /&gt;
	procedure entry void write(int val){&lt;br /&gt;
		if (count == buffer.length) //se il buffer e' pieno mi fermo&lt;br /&gt;
    		      okW.wait();&lt;br /&gt;
  		buffer[front] = val; //scrivo elemento&lt;br /&gt;
  		count++; //aggiorno i prodotti disponibili&lt;br /&gt;
  		front = ((front+1)%(MAXELEM - 1)); // aggiorno il contatore front a puntare nella prossima posizione&lt;br /&gt;
&lt;br /&gt;
  		okR.signal();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Esercizio c.2 ==&lt;br /&gt;
===Soluzione di S.G===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Esercizio c.2: Facendo uso di semafori scrivere la funzione rendezvous che consenta ai processi di sincronizzarsi secondo le&lt;br /&gt;
 * seguenti specifiche:&lt;br /&gt;
 * – Ogni processo indica come parametro della funzione rendezvous con quanti altri processi vuole sincronizzarsi.&lt;br /&gt;
 * – M processi che chiamano la rendezvous con parametro N rimangono bloccati se M&amp;lt;N.&lt;br /&gt;
 * – Quando l'N-mo processo richiama la rendezvous specificando N come parametro, lui e gli N-1 sospesi devono proseguire&lt;br /&gt;
 * nelle propria esecuzione&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
struct S {&lt;br /&gt;
	Semaphore sem;&lt;br /&gt;
	int N; // parametro della funzione rendezvous con quanti altri processi vuole sincronizzarsi&lt;br /&gt;
	int I; // Contatore dei processi che richiamano rendezvous con paramentro N&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
List&amp;lt;S&amp;gt; listS = new List[]; // Lista di possibili semafori&lt;br /&gt;
&lt;br /&gt;
Semaphore mutex = new Semaphore(1);		// Variabile che consente la mutua esclusione&lt;br /&gt;
&lt;br /&gt;
rendevouz(int n){&lt;br /&gt;
	mutex.P();	// Ottengo la mutua esclusione&lt;br /&gt;
	&lt;br /&gt;
	find = FALSE;&lt;br /&gt;
	for(s in listS){	// Cerco il semaforo nella lista&lt;br /&gt;
		if(s.N == n){	&lt;br /&gt;
			find = TRUE;&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if(find == FALSE){	// se non è presente nella lista lo creo&lt;br /&gt;
		struct S s;&lt;br /&gt;
		s.sem = new semaphore(0);&lt;br /&gt;
		s.N = n;&lt;br /&gt;
		s.I = 0;&lt;br /&gt;
		listS.insert(s);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	s.I++;&lt;br /&gt;
	if(s.I == s.N){		// Quando l'N-mo processo richiama la rendezvous specificando N come parametro&lt;br /&gt;
		while(s.I--)	// lui e gli N-1 sospesi devono proseguire nelle propria esecuzione&lt;br /&gt;
			s.sem.V();&lt;br /&gt;
	}&lt;br /&gt;
	else if(s.I &amp;lt; s.N){	// M processi che chiamano la rendezvous con parametro N rimangono bloccati se M&amp;lt;N&lt;br /&gt;
		s.sem.P();&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	mutex.V();	// Rilascio la mutua esclusione&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Ho qualche dubbio su questa soluzione, riguardo la linea presente l'istruzione s.sem.P(), il processo viene immediatamente bloccato? Se si dovrei rilasciare la mutua esclusione prima quindi? [[User:S.G|S.G]] ([[User talk:S.G|talk]]) 16:43, 3 December 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
Si, la mutua esclusione va, in linea di principio, sempre rilasaciata prima di un' operazione P() poichè il processo che la chiama potrebbe bloccarsi tenendo con se la mutua esclusione e causando quindi deadlock.--[[User:FedericoP|FedericoP]] ([[User talk:FedericoP|talk]]) 10:37, 15 January 2017 (CET)&lt;br /&gt;
===Soluzione di MV===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// ------------------------- SOLUZIONE DI MV -------------------------&lt;br /&gt;
&lt;br /&gt;
void rendezvous(int N)&lt;br /&gt;
{&lt;br /&gt;
	struct S { Semaphore sem; int N; int M };&lt;br /&gt;
	&lt;br /&gt;
	static List&amp;lt;struct S&amp;gt; L = new List&amp;lt;struct S&amp;gt;();&lt;br /&gt;
	static Semaphore mutex = new Semaphore(1);&lt;br /&gt;
	&lt;br /&gt;
	if(N &amp;lt;= 1)	// se N==1 significa che non vuole sincronizzarsi&lt;br /&gt;
		return;	// se N&amp;lt;1 non ha senso =&amp;gt; esce dalla funzione&lt;br /&gt;
	&lt;br /&gt;
	bool trovato = false;&lt;br /&gt;
	mutex.P();	// prende la mutua esclusione per evitare incoerenze&lt;br /&gt;
	// scorre la lista in cerca di un semaforo per N&lt;br /&gt;
	for(List&amp;lt;struct S&amp;gt; el = L.head(); !L.last(el) &amp;amp;&amp;amp; !trovato; el = L.next(el) )&lt;br /&gt;
		if(el.content.N == N)	// se esiste&lt;br /&gt;
		{&lt;br /&gt;
			trovato = true;&lt;br /&gt;
			if(el.content.M == N-1)	// se è l'N-esimo processo&lt;br /&gt;
			{&lt;br /&gt;
				while(el.content.M)	// sblocca tutti gli altri&lt;br /&gt;
				{&lt;br /&gt;
					el.content.sem.V();&lt;br /&gt;
					el.content.M--;&lt;br /&gt;
				}&lt;br /&gt;
				mutex.V();&lt;br /&gt;
			}&lt;br /&gt;
			else		// altrimenti si mette in attesa&lt;br /&gt;
			{&lt;br /&gt;
				el.content.M++;&lt;br /&gt;
				mutex.V();&lt;br /&gt;
				el.content.sem.P();&lt;br /&gt;
			}&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	&lt;br /&gt;
	if(!trovato)	// se non esiste lo crea&lt;br /&gt;
	{&lt;br /&gt;
		// NB: ho ancora la mutua esclusione&lt;br /&gt;
		struct S s;&lt;br /&gt;
		s.N = N;&lt;br /&gt;
		s.M = 1;&lt;br /&gt;
		s.sem = new Semaphore(0);&lt;br /&gt;
		L.insert_tail(S)	// inserisce nella lista (in coda)&lt;br /&gt;
		&lt;br /&gt;
		mutex.V():&lt;br /&gt;
		// si mette in attesa sul semaforo appena creato (NB: N!=1)&lt;br /&gt;
		L.tail().content.sem.P();&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* NOTA: gli elementi della lista non vengono deallocati una volta liberato&lt;br /&gt;
	il semaforo; questo perchè è ancora possibile una race condition (rara)&lt;br /&gt;
	nel caso in cui un processo P1 stia per inserirsi su un semaforo già esistente&lt;br /&gt;
	(ha appena fatto mutex.V() ) e un altro P2 riesca ad entrare nel ciclo e&lt;br /&gt;
	liberare tutti =&amp;gt; P2 effettua una V in più e P1 esce subito dalla P&lt;br /&gt;
*/&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Soluzione di Alexp===&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
List&amp;lt;Queue&amp;lt;Semaphore&amp;gt;&amp;gt; waiting;         //Lista di code di semafori&lt;br /&gt;
Semaphore mutex = 1;                    //Semaforo per modificare la lista delle code&lt;br /&gt;
&lt;br /&gt;
void rendezvous(int N){&lt;br /&gt;
    if(N &amp;gt;= 2){&lt;br /&gt;
        //semaforo da accodare(e su cui si dovrà bloccare il thread nel caso waiting[N].size() &amp;lt; N)&lt;br /&gt;
        Semaphore s = 0;&lt;br /&gt;
        int i;&lt;br /&gt;
        //entriamo nella critical section per modificare una struttura condivisa&lt;br /&gt;
        mutex.P();&lt;br /&gt;
        //se il chiamante non è l'N-esimo thread&lt;br /&gt;
        if(waiting[N].size()+1 &amp;lt; N){&lt;br /&gt;
            //accodiamo il semaforo nel caso non ci siano esattamente N thread con cui sincronizzarsi&lt;br /&gt;
            waiting[N].enqueue(s);&lt;br /&gt;
            //usciamo dalla critical section per non causare deadlock&lt;br /&gt;
            mutex.V();&lt;br /&gt;
            //fermiamo il thread sul semaforo accodato prima, nel caso in cui debba aspettare&lt;br /&gt;
            //questo thread verrà risvegliato dall'N-esimo thread che si sincronizzerà&lt;br /&gt;
            s.P();&lt;br /&gt;
        }&lt;br /&gt;
        //se il thread chiamante è proprio l'N-esimo(quindi dovrà sbloccare tutti)&lt;br /&gt;
        else{&lt;br /&gt;
            //per ogni thread accodato sulla coda in cui si sincronizzano N thread&lt;br /&gt;
            /*&lt;br /&gt;
            (in realtà nella coda ci sono N-1 semafori dei thread fermi&lt;br /&gt;
             mentre il thread che entra in questo ramo else sarà l'N-esimo)&lt;br /&gt;
            */&lt;br /&gt;
            for(i = 0; i &amp;lt; N-1; i++){&lt;br /&gt;
                //togliamo dalla coda il semaforo e risvegliamo il thread che lo aspettava&lt;br /&gt;
                waiting[N].dequeue().V();&lt;br /&gt;
            }&lt;br /&gt;
            //struttura condivisa modificata, usciamo dalla critical section&lt;br /&gt;
            mutex.V();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Alexp</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Coding_Contest_25_novembre_2016&amp;diff=1697</id>
		<title>Coding Contest 25 novembre 2016</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Coding_Contest_25_novembre_2016&amp;diff=1697"/>
		<updated>2016-12-08T12:50:34Z</updated>

		<summary type="html">&lt;p&gt;Alexp: /* Esercizio 3 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Esercizio 1 ==&lt;br /&gt;
&lt;br /&gt;
scrivere un programma che passato come parametro il path di una directory (cwd se manca il paramtro) stampi il path relativo di tutti i file con nome palindromo presenti nel sottoalbero.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2720c84e673c75c20a27a07ffcd6826b31c8babc  esercizio1.c  [[User:Leonardo|Leonardo]] ([[User talk:Leonardo|talk]]) 17:23, 25 November 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
86aca4384a296bf12c69541fc5418fe4b6656e0d  es1.c [[User:Fabio.capucci|Fabio.capucci]] ([[User talk:Fabio.capucci|talk]]) 17:30, 25 November 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
071fccfe63b8121f951444cb57001ccdd4ec02d0  Es1.c [[User:FilippoB|FilippoB]] ([[User talk:FilippoB|talk]]) 17:31, 25 November 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
235f2414cfe5b81973b1d388ce9431aa02b5f305 [[User:MatteoC|MatteoC]] ([[User talk:MatteoC|talk]]) 17:43, 25 November 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
168d3422b92fc78519e19d6a8e156b260a7798a6 testdir.c [[User:Tamino|Tamino]] ([[User talk:Tamino|talk]]) 18:06, 25 November 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
== Esercizio 2 ==&lt;br /&gt;
&lt;br /&gt;
lancia tutti gli eseguibili della directory passata come primo paramtro  e concatena gli output.&lt;br /&gt;
(i parametri rimanenti devono essere passati a tutti gli eseguibili)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
lanciatutti dir 1 2 3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
se nella directory ''dir'' sono presenti ''a'', ''b'' e ''c'' (eseguibili) lancia '''a 1 2 3''', '''b 1 2 3''', ''c 1 2 3'''&lt;br /&gt;
&lt;br /&gt;
== Esercizio 3 ==&lt;br /&gt;
&lt;br /&gt;
Scrivere un programma che deve ottenere il pid passato come parametro.&lt;br /&gt;
indicare se e' impossibile&lt;br /&gt;
&lt;br /&gt;
fdf7b609e470c43a32a50801e76e78c32aebfbb1  main.tar.gz [[User:Alexp|Alexp]] ([[User talk:Alexp|talk]]) 17:22, 25 November 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
In seguito il codice dell'esercizio --[[User:Alexp|Alexp]] ([[User talk:Alexp|talk]]) 13:50, 8 December 2016 (CET) :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define PID 6300&lt;br /&gt;
&lt;br /&gt;
void do_something(){&lt;br /&gt;
    //print something&lt;br /&gt;
    printf(&amp;quot;---doing something---\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char* argv[]) {&lt;br /&gt;
    int sPID = PID;&lt;br /&gt;
    if (argc &amp;gt; 1) {&lt;br /&gt;
        sPID = atoi(argv[1]);&lt;br /&gt;
    }&lt;br /&gt;
    pid_t pid;&lt;br /&gt;
    if (getpid() == sPID) {&lt;br /&gt;
        //if sPID matched&lt;br /&gt;
        printf(&amp;quot;GOT PID (%d)!\n&amp;quot;, sPID);&lt;br /&gt;
        //do something&lt;br /&gt;
        do_something();&lt;br /&gt;
    } else {&lt;br /&gt;
        if (kill(sPID, 0) != 0 &amp;amp;&amp;amp; errno == ESRCH) {&lt;br /&gt;
            //while generated child has wrong sPID&lt;br /&gt;
            while ((pid = fork()) != sPID) {&lt;br /&gt;
                //if child&lt;br /&gt;
                if (pid == 0) {&lt;br /&gt;
                    if (getpid() == sPID) {&lt;br /&gt;
                        printf(&amp;quot;GOT PID (%d)!\n&amp;quot;, sPID);&lt;br /&gt;
                        //do something&lt;br /&gt;
                        do_something();&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                    //if parent and error occoured&lt;br /&gt;
                else if (pid == -1) {&lt;br /&gt;
                    printf(&amp;quot;Error: process table is full.\n&amp;quot;);&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                    //if parent and no error occoured&lt;br /&gt;
                else {&lt;br /&gt;
                    //wait for child process to finish&lt;br /&gt;
                    wait(NULL);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            //we must wait for the last child&lt;br /&gt;
            if (pid == sPID) {&lt;br /&gt;
                printf(&amp;quot;Waiting for the last child.&amp;quot;);&lt;br /&gt;
                wait(NULL);&lt;br /&gt;
            }&lt;br /&gt;
        } else {&lt;br /&gt;
            printf(&amp;quot;Impossible to get pid %d(another process has it).\n&amp;quot;, sPID);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Esercizio 4 ==&lt;br /&gt;
&lt;br /&gt;
Trovare i file di contenuto uguale nella directory corrente e convertirli in link dello stesso file.&lt;br /&gt;
&lt;br /&gt;
Esempio di soluzione dell'esercizio [[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 09:06, 6 December 2016 (CET).&lt;br /&gt;
(in aula sembrava avere un bug ma non era vero. Alcuni file in /tmp non sono stati convertiti in link perch&amp;amp;eacute; appartenevano ad altro utente).&lt;br /&gt;
&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;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdarg.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;libgen.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;dirent.h&amp;gt;&lt;br /&gt;
#include &amp;lt;mhash.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct file {&lt;br /&gt;
  struct file *next;&lt;br /&gt;
  unsigned char hash[20];&lt;br /&gt;
  char *name;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
struct file *head;&lt;br /&gt;
&lt;br /&gt;
static int ckcontents(int fd1, int fd2) {&lt;br /&gt;
  char buf1[BUFSIZ],buf2[BUFSIZ];&lt;br /&gt;
  ssize_t n1, n2;&lt;br /&gt;
  do {&lt;br /&gt;
    n1 = read(fd1, buf1, BUFSIZ);&lt;br /&gt;
    n2 = read(fd2, buf2, BUFSIZ);&lt;br /&gt;
  } while (n1 == n2 &amp;amp;&amp;amp; n1 &amp;gt; 0 &amp;amp;&amp;amp; memcmp(buf1, buf2, n2 == 0));&lt;br /&gt;
  return n1 == n2;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static int cksize(int fd1, int fd2) {&lt;br /&gt;
  struct stat st1, st2;&lt;br /&gt;
  return&lt;br /&gt;
    fstat(fd1, &amp;amp;st1) == 0 &amp;amp;&amp;amp;&lt;br /&gt;
    fstat(fd2, &amp;amp;st2) == 0 &amp;amp;&amp;amp;&lt;br /&gt;
    st1.st_size == st2.st_size;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int filecmp(char *path1, char *path2) {&lt;br /&gt;
  int fd1 = open(path1, O_RDONLY);&lt;br /&gt;
  int fd2 = open(path2, O_RDONLY);&lt;br /&gt;
  int rval =&lt;br /&gt;
    fd1 &amp;gt;= 0 &amp;amp;&amp;amp; fd2 &amp;gt;=0 &amp;amp;&amp;amp; cksize(fd1, fd2) &amp;amp;&amp;amp; ckcontents(fd1, fd2);&lt;br /&gt;
  close(fd1);&lt;br /&gt;
  close(fd2);&lt;br /&gt;
  return rval;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#if 0&lt;br /&gt;
void printhash(unsigned char * hash) {&lt;br /&gt;
  int i;&lt;br /&gt;
  for (i = 0; i &amp;lt; mhash_get_block_size(MHASH_SHA1); i++) &lt;br /&gt;
    printf(&amp;quot;%.2x&amp;quot;, hash[i]);&lt;br /&gt;
}&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
void compute_sha1sum(char *path, char *hash) {&lt;br /&gt;
  int fd;&lt;br /&gt;
  MHASH td;&lt;br /&gt;
  if ((fd = open(path, O_RDONLY)) &amp;gt;= 0) {&lt;br /&gt;
    if ((td = mhash_init(MHASH_SHA1)) != MHASH_FAILED) {&lt;br /&gt;
      ssize_t n;&lt;br /&gt;
      char buf[BUFSIZ];&lt;br /&gt;
      while ((n = read(fd, buf, BUFSIZ)) &amp;gt; 0)&lt;br /&gt;
        mhash(td, buf, n);&lt;br /&gt;
      mhash_deinit(td, hash);&lt;br /&gt;
    }&lt;br /&gt;
    close(fd);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static void check_n_link(char *name) {&lt;br /&gt;
  struct file **scan;&lt;br /&gt;
  unsigned char hash[20];&lt;br /&gt;
  compute_sha1sum(name, hash);&lt;br /&gt;
  for (scan = &amp;amp;head; *scan != NULL; scan = &amp;amp;((*scan) -&amp;gt; next)) {&lt;br /&gt;
    //printhash((*scan)-&amp;gt;hash); printf(&amp;quot; - &amp;quot;); printhash(hash); printf(&amp;quot;\n&amp;quot;);&lt;br /&gt;
    if (memcmp((*scan)-&amp;gt;hash, hash, 20) == 0 &amp;amp;&amp;amp;&lt;br /&gt;
        filecmp((*scan)-&amp;gt;name, name)) {&lt;br /&gt;
      unlink(name);&lt;br /&gt;
      link((*scan)-&amp;gt;name, name);&lt;br /&gt;
      printf(&amp;quot;%s and %s have the same contents\n&amp;quot;, (*scan)-&amp;gt;name, name);&lt;br /&gt;
      return;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  *scan = malloc(sizeof(struct file));&lt;br /&gt;
  if (*scan) {&lt;br /&gt;
    (*scan)-&amp;gt;next = NULL;&lt;br /&gt;
    memcpy((*scan)-&amp;gt;hash, hash, 20);&lt;br /&gt;
    (*scan)-&amp;gt;name = name;&lt;br /&gt;
    //printf(&amp;quot;%s\n&amp;quot;,name);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static void freefilelist() {&lt;br /&gt;
  while (head) {&lt;br /&gt;
    struct file *delenda;&lt;br /&gt;
    delenda = head;&lt;br /&gt;
    head = head-&amp;gt;next;&lt;br /&gt;
    free(delenda);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void err_exit(char *fmt, ...) {&lt;br /&gt;
  va_list ap;&lt;br /&gt;
&lt;br /&gt;
  va_start(ap, fmt);&lt;br /&gt;
  vfprintf(stderr, fmt, ap);&lt;br /&gt;
  va_end(ap);&lt;br /&gt;
  exit(2);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static int regular_only(const struct dirent *d) {&lt;br /&gt;
  const char *name = d-&amp;gt;d_name;&lt;br /&gt;
  struct stat st;&lt;br /&gt;
  return stat(name, &amp;amp;st) == 0 &amp;amp;&amp;amp; S_ISREG(st.st_mode);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[]) {&lt;br /&gt;
  struct dirent **list;&lt;br /&gt;
  int n;&lt;br /&gt;
  int i;&lt;br /&gt;
  n = scandir(&amp;quot;.&amp;quot;, &amp;amp;list, regular_only, alphasort);&lt;br /&gt;
  if (n &amp;gt; 0) {&lt;br /&gt;
    for (i=0; i&amp;lt;n; i++) {&lt;br /&gt;
      char *name = list[i]-&amp;gt;d_name;&lt;br /&gt;
      check_n_link(name);&lt;br /&gt;
    }&lt;br /&gt;
    freefilelist();&lt;br /&gt;
    for (i=0; i&amp;lt;n; i++)&lt;br /&gt;
      free(list[i]);&lt;br /&gt;
    free(list);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Alexp</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Coding_Contest&amp;diff=1662</id>
		<title>Coding Contest</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Coding_Contest&amp;diff=1662"/>
		<updated>2016-11-25T16:22:28Z</updated>

		<summary type="html">&lt;p&gt;Alexp: /* Esercizio 3 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Questa pagina serve per la gare di programmazione in aula.&lt;br /&gt;
&lt;br /&gt;
Io (a.k.a. il prof.) mettero' alcuni testi di esercizi.&lt;br /&gt;
&lt;br /&gt;
Le squadre potranno scegliere l'esercizio/gli esercizi da svolgere.&lt;br /&gt;
&lt;br /&gt;
Attenzione: la squadra che completa un esercizio '''non''' pubblica qui il codice ma mette solamente una hash sha1 del file contenente la soluzione (un singolo sorgente o un tile tar.gz/tgz comprendente la directory contenente tutti i file necessari) seguito da quattro caratteri tilde (~) che mediawiki traduce con il nome dell'autore e il timestamp.&lt;br /&gt;
&lt;br /&gt;
Esempio:&lt;br /&gt;
&lt;br /&gt;
725a6bc1afe1853a7990935a7c66894563ca086f  /tmp/cadrop.tgz [[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 13:13, 25 November 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
In fase di correzione il gruppo deve rendere disponibile (per esempio nella directory /public del cluster cs.unibo.it) un file corrispondente alla chiave sha1 pubblicata.&lt;br /&gt;
&lt;br /&gt;
= Nov. 25, 2016 =&lt;br /&gt;
&lt;br /&gt;
== Esercizio 1 ==&lt;br /&gt;
&lt;br /&gt;
scrivere un programma che passato come parametro il path di una directory (cwd se manca il paramtro) stampi il path relativo di tutti i file con nome palindromo presenti nel sottoalbero.&lt;br /&gt;
&lt;br /&gt;
== Esercizio 2 ==&lt;br /&gt;
&lt;br /&gt;
lancia tutti gli eseguibili della directory passata come primo paramtro  e concatena gli output.&lt;br /&gt;
(i parametri rimanenti devono essere passati a tutti gli eseguibili)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
lanciatutti dir 1 2 3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
se nella directory ''dir'' sono presenti ''a'', ''b'' e ''c'' (eseguibili) lancia '''a 1 2 3''', '''b 1 2 3''', ''c 1 2 3'''&lt;br /&gt;
&lt;br /&gt;
== Esercizio 3 ==&lt;br /&gt;
&lt;br /&gt;
Scrivere un programma che deve ottenere il pid passato come parametro.&lt;br /&gt;
indicare se e' impossibile&lt;br /&gt;
&lt;br /&gt;
fdf7b609e470c43a32a50801e76e78c32aebfbb1  main.tar.gz [[User:Alexp|Alexp]] ([[User talk:Alexp|talk]]) 17:22, 25 November 2016 (CET)&lt;br /&gt;
&lt;br /&gt;
== Esercizio 4 ==&lt;br /&gt;
&lt;br /&gt;
Trovare i file di contenuto uguale nella directory corrente e convertirli in link dello stesso file.&lt;/div&gt;</summary>
		<author><name>Alexp</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1564</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=1564"/>
		<updated>2016-11-08T23:16:08Z</updated>

		<summary type="html">&lt;p&gt;Alexp: terminated a sentence&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;
'''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>Alexp</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Lezioni_Anno_Accademico_2016/17&amp;diff=1563</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=1563"/>
		<updated>2016-11-08T23:14:24Z</updated>

		<summary type="html">&lt;p&gt;Alexp: /* Lezione del 8 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;
: 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;
'''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>Alexp</name></author>
	</entry>
	<entry>
		<id>https://so.v2.cs.unibo.it/wiki/index.php?title=Esperimenti_con_semafori_e_monitor_in_C&amp;diff=1523</id>
		<title>Esperimenti con semafori e monitor in C</title>
		<link rel="alternate" type="text/html" href="https://so.v2.cs.unibo.it/wiki/index.php?title=Esperimenti_con_semafori_e_monitor_in_C&amp;diff=1523"/>
		<updated>2016-10-26T18:55:31Z</updated>

		<summary type="html">&lt;p&gt;Alexp: Corretto il link al materiale&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ho messo a vostra disposizione alcuni sorgenti che implementano in C le astrazioni di semaforo e monitor come le abbiamo viste (o vedremo presto) a lezione.&lt;br /&gt;
&lt;br /&gt;
Ho messo il materiale in [http://www.cs.unibo.it/~renzo/so/tools/sm.tgz questo file].&lt;br /&gt;
&lt;br /&gt;
La directory comprende:&lt;br /&gt;
* un Makefile&lt;br /&gt;
* tlist.[ch] un modulo di implementazione di liste circolari di thread_id (usate per gestire i processi bloccati). Le liste possono essere usate come code o stack.&lt;br /&gt;
* suspend.[ch] &amp;amp;egrave; il modulo che implementa la sospensione e riattivazione dei pthread (usando il segnale SIGUSR1).&lt;br /&gt;
* semaphore.[ch] implementazione dei semafori '''fair'''&lt;br /&gt;
* monitor.[ch] implementazione dei monitor (con semantica '''signal urgent''' per le variabili di condizione).&lt;br /&gt;
* prod_cons.c esempio: produttore consumatore con i semafori&lt;br /&gt;
* bounded_buf.c esempio: bounded_buffer con i semafori&lt;br /&gt;
* bounded_buf_mon.c esempio: bounded buffer con i monitor&lt;br /&gt;
* philo.c: cena dei filosofi (la soluzione &amp;amp;egrave; volutamente errata e pu&amp;amp;ograve; generare deadlock&lt;br /&gt;
&lt;br /&gt;
== uso dei semafori ==&lt;br /&gt;
&lt;br /&gt;
Se volete fare altri esperimenti con i semafori dovete includere il file semaphore.h nei vostri sorgenti C.&lt;br /&gt;
&lt;br /&gt;
Un semaforo ha tipo ''semaphore'' e viene creato in questo modo&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
semaphore mutex;&lt;br /&gt;
mutex = mutex_create(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(ovviamente le variabili semaphore devono essere condivise e 1 &amp;amp;egrave; solo l'esempio di valore iniziale per un semaforo mutex)&lt;br /&gt;
&lt;br /&gt;
Le operazioni P e V su mutex si scrivono cos&amp;amp;igrave;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
semaphore_P(mutex);&lt;br /&gt;
semaphore_V(mutex);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== produttore e consumatore ==&lt;br /&gt;
&lt;br /&gt;
Il file ''prod_cons.c'' implementa una soluzione del problemadel produttore/consumatore.&lt;br /&gt;
&lt;br /&gt;
Il buffer e i semafori sono definiti come variabili condivise.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
semaphore full;&lt;br /&gt;
semaphore empty;&lt;br /&gt;
volatile int buffer;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il main inizializza i semafori e crea i thread.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
int main(int argc, char *argv[]) {&lt;br /&gt;
  pthread_t prod_t;&lt;br /&gt;
  pthread_t cons_t;&lt;br /&gt;
  full=semaphore_create(0);&lt;br /&gt;
  empty=semaphore_create(1);&lt;br /&gt;
&lt;br /&gt;
  pthread_create(&amp;amp;prod_t, NULL, producer, NULL);&lt;br /&gt;
  pthread_create(&amp;amp;cons_t, NULL, consumer, NULL);&lt;br /&gt;
  pthread_join(prod_t, NULL);&lt;br /&gt;
  pthread_join(cons_t, NULL);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La sincronizzazione fra produttore e il consumatore viene realizzata come segue:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
void *producer(void *arg) {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    int value;&lt;br /&gt;
    // produce value&lt;br /&gt;
    semaphore_P(empty);&lt;br /&gt;
    buffer = value;&lt;br /&gt;
    semaphore_V(full);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void *consumer(void *arg) {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    int value;&lt;br /&gt;
    semaphore_P(full);&lt;br /&gt;
    value = buffer;&lt;br /&gt;
    semaphore_V(empty);&lt;br /&gt;
    // consume value&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Uso dei monitor ==&lt;br /&gt;
&lt;br /&gt;
Un monitor viene creato con la funzione:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
monitor mymon;&lt;br /&gt;
mymon = monitor_create()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le variabili di condizione vengono create nel seguente modo:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
condition oktoproceed;&lt;br /&gt;
oktoproceed = condition_create(mymon);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
(NB: le variabili di condizione sono '''sempre''' riferite ad uno specifico monitor).&lt;br /&gt;
&lt;br /&gt;
Le procedure entry sono normali funzioni C ma occorre mettere all'entrata e all'uscita le chiamate&lt;br /&gt;
''monitor_{enter,exit}':&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
int mymon_dosomething(/* params */) {&lt;br /&gt;
  rval int;&lt;br /&gt;
  monitor_enter(mymon);&lt;br /&gt;
  ....&lt;br /&gt;
  monitor_exit(mymon);&lt;br /&gt;
  return rval;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le operazioni wait e signal sulle variabili di condizione si scrivono cos&amp;amp;igrave;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
condition_wait(oktoproceed);&lt;br /&gt;
condition_signal(oktoproceed);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bounded buffer con i monitor ==&lt;br /&gt;
Vengono definite come variabili condivise il monitor e le variabili di condizione&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
monitor bb;&lt;br /&gt;
condition ok2write;&lt;br /&gt;
condition ok2read;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
oltre al buffer e alle varibili per la gestione delle strutture dati, fra le quali il contatore ''buflen''.&lt;br /&gt;
&lt;br /&gt;
Il monitor viene creato da questa funzione:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
void bb_create(void) {&lt;br /&gt;
  bb = monitor_create();&lt;br /&gt;
  ok2write = condition_create(bb);&lt;br /&gt;
  ok2read = condition_create(bb);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
che viene richiamata dal main prima di far partire i thread:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
int main(int argc, char *argv[]) {&lt;br /&gt;
  pthread_t prod_t;&lt;br /&gt;
  pthread_t cons_t;&lt;br /&gt;
  bb_create();&lt;br /&gt;
&lt;br /&gt;
  pthread_create(&amp;amp;prod_t, NULL, producer, NULL);&lt;br /&gt;
  pthread_create(&amp;amp;cons_t, NULL, consumer, NULL);&lt;br /&gt;
  pthread_join(prod_t, NULL);&lt;br /&gt;
  pthread_join(cons_t, NULL);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il monitor implementa due procedure entry put e get:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
void bb_put(int value) {&lt;br /&gt;
  monitor_enter(bb);&lt;br /&gt;
  if (buflen &amp;gt;= SIZE)&lt;br /&gt;
    condition_wait(ok2write);&lt;br /&gt;
  // enqueue value in the buffer&lt;br /&gt;
  buflen++;&lt;br /&gt;
  condition_signal(ok2read);&lt;br /&gt;
  monitor_exit(bb);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int bb_get(void) {&lt;br /&gt;
  int rval;&lt;br /&gt;
  monitor_enter(bb);&lt;br /&gt;
  if (buflen &amp;lt;= 0)&lt;br /&gt;
    condition_wait(ok2read);&lt;br /&gt;
  // dequeue value from the buffer&lt;br /&gt;
  buflen--;&lt;br /&gt;
  condition_signal(ok2write);&lt;br /&gt;
  monitor_exit(bb);&lt;br /&gt;
  return rval;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il codice dei processi produttore e del consumatore usa le procedure entry del monitor bb in questo modo:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=C&amp;gt;&lt;br /&gt;
void *producer(void *arg) {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    int value;&lt;br /&gt;
    // produce value&lt;br /&gt;
    bb_put(value);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void *consumer(void *arg) {&lt;br /&gt;
  while (1) {&lt;br /&gt;
    int value;&lt;br /&gt;
    value = bb_get();&lt;br /&gt;
    // consume value&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Alexp</name></author>
	</entry>
</feed>