tag:blogger.com,1999:blog-39550874387770787782024-03-21T10:40:59.131+01:00L'arte della programmazione in CScrivere Software è un piacere. Un programma non solo deve funzionare bene ed essere efficiente (questo si dà per scontato), ma deve essere anche bello ed elegante da leggere, comprensibile e facile da manutenere, sia per l'autore che per eventuali lettori futuri. Programmare bene in C è un'arte.Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.comBlogger140125tag:blogger.com,1999:blog-3955087438777078778.post-86993514525414768682024-03-20T19:50:00.002+01:002024-03-21T10:40:26.663+01:00busy-waiting? Forse! come scrivere un busy-wait loop in C<blockquote><b>Alain:</b> <i>Aal mondo ci sono 8 miliardi di persone, la possibilità di nascere è una su 400 bilioni, eppure tu ed io siamo qui, vuole dire che abbiamo vinto la lotteria cosmica.</i><br /></blockquote><p>Visto che l'ultimo articolo era della serie <strong>"Forse!"</strong> ho deciso di battere il ferro finché è caldo e ve ne propongo un altro (non dimenticate, però, di leggere anche gli articoli della serie <strong>"No, grazie!"</strong> che sono, spero, interessanti: li potete trovare <span style="background-color: #fff9ee; color: #222222; font-family: Georgia, Utopia, "Palatino Linotype", Palatino, serif; font-size: 15.4px;"><a href="https://artcprogramming.blogspot.com/2022/08/thread-cancel-no-grazie-considerazioni.html" target="_blank"><b>qui</b></a>, </span><a href="https://artcprogramming.blogspot.com/2022/06/system-no-grazie-considerazioni-sul.html" rel="noopener" target="_blank"><b>qui</b><strong>,</strong></a> <a href="https://artcprogramming.blogspot.com/2020/09/sleep-no-grazie-considerazioni-sul.html" rel="noopener" target="_blank"><strong>qui</strong></a>, <a href="https://artcprogramming.blogspot.com/2019/12/sprintf-no-grazie-considerazioni.html" rel="noopener" target="_blank"><strong>qui</strong>,</a> <a href="https://artcprogramming.blogspot.com/2019/10/debugger-no-grazie-considerazioni.html" rel="noopener" target="_blank"><strong>qui</strong></a> e <a href="https://artcprogramming.blogspot.com/2019/02/variabili-globali-no-grazie-come-non.html" rel="noopener" target="_blank"><strong>qui</strong></a>). Oggi parleremo di <strong>busy-waiting</strong>, o meglio di <strong>busy-wait loop</strong>, un argomento che, per quel che ho visto in rete, è fonte di molti dubbi, e noi siamo qui per questo, per fugarli! L'argomento dell'articolo, si intona con il cinquantesimo film del Maestro <a href="https://letterboxd.com/director/woody-allen/" rel="noopener" target="_blank"><strong>Woody Allen</strong></a>, il bel <a href="https://letterboxd.com/aldoz/film/coup-de-chance/" rel="noopener" target="_blank"><strong>Coup de Chance</strong></a>, un film sulla importanza, nella vita, del caso e della fortuna: affidarsi a un <em>busy-wait loop</em> è un po' affidarsi al caso, e quindi bisogna usarli con le dovute precauzioni... <strong>Forse!</strong></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEVPAgFZ1w_h0g2ZCH4gGXZrTBwI2EP-i7rttXMypDibHfd5L4G9u-KAU6nqkXeSyri52N6IC63Zi1iud8zNRPXsWcopuAGjkYzsHF6BGOiF3PqRpVbG5nH3AYPwwIbWCBDnEuV_WelF2SXYTzPFz-sdfK1Lo7dLZ1wbFGNp4wbRm-Vtgs3a-7CcH1zzZ0/s1280/busywaiting.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="720" data-original-width="1280" height="331" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEVPAgFZ1w_h0g2ZCH4gGXZrTBwI2EP-i7rttXMypDibHfd5L4G9u-KAU6nqkXeSyri52N6IC63Zi1iud8zNRPXsWcopuAGjkYzsHF6BGOiF3PqRpVbG5nH3AYPwwIbWCBDnEuV_WelF2SXYTzPFz-sdfK1Lo7dLZ1wbFGNp4wbRm-Vtgs3a-7CcH1zzZ0/w589-h331/busywaiting.jpg" width="589" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...secondo me quel codice funzionava solo per caso...</td></tr></tbody></table><p>Cosa sono i <em>busy wait-loop</em>? Senza girarci troppo intorno è meglio vedere un brevissimo esempio con una versione elementare. Vai col codice!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// faccio un busy-wait loop "elementare"</span></div><div><span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">(</span>condizione<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// non faccio nulla: sto aspettando che la condizione diventi false</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// busy-wait loop terminato: proseguo (e, magari, mi scuso con la povera CPU...)</span></div><div>...</div></div></pre><p>Facile, no? Il codice è semplicissimo e <a href="https://artcprogramming.blogspot.com/2012/08/no-comment.html" rel="noopener" target="_blank"><strong>ben commentato</strong></a>, e c'è poco da aggiungere se non che il commento finale <em>"...mi scuso con la povera CPU..."</em> anticipa un problema abbastanza grave... In realtà questo difetto è facilmente evitabile con un trucchetto (di cui ho già parlato varie volte, ad esempio <a href="https://artcprogramming.blogspot.com/2020/09/sleep-no-grazie-considerazioni-sul.html" rel="noopener" target="_blank"><strong>qui</strong></a>), ma ne parlerò solo alla fine dell'articolo, spiegandovi anche il perché non ne parlo ora (<em>pazientare, prego...</em>).</p><p>Andiamo avanti: un <em>busy-wait loop</em> elementare come quello mostrato sopra ha vari difetti e, per sintetizzare, possiamo isolare i due più gravi (di cui il primo è quello appena anticipato):</p><ol><li>È un <em>cpu-killer</em>, nel senso che durante l'attesa si mangia tutte le risorse del sistema, visto che continua a testare la condizione alla massima velocità possibile.</li><li>Non ha una condizione d'uscita: il <em>loop</em> potrebbe trasformarsi in infinito.</li></ol><p>(<em>...apro una parentesi per fare il precisino: in alcuni ambienti di programmazione, tipicamente quelli del firmware di basso livello senza sistema operativo (il bare-metal, per gli amici) i busy-wait loop "elementari" sono usati e ammessi. Ma questa è un'altra storia...</em>)</p><p>E come si procede (correttamente) se abbiamo bisogno di eseguire qualcosa <em>"tipo un busy-wait loop"</em>? Ci sono varie maniere, e dipendono dal tipo di condizione da testare. Vi propongo un piccolo riassuntino di tre casi: sicuramente non è esauriente ma che credo renda bene l'idea; gli esempi forniti sono molto (ma moooolto) semplificati e servono solo a rendere un po' l'idea:</p><ol><li>La condizione è l'attesa di un segnale del sistema: invece di non fare nulla nel <em>loop</em> si usa una funzione bloccante come <a href="https://man7.org/linux/man-pages/man2/pause.2.html" rel="noopener" target="_blank"><strong>pause(2)</strong></a> o la più moderna <a href="https://man7.org/linux/man-pages/man2/sigsuspend.2.html" rel="noopener" target="_blank"><strong>sigsuspend(2)</strong></a>, e questo comporta un carico nullo per la CPU. Vediamo un piccolo esempio:<br /><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// faccio un busy-wait loop con pause(2) (o sigsuspend(2))</span></div><div><span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">(true)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// aspetto che la pause(2) ritorni perché è arrivato un segnale</span></div><div> pause<span style="color: yellow; font-weight: bold;">();</span><span style="color: silver; font-style: italic;"> // questa è bloccante e non consuma CPU</span></div><div> <span style="color: yellow; font-weight: bold;">break;</span><span style="color: silver; font-style: italic;"> // pause(2) è uscita: interrompo il loop</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// busy-wait loop terminato: proseguo (e, non devo scusarmi con la CPU...)</span></div><div>...</div></div></pre></li><li>La condizione è l'attesa di un evento su una variabile: useremo allora una <a href="https://man7.org/linux/man-pages/man3/pthread_cond_destroy.3p.html" rel="noopener" target="_blank"><strong>condition variable</strong></a> attraverso la funzione <a href="https://man7.org/linux/man-pages/man3/pthread_cond_timedwait.3p.html" rel="noopener" target="_blank"><strong>pthread_cond_wait(3)</strong></a> che è bloccante (come la <strong>pause(2)</strong> dell'esempio precedente). Vediamo l'esempio:<br /><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// lock mutex</span></div><div>pthread_mutex_lock<span style="color: yellow; font-weight: bold;">(&</span>my_mutex<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: silver; font-style: italic;">// faccio un busy-wait loop con una condition variable</span></div><div><span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">(</span>condizione<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// aspetto che la pthread_cond_wait(2) ritorni quando si segnala la condizione</span></div><div> pthread_cond_wait<span style="color: yellow; font-weight: bold;">(&</span>my_cond<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>my_mutex<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // questa è bloccante e non consuma CPU</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// il break non è necessario: la condizione non è più valida</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// unlock mutex</span></div><div>pthread_mutex_unlock<span style="color: yellow; font-weight: bold;">(&</span>my_mutex<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: silver; font-style: italic;">// busy-wait loop terminato: proseguo (e, non devo scusarmi con la CPU...)</span></div><div>...</div></div></pre></li><li>La condizione è l'attesa di una evento di I/O: useremo allora una funzione bloccante (come, ad esempio, la <a href="https://man7.org/linux/man-pages/man2/read.2.html" rel="noopener" target="_blank"><strong>read(2)</strong></a> o la <a href="https://man7.org/linux/man-pages/man2/recv.2.html" rel="noopener" target="_blank"><strong>recv(2)</strong></a>). Vediamo, di nuovo, un piccolo esempio:<br /><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// faccio un busy-wait loop con read(2) (o recv(2))</span></div><div><span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">(true)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// aspetto che la read(2) ritorni perché è arrivato un buffer di I/O</span></div><div> read<span style="color: yellow; font-weight: bold;">(</span>my_fd<span style="color: yellow; font-weight: bold;">,</span> my_buf<span style="color: yellow; font-weight: bold;">,</span> my_count<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // questa è bloccante e non consuma CPU</span></div><div> <span style="color: yellow; font-weight: bold;">break;</span><span style="color: silver; font-style: italic;"> // read(2) è uscita: interrompo il loop</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// busy-wait loop terminato: proseguo (e, non devo scusarmi con la CPU...)</span></div><div>...</div></div></pre></li></ol><p>Notare che l'esempio n.3 si poteva anche scrivere usando la nostra cara <a href="https://artcprogramming.blogspot.com/2023/03/the-big-select-come-usare-la-select2-in.html" rel="noopener" target="_blank"><strong>select(2)</strong></a>, che è bloccante e si sveglia quando arriva l'I/O sul canale sorvegliato... ma in questo caso non sarebbe un <em>busy-wait loop</em>! Notare anche che, per semplificare e unificare gli esempi, ho usato dei <em>loop</em> che sembrano innecessari, ma potrebbero essere utili per risolvere il difetto n.2 della lista-difetti mostrata più sopra, come vedremo più avanti.</p><p>E, come promesso all'inizio dell'articolo, vediamo quale è il trucchetto (che molti avranno già intuito) per migliorare il <em>busy-wait loop "elementare". </em> Vediamolo!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// faccio un busy-wait loop "elementare" migliorato</span></div><div><span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">(</span>condizione<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// aspetto che la condizione diventi false rilasciando la CPU ad ogni ciclo</span></div><div> sleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // o nanosleep o usleep, ecc.</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// busy-wait loop terminato: proseguo (e, non devo scusarmi con la CPU...)</span></div><div>...</div></div></pre><p>Con questo semplice trucco il <em>busy-wait loop</em> non è più un <em>cpu-killer</em>, perché rilascia la CPU (durante il tempo di <em>sleep)</em> permettendo ad altri <em>thread</em> e/o processi di lavorare liberamente. Ma allora perché ho lasciato questo interessante esempio per ultimo? Semplicissimo: perché un <em>loop</em> fatto così non è più un <em>busy-wait loop</em>! Infatti i cicli vengono eseguiti molte volte (magari infinite) ma, visto che ogni volta viene rilasciata la CPU, usare il termine <em>busy</em> è poco appropriato.</p><p>(<em>...e apro un'altra parentesi da precisino: i tre esempi "buoni", 1, 2 e 3 qui sopra, si possono considerare dei veri esempi di busy-waiting solo pensando che il loop è bloccato ma è anche attivo; ma c'è chi, con argomenti validi, non li considera degli esempi calzanti... va a finire che l'unico vero busy-wait loop è quello "elementare" mostrato all'inizio!...</em>)</p><p>Comunque questo ultimo caso ci può aiutare a risolvere il secondo dei difetti descritti sopra: <em>"Non ha una condizione d'uscita: il loop potrebbe trasformarsi in infinito."</em>. Visto che (andando in <em>sleep)</em> il <em>loop</em> si sospende per un certo tempo e poi riparte, è abbastanza semplice modificarlo per contare il numero di ripartenze e alzare un allarme o scrivere un messaggio di errore (e, magari, forzare un <em>break)</em> nel caso che si superi un certo tempo. Vediamolo!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// faccio un busy-wait loop "elementare" migliorato e con condizione d'uscita</span></div><div><span style="color: yellow; font-weight: bold;">int</span> seconds <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">(</span>condizione<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// aspetto che la condizione diventi false rilasciando la CPU ad ogni ciclo</span></div><div> sleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // o nanosleep o usleep, ecc.</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(++</span>seconds <span style="color: yellow; font-weight: bold;">></span> <span style="color: white; font-weight: bold;">5</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">impossibile rispettare la condizione in %d sec\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> seconds<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">break;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// busy-wait loop terminato: proseguo (e, non devo scusarmi con la CPU...)</span></div><div>...</div></div></pre><p>Semplice ed efficace, no? Ma allora quest'ultimo esempio (ribadisco: super semplificato) è quello da usare sempre? Io direi di no: in alcuni casi è l'unico possibile e raccomandabile, ma (quando possibile) è consigliabile usare uno degli esempi (1, 2 o 3) descritti sopra, visto che non usano la <a href="https://man7.org/linux/man-pages/man3/sleep.3.html" rel="noopener" target="_blank"><strong>sleep(3)</strong></a> (che è una fonte notevole di problemi, come scrissi <a href="https://artcprogramming.blogspot.com/2020/09/sleep-no-grazie-considerazioni-sul.html" rel="noopener" target="_blank"><strong>qui</strong></a> e <a href="https://artcprogramming.blogspot.com/2020/10/sleep-no-grazie-considerazioni-sul.html" rel="noopener" target="_blank"><strong>qui</strong></a>). Certo, con il codice di quegli esempi diventa un po' più complicato (ma comunque possibilissimo) gestire le condizioni d'uscita temporizzate, ma con un po' di inventiva si può fare! <em>Anzi, ve lo lascio come divertente attività per le vacanze di Pasqua, sempre che le facciate, ah ah ah.</em> E per oggi <a href="https://letterboxd.com/film/toto-peppino-and-the-hussy/" rel="noopener" target="_blank"><strong>ho detto tutto!</strong></a></p><p>Ciao, e al prossimo post!</p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-57580988960157460752024-02-27T19:45:00.001+01:002024-02-29T20:16:18.079+01:00Librerie header-only? Forse! come scrivere una libreria header-only in C<blockquote><b>Holland March:</b> <i>Alla fine nessuno si è fatto male.</i><br /><b>Jackson Healy:</b> <i>A me sembra di sì...</i><br /><b>Holland March:</b> <i>Nel senso che sono morti in fretta, non è che si sono fatti male.</i><br /></blockquote><p>Ebbene si, oggi parleremo di un argomento abbastanza inusuale e poco conosciuto del<strong> C</strong> (almeno per quello che mi riguarda): le librerie <strong>header-only</strong>. Come vedremo più avanti questo è un argomento un po' controverso e (forse) di dubbia utilità, ma per evitare gli strali dei fan (ce ne sono) di questo tipo di librerie ho deciso che questo sarà un articolo del tipo <strong>"Forse!"</strong> invece che un <strong>"No, grazie!"</strong> (<em>a proposito, di <strong>"No, grazie!"</strong> ne ho scritti un po', li potete trovare </em><em style="background-color: #fff9ee; color: #222222; font-family: Georgia, Utopia, "Palatino Linotype", Palatino, serif; font-size: 15.4px;"><a href="https://artcprogramming.blogspot.com/2022/08/thread-cancel-no-grazie-considerazioni.html" target="_blank"><b>qui</b></a>, </em><em><a href="https://artcprogramming.blogspot.com/2022/06/system-no-grazie-considerazioni-sul.html" rel="noopener" target="_blank"><b>qui</b><strong>,</strong></a> </em><em><a href="https://artcprogramming.blogspot.com/2020/09/sleep-no-grazie-considerazioni-sul.html" rel="noopener" target="_blank"><strong>qui</strong></a>, <a href="https://artcprogramming.blogspot.com/2019/12/sprintf-no-grazie-considerazioni.html" rel="noopener" target="_blank"><strong>qui</strong>,</a> <a href="https://artcprogramming.blogspot.com/2019/10/debugger-no-grazie-considerazioni.html" rel="noopener" target="_blank"><strong>qui</strong></a> e <a href="https://artcprogramming.blogspot.com/2019/02/variabili-globali-no-grazie-come-non.html" rel="noopener" target="_blank"><strong>qui</strong></a></em><em>, mentre un esempio di <strong>"Forse!"</strong>, o meglio, era uno <strong>"Scusate"</strong>, lo trovate <a href="https://artcprogramming.blogspot.com/2023/11/scusate-la-memcmp-considerazioni.html" rel="noopener" target="_blank"><strong>qui</strong></a></em>). L'argomento dell'articolo, tra il serio e il faceto, si intona con il bel film <a href="https://letterboxd.com/aldoz/film/the-nice-guys/1/" rel="noopener" target="_blank"><strong>The Nice Guys</strong></a> del bravo <a href="https://letterboxd.com/director/shane-black/" rel="noopener" target="_blank"><strong>Shane Black</strong></a>, un film brillante di quelli che Hollywood anticamente sfornava a ripetizione, ma che ora non sanno quasi più fare...</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwgjzI333L3fi0uDPlZkmPADIquMWOhoAkARexz6jDfcqOyN7iCfhP8Ydc4lyRkECuhE8bFQKQLJS8WKzMmcb-qsjA41N22VoxVBrltjg0KmNcQffXVC5joJ_ldnCXb7DdcKI9Ea61ns0EMwctJY1qlOF02HNL8_oWlIVHJUun6xQr2-FwjKg-IMEUXIlJ/s2048/header-only.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="852" data-original-width="2048" height="248" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwgjzI333L3fi0uDPlZkmPADIquMWOhoAkARexz6jDfcqOyN7iCfhP8Ydc4lyRkECuhE8bFQKQLJS8WKzMmcb-qsjA41N22VoxVBrltjg0KmNcQffXVC5joJ_ldnCXb7DdcKI9Ea61ns0EMwctJY1qlOF02HNL8_oWlIVHJUun6xQr2-FwjKg-IMEUXIlJ/w594-h248/header-only.jpg" width="594" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...secondo me è una libreria header-only...</td></tr></tbody></table><p>E cosa sono le queste librerie <strong>header-only</strong>? Ok, partiamo dalle basi (<strong>Linux</strong>, eh! Ma anche in altri S.O. funziona più o meno allo stesso modo): qui stiamo per parlare di librerie di sviluppo (su <strong>Linux,</strong> famiglia <strong>Deb,</strong> sono i pacchetti <em>*-dev</em>), che sono quelle che servono per realizzare una applicazione. Normalmente (anzi canonicamente) una libreria di sviluppo si distribuisce (semplificando un po') attraverso due file:</p><ul><li>Un <em>header-file</em> (un <em>*.h</em>) che contiene i prototipi delle funzioni disponibili più la definizione di eventuali strutture dati tipiche della libreria: questo permette di scrivere e compilare una applicazione che usa quello che mette a disposizione la libreria.</li><li>Un <em>lib-file</em> (un <em>*.a</em> o un <em>*.so</em>), che è una versione compilata dei file di implementazione (i <em>*.c</em>) che compongono la libreria. Questo <em>lib-file</em> può avere formato a link statico (<em>*.a</em>) o dinamico (<em>*.so</em>): grazie a questo si può <em>linkare</em> ed eseguire la applicazione che usa la libreria. Quindi quando si distribuisce la applicazione in formato eseguibile bisogna anche distribuire la libreria associata.</li><li>La libreria da distribuire avrà varie versioni in base a tipo/versione del S.O. della macchina su cui si vuole installare e usare.</li></ul><p>Come si può intuire (<em>e se no che lo scrivo a fare questo articolo?</em>) c'è anche una maniera <em>"non canonica"</em> di distribuzione, ed è proprio quella delle librerie <strong>header-only</strong>:</p><ul><li>Si distribuisce un solo file, un <em>header-file</em> un po' strano che contiene prototipi, strutture dati e anche implementazioni! Questo <em>header</em> si deve includere in tutti i file del progetto che usano funzioni e/o strutture dati della libreria.</li><li>Si compila e via! Si può eseguire l'applicazione <em>linkando</em> solo le (eventuali) altre librerie necessarie all'applicazione. Quindi quando si distribuisce la applicazione in formato eseguibile non c'è bisogno di distribuire anche la libreria associata.</li><li>La portabilità è buona: una libreria <strong>header-only</strong> è, a tutti gli effetti, un file sorgente, quindi se si riesce a compilare sarà perfettamente compatibile con la macchina che stiamo usando (eventuali problemi verranno dopo, per distribuire la sola applicazione precompilata ed eseguibile).</li><li>Notare che alcune delle librerie <strong>header-only</strong> disponibili in rete usano alcuni trucchi (basati sulla definizione di alcune macro ad-hoc) per far si che uno solo dei file <em>*.c</em> (del progetto che userà la libreria) includa la parte di implementazione del <em>header-file</em>, mentre gli altri <em>*.c</em> includeranno solo la lista dei prototipi contenuta nell'<em>header-file</em>. In questo caso si dovrebbe parlare di librerie <strong>pseudo-header-only</strong>, che non sono l'argomento di questo articolo: qui parleremo solo di quelle <em>"vere"</em>.</li></ul><p>Apparentemente questo metodo di distribuzione è interessante, ma non è oro tutto quello che luccica: in letteratura tecnica sono riportati alcuni difetti:</p><ul><li>In un grande progetto con molti sorgenti <em>*.c</em> che includono la libreria <strong>header-only</strong> una modifica a quest'ultima provocherà un notevole allargamento dei tempi di compilazione (l'<em>header</em> verrà ricompilato <em>n-volte</em>). Con la potenza delle macchine attuali questo potrebbe non essere un gran problema (anche se non bisogna sottovalutarlo: alcuni progetti sono veramente grandi e comprendono migliaia di sorgenti).</li><li>Per evitare gli ovvi errori di ridefinizione in compilazione si fa grande uso (come vedremo più avanti) di <em>storage classes</em> di tipo <strong>static</strong> e/o <strong>static inline</strong>: questo provoca una crescita notevole del codice macchina generato, un problema difficilmente ottimizzabile dal compilatore.</li></ul><p>Due problemi da niente, no?</p><p>E vabbé, è ora di dare un esempio reale (ultra-semplificato) di un progetto che usa una libreria <strong>header-only</strong>; il progetto include:</p><ul><li><strong>mylib.h</strong>: la libreria <strong>header-only</strong> (è una <em>"vera"</em>, senza i trucchi citati sopra).</li><li><strong>test1.c</strong>: un sorgente <strong>C</strong> che definisce una funzione <strong>fun1()</strong> che usa internamente una funzione della libreria <strong>mylib.h</strong>.</li><li><strong>test2.c</strong>: un sorgente <strong>C</strong> che definisce una funzione <strong>fun2()</strong> che usa internamente una funzione della libreria <strong>mylib.h</strong>.</li><li><strong>test.c</strong>: un sorgente con un <strong>main()</strong> che chiama le funzioni definite in <strong>test1.c</strong> e t<strong>est2.c</strong>.</li></ul><p>Vai col codice!</p><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px; white-space: pre;"><div><span style="color: silver; font-style: italic;">// mylib.h - una libreria header-only</span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: yellow; font-weight: bold;">#ifdef</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">STATICINLINE</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">MY_API</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">static</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">inline</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">MY_API_STR</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">static inline api</span><span style="color: yellow; font-weight: bold;">"</span></div><div><span style="color: yellow; font-weight: bold;">#elif</span> defined STATIC</div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">MY_API</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">static</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">MY_API_STR</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">static api</span><span style="color: yellow; font-weight: bold;">"</span></div><div><span style="color: yellow; font-weight: bold;">#elif</span> defined INLINE</div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">MY_API</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">inline</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">MY_API_STR</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">inline api</span><span style="color: yellow; font-weight: bold;">"</span></div><div><span style="color: yellow; font-weight: bold;">#elif</span> defined EXTERN</div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">MY_API</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">extern</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">MY_API_STR</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">extern api</span><span style="color: yellow; font-weight: bold;">"</span></div><div><span style="color: yellow; font-weight: bold;">#else</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">MY_API</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">MY_API_STR</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">api</span><span style="color: yellow; font-weight: bold;">"</span></div><div><span style="color: yellow; font-weight: bold;">#endif</span></div><br /><div><span style="color: silver; font-style: italic;">// libfun1 - una funzione generica della libreria</span></div><div><span style="color: white; font-weight: bold;">MY_API</span> <span style="color: yellow; font-weight: bold;">void</span> libfun1<span style="color: yellow; font-weight: bold;">(void)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: sono %s\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">MY_API_STR</span><span style="color: yellow; font-weight: bold;">,</span> __func__<span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// libfun2 - una funzione generica della libreria</span></div><div><span style="color: white; font-weight: bold;">MY_API</span> <span style="color: yellow; font-weight: bold;">void</span> libfun2<span style="color: yellow; font-weight: bold;">(void)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: sono %s\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">MY_API_STR</span><span style="color: yellow; font-weight: bold;">,</span> __func__<span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">/ test1.c - modulo per il test della libreria header-only mylib</span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">mylib.h</span><span style="color: yellow; font-weight: bold;">"</span></div><br /><div><span style="color: yellow; font-weight: bold;">void</span> fun1<span style="color: yellow; font-weight: bold;">(void)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// chiamo una funzione della libreria header-only mylib</span></div><div> libfun1<span style="color: yellow; font-weight: bold;">();</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">/ test2.c - modulo per il test della libreria header-only mylib</span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">mylib.h</span><span style="color: yellow; font-weight: bold;">"</span></div><br /><div><span style="color: yellow; font-weight: bold;">void</span> fun2<span style="color: yellow; font-weight: bold;">(void)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// chiamo una funzione della libreria header-only mylib</span></div><div> libfun2<span style="color: yellow; font-weight: bold;">();</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">/ test.c - main di test della libreria header-only mylib</span></div><br /><div><span style="color: silver; font-style: italic;">// test - funzione main</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(void)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">extern</span> <span style="color: yellow; font-weight: bold;">void</span> fun1<span style="color: yellow; font-weight: bold;">(void);</span></div><div> <span style="color: yellow; font-weight: bold;">extern</span> <span style="color: yellow; font-weight: bold;">void</span> fun2<span style="color: yellow; font-weight: bold;">(void);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// chiamo le funzioni dei moduli test1 e test2</span></div><div> fun1<span style="color: yellow; font-weight: bold;">();</span></div><div> fun2<span style="color: yellow; font-weight: bold;">();</span></div><br /><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Come sempre il codice è <a href="https://artcprogramming.blogspot.com/2012/08/no-comment.html" rel="noopener" target="_blank"><strong>ben commentato</strong></a>, però in questo caso qualche spiegazione è doverosa: a parte le espressioni condizionali iniziali (che vedremo più avanti) il meccanismo di avere prototipi e definizioni in <strong>mylib.h</strong> è semplice e abbastanza chiaro, no ? Quindi visto che <strong>test1.c</strong> e <strong>test2.c</strong> includono <strong>mylib.h</strong> possono chiamare, rispettivamente, <strong>libfun1()</strong> e <strong>libfun2()</strong> internamente alle funzioni globali <strong>fun1()</strong> e <strong>fun2()</strong>. Dopodiché <strong>test.c</strong>, che non ha bisogno di includere <strong>mylib.h</strong>, chiama le funzioni globali <strong>fun1()</strong> e <strong>fun2()</strong>: tutto abbastanza lineare.</p><p>Ma l'inclusione in più sorgenti di uno strano <em>header-file</em> con prototipi e implementazioni non è triviale, e quindi bisogna scegliere accuratamente le <em>storage classes</em> delle funzioni, perché gli errori di ridefinizione sono dietro l'angolo. Il meccanismo che permette il funzionamento è, come anticipato sopra, quello che si vede nelle prime linee di <strong>mylib.h</strong>, dove ho messo alcune espressioni condizionali che permettono di testare e mostrare le varie maniere operative. Poi, una volta capito il meccanismo, <strong>mylib.h</strong> si può semplificare togliendo tutti i condizionali e lasciando solo il tipo di <em>storage classes </em>scelto.</p><p>Vediamo, allora, cosa succede compilando (e, se possibile, eseguendo) la nostra applicazione:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>aldo@Linux $ gcc test.c test1.c test2.c <span style="color: yellow; font-weight: bold;">-</span>o test <span style="color: yellow; font-weight: bold;">-</span>DSTATICINLINE</div><div>aldo@Linux $ .<span style="color: yellow; font-weight: bold;">/</span>test</div><div><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">inline</span> api: sono libfun1</div><div><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">inline</span> api: sono libfun2</div><br /><div>aldo@Linux $ gcc test.c test1.c test2.c <span style="color: yellow; font-weight: bold;">-</span>o test <span style="color: yellow; font-weight: bold;">-</span>DSTATIC</div><div>aldo@Linux $ .<span style="color: yellow; font-weight: bold;">/</span>test</div><div><span style="color: yellow; font-weight: bold;">static</span> api: sono libfun1</div><div><span style="color: yellow; font-weight: bold;">static</span> api: sono libfun2</div><br /><div>aldo@Linux $ gcc test.c test1.c test2.c <span style="color: yellow; font-weight: bold;">-</span>o test <span style="color: yellow; font-weight: bold;">-</span>DINLINE</div><div><span style="color: yellow; font-weight: bold;">/</span>usr<span style="color: yellow; font-weight: bold;">/</span>bin<span style="color: yellow; font-weight: bold;">/</span>ld: <span style="color: yellow; font-weight: bold;">/</span>tmp<span style="color: yellow; font-weight: bold;">/</span>ccdLwOwW.o: in function `fun1<span style="color: yellow; font-weight: bold;">'</span><span style="color: white; font-weight: bold;">:</span></div><div><span style="color: white; font-weight: bold;">test1.c:(.text+0x9): undefined reference to `libfun1</span><span style="color: yellow; font-weight: bold;">'</span></div><div><span style="color: yellow; font-weight: bold;">/</span>usr<span style="color: yellow; font-weight: bold;">/</span>bin<span style="color: yellow; font-weight: bold;">/</span>ld: <span style="color: yellow; font-weight: bold;">/</span>tmp<span style="color: yellow; font-weight: bold;">/</span>ccK47RcM.o: in function `fun2<span style="color: yellow; font-weight: bold;">'</span><span style="color: white; font-weight: bold;">:</span></div><div><span style="color: white; font-weight: bold;">test2.c:(.text+0x9): undefined reference to `libfun2</span><span style="color: yellow; font-weight: bold;">'</span></div><div>collect2: error: ld returned <span style="color: white; font-weight: bold;">1</span> exit status</div><br /><div>aldo@Linux $ gcc test.c test1.c test2.c <span style="color: yellow; font-weight: bold;">-</span>o test <span style="color: yellow; font-weight: bold;">-</span>DEXTERN</div><div><span style="color: yellow; font-weight: bold;">/</span>usr<span style="color: yellow; font-weight: bold;">/</span>bin<span style="color: yellow; font-weight: bold;">/</span>ld: <span style="color: yellow; font-weight: bold;">/</span>tmp<span style="color: yellow; font-weight: bold;">/</span>ccNPBHJP.o: in function `libfun1<span style="color: yellow; font-weight: bold;">'</span><span style="color: white; font-weight: bold;">:</span></div><div><span style="color: white; font-weight: bold;">test2.c:(.text+0x0): multiple definition of `libfun1</span><span style="color: yellow; font-weight: bold;">'</span><span style="color: yellow; font-weight: bold;">;</span> <span style="color: yellow; font-weight: bold;">/</span>tmp<span style="color: yellow; font-weight: bold;">/</span>cczGXRP8.o:test1.c:<span style="color: yellow; font-weight: bold;">(</span>.text<span style="color: yellow; font-weight: bold;">+</span><span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span>: first defined here</div><div><span style="color: yellow; font-weight: bold;">/</span>usr<span style="color: yellow; font-weight: bold;">/</span>bin<span style="color: yellow; font-weight: bold;">/</span>ld: <span style="color: yellow; font-weight: bold;">/</span>tmp<span style="color: yellow; font-weight: bold;">/</span>ccNPBHJP.o: in function `libfun2<span style="color: yellow; font-weight: bold;">'</span><span style="color: white; font-weight: bold;">:</span></div><div><span style="color: white; font-weight: bold;">test2.c:(.text+0x33): multiple definition of `libfun2</span><span style="color: yellow; font-weight: bold;">'</span><span style="color: yellow; font-weight: bold;">;</span> <span style="color: yellow; font-weight: bold;">/</span>tmp<span style="color: yellow; font-weight: bold;">/</span>cczGXRP8.o:test1.c:<span style="color: yellow; font-weight: bold;">(</span>.text<span style="color: yellow; font-weight: bold;">+</span><span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">33</span><span style="color: yellow; font-weight: bold;">)</span>: first defined here</div><div>collect2: error: ld returned <span style="color: white; font-weight: bold;">1</span> exit status</div><br /><div>aldo@Linux $ gcc test.c test1.c test2.c <span style="color: yellow; font-weight: bold;">-</span>o test</div><div><span style="color: yellow; font-weight: bold;">/</span>usr<span style="color: yellow; font-weight: bold;">/</span>bin<span style="color: yellow; font-weight: bold;">/</span>ld: <span style="color: yellow; font-weight: bold;">/</span>tmp<span style="color: yellow; font-weight: bold;">/</span>cc5t0uT9.o: in function `libfun1<span style="color: yellow; font-weight: bold;">'</span><span style="color: white; font-weight: bold;">:</span></div><div><span style="color: white; font-weight: bold;">test2.c:(.text+0x0): multiple definition of `libfun1</span><span style="color: yellow; font-weight: bold;">'</span><span style="color: yellow; font-weight: bold;">;</span> <span style="color: yellow; font-weight: bold;">/</span>tmp<span style="color: yellow; font-weight: bold;">/</span>ccLox6Tj.o:test1.c:<span style="color: yellow; font-weight: bold;">(</span>.text<span style="color: yellow; font-weight: bold;">+</span><span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span>: first defined here</div><div><span style="color: yellow; font-weight: bold;">/</span>usr<span style="color: yellow; font-weight: bold;">/</span>bin<span style="color: yellow; font-weight: bold;">/</span>ld: <span style="color: yellow; font-weight: bold;">/</span>tmp<span style="color: yellow; font-weight: bold;">/</span>cc5t0uT9.o: in function `libfun2<span style="color: yellow; font-weight: bold;">'</span><span style="color: white; font-weight: bold;">:</span></div><div><span style="color: white; font-weight: bold;">test2.c:(.text+0x33): multiple definition of `libfun2</span><span style="color: yellow; font-weight: bold;">'</span><span style="color: yellow; font-weight: bold;">;</span> <span style="color: yellow; font-weight: bold;">/</span>tmp<span style="color: yellow; font-weight: bold;">/</span>ccLox6Tj.o:test1.c:<span style="color: yellow; font-weight: bold;">(</span>.text<span style="color: yellow; font-weight: bold;">+</span><span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">33</span><span style="color: yellow; font-weight: bold;">)</span>: first defined here</div><div>collect2: error: ld returned <span style="color: white; font-weight: bold;">1</span> exit status</div></div></pre><p>Come si nota il programma si compila ed esegue regolarmente solo nei modi <strong>STATICINLINE</strong> e <strong>STATIC</strong>: la parola chiave è, in entrambi i casi <strong>static</strong>, perché definendo così le funzioni la definizione resta limitata al singolo file che include <strong>mylib.h</strong>, quindi non ci sarà nessun errore di ridefinizione (ma si cade nel problema descritto sopra: una crescita del codice macchina risultante). Aggiungendo a <strong>static</strong> anche il <em>function specifier </em> <strong>inline</strong>, il risultato non cambia: funziona bene e, inoltre, il compilatore cerca, se possibile, di <em>"inlineare"</em> la funzione (e anche in questo caso si cade nello stesso problema descritto sopra, però l'eseguibile potrebbe essere più efficiente). Notare che le funzioni mostrano anche il tipo di interfaccia <strong>MY_API</strong> in uso, ottenuta dalla macro <strong>MY_API_STR</strong> di <strong>mylib.h</strong>.</p><p>E cosa succede compilando coi modi <strong>INLINE</strong>, <strong>EXTERN</strong> e <em>"senza modo"</em>? Succede che ci sono errori di compilazione, quindi non possiamo neanche eseguire. Nel modo <strong>INLINE</strong> (senza <strong>static</strong>) notiamo che la funzione non viene resa disponibile al <em>linker,</em> e quindi abbiamo degli errori del tipo <em>"undefined reference to libfun1"</em>: il <em>linker</em> ha bisogno, per questo tipo di librerie della parola magica <strong>static</strong>. Nei modi <strong>EXTERN</strong> e <em>"senza modo"</em> abbiamo, invece, degli errori del tipo <em>"multiple definition of libfun1"</em>, che indicano che, chiedendo un <em>linkaggio</em> di tipo <strong>extern</strong> (o senza tipo) e fornendo poi la funzione ogni volta che si include <strong>mylib.h</strong> si verifica il problema di avere multiple definizioni. Come previsto.</p><p>È tutto chiaro? Spero di si.</p><p>E qui ci sta bene aggiungere qualche considerazione personale, perché non ho ancora detto se considero buone o cattive le librerie <strong>header-only </strong>(<em>magari l'ho fatto intuire, però</em>): devo ammettere che non mi piacciono, perché le considero una forzatura del linguaggio: il fatto che, con le dovute accortezze, si riesca a farle digerire al compilatore e al <em>linker,</em> non significa che sia una buona idea usarle.</p><p>E poi inserire la definizione di una funzione dentro un <em>header-file</em> è più roba da <strong>C++</strong> (<a href="https://letterboxd.com/film/star-wars/" rel="noopener" target="_blank"><strong>il lato oscuro della forza</strong></a>): li è abbastanza usuale che in un <em>header-file</em> ci sia, all'interno della definizione di una classe, anche il codice dei metodi (<em>questo è Ok ma non mi piace: io preferisco scriverlo nel file di implementazione della classe</em>); per non parlare poi dei (famigerati) <em>template</em>, dove è addirittura obbligatorio (o perlomeno molto raccomandabile) avere <em>"tutto"</em> (<em>class template</em> e codice dei metodi) in un solo <em>header-file</em>. Anche per questo i compilatori <strong>C</strong> e <strong>C++</strong> trattano in maniera un po' differente le <em>storage classes</em> descritte sopra. Conclusione: se proprio vi piacciono le librerie <strong>header-only</strong> passate al <strong>C++</strong>, <em>ah ah ah.</em></p><p>Per oggi può bastare, sono contento di avere scritto un nuovo articolo del tipo <strong>"Forse!"</strong>, perché su alcuni argomenti non è necessario essere troppo radicali... ma su altri si, quindi aspettatevi qualche altro <strong>"No, grazie!"</strong> in futuro! <em>E non trattenete il respiro nell'attesa, mi raccomando!</em></p><p>Ciao, e al prossimo post!</p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-68335908364371419502024-01-24T19:54:00.000+01:002024-01-24T19:54:08.399+01:00Valgrind e vengo da lontano come usare Valgrind con il C (e C++)<blockquote><b>Willy:</b> <i>...quindi ora, se lei è d'accordo, se lei è d'accordo con me, io farei un articolo bellissimo, complesso, ardito anche, su questo argomento a tutta pagina dal titolo: "Con chi lo facciamo accoppiare il lucertolone del Sudan?... Con chi lo facciamo accoppiare?".</i><br /><b>caporedattore</b><b>:</b> <i>[non risponde e gli chiude la porta in faccia]</i><br /><b>Willy</b><b>:</b> <i>[parlando da solo] ...con la troia de la tu moglie lo facciamo accoppiare...</i><br /></blockquote><p>Il dialogo surreale qui sopra è tratto dal bel <a href="https://letterboxd.com/aldoz/film/willy-signori-e-vengo-da-lontano/" rel="noopener" target="_blank"><strong>Willy Signori e vengo da lontano</strong></a> del compianto <a href="https://letterboxd.com/director/francesco-nuti/" rel="noopener" target="_blank"><strong>Francesco Nuti</strong></a>. Il Willy del film è la rappresentazione del <em>"vero amico"</em>, quello che ti aiuta nel momento del bisogno, che ti accompagna in maniera disinteressata (vabbé, nel film Willy è guidato anche dai sensi di colpa, ma nel complesso è un vero <em>"cuore d'oro"</em>). E cosa centra <a href="https://valgrind.org/" rel="noopener" target="_blank"><strong>Valgrind</strong></a> in tutto questo? Beh, <strong>Valgrind</strong> è un vero amico del programmatore C (e C++), uno che ti aiuta a scrivere programmi <em>bug-free</em> a prova di bomba, come vedremo tra poco.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiG3O-93avPt5eWvGRvFP6WxOvXeAgNvLZtJaMgAdlZQAXlRCPGxnWhT2MSupgLs1LJbV8Nl4W1UV5wHQokE8xtcVEs_Vbe2Jaz-3AbDEvPZj_vskb-w1FvFoCpVo-O58EAp-OmJCCiLhzsoA098E6MJWNvKqbeuhrkjeu5LUAJK8zZ3cXMc4gmwByeDUoZ/s1919/Valgrind.jpeg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1079" data-original-width="1919" height="331" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiG3O-93avPt5eWvGRvFP6WxOvXeAgNvLZtJaMgAdlZQAXlRCPGxnWhT2MSupgLs1LJbV8Nl4W1UV5wHQokE8xtcVEs_Vbe2Jaz-3AbDEvPZj_vskb-w1FvFoCpVo-O58EAp-OmJCCiLhzsoA098E6MJWNvKqbeuhrkjeu5LUAJK8zZ3cXMc4gmwByeDUoZ/w589-h331/Valgrind.jpeg" width="589" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...con chi lo facciamo accoppiare il Valgrind?...</td></tr></tbody></table><p>Allora, prima di tutto bisogna spiegare cos'è <strong>Valgrind</strong>, perché non è detto che tutti lo sappiano. <strong>Valgrind</strong> è un analizzatore dinamico di comportamento del codice, cioè serve per testare il funzionamento di un programma in condizioni reali (o, perlomeno, il più possibile vicino alla realtà). Tanto per rinfrescare i concetti (nel caso che qualcuno ne abbia bisogno) ricordiamo che il primo passo di analisi si fa con gli analizzatori statici, e cioè:</p><ol><li>La analisi statica più statica che c'è è, ovviamente, quella della compilazione: attivando gli opportuni flag per il compilatore e interpretando i <em>Warning</em> proposti (<em>errori, per definizione, non ce ne sono mai, ah ah ah</em>) facendo in maniera che spariscano tutti, possiamo già dire di avere un codice sintatticamente corretto. Io consiglio sempre un alto livello di controllo, che con <strong>GCC</strong> significa attivare i <em>flag Wall </em>e<em> pedantic.</em></li><li>Si può poi passare il codice con un vero e proprio analizzatore statico, quelli della famiglia <strong>lint</strong>. Ce ne sono vari, ad esempio per C/C++ io uso l'ottimo <a href="https://cppcheck.sourceforge.io/" rel="noopener" target="_blank"><strong>Cppcheck</strong></a>; notare che un buon <strong>lint</strong> va oltre i suoi scopi originari, e può mostrare anche errori di tipo <em>"dinamico"</em>, ma solo quelli evidenti anche staticamente (<em>uhm, sembra strano ma è così...</em>).</li><li>Sia per il caso 1 che per il caso 2 la analisi deve essere fatta <em>cum grano salis</em>: non è detto che non ci siano casi di falsi positivi del <strong>lint</strong> o che qualche <em>Warning</em> di compilazione non sia così eccessivo e pedante che è opportuno eliminarlo: <strong>GCC</strong> in questo caso fornisce per ogni tipo di <em>Warning</em> un <em>flag</em> per ometterlo dai risultati.</li></ol><p>E adesso veniamo al dunque: un programma che ha passato i 3 punti sopra potrebbe ancora, magicamente, <em>schiantarsi</em> durante l'uso reale o, ancora peggio, schiantarsi ogni tanto, anzi, per la <em>legge di Murphy</em> potrebbe non <em>schiantarsi</em> mai durante lo sviluppo e il <em>testing</em> e cominciare a dare problemi ogni tanto dopo la distribuzione sulle macchine di produzione (<em>con grande felicità dei clienti...</em>) il che rende molto difficile capire dove è il problema. Questo è dovuto, quasi sempre, a problemi nella gestione della memoria, che pregiudicano il comportamento dinamico.</p><p>(<em>...apro una parentesi: in questi casi il programmatore inesperto e/o che non ha il controllo totale del codice scritto ricorre a un debugger per trovare l'errore; ecco, bisognerebbe ricordarsi che, <a href="https://artcprogramming.blogspot.com/2019/10/debugger-no-grazie-considerazioni.html" rel="noopener" target="_blank"><b>come ho già scritto in un vecchio articolo</b></a>, il debugger aiuta solo a risolvere problemi facili (e ripetibili), mentre per problemi difficili (o non ripetibili) non serve a nulla, anzi potrebbe dare informazioni fuorvianti. Riepiloghiamo: il debugger aiuta solo a risolvere i problemi semplici, ma questi problemi si possono risolvere, con competenza e un po' di intuito, senza usare il debugger: uhm... è una classica situazione da "gatto che si morde la coda". Chiudo la parentesi...</em>)</p><p>E come si procede in questo caso? ma usando un analizzatore dinamico! Uno come <strong>Valgrind</strong> che analizza il comportamento del programma <em>"mentre sta lavorando"</em>, e fornisce dati utilissimi di memoria occupata e non liberata, memoria non inizializzata, ecc. arrivando anche a dettagliare lo <em>stack</em> di chiamata delle istruzioni che hanno generato l'errore, numeri di linea inclusi. Un bell'aiuto, non c'è che dire! Ovviamente anche <strong>Valgrind</strong> deve essere usato correttamente, per evitare di farsi ingannare da eventuali falsi positivi e, soprattutto, la analisi deve essere effettuata in condizioni reali.</p><p>A questo riguardo vi faccio un esempio semplicissimo: supponiamo di analizzare il comportamento di un <em>Server TCP</em>: se lo lanciamo attraverso <strong>Valgrind</strong> e non gli facciamo fare nulla il risultato sarà: <em>"Il programma è perfetto!"</em>, e in questo caso la considerazione successiva sarebbe <em>"E grazie al..."</em>! È evidente che il funzionamento dinamico di un <em>Server TCP</em> si deve analizzare in condizione di forte carico di rete, magari anche molto più alto di quello usuale, e con molteplici tipi di messaggi, perché i problemi (se ci sono) verranno fuori solo durante l'uso intensivo.</p><p>E, a questo punto ci vuole un po' di codice: vi propongo un semplicissimo programma pieno di errori, che si vedono a prima vista, ma pensate che questi errori potrebbero essere distribuiti, uno qua, uno la, su <em>n-mila</em> linee di codice scritte su<em> n-file</em> (quindi non più facilmente visibili). Questo programma passa bene la compilazione con tutti i <em>Warning</em> attivati, il <strong>Cppcheck</strong> trova qualche errore <em>pseudo-dinamico</em> (ma non tutti) e, <em>dulcis in fundo</em>, nell'uso reale apparentemente non si schianta (ma questo dipende un po' dalla fortuna del momento). Vai col codice!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// test.c - test per Valgrind</span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdlib.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">string.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">LEN_ARRAY</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">16</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">NUM_STRINGS</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">8</span></div><br /><div><span style="color: silver; font-style: italic;">// test - funzione main</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(void)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// alloco memoria per un array di int di lunghezza LEN_ARRAY</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> <span style="color: yellow; font-weight: bold;">*</span>myarray <span style="color: yellow; font-weight: bold;">=</span> malloc<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">LEN_ARRAY</span> <span style="color: yellow; font-weight: bold;">*</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(int));</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore 1: scrivo una posizione non disponibile (write out-of-bound)</span></div><div> myarray<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">LEN_ARRAY</span><span style="color: yellow; font-weight: bold;">]</span> <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore 2: leggo una posizione non disponibile (read out-of-bound)</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">myarray[%d] = %d\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">LEN_ARRAY</span><span style="color: yellow; font-weight: bold;">,</span> myarray<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">LEN_ARRAY</span><span style="color: yellow; font-weight: bold;">]);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// alloco memoria per le stringhe di un array di NUM_STRINGS char pointers</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>strings<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">NUM_STRINGS</span><span style="color: yellow; font-weight: bold;">];</span></div><div> <span style="color: yellow; font-weight: bold;">for</span> <span style="color: yellow; font-weight: bold;">(int</span> i <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span> i <span style="color: yellow; font-weight: bold;"><</span> <span style="color: white; font-weight: bold;">NUM_STRINGS</span><span style="color: yellow; font-weight: bold;">;</span> i<span style="color: yellow; font-weight: bold;">++)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div> strings<span style="color: yellow; font-weight: bold;">[</span>i<span style="color: yellow; font-weight: bold;">]</span> <span style="color: yellow; font-weight: bold;">=</span> malloc<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">LEN_ARRAY</span> <span style="color: yellow; font-weight: bold;">*</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(char));</span></div><div> memset<span style="color: yellow; font-weight: bold;">(</span>strings<span style="color: yellow; font-weight: bold;">[</span>i<span style="color: yellow; font-weight: bold;">],</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">LEN_ARRAY</span> <span style="color: yellow; font-weight: bold;">*</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(char));</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore 3: manca la free(3) delle stringhe e anche di myarray (memory leak)</span></div><br /><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Come vedete sono pochissime linee di programma, con ben tre errori che, ripeto, qui si vedono immediatamente, ma se immersi in <em>n-mila</em> linee di codice sono difficili da trovare.</p><p>E come si usa <strong>Valgrind</strong>? Allora bisogna, semplicemente, compilare il programma (con l'opzione di mantenere i simboli, così avremo anche le informazioni dei numeri di linea) ed eseguire attraverso <strong>Valgrind</strong>. Vediamo, ad esempio cosa succede con <strong>GCC</strong>:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div style="line-height: 13px;"><div>aldo@Linux $ gcc <span style="color: yellow; font-weight: bold;">-</span>g <span style="color: yellow; font-weight: bold;">-</span>Wall <span style="color: yellow; font-weight: bold;">-</span>pedantic test.c <span style="color: yellow; font-weight: bold;">-</span>o test</div><div>aldo@Linux $ .<span style="color: yellow; font-weight: bold;">/</span>test</div><div>myarray<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">16</span><span style="color: yellow; font-weight: bold;">]</span> <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span></div><div>aldo@Linux $ valgrind .<span style="color: yellow; font-weight: bold;">/</span>test <span style="color: yellow; font-weight: bold;">--</span>leak<span style="color: yellow; font-weight: bold;">-</span>check<span style="color: yellow; font-weight: bold;">=</span>full</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> Memcheck<span style="color: yellow; font-weight: bold;">,</span> a memory error detector</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> Copyright <span style="color: yellow; font-weight: bold;">(</span>C<span style="color: yellow; font-weight: bold;">)</span> <span style="color: white; font-weight: bold;">2002</span><span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">2017</span><span style="color: yellow; font-weight: bold;">,</span> and GNU GPL d<span style="color: yellow; font-weight: bold;">,</span> by Julian Seward et al.</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> Using Valgrind<span style="color: yellow; font-weight: bold;">-</span><span style="color: #f44747;">3.18.1</span> and LibVEX<span style="color: yellow; font-weight: bold;">;</span> rerun with <span style="color: yellow; font-weight: bold;">-</span>h <span style="color: yellow; font-weight: bold;">for</span> copyright info</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> Command: .<span style="color: yellow; font-weight: bold;">/</span>test <span style="color: yellow; font-weight: bold;">--</span>leak<span style="color: yellow; font-weight: bold;">-</span>check<span style="color: yellow; font-weight: bold;">=</span>full</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> Invalid write of size <span style="color: white; font-weight: bold;">4</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> at <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">1091DA</span>: main <span style="color: yellow; font-weight: bold;">(</span>test.c:<span style="color: white; font-weight: bold;">16</span><span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> Address <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">4a9c080</span> is <span style="color: white; font-weight: bold;">0</span> bytes after a block of size <span style="color: white; font-weight: bold;">64</span> alloc d</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> at <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">4848899</span>: malloc <span style="color: yellow; font-weight: bold;">(</span>in <span style="color: yellow; font-weight: bold;">/</span>usr<span style="color: yellow; font-weight: bold;">/</span>libexec<span style="color: yellow; font-weight: bold;">/</span>valgrind<span style="color: yellow; font-weight: bold;">/</span>vgpreload_memcheck<span style="color: yellow; font-weight: bold;">-</span>amd64<span style="color: yellow; font-weight: bold;">-</span>linux.so<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> by <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">1091CD</span>: main <span style="color: yellow; font-weight: bold;">(</span>test.c:<span style="color: white; font-weight: bold;">13</span><span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> Invalid read of size <span style="color: white; font-weight: bold;">4</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> at <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">1091E8</span>: main <span style="color: yellow; font-weight: bold;">(</span>test.c:<span style="color: white; font-weight: bold;">19</span><span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> Address <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">4a9c080</span> is <span style="color: white; font-weight: bold;">0</span> bytes after a block of size <span style="color: white; font-weight: bold;">64</span> alloc d</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> at <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">4848899</span>: malloc <span style="color: yellow; font-weight: bold;">(</span>in <span style="color: yellow; font-weight: bold;">/</span>usr<span style="color: yellow; font-weight: bold;">/</span>libexec<span style="color: yellow; font-weight: bold;">/</span>valgrind<span style="color: yellow; font-weight: bold;">/</span>vgpreload_memcheck<span style="color: yellow; font-weight: bold;">-</span>amd64<span style="color: yellow; font-weight: bold;">-</span>linux.so<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> by <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">1091CD</span>: main <span style="color: yellow; font-weight: bold;">(</span>test.c:<span style="color: white; font-weight: bold;">13</span><span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span></div><div>myarray<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">16</span><span style="color: yellow; font-weight: bold;">]</span> <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> HEAP SUMMARY:</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> in use at exit: <span style="color: white; font-weight: bold;">192</span> bytes in <span style="color: white; font-weight: bold;">9</span> blocks</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> total heap usage: <span style="color: white; font-weight: bold;">10</span> allocs<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">1</span> frees<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">,</span><span style="color: white; font-weight: bold;">216</span> bytes allocated</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> LEAK SUMMARY:</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> definitely lost: <span style="color: white; font-weight: bold;">192</span> bytes in <span style="color: white; font-weight: bold;">9</span> blocks</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> indirectly lost: <span style="color: white; font-weight: bold;">0</span> bytes in <span style="color: white; font-weight: bold;">0</span> blocks</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> possibly lost: <span style="color: white; font-weight: bold;">0</span> bytes in <span style="color: white; font-weight: bold;">0</span> blocks</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> still reachable: <span style="color: white; font-weight: bold;">0</span> bytes in <span style="color: white; font-weight: bold;">0</span> blocks</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> suppressed: <span style="color: white; font-weight: bold;">0</span> bytes in <span style="color: white; font-weight: bold;">0</span> blocks</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> Rerun with <span style="color: yellow; font-weight: bold;">--</span>leak<span style="color: yellow; font-weight: bold;">-</span>check<span style="color: yellow; font-weight: bold;">=</span>full to see details of leaked memory</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> For lists of detected and suppressed errors<span style="color: yellow; font-weight: bold;">,</span> rerun with: <span style="color: yellow; font-weight: bold;">-</span>s</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> ERROR SUMMARY: <span style="color: white; font-weight: bold;">2</span> errors from <span style="color: white; font-weight: bold;">2</span> contexts <span style="color: yellow; font-weight: bold;">(</span>suppressed: <span style="color: white; font-weight: bold;">0</span> from <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span></div></div></div></pre><p>Visto? La compilazione non ha dato <em>Warning</em> (nonostante l'uso di <em>Wall</em> e <em>pedantic)</em> e anche l'esecuzione è andata apparentemente bene (<em>che fortuna!</em>). Poi <strong>Valgrind</strong> ha trovato tutti i problemi del programma, permettendoci di modificare a colpo sicuro il codice per evitarli. Ovviamente questo è un caso semplice e, come già detto sopra, possono esserci dei falsi positivi o informazioni un po criptiche (specialmente nei programmi <em>multithread)</em> che si devono interpretare. Comunque <strong>Valgrind</strong> è un aiuto preziosissimo e altamente raccomandabile anche (e soprattutto, direi) per programmi grandi e complessi, basta trovare la maniera di eseguirlo simulando carichi di lavoro reali.</p><p>Nell'esempio qui sopra l'interpretazione è abbastanza semplice: il numero <em>==40693==</em> è il PID del processo eseguito, le prime linee seguenti sono, evidentemente, di presentazione, dopodiché vengono mostrati i veri e propri errori:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div style="line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> Invalid write of size <span style="color: white; font-weight: bold;">4</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> at <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">1091DA</span>: main <span style="color: yellow; font-weight: bold;">(</span>test.c:<span style="color: white; font-weight: bold;">16</span><span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> Address <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">4a9c080</span> is <span style="color: white; font-weight: bold;">0</span> bytes after a block of size <span style="color: white; font-weight: bold;">64</span> alloc d</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> at <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">4848899</span>: malloc <span style="color: yellow; font-weight: bold;">(</span>in <span style="color: yellow; font-weight: bold;">/</span>usr<span style="color: yellow; font-weight: bold;">/</span>libexec<span style="color: yellow; font-weight: bold;">/</span>valgrind<span style="color: yellow; font-weight: bold;">/</span>vgpreload_memcheck<span style="color: yellow; font-weight: bold;">-</span>amd64<span style="color: yellow; font-weight: bold;">-</span>linux.so<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> by <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">1091CD</span>: main <span style="color: yellow; font-weight: bold;">(</span>test.c:<span style="color: white; font-weight: bold;">13</span><span style="color: yellow; font-weight: bold;">)</span></div></div></div></pre><p>questo è, evidentemente, quello che nel codice è alla linea 14, come anticipato nel commento del codice:</p><pre class="EnlighterJSRAW" data-enlighter-language="c" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div style="line-height: 13px;"><div><span style="color: silver; font-style: italic;">// errore 1: scrivo una posizione non disponibile (write out-of-bound)</span></div><div>myarray<span style="color: yellow; font-weight: bold;">[</span>LEN_ARRAY<span style="color: yellow; font-weight: bold;">]</span> <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div></div></div></pre><p>Notare che viene fornito anche lo <em>stack</em> delle chiamate che finalizzano con la linea 16 di <em>test.c</em>. Dopodiché c'è il secondo errore:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div style="line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> Invalid read of size <span style="color: white; font-weight: bold;">4</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> at <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">1091E8</span>: main <span style="color: yellow; font-weight: bold;">(</span>test.c:<span style="color: white; font-weight: bold;">19</span><span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> Address <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">4a9c080</span> is <span style="color: white; font-weight: bold;">0</span> bytes after a block of size <span style="color: white; font-weight: bold;">64</span> alloc d</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> at <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">4848899</span>: malloc <span style="color: yellow; font-weight: bold;">(</span>in <span style="color: yellow; font-weight: bold;">/</span>usr<span style="color: yellow; font-weight: bold;">/</span>libexec<span style="color: yellow; font-weight: bold;">/</span>valgrind<span style="color: yellow; font-weight: bold;">/</span>vgpreload_memcheck<span style="color: yellow; font-weight: bold;">-</span>amd64<span style="color: yellow; font-weight: bold;">-</span>linux.so<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> by <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">1091CD</span>: main <span style="color: yellow; font-weight: bold;">(</span>test.c:<span style="color: white; font-weight: bold;">13</span><span style="color: yellow; font-weight: bold;">)</span></div></div></div></pre><p>questo è, evidentemente, quello che nel codice è alla linea 19, come anticipato nel commento del codice:</p><pre class="EnlighterJSRAW" data-enlighter-language="c" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div style="line-height: 13px;"><div><span style="color: silver; font-style: italic;">// errore 2: leggo una posizione non disponibile (read out-of-bound)</span></div><div>printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">myarray[%d] = %d\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> LEN_ARRAY<span style="color: yellow; font-weight: bold;">,</span> myarray<span style="color: yellow; font-weight: bold;">[</span>LEN_ARRAY<span style="color: yellow; font-weight: bold;">]);</span></div></div></div></pre><p>A questo punto, visto che <strong>Valgrind</strong> ha già mostrato gli errori effettivi, viene mostrato un riepilogo del'uso della memoria, che evidenzia il terzo errore, che non si riferisce alle istruzioni errate ma, bensì, alle istruzioni mancanti, le <a href="https://man7.org/linux/man-pages/man3/free.3.html" rel="noopener" target="_blank"><strong>free(3)</strong></a>:</p><div><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> HEAP SUMMARY:</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> in use at exit: <span style="color: white; font-weight: bold;">192</span> bytes in <span style="color: white; font-weight: bold;">9</span> blocks</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> total heap usage: <span style="color: white; font-weight: bold;">10</span> allocs<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">1</span> frees<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">,</span><span style="color: white; font-weight: bold;">216</span> bytes allocated</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> LEAK SUMMARY:</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> definitely lost: <span style="color: white; font-weight: bold;">192</span> bytes in <span style="color: white; font-weight: bold;">9</span> blocks</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> indirectly lost: <span style="color: white; font-weight: bold;">0</span> bytes in <span style="color: white; font-weight: bold;">0</span> blocks</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> possibly lost: <span style="color: white; font-weight: bold;">0</span> bytes in <span style="color: white; font-weight: bold;">0</span> blocks</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> still reachable: <span style="color: white; font-weight: bold;">0</span> bytes in <span style="color: white; font-weight: bold;">0</span> blocks</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">40693</span><span style="color: yellow; font-weight: bold;">==</span> suppressed: <span style="color: white; font-weight: bold;">0</span> bytes in <span style="color: white; font-weight: bold;">0</span> blocks</div></div></pre></div><p>Come si nota, il numero di <em>allocs</em> e <em>frees</em> non corrisponde, il che indica che mancano 9 <strong>free(3)</strong> (1 per <em>myarray</em> e 8 per <em>strings),</em> e i <em>byte</em> persi, che sono 192, corrispondono esattamente ai dati allocati e non liberati.</p><p>Ok, per oggi può bastare. Vi sentite già esperti del <strong>Valgrind?</strong> Ecco, se lo provate su un programma grande, complesso e (magari) pieno di errori, il risultato potrebbe essere esteso e un po' preoccupante; in quel caso bisogna interpretare bene le informazioni e correggere uno per uno gli errori. Vi raccomando di farlo, vi eviterete molti mal di testa dovuti a problemi stranissimi segnalati dai clienti.</p><p>E poi mettiamo le cose in chiaro: l'obiettivo di un buon programmatore è produrre e distribuire <em>Software bug-free</em>... E si può! Si può'! Anzi in alcuni tipi di applicazioni, tipicamente quelle <em>mission-critical</em> e quelle <em>business-critical</em>, il <em>Software</em> distribuito <strong>deve</strong> essere sempre <em>bug-free</em>, ricordatelo! E <strong>Valgrind</strong> sarà un prezioso amico per raggiungere l'obiettivo, ve l'assicuro. Ovviamente dopo l'analisi statica vista all'inizio dell'articolo e la seguente analisi dinamica fatta con <strong>Valgrind</strong> mancherebbe una terza analisi, quella logica: cioè, se un programma è staticamente e dinamicamente perfetto ma non fa bene quello che dovrebbe fare non ci sono analizzatori che tengano: <em>bisogna studiarlo e implementarlo bene! Tenetelo presente... ah ah ah.</em></p><p>Ciao, e al prossimo post!</p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-322761250658790082023-12-25T20:36:00.004+01:002023-12-25T20:38:26.141+01:00Buon Natale e Buon Anno!<p> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9ERYGQp0Xrfk2C2DoQil7xjJHz9cVrC4tlYBN1O-Mdfel6fCVynhDQFWjRC8u1yAFjunsi94YfyT0wYpNg9iVkTIwLSstqwdduJmY8jubvcrs1uI4i3L2tugV7XMAPYQdsAcarpMan3O2rJiF-Lbzko0yP9zccKnWWsouHEghxSb01bhYU8QC8j8xKp5m/s640/BuoneFeste2.jpg" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="400" data-original-width="640" height="358" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9ERYGQp0Xrfk2C2DoQil7xjJHz9cVrC4tlYBN1O-Mdfel6fCVynhDQFWjRC8u1yAFjunsi94YfyT0wYpNg9iVkTIwLSstqwdduJmY8jubvcrs1uI4i3L2tugV7XMAPYQdsAcarpMan3O2rJiF-Lbzko0yP9zccKnWWsouHEghxSb01bhYU8QC8j8xKp5m/w572-h358/BuoneFeste2.jpg" width="572" /></a></p><br /><p></p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-91841020344223861832023-12-19T18:30:00.009+01:002024-01-24T12:31:14.175+01:00Tutta colpa della fork come usare la fork(2) in multithreading in C<blockquote><b>albergatore:</b> <i>Lui mi sembra una persona in gamba, intelligente, per bene... dice sempre buongiorno e buonasera... sempre buongiorno e buonasera... però... boh.</i><br /><b>Romeo</b><b>:</b> <i>Come boh?</i><br /><b>albergatore</b><b>:</b> <i>No per carità, mica per dirne male, per l'amor di Dio... uno che dice sempre buongiorno e buonasera... però... boh.</i><br /></blockquote><p>Il dialogo surreale qui sopra è tratto dal bel <a href="https://letterboxd.com/aldoz/film/blame-it-on-paradise/" rel="noopener" target="_blank"><strong>Tutta colpa del Paradiso</strong></a> del compianto <a href="https://letterboxd.com/director/francesco-nuti/" rel="noopener" target="_blank"><strong>Francesco Nuti</strong></a>. Un dialogo che rappresenta l'incertezza nel giudicare persone e cose, e che ci aiuta a introdurre il tema del giorno: la <a href="https://man7.org/linux/man-pages/man2/fork.2.html" rel="noopener" target="_blank"><strong>fork(2)</strong></a> che è una <em>system call</em> veramente classica, preziosa e indispensabile dei sistemi <strong>POSIX</strong> (e di cui abbiamo parlato <a href="https://artcprogramming.blogspot.com/2020/12/processi-o-thread-considerazioni-sulla.html" rel="noopener" target="_blank"><strong>qui</strong></a> e non solo) si può sempre usare <em>"come se niente fosse"</em> o bisogna usarla con cautela? Uhmm... vediamolo!</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2JuoTVNACgVwomRYmsifC2d9_zq-bwPuN0o9mCSRhpTQuUvLffZ53x_tBkdEL_eqckLlsGeKT_Aat_Jym-2putcK-Cs2EU43-4nP0IPyH2P_s7LSJNmAnKbn1nZD_ZhL4-86fJr8yfrlb171hPO32fqlCFAGI2RYqaods-Sp2sGTnrW1xe_2q55SfuuSD/s1769/tuttacolpadellafork.jpeg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="946" data-original-width="1769" height="314" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2JuoTVNACgVwomRYmsifC2d9_zq-bwPuN0o9mCSRhpTQuUvLffZ53x_tBkdEL_eqckLlsGeKT_Aat_Jym-2putcK-Cs2EU43-4nP0IPyH2P_s7LSJNmAnKbn1nZD_ZhL4-86fJr8yfrlb171hPO32fqlCFAGI2RYqaods-Sp2sGTnrW1xe_2q55SfuuSD/w588-h314/tuttacolpadellafork.jpeg" width="588" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...la fork? Si, funziona bene, però... boh...</td></tr></tbody></table><p>Allora, tanto per tagliare subito la testa al toro possiamo dire che la <strong>fork(2)</strong> in un <em>"programma normale"</em> è affidabile al 100%, e ci mancherebbe solo: è una <em>system call</em> che esiste da sempre su <strong>UNIX</strong> (e, dopo, anche su <strong>Linux</strong> e in tutta la famiglia <strong>POSIX</strong>) ed è alla base della scrittura delle applicazioni <strong>multiprocess</strong>.</p><p>Ma cosa si intende per <em>"programma normale"</em> ? Ecco, direi che in questo caso si intende una applicazione <strong>singlethread</strong>: la <strong>fork(2)</strong> risale a una delle primissime versioni di <strong>UNIX</strong>: la <strong>V1</strong> (detta anche <em>UNIX First Edition</em>, 1971) ed è stata poi standardizzata nel primo standard <strong>POSIX</strong> <strong>1003.1-1988</strong>. Internamente la <strong>fork(2)</strong> ha avuto varie evoluzioni: su <strong>Linux</strong>, ad esempio, è implementata internamente tramite una chiamata alla <em>system call</em> <a href="https://man7.org/linux/man-pages/man2/clone.2.html" rel="noopener" target="_blank"><strong>clone(2)</strong></a>, ma alla fin fine rimane sempre e comunque la cara, vecchia e affidabile <strong>fork(2)</strong>. Il <strong>multithreading</strong> è apparso su <strong>UNIX</strong> ben più tardi, ed è stato standardizzato, poi, con <strong>POSIX 1003.1c-1995</strong>, e i (presunti) problemi della <strong>fork(2)</strong>, come vedremo più avanti, sono cominciati lì...</p><p>E come si usa la <strong>fork(2)</strong>? Come detto sopra ne abbiamo già parlato, comunque vi propongo, di seguito, uno degli esempi più classici di <em>fork + exec + wait</em>. Vai col codice!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// testfork.c - test della fork(2)</span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">string.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdlib.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">unistd.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/wait.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">errno.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char*</span> argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// eseguo la fork()</span></div><div> <span style="color: yellow; font-weight: bold;">pid_t</span> pid <span style="color: yellow; font-weight: bold;">=</span> fork<span style="color: yellow; font-weight: bold;">();</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>pid <span style="color: yellow; font-weight: bold;">==</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// sono il figlio: eseguo il comando "ls ./testfork"</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>pathname <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">/usr/bin/ls</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>newargv<span style="color: yellow; font-weight: bold;">[]</span> <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">{</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">ls</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">./testfork</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span> <span style="color: yellow; font-weight: bold;">};</span></div><div> execv<span style="color: yellow; font-weight: bold;">(</span>pathname<span style="color: yellow; font-weight: bold;">,</span> newargv<span style="color: yellow; font-weight: bold;">);</span></div><div> exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // exec non ritorna mai</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>pid <span style="color: yellow; font-weight: bold;">></span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// sono il padre: attendo l'uscita del figlio</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> status<span style="color: yellow; font-weight: bold;">;</span></div><div> waitpid<span style="color: yellow; font-weight: bold;">(</span>pid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>status<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">);</span></div><div> exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">EXIT_SUCCESS</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore fork()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">error: %s\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>E non credo che ci sia nulla da aggiungere su questo codice chiaro <em>(spero)</em> e <a href="https://artcprogramming.blogspot.com/2012/08/no-comment.html" rel="noopener" target="_blank"><strong>ben commentato</strong></a> (<em>o meglio: rileggetevi gli articoli citati sopra, grazie</em>).</p><p>Ma veniamo al nocciolo della questione: come siamo messi col <strong>multithreading</strong> mischiato col <strong>multiprocessing</strong>? Ecco, prima di impazzire cercando articoli e riferimenti sull'argomento (spinoso, devo dire) e/o scrivere test complicati, è meglio dare un occhiatina al manuale, che è sempre la fonte primaria e più completa di informazioni. La pagina ufficiale del <strong>The Linux man-pages project</strong>, <a href="https://man7.org/linux/man-pages/man2/fork.2.html" rel="noopener" target="_blank"><strong>System Calls Manual - fork(2)</strong></a> dice:</p><pre><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>After a fork<span style="color: yellow; font-weight: bold;">()</span> in a multithreaded program<span style="color: yellow; font-weight: bold;">,</span> the child can</div><div>safely call only async<span style="color: yellow; font-weight: bold;">-</span>signal<span style="color: yellow; font-weight: bold;">-</span>safe functions <span style="color: yellow; font-weight: bold;">(</span>see</div><div>signal<span style="color: yellow; font-weight: bold;">-</span>safety<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">7</span><span style="color: yellow; font-weight: bold;">))</span> until such time as it calls execve<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">2</span><span style="color: yellow; font-weight: bold;">)</span>.</div></div></pre><p>e, per completare il quadro, vediamo anche cosa dice il <a href="https://man7.org/linux/man-pages/man3/fork.3p.html" rel="noopener" target="_blank"><strong>POSIX Programmer's Manual - FORK(3P)</strong></a>:</p><pre><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>A process shall be created with a single thread. If a multi<span style="color: yellow; font-weight: bold;">-</span></div><div>threaded process calls fork<span style="color: yellow; font-weight: bold;">(),</span> the new process shall contain</div><div>a replica of the calling thread and its entire address space<span style="color: yellow; font-weight: bold;">,</span></div><div>possibly including the states of mutexes and other resources.</div><div>Consequently<span style="color: yellow; font-weight: bold;">,</span> to avoid errors<span style="color: yellow; font-weight: bold;">,</span> the child process may only</div><div>execute async<span style="color: yellow; font-weight: bold;">-</span>signal<span style="color: yellow; font-weight: bold;">-</span>safe operations until such time as one</div><div>of the exec functions is called.</div></div></pre><p>Ok, sembra che qualche problemino c'è...</p><p>In pratica cosa succede? Succede che se un programma <em>multithread</em> chiama la <strong>fork(2)</strong>, si crea, come previsto, un processo figlio che è una copia esatta del processo padre (un <em>clone:</em> non per nulla su <strong>Linux</strong>, come visto sopra, la <strong>fork(2)</strong> usa internamente la <em>system call</em> <strong>clone(2)</strong>), ma il nuovo processo è <em>singlethread</em>, ed è una copia del <em>thread</em> in cui è stata invocata la <strong>fork(2)</strong>. Infatti, sempre nel manuale di <strong>Linux</strong> troviamo:</p><pre><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>A process shall be created with a single thread. If a multi<span style="color: yellow; font-weight: bold;">-</span></div><div>threaded process calls fork<span style="color: yellow; font-weight: bold;">(),</span> the new process shall contain</div><div>a replica of the calling thread and its entire address space<span style="color: yellow; font-weight: bold;">,</span></div><div>possibly including the states of mutexes and other resources.</div><div>Consequently<span style="color: yellow; font-weight: bold;">,</span> to avoid errors<span style="color: yellow; font-weight: bold;">,</span> the child process may only</div><div>execute async<span style="color: yellow; font-weight: bold;">-</span>signal<span style="color: yellow; font-weight: bold;">-</span>safe operations until such time as one</div><div>of the exec functions is called.</div></div></pre><p>E quindi, alla fine della fiera, il problema principale risiede nelle <em>race-conditions</em> dovute a eventuali <em>mutex</em> (o altri tipi di <em>lock)</em> che risiedono, contemporaneamente, nei processi padre e figlio, il che crea la possibilità che si verifichino strani blocchi. E, se vogliamo completare il discorso, bisogna aggiungere che anche la gestione dei <a href="https://man7.org/linux/man-pages/man7/signal.7.html" rel="noopener" target="_blank"><strong>segnali</strong></a> diretti al nostro processo padre diventa problematica, visto che dopo avere eseguito <strong>fork(2)</strong> abbiamo in circolazione anche un figlio-clone.</p><p>Ma come si risolve tutto questo? Beh, negli estratti dei manuali appena presentati si raccomanda di usare nel processo figlio, prima di una eventuale <a href="https://man7.org/linux/man-pages/man3/exec.3.html" rel="noopener" target="_blank"><strong>exec(3)</strong></a>, solo funzioni della famiglia <a href="https://man7.org/linux/man-pages/man7/signal-safety.7.html" rel="noopener" target="_blank"><strong>async-signal-safe</strong></a>; queste sembrano tante ma... non fatevi ingannare, in realtà sono pochissime! Pensate che, tra le tante cose assenti, c'è tutto <em>stdio</em>, inclusa la <a href="https://man7.org/linux/man-pages/man3/printf.3.html" rel="noopener" target="_blank"><strong>printf(3)</strong></a>! E non si può neanche manipolare la memoria con <a href="https://man7.org/linux/man-pages/man3/malloc.3.html" rel="noopener" target="_blank"><strong>malloc(3)</strong></a> e <strong>free(3)</strong>! E non si può neanche registrare un problema con <a href="https://man7.org/linux/man-pages/man3/syslog.3.html" rel="noopener" target="_blank"><strong>syslog(3)</strong></a> visto che usa dei <em>mutex</em> (<em>un grazie al collega Xavier P. per avermelo fatto notar</em>e). È un bel problema... In parallelo alla raccomandazione precedente si può, poi, usare <a href="https://man7.org/linux/man-pages/man3/pthread_atfork.3.html" rel="noopener" target="_blank"><strong>pthread_atfork(3)</strong></a>, ma con molta cautela, perché è possibile dimenticarsi qualche dettaglio visto che non è una soluzione molto semplice da realizzare.</p><p>E quali sono i sintomi tipici di <em>"qualcosa è andato male nella fork + exec" </em>? Direi che l'evento più probabile è che la <strong>exec(3)</strong> non si esegua perché il <em>child process</em> si è bloccato a causa di un <em>lock</em> ereditato dal padre: in questo caso, usando semplicemente il comando <a href="https://man7.org/linux/man-pages/man1/ps.1.html" rel="noopener" target="_blank"><strong>ps</strong></a>, si noterà che abbiamo due processi con lo stesso nome, padre e figlio, con il figlio che non è stato <em>(ahimè)</em> sostituito da un altro programma tramite la <strong>exec(3)</strong>.</p><p>Credo che a questo punto sia il caso di mostrare una piccola guida riassuntiva di come procedere quando non si può fare a meno di usare la <strong>fork(2)</strong> in un programma <em>multithread</em> (applicherò, semplicemente, le avvertenze dei manuali). E quindi: la <strong>fork(2)</strong> si usa senza paura anche in <em>multitreading</em> (io l'ho fatto molte volte) ma seguendo il seguente schema numerato in ordine di importanza:</p><ol><li><strong>Se possibile</strong> usate sempre la sequenza <em>fork + exec + wait</em>, ed eseguite <strong>exec(2)</strong> immediatamente dopo la<strong> fork(2)</strong> senza mettere praticamente nulla in mezzo (esattamente come nell'esempio mostrato più sopra): la <strong>exec(2)</strong> cancella tutti gli (eventuali) <em>lock</em> in comune tra padre e figlio e il problema è risolto alla radice. Notare che <strong>POSIX</strong> mette addirittura a disposizione una funzione, la <a href="https://man7.org/linux/man-pages/man3/posix_spawn.3.html" rel="noopener" target="_blank"><strong>posix_spawn(3)</strong></a>, che esegue <em>fork + exec</em> in un passaggio solo, ed è quindi intrinsecamente sicura, ma non è semplicissima da usare (bene).</li><li><strong>Se proprio non potete</strong> eseguire immediatamente <strong>exec(2)</strong>, prima di eseguirla dovete avere l'accortezza di usare solo funzioni di tipo <strong>async-signal-safe</strong>, e ricordate: non potete usare neanche la <em>"innocua"</em> <strong>printf(3)</strong>, per cui dovrete arrangiarvi con la <a href="https://man7.org/linux/man-pages/man2/write.2.html" rel="noopener" target="_blank"><strong>write(2)</strong></a>.</li><li><strong>Se proprio non dovete</strong> eseguire <strong>exec(2)</strong>, riducete al minimo il codice del processo figlio (usando solo funzioni di tipo <strong>async-signal-safe</strong>) e uscite quanto prima usando <a href="https://man7.org/linux/man-pages/man2/exit.2.html" rel="noopener" target="_blank"><strong>_exit(2)</strong></a> (e non <a href="https://man7.org/linux/man-pages/man3/exit.3.html" rel="noopener" target="_blank"><strong>exit(3)</strong></a>!). Non chiamate altre funzioni del programma, a meno che non siate sicuri al 100% che siano assolutamente innocue a livello di lock del <em>multithreading</em> e che usino solo funzioni <strong>async-signal-safe</strong>.</li><li><strong>Se proprio siete</strong> in una situazione <em>"speciale"</em> (non compresa nei tre punti precedenti) usate, con molta attenzione, <strong>pthread_atfork(3)</strong>, che è l'ultima risorsa disponibile.</li></ol><p>E per oggi può bastare. Spero di aver contribuito a sfatare alcuni (falsi) miti sulla problematicità della <strong>fork(2)</strong>, una <em>system call</em> storica e indispensabile, e che funziona benissimo... <em>ma bisogna saperla usare</em>. Immagino che molti di voi saranno già in pieno assetto pre-festivo, e invasi dallo Spirito Natalizio (beh, io si). Quindi vi lascerò in pace per un po': Buon Natale e Buon Anno a tutti!</p><p>Ciao, e al prossimo post!</p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-2799954281711819992023-11-13T18:32:00.002+01:002024-01-31T09:37:20.343+01:00Scusate la memcmp considerazioni sull'uso della memcmp(3) in C<blockquote><b>Tonino</b><b>:</b> <i>Vincè, io mi uccido: meglio un giorno da leone che cento giorni da pecora! O no Vincè? Meglio un giorno da leone!</i><br /><b>Vincenzo</b><b>:</b> <i>Tonì, che ne saccio io d''a pècura o d''o leone? Fa' cinquanta juórne da orsacchiotto.</i><br /></blockquote><p>Nonostante l'intenzione <em>(giuro)</em> di scrivere un altro articolo non propriamente di programmazione (come ho fatto negli gli ultimi due, <a href="https://artcprogramming.blogspot.com/2023/09/ricomincio-da-deb-come-creare-un-debian.html" rel="noopener" target="_blank"><strong>qui</strong></a> e <a href="https://artcprogramming.blogspot.com/2023/10/ricomincio-da-deb-come-creare-un-debian.html" rel="noopener" target="_blank"><strong>qui)</strong></a> sono stato improvvisamente travolto da un problema reale (<em>di codice, eh!</em>) e ho deciso di soprassedere (<em>e rimandiamo, rimandiamo...</em>) e di tornare al nostro amato <strong>C</strong>.</p><p>Il problema reale citato riguardava l'uso (<em>e il mal uso</em>) della funzione della <strong>libc</strong> <a href="https://man7.org/linux/man-pages/man3/memcmp.3.html" rel="noopener" target="_blank"><strong>memcmp(3)</strong></a>, che è utilissima, ben fatta e, a volte, indispensabile, ma che può anche provocare dei notevoli mal di testa. E quindi ci vuole un articolo: inizialmente avevo pensato a uno della serie <strong>"No, Grazie!"</strong> (<em>ne ho scritti già un po' e vi invito a </em><em style="background-color: #fff9ee; color: #222222; font-family: Georgia, Utopia, "Palatino Linotype", Palatino, serif; font-size: 15.4px;">leggerli o rileggerli, <a href="https://artcprogramming.blogspot.com/2022/08/thread-cancel-no-grazie-considerazioni.html" target="_blank"><b>qui</b></a>, </em><em><a href="https://artcprogramming.blogspot.com/2022/06/system-no-grazie-considerazioni-sul.html" rel="noopener" target="_blank"><b>qui</b><strong>,</strong></a> </em><em><a href="https://artcprogramming.blogspot.com/2020/09/sleep-no-grazie-considerazioni-sul.html" rel="noopener" target="_blank"><strong>qui</strong></a>, <a href="https://artcprogramming.blogspot.com/2019/12/sprintf-no-grazie-considerazioni.html" rel="noopener" target="_blank"><strong>qui</strong>,</a> <a href="https://artcprogramming.blogspot.com/2019/10/debugger-no-grazie-considerazioni.html" rel="noopener" target="_blank"><strong>qui</strong></a> e <a href="https://artcprogramming.blogspot.com/2019/02/variabili-globali-no-grazie-come-non.html" rel="noopener" target="_blank"><strong>qui</strong></a></em>)), ma sarebbe stato ingeneroso verso la <strong>memcmp(3)</strong>, che, come detto sopra, è una buona funzione, basta usarla bene. E allora, invece di un <strong>"No, grazie!"</strong> sarà uno <strong>"Scusate"</strong>, ispirato nuovamente al grande <a href="https://letterboxd.com/director/massimo-troisi/" rel="noopener" target="_blank"><strong>Massimo Troisi</strong></a> che con il suo <a href="https://letterboxd.com/aldoz/film/sorry-for-the-delay/" rel="noopener" target="_blank"><strong>Scusate il Ritardo</strong></a> si scusava per il tempo passato dal suo ultimo film (ma il gioco di parole significava anche altro).</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5fObGTSsqiXJNWTbDjwvZuXDLhUGY7itvJNx75bwkRyJsxrzmAzLXVCh7feEdr_UcowV1mRpLKvPabIsag8_Oz9p0EVijIcqot-VIoVKnQIq_M062n92sc-rvnucDw3Wpk9YxeRuVWAG8K2mg65WSHMXoJ-oV2y65NTf7b_uKNAU7R17Cr-_O9GmIVpCz/s1165/scusatelamemcmp.jpeg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="632" data-original-width="1165" height="319" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5fObGTSsqiXJNWTbDjwvZuXDLhUGY7itvJNx75bwkRyJsxrzmAzLXVCh7feEdr_UcowV1mRpLKvPabIsag8_Oz9p0EVijIcqot-VIoVKnQIq_M062n92sc-rvnucDw3Wpk9YxeRuVWAG8K2mg65WSHMXoJ-oV2y65NTf7b_uKNAU7R17Cr-_O9GmIVpCz/w588-h319/scusatelamemcmp.jpeg" width="588" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...ma quante volte ti ho detto di non usare la memcmp?...</td></tr></tbody></table><p>E allora, andiamo al dunque: la <strong>memcmp(3)</strong> esegue, come dice il nome, una comparazione di memoria, ossia ci dice se due blocchi di memoria sono uguali (o diversi). Vediamone una semplice implementazione tanto per chiarire di cosa stiamo parlando. Vai col codice!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">string.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: yellow; font-weight: bold;">int</span> memcmp<span style="color: yellow; font-weight: bold;">(const</span> <span style="color: yellow; font-weight: bold;">void</span> <span style="color: yellow; font-weight: bold;">*</span>vl<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">const</span> <span style="color: yellow; font-weight: bold;">void</span> <span style="color: yellow; font-weight: bold;">*</span>vr<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">size_t</span> n<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">const</span> <span style="color: yellow; font-weight: bold;">unsigned</span> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>l<span style="color: yellow; font-weight: bold;">=</span>vl<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">*</span>r<span style="color: yellow; font-weight: bold;">=</span>vr<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">for</span> <span style="color: yellow; font-weight: bold;">(;</span> n <span style="color: yellow; font-weight: bold;">&&</span> <span style="color: yellow; font-weight: bold;">*</span>l <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">*</span>r<span style="color: yellow; font-weight: bold;">;</span> n<span style="color: yellow; font-weight: bold;">--,</span> l<span style="color: yellow; font-weight: bold;">++,</span> r<span style="color: yellow; font-weight: bold;">++);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> n <span style="color: yellow; font-weight: bold;">?</span> <span style="color: yellow; font-weight: bold;">*</span>l<span style="color: yellow; font-weight: bold;">-*</span>r <span style="color: yellow; font-weight: bold;">:</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>questa è l'implementazione della (notevole e raccomandabile) <a href="https://musl.libc.org/" rel="noopener" target="_blank"><strong>musl libc</strong></a>, ed è, come spesso accade in questa libreria, ridotta all'osso privilegiando semplicità e funzionalità. La <strong>memcmp(3)</strong> della <a href="https://www.gnu.org/software/libc/" rel="noopener" target="_blank"><strong>glibc</strong></a>, pur eseguendo lo stesso compito, è implementata in una maniera abbastanza più complicata (per correggere alcune vulnerabilità della funzione) e quindi ve la risparmio: per rendere l'idea va benissimo la versione della <strong>musl libc</strong>.</p><p>Come si nota, stiamo parlando di una funzione abbastanza semplice (nell'esempio sono 3 linee!) che compara <em>"byte-a-byte"</em> (anzi, <em>"char-a-char"</em>) due zone di memoria, ritornando zero se sono uguali, e un valore diverso da zero (positivo o negativo) se sono diverse: il codice canta. Apparentemente non ci possono essere grossi problemi, e infatti, normalmente, la funzione è utilissima e funzionale, ma... in alcuni casi i problemi ci sono! Eccome! Il trucco è ben descritto nello <strong>standard del C11</strong> (cap. 6.2.6.1/6):</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>When a value is stored in an object of structure or <span style="color: yellow; font-weight: bold;">union</span> type<span style="color: yellow; font-weight: bold;">,</span> including in a member</div><div>object<span style="color: yellow; font-weight: bold;">,</span> the bytes of the object representation that correspond to any padding bytes</div><div>take unspecified values.</div></div></pre><p>Ecco, il problema principale della <strong>memcmp(3)</strong> (a parte le vulnerabilità corrette dalla <strong>glibc</strong>) è questo, e si chiama <strong>"Structure Padding"</strong>. È noto (o almeno dovrebbe esserlo) che una <strong>C</strong> <em>struct</em> viene, per <em>default,</em> allineata dal compilatore in maniera che il <em>size</em> della struttura sia un multiplo di 4. È evidente, quindi, che una <em>struct,</em> come la vediamo noi, può essere abbastanza diversa da come la rappresenta il compilatore. E quindi? Quindi, bisogna usare la <strong>memcmp(3)</strong> con molta cautela quando si comparano i contenuti di strutture, i bug sono in agguato e possono provocare comportamenti molto inaspettati. Vediamo un semplicissimo esempio:</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">string.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdint.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// una struttura di esempio</span></div><div><span style="color: yellow; font-weight: bold;">typedef</span> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">uint8_t</span> achar<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">uint32_t</span> anint<span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span> <span style="color: yellow; font-weight: bold;">Test</span><span style="color: yellow; font-weight: bold;">;</span></div><br /><div><span style="color: silver; font-style: italic;">// testmemcmp - funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// definisco e inizializzo test1 e test2</span></div><div> <span style="color: yellow; font-weight: bold;">Test</span> test1<span style="color: yellow; font-weight: bold;">;</span></div><div> test1<span style="color: yellow; font-weight: bold;">.</span>achar <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">11</span><span style="color: yellow; font-weight: bold;">;</span></div><div> test1<span style="color: yellow; font-weight: bold;">.</span>anint <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">2222222222</span><span style="color: yellow; font-weight: bold;">;</span></div><br /><div> <span style="color: yellow; font-weight: bold;">Test</span> test2<span style="color: yellow; font-weight: bold;">;</span></div><div> test2<span style="color: yellow; font-weight: bold;">.</span>achar <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">11</span><span style="color: yellow; font-weight: bold;">;</span></div><div> test2<span style="color: yellow; font-weight: bold;">.</span>anint <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">2222222222</span><span style="color: yellow; font-weight: bold;">;</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// comparo test1 e test2 con memcmp(3)</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>memcmp<span style="color: yellow; font-weight: bold;">(&</span>test1<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>test2<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">Test</span><span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: test1 e test2 sono uguali\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">]);</span></div><div> <span style="color: yellow; font-weight: bold;">else</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: test1 e test2 sono diverse\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">]);</span></div><br /><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Ecco, questo esempio, una volta compilato ed eseguito produce questo risultato:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>aldo@Linux $ gcc testmemcmp.c <span style="color: yellow; font-weight: bold;">-</span>o testmemcmp</div><div>aldo@Linux $ .<span style="color: yellow; font-weight: bold;">/</span>testmemcmp</div><div>.<span style="color: yellow; font-weight: bold;">/</span>testmemcmp: test1 e test2 sono diverse</div><div>aldo@Linux $</div></div></pre><p>Ma come? Le due strutture <em>test1</em> e <em>test2</em> hanno un contenuto diverso? Ma se le ho inizializzate nella stessa maniera! Ma che cavolo dice la <strong>memcmp(3)</strong>?... Ebbene si! Sono diverse, perché io ho inizializzato solo i campi che visibili (<em>achar</em> e <em>anint</em>) ma non ho inizializzato i campi invisibili, e cioè i <em>byte</em> di <em>padding</em> aggiunti dal compilatore! Infatti, se aggiungete una <a href="https://man7.org/linux/man-pages/man3/printf.3.html" rel="noopener" target="_blank"><strong>printf(3)</strong></a> che vi stampi i <em>sizeof</em> di <em>test1</em> e <em>test2</em> scoprirete che il <em>size</em> è 8, anche se, a prima vista, dovrebbe essere 5 (un <em>char</em> da 1 <em>byte</em> + un <em>int</em> da 4 <em>byte):</em> i 3 <em>byte</em> in più sono proprio i <em>byte</em> di <em>padding</em> aggiunti dal compilatore per allineare il membro <em>achar,</em> che ora occupa 4 <em>byte. Maledetto compilatore, mi hai fregato un'altra volta!</em></p><p>Il riassunto di tutto questo potrebbe essere: "L<em>a <strong>memcmp(3)</strong> è una ottima funzione per comparare memoria, ma non usatela per comparare strutture</em>!". Ma questo è un po' semplicistico, <em>eddài, sicuramente si può trovare una soluzione!</em>. Ok, e allora vediamo alcune possibili soluzioni:</p><h3 style="text-align: left;">1) fare un reset della memoria della struct prima di usarla</h3><p>Avete mai sentito parlare della <a href="https://man7.org/linux/man-pages/man3/memset.3.html" rel="noopener" target="_blank"><strong>memset(3)</strong></a>? Immagino di si. Può tornare utile per far funzionare l'esempio mostrato sopra. Vediamo come:</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">string.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdint.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// una struttura di esempio</span></div><div><span style="color: yellow; font-weight: bold;">typedef</span> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">uint8_t</span> achar<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">uint32_t</span> anint<span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span> <span style="color: yellow; font-weight: bold;">Test</span><span style="color: yellow; font-weight: bold;">;</span></div><br /><div><span style="color: silver; font-style: italic;">// testmemcmp - funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// definisco e inizializzo test1 e test2</span></div><div> <span style="color: yellow; font-weight: bold;">Test</span> test1<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // oppure, senza memset(3): Test test1 = {0};</span></div><div> memset<span style="color: yellow; font-weight: bold;">(&</span>test1<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">Test</span><span style="color: yellow; font-weight: bold;">));</span></div><div> test1<span style="color: yellow; font-weight: bold;">.</span>achar <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">11</span><span style="color: yellow; font-weight: bold;">;</span></div><div> test1<span style="color: yellow; font-weight: bold;">.</span>anint <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">2222222222</span><span style="color: yellow; font-weight: bold;">;</span></div><br /><div> <span style="color: yellow; font-weight: bold;">Test</span> test2<span style="color: yellow; font-weight: bold;">;</span></div><div> memset<span style="color: yellow; font-weight: bold;">(&</span>test2<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">Test</span><span style="color: yellow; font-weight: bold;">));</span></div><div> test2<span style="color: yellow; font-weight: bold;">.</span>achar <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">11</span><span style="color: yellow; font-weight: bold;">;</span></div><div> test2<span style="color: yellow; font-weight: bold;">.</span>anint <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">2222222222</span><span style="color: yellow; font-weight: bold;">;</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// comparo test1 e test2 con memcmp(3)</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>memcmp<span style="color: yellow; font-weight: bold;">(&</span>test1<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>test2<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">Test</span><span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: test1 e test2 sono uguali\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">]);</span></div><div> <span style="color: yellow; font-weight: bold;">else</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: test1 e test2 sono diverse\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">]);</span></div><br /><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Ecco, questo esempio dà il seguente risultato:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>aldo@Linux $ gcc testmemcmp.c <span style="color: yellow; font-weight: bold;">-</span>o testmemcmp</div><div>aldo@Linux $ .<span style="color: yellow; font-weight: bold;">/</span>testmemcmp</div><div>.<span style="color: yellow; font-weight: bold;">/</span>testmemcmp: test1 e test2 sono uguali</div><div>aldo@Linux $</div></div></pre><p>il trucco usato è abbastanza evidente: con la <strong>memset(3)</strong> azzeriamo anche i <em>byte</em> di <em>padding,</em> quindi la successiva chiamata a <strong>memcmp(3)</strong> funziona bene, visto che ora i campi nascosti sono uguali nelle due strutture. Il prezzo da pagare è, però, relativamente alto, perché bisogna ricordarsi di usare la <strong>memset(3)</strong> ogni volta che si definisce (o si alloca) una nuova <em>struct</em>. da comparare.</p><p>Notare che, come indicato nel commento del codice qui sopra, si può azzerare la <em>struct</em> anche senza usare <strong>memset(3)</strong> inizializzandola nella definizione (questo però non vale se definiamo un <em>pointer</em> da usare successivamente con <a href="https://man7.org/linux/man-pages/man3/malloc.3.html" rel="noopener" target="_blank"><strong>malloc(3)</strong></a>). E non fatevi ingannare da falsi risultati provocati dalla fortuna: può succedere che, casualmente, i <em>byte</em> di <em>padding</em> abbiano lo stesso valore anche senza eseguire la <strong>memset(3)</strong>, sia dopo una definizione sia dopo una allocazione (e <strong>malloc(3)</strong> non inizializza la memoria!), <em>quindi oggi il codice funziona, e magari domani no... occhio a questi dettagli!</em></p><h3 style="text-align: left;">2) istruire il compilatore su come usare il padding</h3><p>Questo si può fare modificando minimamente il codice, dicendo al compilatore di non fare il <em>padding</em> per quella particolare <em>struct.</em> Si fa così (riporto solo il dettaglio del codice modificato rispetto al primo esempio di <em>testmemcmp.c):</em></p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// una struttura di esempio "packed"</span></div><div><span style="color: yellow; font-weight: bold;">typedef</span> <span style="color: yellow; font-weight: bold;">struct</span> __attribute__<span style="color: yellow; font-weight: bold;">((</span>__packed__<span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">uint8_t</span> achar<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">uint32_t</span> anint<span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span> <span style="color: yellow; font-weight: bold;">Test</span><span style="color: yellow; font-weight: bold;">;</span></div></div></pre><p>questo funziona, ma ha alcune controindicazioni: la prima è che il <em>padding</em> serve a gestire più efficientemente la memoria e quindi ometterlo potrebbe peggiorare le prestazioni. E poi bisogna ricordasi di farlo sempre (stesso problema del punto 1). L'ultima controindicazione è la scarsa portabilità di questo trucco, che dipende dal compilatore (<em>__packed__</em> è un attributo del solo <a href="https://gcc.gnu.org/" rel="noopener" target="_blank"><strong>GCC</strong></a>) e dalla CPU (potrebbe non funzionare su CPU diverse da <strong>x86</strong> o <strong>amd64</strong>). Tra l'altro in rete si trovano esempi di casi particolari di malfunzionamento e, <em>dulcis in fundo</em>, bisogna tener presente che <strong>GCC,</strong> anticamente, trattava questo caso come codice pericoloso segnalando dei <em>Warning</em> preoccupanti (adesso non più, probabilmente hanno trovato la maniera di renderlo meno pericoloso). In ogni caso vi ricordo che scrivere codice non portabile non è mai una buona idea.</p><h3 style="text-align: left;">3) comparare le struct senza usare memcmp(3) (spoiler: questa è la mia opzione preferita).</h3><p>Come anticipato dallo <em>spoiler,</em> questa è la mia opzione preferita, anche se non è a costo zero, visto che bisogna scrivere un po' di codice in più (ma neanche tanto). Vi avverto: è una soluzione un po' lapalissiana ed è molto semplice: bisogna scrivere una versione <em>custom</em> della <strong>memcmp(3)</strong> per ogni tipo di struttura da comparare, e usarla sempre al posto della versione generica. Senza ulteriori giri di parole, facciamo cantare un'altra volta il codice!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdint.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdbool.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// una struttura di esempio</span></div><div><span style="color: yellow; font-weight: bold;">typedef</span> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">uint8_t</span> achar<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">uint32_t</span> anint<span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span> <span style="color: yellow; font-weight: bold;">Test</span><span style="color: yellow; font-weight: bold;">;</span></div><br /><div><span style="color: silver; font-style: italic;">// prototipi locali</span></div><div><span style="color: white; font-weight: bold;">bool</span> myMemcmp<span style="color: yellow; font-weight: bold;">(const</span> <span style="color: yellow; font-weight: bold;">Test</span> <span style="color: yellow; font-weight: bold;">*</span>s1<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">const</span> <span style="color: yellow; font-weight: bold;">Test</span> <span style="color: yellow; font-weight: bold;">*</span>s2<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: silver; font-style: italic;">// testmemcmp - funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// definisco e inizializzo test1 e test2</span></div><div> <span style="color: yellow; font-weight: bold;">Test</span> test1<span style="color: yellow; font-weight: bold;">;</span></div><div> test1<span style="color: yellow; font-weight: bold;">.</span>achar <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">11</span><span style="color: yellow; font-weight: bold;">;</span></div><div> test1<span style="color: yellow; font-weight: bold;">.</span>anint <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">2222222222</span><span style="color: yellow; font-weight: bold;">;</span></div><br /><div> <span style="color: yellow; font-weight: bold;">Test</span> test2<span style="color: yellow; font-weight: bold;">;</span></div><div> test2<span style="color: yellow; font-weight: bold;">.</span>achar <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">11</span><span style="color: yellow; font-weight: bold;">;</span></div><div> test2<span style="color: yellow; font-weight: bold;">.</span>anint <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">2222222222</span><span style="color: yellow; font-weight: bold;">;</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// comparo test1 e test2 con myMemcmp()</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(!</span>myMemcmp<span style="color: yellow; font-weight: bold;">(&</span>test1<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>test2<span style="color: yellow; font-weight: bold;">))</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: test1 e test2 sono uguali\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">]);</span></div><div> <span style="color: yellow; font-weight: bold;">else</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: test1 e test2 sono diverse\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">]);</span></div><br /><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// myMemcmp - versione specializzata di memcmp(3) per il tipo Test</span></div><div><span style="color: white; font-weight: bold;">bool</span> myMemcmp<span style="color: yellow; font-weight: bold;">(const</span> <span style="color: yellow; font-weight: bold;">Test</span> <span style="color: yellow; font-weight: bold;">*</span>s1<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">const</span> <span style="color: yellow; font-weight: bold;">Test</span> <span style="color: yellow; font-weight: bold;">*</span>s2<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>s1 <span style="color: yellow; font-weight: bold;">&&</span> s2 <span style="color: yellow; font-weight: bold;">&&</span> <span style="color: yellow; font-weight: bold;">(</span>s1<span style="color: yellow; font-weight: bold;">-></span>achar <span style="color: yellow; font-weight: bold;">==</span> s2<span style="color: yellow; font-weight: bold;">-></span>achar<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">&&</span> <span style="color: yellow; font-weight: bold;">(</span>s1<span style="color: yellow; font-weight: bold;">-></span>anint <span style="color: yellow; font-weight: bold;">==</span> s2<span style="color: yellow; font-weight: bold;">-></span>anint<span style="color: yellow; font-weight: bold;">))</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">false</span><span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // sono uguali: la memcmp(3) ritornerebbe 0</span></div><div> <span style="color: yellow; font-weight: bold;">else</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">true</span><span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // sono diverse: la memcmp(3) ritornerebbe !0</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Che ne dite? La versione specializzata della <strong>memcmp(3)</strong> confronta <em>campo-a-campo</em> invece che <em>byte-a-byte</em> (<em>e grazie al... che funziona: per questo è lapalissiana</em>) è il risultato è sempre sicuro. È un po' più laborioso, perché per ogni tipo di <em>struct</em> bisogna scrivere la versione specializzata, però è a prova di errore, e ne vale la pena. In più c'è il vantaggio di poter ristornare direttamente un <em>bool,</em> il che è molto comodo. Io normalmente seguo questa via.</p><p>Direi che per oggi può bastare. Ho introdotto questo nuovo tipo di articolo <em>"Scusate"</em>, che è meno drastico del <em>"No Grazie!"</em>, visto che la <strong>memcmp(3)</strong> ha alcuni difetti a livello di uso, ma, nel complesso, è una funzione buona e utile, basta usarla come e quando si deve. Non so esattamente di cosa parlerò nel prossimo articolo, ma sarà di sicuro interessante, ve lo prometto! <em>E, come sempre, non trattenete il respiro nell'attesa!</em></p><p>Ciao, e al prossimo post!</p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-58589272782779795712023-10-11T19:56:00.000+02:002023-10-11T19:56:38.794+02:00Ricomincio da DEB come creare un Debian Package - pt.2<blockquote><b>Robertino</b><b>:</b> <i>Ma mammina dice che io ho i complessi nella testa.</i><br /><b>Gaetano</b><b>:</b> <i>E foss' 'o Ddio! Quali complessi? Tu tieni l'orchestra intera 'ncapa, Robbe'.</i><br /></blockquote><p>E allora, dove eravamo rimasti? Ah, si! Abbiamo <a href="https://letterboxd.com/aldoz/film/im-starting-from-three/" rel="noopener" target="_blank"><strong>ricominciato da tre</strong></a> (<em>anzi, da DEB</em>), con <a href="https://artcprogramming.blogspot.com/2023/09/ricomincio-da-deb-come-creare-un-debian.html" rel="noopener" target="_blank"><strong>un articolo</strong></a> che invece di descrivere <em>"come sviluppare"</em> (nel nostro amato <strong>C</strong>) una applicazione, descriveva <em>"come distribuire"</em> la stessa applicazione (nel nostro amato <strong>Linux</strong>). E noi non faremo come il <strong>Robertino</strong> qui sopra, che aveva un po' di confusione in testa su quale via intraprendere, ma faremo come gli consigliava l'impagabile <a href="https://letterboxd.com/director/massimo-troisi/" rel="noopener" target="_blank"><strong>Gaetano</strong></a>, e prenderemo una via molto creativa, scriveremo uno shell script per creare automaticamente un <b>Debian Package</b>!</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgE_jcAnIYXe4Q7wGkUtcp8MYUL9S7CyTXtPsATIx9EFrGGpdrm4xwcFrzJFiXnzhPsrSVIfVens3Ubga1sht4g601LpbS75FsNQfdTpIwibWAbPIslO0twtr-fauYcRjYOcrXUCjyXtqMhYNqds8v3NlC4jotJO9SrQCu1DA18W3eRHgWiHqQXBKm2Tdj7/s1268/ricomincio-da-deb2.jpeg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="945" data-original-width="1268" height="437" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgE_jcAnIYXe4Q7wGkUtcp8MYUL9S7CyTXtPsATIx9EFrGGpdrm4xwcFrzJFiXnzhPsrSVIfVens3Ubga1sht4g601LpbS75FsNQfdTpIwibWAbPIslO0twtr-fauYcRjYOcrXUCjyXtqMhYNqds8v3NlC4jotJO9SrQCu1DA18W3eRHgWiHqQXBKm2Tdj7/w587-h437/ricomincio-da-deb2.jpeg" width="587" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...ma possibile che non hai mai scritto un installer?...</td></tr></tbody></table><p><a href="https://artcprogramming.blogspot.com/2023/09/ricomincio-da-deb-come-creare-un-debian.html" rel="noopener" target="_blank"><strong>La prima parte dell'articolo</strong></a> l'avevamo conclusa su questa lista di cose da fare (<em>con il grado di difficoltà incluso!</em>):</p><ul><li>costruire l'albero di installazione (<em>facile!</em>)</li><li>copiarci dentro eseguibili e servizi (<em>facilissimo!</em>)</li><li>scrivere il file control (<em>si può fare...</em>)</li><li>scrivere gli eventuali script di pre/post installazione (<em>spoiler: in questo semplice esempio ci serve solo lo script postinst</em>)</li></ul><p>E avevo anticipato che nella seconda parte (<em>e cioè qui</em>) avremmo cercato di fare il tutto con lo <em>spirito del programmatore</em>, e cioè avremmo presentato uno <em>shell script</em> che esegue i punti precedenti in modo automatico, e che diventerà la base (con piccoli adattamenti) per altri <em>script</em> di creazione per qualsiasi applicazione che svilupperemo... un compito troppo ambizioso? In realtà è molto semplice!.</p><p>Ok, andiamo per passi: vediamo di descrivere i punti della lista e poi, alla fine, automatizzeremo il tutto. Cominciamo con i primi 2 punti (condensati in 1 solo)!</p><h3 style="text-align: left;">1) costruire l'albero di installazione + copiarci dentro eseguibili e servizi</h3><p>Nel nostro caso l'albero di installazione avrà questa forma:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>├── DEBIAN</div><div>│ ├── control</div><div>│ └── postinst</div><div>├── etc</div><div>│ └── systemd</div><div>│ └── system</div><div>│ <span style="color: yellow; font-weight: bold;">|</span>── cansetup.service</div><div>│ └── canrecv.service</div><div>└── usr</div><div> └── bin</div><div> <span style="color: yellow; font-weight: bold;">|</span>── cansetup.sh</div><div> └── canrecv</div></div></pre><p>per cui, una volta creato (da qualche parte nella nostra directory di lavoro) un albero della forma descritta, sarà un gioco da ragazzi copiarci dentro i file presentati nella prima parte dell'articolo (a parte i <em>file control</em> e <em>postinst</em> che non abbiamo ancora visto). Notare che questo primo punto lo faremo, automaticamente, attraverso il nostro <em>shell script</em> di creazione del package, quello che vedremo tra poco.</p><h3 style="text-align: left;">2) scrivere il file control</h3><p>Il <em>file control</em> ha, come visto nel precedente articolo, una struttura minima abbastanza semplice. Una struttura che si può complicare, a piacere, aggiungendo altre linee di dettagli che, in alcuni casi, possono anche essere utili, ma direi che per la nostra applicazione <em>canrecv</em> (che è un semplice <em>CAN Server</em>) è sufficiente un <em>file control</em> come questo:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>Package: canrecv</div><div>Version: <span style="color: white; font-weight: bold;">1.0</span><span style="color: yellow; font-weight: bold;">-</span>amd64</div><div>Architecture: amd64</div><div>Depends: libc6 <span style="color: yellow; font-weight: bold;">(>=</span> <span style="color: white; font-weight: bold;">2.34</span><span style="color: yellow; font-weight: bold;">)</span></div><div>Maintainer: A.Abate <span style="color: yellow; font-weight: bold;"><</span>artcprogramming@gmail.com<span style="color: yellow; font-weight: bold;">></span></div><div>Description: canrecv DEB package</div><div> installa la applicazione canrecv e i relativi servizi systemd</div></div></pre><p>dove si possono cambiare, se necessario, la versione <em>Version</em> (<em>N.B.: deve cominciare sempre con un numero</em>) e l'architettura <em>Architecture</em> (nel''esempio ho messo quella su cui lavoro io). Le dipendenze <em>Depends</em> sono quelle ottenute con il procedimento descritto <a href="https://artcprogramming.blogspot.com/2023/09/ricomincio-da-deb-come-creare-un-debian.html" rel="noopener" target="_blank"><strong>nello scorso articolo</strong></a>, il responsabile <em>Mantainer</em> sarà l'autore dell'<em>installer</em> stesso e la descrizione corta <em>Description</em> sarà quella adatta all'<em>installer</em> in oggetto, seguita nella linea successiva dalla descrizione lunga, che può essere anche multi-linea, con linee che cominciano sempre con uno spazio. Facile, no?</p><h3 style="text-align: left;">3) scrivere gli eventuali script di pre/post installazione.</h3><p>Per il nostro <em>package</em> è necessario solo uno <em>script</em> di post-installazione, uno <em>script</em> che installa e attiva i nuovi servizi. E vediamolo!</p><pre class="EnlighterJSRAW" data-enlighter-language="bash"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">#!/bin/bash</span></div><br /><div><span style="color: silver; font-style: italic;"># set uscita immediata su errore</span></div><div>set <span style="color: white; font-weight: bold;">-e</span></div><br /><div><span style="color: silver; font-style: italic;"># abilita e avvia il servizio cansetup</span></div><div>systemctl <span style="color: white; font-weight: bold;">enable</span> <span style="color: white; font-weight: bold;">cansetup.service</span></div><div>systemctl <span style="color: white; font-weight: bold;">start</span> <span style="color: white; font-weight: bold;">cansetup.service</span></div><div>systemctl <span style="color: white; font-weight: bold;">daemon-reload</span></div><br /><div><span style="color: silver; font-style: italic;"># abilita e avvia il servizio canrecv</span></div><div>systemctl <span style="color: white; font-weight: bold;">enable</span> <span style="color: white; font-weight: bold;">canrecv.service</span></div><div>systemctl <span style="color: white; font-weight: bold;">start</span> <span style="color: white; font-weight: bold;">canrecv.service</span></div><div>systemctl <span style="color: white; font-weight: bold;">daemon-reload</span></div></div></pre><p>Questo <em>script</em> è necessario, in questo caso, perché il nostro <em>package</em> contiene dei servizi <em>systemd,</em> che devono essere opportunamente installati nel sistema. Il procedimento consiste nell'abilitazione del servizio, seguita dal suo avvio e dal riavvio del <em>daemon</em> di <em>systemd</em> (tutto questo ripetuto per i due servizi inclusi nel <em>package).</em> Ovviamente per altri tipi di applicazione uno script di <em>postinst</em> potrebbe non essere necessario o fare ben altre cose. Comunque i tipi di <em>script</em> disponibili li trovate elencati nella <a href="https://artcprogramming.blogspot.com/2023/09/ricomincio-da-deb-come-creare-un-debian.html" rel="noopener" target="_blank"><strong>prima parte dell'articolo</strong></a>.</p><p>A questo punto abbiamo anche i <em>file</em> che ci mancavano da aggiungere al nostro albero creato qui sopra, quindi cosa ci manca? Ma ci manca il nostro (<em>oramai mitico</em>) <em>shell script</em> per automatizzare il tutto! Vediamolo!</p><pre class="EnlighterJSRAW" data-enlighter-language="bash"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">#!/bin/bash</span></div><br /><div><span style="color: silver; font-style: italic;"># check degli argomenti</span></div><div><span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">[</span> <span style="color: yellow; font-weight: bold;">$#</span> <span style="color: yellow; font-weight: bold;">-eq</span> <span style="color: white; font-weight: bold;">0</span> <span style="color: yellow; font-weight: bold;">];</span> <span style="color: yellow; font-weight: bold;">then</span></div><div> echo <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">numero argomenti errato</span><span style="color: yellow; font-weight: bold;">"</span></div><div> echo <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">uso: </span>$0<span style="color: white; font-weight: bold;"> nome_release (e.g.: </span>$0<span style="color: white; font-weight: bold;"> 1.0-amd64)</span><span style="color: yellow; font-weight: bold;">"</span></div><div> exit <span style="color: white; font-weight: bold;">1</span></div><div><span style="color: yellow; font-weight: bold;">fi</span></div><br /><div><span style="color: silver; font-style: italic;"># set della directory di lavoro</span></div><div>mydir<span style="color: yellow; font-weight: bold;">=</span><span style="color: white; font-weight: bold;">canrecv-</span>$1</div><br /><div><span style="color: silver; font-style: italic;"># compilo l'ultima versione di canrecv.c</span></div><div>cd <span style="color: white; font-weight: bold;">..</span></div><div>gcc <span style="color: white; font-weight: bold;">canrecv.c</span> <span style="color: white; font-weight: bold;">-o</span> <span style="color: white; font-weight: bold;">install/base/canrecv</span></div><div>cd <span style="color: white; font-weight: bold;">install</span></div><br /><div><span style="color: silver; font-style: italic;"># creo l'albero di directory destinazione</span></div><div>mkdir $mydir</div><div>mkdir $mydir<span style="color: white; font-weight: bold;">/DEBIAN</span></div><div>mkdir $mydir<span style="color: white; font-weight: bold;">/usr</span></div><div>mkdir $mydir<span style="color: white; font-weight: bold;">/usr/local</span></div><div>mkdir $mydir<span style="color: white; font-weight: bold;">/usr/local/bin</span></div><div>mkdir $mydir<span style="color: white; font-weight: bold;">/etc</span></div><div>mkdir $mydir<span style="color: white; font-weight: bold;">/etc/systemd</span></div><div>mkdir $mydir<span style="color: white; font-weight: bold;">/etc/systemd/system</span></div><br /><div><span style="color: silver; font-style: italic;"># copio i file nella directory destinazione</span></div><div>cp <span style="color: white; font-weight: bold;">base/control</span> $mydir<span style="color: white; font-weight: bold;">/DEBIAN</span></div><div>cp <span style="color: white; font-weight: bold;">base/postinst</span> $mydir<span style="color: white; font-weight: bold;">/DEBIAN</span></div><div>cp <span style="color: white; font-weight: bold;">base/cansetup.sh</span> $mydir<span style="color: white; font-weight: bold;">/usr/local/bin</span></div><div>cp <span style="color: white; font-weight: bold;">base/canrecv</span> $mydir<span style="color: white; font-weight: bold;">/usr/local/bin</span></div><div>cp <span style="color: white; font-weight: bold;">base/cansetup.service</span> $mydir<span style="color: white; font-weight: bold;">/etc/systemd/system</span></div><div>cp <span style="color: white; font-weight: bold;">base/canrecv.service</span> $mydir<span style="color: white; font-weight: bold;">/etc/systemd/system</span></div><br /><div><span style="color: silver; font-style: italic;"># costruisco il DEB package</span></div><div>dpkg-deb <span style="color: white; font-weight: bold;">--build</span> <span style="color: white; font-weight: bold;">--root-owner-group</span> $mydir<span style="color: white; font-weight: bold;">/</span></div></div></pre><p>Avrete notato che è uno script molto compatto che esegue solo le operazioni indispensabili (<a href="https://artcprogramming.blogspot.com/2012/08/no-comment.html" rel="noopener" target="_blank"><strong>opportunamente commentate</strong></a>):</p><ul><li>check degli argomenti</li><li>set della directory di lavoro</li><li>compilo l'ultima versione di <em>canrecv.c </em>(<em>nel package deve entrare sempre l'ultima versione, no?</em>)</li><li>creo l'albero di directory destinazione (<em>usando delle semplici istruzioni mkdir</em>)</li><li>copio i file nella directory destinazione (<em>usando delle semplici istruzioni cp</em>)</li><li>costruisco il <strong>DEB package</strong> (<em>usando il classico comando <strong>Linux</strong> dpkg-deb</em>)</li></ul><p>Semplice, no? E come si può usare lo <em>script</em>? Io suggerisco suggerisco di creare un ambiente di creazione di questo tipo:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>├── base</div><div>│ ├── canrecv</div><div>│ ├── canrecv.service</div><div>│ ├── cansetup.service</div><div>│ ├── cansetup.sh</div><div>│ ├── control</div><div>│ └── postinst</div><div>└── buildpkg.sh</div></div></pre><p>con una directory <em>"base"</em> dove risiede la base di installazione (che è, anche, la destinazione della istruzione di compilazione presente nello <em>script,</em> non so se avete notato). Entrando in questo ambiente di creazione ed eseguendo il comando:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>aldo@Linux $ .<span style="color: yellow; font-weight: bold;">/</span>buildpkg.sh <span style="color: white; font-weight: bold;">1.0</span><span style="color: yellow; font-weight: bold;">-</span>amd64</div><div>dpkg<span style="color: yellow; font-weight: bold;">-</span>deb: generazione del pacchetto <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">canrecv</span><span style="color: yellow; font-weight: bold;">"</span> in <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">canrecv-1.0-amd64.deb</span><span style="color: yellow; font-weight: bold;">"</span>.</div><div>aldo@Linux $</div></div></pre><p>otterremo che il nostro albero di creazione si espanderà così:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>├── base</div><div>│ ├── canrecv</div><div>│ ├── canrecv.service</div><div>│ ├── cansetup.service</div><div>│ ├── cansetup.sh</div><div>│ ├── control</div><div>│ └── postinst</div><div>├── buildpkg.sh</div><div>├── canrecv<span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1.0</span><span style="color: yellow; font-weight: bold;">-</span>amd64</div><div>│ ├── DEBIAN</div><div>│ │ ├── control</div><div>│ │ └── postinst</div><div>│ ├── etc</div><div>│ │ └── systemd</div><div>│ │ └── system</div><div>│ │ ├── canrecv.service</div><div>│ │ └── cansetup.service</div><div>│ └── usr</div><div>│ └── local</div><div>│ └── bin</div><div>│ ├── canrecv</div><div>│ └── cansetup.sh</div><div>└── canrecv<span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1.0</span><span style="color: yellow; font-weight: bold;">-</span>amd64.deb</div></div></pre><p>ovvero, troveremo il nostro <strong>Debian Package</strong> in forma espansa (la <em>directory</em> <strong>canrecv-1.0-amd64</strong>) e compressa (il <em>file</em> <strong>canrecv-1.0-amd64.deb</strong>), e quest'ultima ci permetterà di distribuire il nostro installer a chi lo vorrà!</p><p>E qui direi che l'articolo si può dire concluso. Abbiamo <strong>ricominciato da tre</strong> (anzi, <strong>da DEB</strong>), e per una volta abbiamo parlato di <em>distribuzione</em> invece che di <em>programmazione.</em> Quasi quasi nel prossimo articolo, per cavalcare l'onda, potrei parlare si una argomento toccato di striscio <a href="https://artcprogramming.blogspot.com/2023/09/ricomincio-da-deb-come-creare-un-debian.html" rel="noopener" target="_blank"><strong>nella prima parte</strong></a>, e cioè la compilazione abbinata alla distribuzione (<strong>Autotool</strong>). Beh, vedremo, e in ogni caso sarà una sorpresa! (<em>spero lieta...</em>).</p><p>Ciao, e al prossimo post!</p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-82971734519868134642023-09-25T19:00:00.003+02:002023-10-10T12:01:17.001+02:00Ricomincio da DEB come creare un Debian Package - pt.1<blockquote><b>Gaetano:</b> <i>Chello ch'è stato è stato, basta! Ricomincio da tre!</i><br /><b>Lello</b><b>:</b> <i>Da zero.</i><br /><b>Gaetano</b><b>:</b> <i>Eh?</i><br /><b>Lello</b><b>:</b> <i>Da zero! Ricominci da zero!</i><br /><b>Gaetano</b><b>: </b><i>Nossignore, ricomincio da... cioè, tre cose me so' riuscite ind'a vita, pecchè aggià perdere pure cheste?! Aggià ricominciare da zero? Da tre!</i><br /></blockquote><p>Dopo il tormentone del CAN bus (<a href="https://artcprogramming.blogspot.com/2023/05/guardians-of-can-bus-come-usare-il-can.html" rel="noopener" target="_blank"><strong>un articolo</strong></a> in ben 4 parti!) ho deciso di ricominciare da zero, anzi... <a href="https://letterboxd.com/aldoz/film/im-starting-from-three/" rel="noopener" target="_blank"><strong>Ricomincio da tre</strong></a>, come il grande <a href="https://letterboxd.com/director/massimo-troisi/" rel="noopener" target="_blank"><strong>Massimo Troisi</strong></a> nella sua opera prima capolavoro. Ricominciare in che senso? Nel senso che, almeno per questa volta, invece di parlare della mia (nostra) amata <strong>programmazione in C</strong> parleremo di un argomento collegato e un po' trascurato, che corrisponde alla domanda: <em>"Ma dopo aver sviluppato una applicazione Linux come la distribuisco?"</em>. Ecco, ci sono vari metodi, ma oggi ho voglia di parlare del più elementare e facile da usare, e cioè il <em>"pacchetto di distribuzione"</em> e, in particolare, mi soffermerò sul tipo più usato, il <strong>Debian Package</strong> (<em>DEB Package</em> o <em>Pacchetto Debian</em> per gli amici) che si usa nelle distribuzioni <strong>Linux</strong> più diffuse (<em>Ubuntu</em>, <em>Mint</em>, <em>Debian</em>, etc.).</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpJURxY0PN3l5Zievb0UgsYeXedziF6GKYlprb7ZcLP0Ki6T6TBvEZMFRE8-dPZvGTilRSZS80gPmqyzS4VrMKDzu8M70ahvUzFj-yHU_xbVCWrNcxzUZeJAt4AdqF4i4_ZcrkuDOsrh0qH19o8WkmgiBxsSxAq-oJtP2iRCfjKMdQ8HMpP5fYwN8nCLfl/s1270/ricomincio-da-deb.jpeg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="947" data-original-width="1270" height="436" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpJURxY0PN3l5Zievb0UgsYeXedziF6GKYlprb7ZcLP0Ki6T6TBvEZMFRE8-dPZvGTilRSZS80gPmqyzS4VrMKDzu8M70ahvUzFj-yHU_xbVCWrNcxzUZeJAt4AdqF4i4_ZcrkuDOsrh0qH19o8WkmgiBxsSxAq-oJtP2iRCfjKMdQ8HMpP5fYwN8nCLfl/w583-h436/ricomincio-da-deb.jpeg" width="583" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...ma perché devo ricominciare da zero? Io ricomincio da DEB!...</td></tr></tbody></table><p>Come detto sopra, ci sono vari metodi per distribuire una applicazione <strong>Linux</strong> e, prima di parlare del <strong>Debian Package</strong>, bisogna fare alcune precisazioni: indubbiamente, il metodo più rigoroso di distribuzione è basato su <strong><a href="https://www.gnu.org/software/automake/manual/html_node/Autotools-Introduction.html" rel="noopener" target="_blank">autotool</a>:</strong> si distribuisce tutto l'ambiente di sviluppo (sorgenti, <em>makefile</em> e file di configurazione), e quindi si compila e installa usando il classico comando <em>"./configure && make install".</em> Questo metodo è rigoroso perché si <em>auto-adatta</em> (grazie ad <em>autotool</em>) alla macchina destinazione su cui può essere in uso una qualsiasi versione di <strong>Linux</strong>, tanto l'applicazione viene compilata e <em>linkata</em> localmente con le risorse a disposizione.</p><p>Ma usare <em>autotool</em> è un metodo molto specialistico (<em>credo sia evidente... è roba da programmatori ah ah ah</em>) mentre gli utenti <em>"normali"</em> vogliono installare e usare in quattro e quattr’otto, (<em>e se gli utenti hanno un passato Windows non ne parliamo neanche...</em>). E quindi anche nel mondo <strong>Linux</strong> si usano degli <em>installer</em> che si chiamano <em>"pacchetti di distribuzione"</em>, e che sono facilissimi da usare ma sono un po' meno flessibili di <em>autotool:</em> non si adattano automaticamente alla macchina (contengono l' applicazione precompilata) e quindi in alcuni casi potrebbero non essere installabili al primo colpo: ad esempio è possibile che la applicazione da installare usi una libreria di una versione differente di quella già presente nella macchina, e questo genera la segnalazione di un errore. In generale, però, è sempre abbastanza facile risolvere eventuali problemi, se l'<em>installer</em> è ben fatto.</p><p>Ah, dimenticavo: ovviamente un <strong>Debian Package</strong> ha un doppio uso: <strong>installare</strong> e <strong>disinstallare</strong> (e anche questa seconda attività è importante, no?). E allora, come si crea un pacchetto di questo tipo? Vediamo prima di tutto come è fatto: il <strong>Debian Package</strong> è un file di tipo <em>archivio compresso</em> che, internamente, ha la seguente struttura tipica:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>├── DEBIAN</div><div>│ ├── control</div><div>│ ├── preinst</div><div>│ ├── postinst</div><div>│ ├── prerm</div><div>│ └── postrm</div><div>├── etc</div><div>│ └── systemd</div><div>│ └── system</div><div>│ └── myservice.service</div><div>└── usr</div><div> └── bin</div><div> └── myapp</div></div></pre><p>ossia: ci sono tre directory principali (<em>DEBIAN</em>, <em>etc</em> e <em>usr</em>) che hanno il seguente uso:</p><ul><li><strong>DEBIAN:</strong> contiene i file di controllo e di pre/post installazione/disinstallazione:<ul><li><strong>control:</strong> è il file di controllo master, quello che guida l'installazione, infatti è l'unico file obbligatorio.</li><li><strong>preinst:</strong> è uno shell script preparatorio eseguito automaticamente prima dell'installazione vera e propria. È opzionale.</li><li><strong>postinst:</strong> è uno shell script di finalizzazione eseguito automaticamente dopo l'installazione. È opzionale.</li><li><strong>prerm:</strong> è uno shell script preparatorio eseguito automaticamente prima della disinstallazione vera e propria. È opzionale.</li><li><strong>postrm:</strong> è uno shell script di finalizzazione eseguito automaticamente dopo la disinstallazione. È opzionale.</li></ul></li><li><strong>lib:</strong> contiene l'albero di directory che replica l'albero dei servizi <em>systemd</em> della macchina destino: in questo <em>lib/systemd/system</em> si copieranno i servizi (file con estensione <em>.service</em>) necessari alla nostra applicazione (questa <em>directory lib</em> è opzionale, serve solo se il <em>Package</em> installa anche dei servizi <em>systemd).</em></li><li><strong>usr:</strong> contiene l'albero di directory che replica l'albero delle applicazioni della macchina destino: in questo <em>usr/bin</em> si copieranno gli eseguibili che compongono la nostra applicazione (nell'esempio sopra è una sola applicazione che si chiama <em>"myapp"</em>).</li></ul><p>E come è fatto un file <strong>control?</strong> È un file di testo che contiene alcune linee che descrivono il <em>Package</em>, e le linee che, come minimo, devono essere presenti sono:</p><ul><li><strong>Package</strong>: il nome dell'applicazione da installare.</li><li><strong>Version</strong>: la versione dell'applicazione da installare.</li><li><strong>Maintainer</strong> – il nome e l'indirizzo email del responsabile del Package.</li><li><strong>Description</strong> – una descrizione corta dell'applicazione. Di solito questa è l'ultima linea del <em>control:</em> sotto questa linea si può aggiungere una descrizione più lunga che deve, però, cominciare con uno spazio.</li></ul><p>si possono aggiungere molte altre linee con funzioni particolari, in particolare due linee che non dovrebbero mai mancare sono queste (<em>N.B.: in realtà la lista dei campi required/recommended/optional varia un po' tra un manuale e l'altro... diciamo che usando i quattro campi qui sopra più i due qui sotto non si dovrebbero avere problemi</em>):</p><ul><li><strong>Architecture</strong> – la architettura dove può correre la applicazione (i.e.: <em>all,</em> oppure <em>amd64</em> oppure <em>i386,</em> oppure <em>sparc</em>... e molte altre. <em>all</em> si usa per le installazioni compatibili con qualsiasi macchina).</li><li><strong>Depends:</strong> descrive le librerie (con le rispettive versioni) indispensabili all'installazione.</li></ul><p>La linea <em>Depends</em> è un poco particolare: ci sono vari metodi per cercare quali sono le librerie indispensabili alla nostra applicazione, e uno dei metodi più interessanti usa un meccanismo dello stesso ambiente di generazione del <em>Package,</em> però con alcune stranezze (la prima volta che l'ho usato mi è costato un po' farlo funzionare). Ma uno dei miei compiti è svelare i trucchi, no? E vediamoli! Bisogna creare un <em>mini-albero</em> secondario che somiglia a quello principale mostrato sopra, un <em>mini-albero</em> come questo:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>├── debian</div><div>│ └── control</div><div>└── usr</div><div> └── bin</div><div> └── myapp</div></div></pre><p>sempre supponendo che la applicazione si chiami <em>"myapp"</em>. Notare che <em>debian</em> è in minuscolo e che il file <em>control</em> deve contenere solo questa linea:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>source: myapp</div></div></pre><p>dopodiché, posizionandosi nella r<em>oot-directory</em> del <em>mini-albero</em> , si può eseguire il comando <em>dpkg-shlibdeps </em>ottenendo un risultato di questo tipo:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>aldo@Linux $ dpkg<span style="color: yellow; font-weight: bold;">-</span>shlibdeps <span style="color: yellow; font-weight: bold;">-</span>O usr<span style="color: yellow; font-weight: bold;">/</span>local<span style="color: yellow; font-weight: bold;">/</span>bin<span style="color: yellow; font-weight: bold;">/</span>myapp</div><div>dpkg<span style="color: yellow; font-weight: bold;">-</span>shlibdeps: Avviso: binaries to analyze should already be installed in their package<span style="color: yellow; font-weight: bold;">'</span><span style="color: white; font-weight: bold;">s directory</span></div><div><span style="color: white; font-weight: bold;">shlibs:Depends=libc6 (>= 2.34)</span></div><div><span style="color: white; font-weight: bold;">aldo@Linux $</span></div></div></pre><p>Il risultato è, quindi, la lista delle librerie indispensabili (nell'esempio sopra c'è solo: <em>libc6 dalla ver.2.34 in su)</em> che potremo aggiungere in <em>DEBIAN/control</em> nella linea <strong>Depends</strong>. Dopodiché il <em>mini-albero</em> che abbiamo usato possiamo cancellarlo (ha già esaurito il suo compito) e possiamo tornare a usare l'albero principale.</p><p>Dopo questa introduzione teorica è l'ora di passare a un caso pratico, no? Vedendo un caso reale molti dubbi vengono cancellati automaticamente (<em>beh, almeno a me succede spesso</em>). Visto che ce l'abbiamo fresco in mente e abbiamo già il codice disponibile negli ultimi articoli pubblicati, cercheremo di creare un <strong>Debian Package</strong> che installa nel sistema <em>canrecv</em>, che è un <strong>CAN Server</strong> (<em>e scusate se ritiro fuori un argomento appena visto!</em>).</p><p>Ah, fino a qui ho dato per scontato che tutti sappiano cos'è un servizio <a href="https://systemd.io/" rel="noopener" target="_blank"><strong>systemd</strong></a> (e magari ci tornerò in futuro con un articolo) comunque in questo caso è sufficiente sapere che è la maniera più <em>"moderna"</em> di assegnare compiti a <strong>Linux</strong>, compiti del tipo: <em>"al boot avvia questo", "se si interrompe riavvialo",</em> ecc. Prima si usavano altri metodi (non so se a tutti è familiare il vecchio e sempre valido <em>"init"</em> derivato da <em>"SysVinit"</em>), ma da qualche anno a questa parte si usa quasi sempre <em>systemd</em> per cui lo useremo anche noi.</p><p>E torniamo al punto: l'obiettivo è, quindi, che il nostro <strong>Debian Package</strong> installi queste cose nel sistema:</p><ul><li>un eseguibile <strong>cansetup.sh</strong></li><li>un eseguibile <strong>canrecv</strong></li><li>un servizio <em>systemd</em> <strong>cansetup.service</strong> che produce l'esecuzione del <em>CAN setup</em> al <em>boot</em> del sistema</li><li>un servizio <em>systemd</em> <strong>canrecv.service</strong> che produce l'avvio del <em>Server</em> al <em>boot </em>del sistema</li></ul><p>Il codice di <strong>canrecv</strong> (<em>canrecv.c</em>) l'ho già pubblicato e lo <a href="https://artcprogramming.blogspot.com/2023/05/guardians-of-can-bus-come-usare-il-can.html" rel="noopener" target="_blank"><strong>possiamo trovare qui</strong></a>: è un <em>Server</em> molto minimale (riceve un messaggio ed esce) però per questo semplice esempio va più che bene (<em>compito a casa: modificare canrecv.c per ottenere un CAN Server che riceva messaggi in loop</em>). Anche lo <em>shell script</em> <strong>cansetup.sh</strong> lo abbiamo visto <a href="https://artcprogramming.blogspot.com/2023/06/guardians-of-can-bus-come-usare-il-can.html" rel="noopener" target="_blank"><strong>in quest'altro articolo</strong></a>, ed è indispensabile al funzionamento del <em>Server,</em> visto che prepara il dispositivo <em>virtual CAN</em> del sistema. Ci mancano solo i due servizi: questo è <em>cansetup.service</em>:</p><pre class="EnlighterJSRAW" data-enlighter-language="generic"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">[</span>Unit<span style="color: yellow; font-weight: bold;">]</span></div><div>Description<span style="color: yellow; font-weight: bold;">=</span>setup del vcan0 device</div><div>After<span style="color: yellow; font-weight: bold;">=</span>network.target</div><br /><div><span style="color: yellow; font-weight: bold;">[</span>Service<span style="color: yellow; font-weight: bold;">]</span></div><div>ExecStart<span style="color: yellow; font-weight: bold;">=/</span>usr<span style="color: yellow; font-weight: bold;">/</span>local<span style="color: yellow; font-weight: bold;">/</span>bin<span style="color: yellow; font-weight: bold;">/</span>cansetup.sh</div><br /><div><span style="color: yellow; font-weight: bold;">[</span>Install<span style="color: yellow; font-weight: bold;">]</span></div><div>WantedBy<span style="color: yellow; font-weight: bold;">=</span>default.target</div></div></pre><div>mentre questo è <em>canrecv.service</em>:</div><pre class="EnlighterJSRAW" data-enlighter-language="raw"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">[</span>Unit<span style="color: yellow; font-weight: bold;">]</span></div><div>Description<span style="color: yellow; font-weight: bold;">=</span>avvia il processo canrecv</div><div>After<span style="color: yellow; font-weight: bold;">=</span>cansetup.service</div><br /><div><span style="color: yellow; font-weight: bold;">[</span>Service<span style="color: yellow; font-weight: bold;">]</span></div><div>ExecStart<span style="color: yellow; font-weight: bold;">=/</span>usr<span style="color: yellow; font-weight: bold;">/</span>local<span style="color: yellow; font-weight: bold;">/</span>bin<span style="color: yellow; font-weight: bold;">/</span>canrecv</div><br /><div><span style="color: yellow; font-weight: bold;">[</span>Install<span style="color: yellow; font-weight: bold;">]</span></div><div>WantedBy<span style="color: yellow; font-weight: bold;">=</span>default.target</div></div></pre><p>Come potete vedere sono abbastanza semplici (oserei dire: <em>auto-esplicativi</em>) e, comunque, spiegare come funzionano non è l'argomento di questo articolo (<em>ripeto la semi-promessa: magari ci tornerò in futuro</em>). L'importante è lo scopo di questi servizi, e quello credo che sia abbastanza chiaro. Quindi non ci resta che:</p><ul><li>costruire l'albero di installazione <em>(facile!)</em></li><li>copiarci dentro eseguibili e servizi <em>(facilissimo!)</em></li><li>scrivere il file control (<em>si può fare...</em>)</li><li>scrivere gli eventuali script di <em>pre/post</em> installazione (<em>spoiler: in questo semplice esempio ci serve solo lo script postinst</em>)</li></ul><p>E qui potrei concludere l'articolo aggiungendo le parti appena elencate... ma qui viene fuori la mia (nostra) anima di programmatore: perché fare a mano le operazioni descritte quando si possono automatizzare? <em>Non posso concludere un articolo senza aggiungere una minima parte di programmazione, no?</em> E allora, nella seconda parte che arriverà prossimamente <em>(giuro),</em> vi presenterò un utile <em>shell script</em> che ho scritto per creare e ricreare (<em>a piacere</em>) il nostro <strong>Debian Package</strong>: è facilmente adattabile ad ogni uso futuro ed è una buona base per qualsiasi tipo di applicazione che abbisogna di un <em>installer.</em> Quindi per il momento vi saluto e, come sempre, <em>vi consiglio di non trattenere il respiro nell'attesa!</em></p><p>Ciao, e al prossimo post! </p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-968526661101066032023-08-23T20:40:00.003+02:002023-08-23T20:40:43.533+02:00Guardians of the CAN bus come usare il CAN bus in C - pt.4<blockquote><b>Stakar Ogord:</b> <i>Uniformi della OrgoCorp. Dovrete indossarle per muovervi nell'Orgosfera senza attirare l'attenzione.</i><br /><b>Drax</b><b>:</b> <i>Questo non è il mio colore.</i><br /><b>Stakar Ogord:</b> <i>Cosa hai detto?</i><br /><b>Drax</b><b>:</b> <i>Stona con i miei occhi.</i><br /></blockquote>Ed eccoci alla quarta (e ultima!) parte di <strong>Guardians of the CAN bus</strong>, un ciclo di articoli ispirati al bellissimo <a href="https://letterboxd.com/aldoz/film/guardians-of-the-galaxy-vol-3/" rel="noopener" target="_blank"><strong>Guardiani della Galassia Vol.3</strong></a> (ripassatevi <strong><a href="https://artcprogramming.blogspot.com/2023/05/guardians-of-can-bus-come-usare-il-can.html" rel="noopener" target="_blank">la prima</a></strong>, <strong><a href="https://artcprogramming.blogspot.com/2023/06/guardians-of-can-bus-come-usare-il-can.html" rel="noopener" target="_blank">la seconda</a></strong> e <a href="https://artcprogramming.blogspot.com/2023/07/guardians-of-can-bus-come-usare-il-can.html" rel="noopener" target="_blank"><strong>la terza</strong></a> parte, please…). Nel dialogo qui sopra c'è, di nuovo, tutta la vena comica del film, che viaggia come al solito in parallelo alla parte seria. Mi ripeterò: è po’ come il <strong>CAN bus</strong>, che è così ben studiato ed efficace che rende divertente scrivere Software che lo usa, salvo quando devi risolvere un problema molto complicato e allora entri nella fase seria e drammatica... In questa quarta parte scopriremo gli ultimi segreti che ci mancano per diventare dei veri e propri <strong>Guardians of the CAN bus</strong>!<div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1xqVtkCYyeQhnhBlv_3GgSFNBtrVIK28oRIQjXiwtHL8tiZ_wB-V5531pPuqr7PtN6aWCmzs0amxScJZrvCcIkPqfM-cJLrqHZRebBttZ_r8A5Q4Yq0tsupGpWKsgKBNnSPHINpQA26tRZz-VMMZlTEV0YAceHAmhH134_l7MyPET8NHf3tNN-ZtDrLXc/s900/guardiansofthecanbus4.jpeg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="600" data-original-width="900" height="392" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1xqVtkCYyeQhnhBlv_3GgSFNBtrVIK28oRIQjXiwtHL8tiZ_wB-V5531pPuqr7PtN6aWCmzs0amxScJZrvCcIkPqfM-cJLrqHZRebBttZ_r8A5Q4Yq0tsupGpWKsgKBNnSPHINpQA26tRZz-VMMZlTEV0YAceHAmhH134_l7MyPET8NHf3tNN-ZtDrLXc/w588-h392/guardiansofthecanbus4.jpeg" width="588" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...ma anche il CAN bus stona coi tuoi occhi?...</td></tr></tbody></table><div><p>Come ho fatto notare nella chiusura <a href="https://artcprogramming.blogspot.com/2023/07/guardians-of-can-bus-come-usare-il-can.html" rel="noopener" target="_blank"><strong>dell'ultimo articolo</strong></a> oramai ci manca solo il <strong>CAN FD</strong> per ultimare il nostro viaggio nei segreti del CAN bus. E allora veniamo subito al dunque: cosa è il CAN FD? Il CAN FD (CAN Flexible Data Rate) è un'espansione del CAN bus "classico" (Classical CAN) ed è definito dalla norma ISO 11898-1:2015. Il CAN FD facilita non solo l'invio di volumi di dati più grandi, visto che il limite di 8 byte del CAN classico è stato aumentato a 64 byte. Inoltre, offre la possibilità di una velocità di trasmissione più elevata nella fase dei dati, raggiungendo agevolmente i 5 Mbit/s. Anche del CAN FD esiste una versione extended, quindi, alla fin fine, abbiamo quattro tipi di frame, quelli mostrati in questa figura riassuntiva:</p><p><img alt="can bus versions" class="alignnone wp-image-7016 size-full" height="216" src="https://italiancoders.it/wp-content/uploads/2023/08/canbusversions.png" width="587" /></p><p>Si può commentare che, con questa versione FD, il CAN bus si snatura un poco, passando da semplice e affidabilissimo <em>"bus di campo"</em> a un rango superiore, quasi quello dei protocolli di rete sofisticati (tipo Ethernet) che permettono di trasferire notevoli moli di dati: comunque, anche se <em>"snaturato"</em>, è assolutamente pronto all'uso e quando è il caso si può/deve usare, perché offre delle grandi prestazioni.</p><p>Ok, con ben quattro tipi disponibili sembrerebbe abbastanza complicato scrivere Software CAN <em>"compatibile"</em> (<em>o quasi</em>) passando da un tipo all'altro senza fare troppi cambi... Ma per questo c'è il nostro amico <strong>SocketCAN</strong>! Come abbiamo già visto<strong> <a href="https://artcprogramming.blogspot.com/2023/07/guardians-of-can-bus-come-usare-il-can.html" rel="noopener" target="_blank">nell'articolo precedente</a></strong>, passare da CAN classic a CAN extended è decisamente semplice, e sono felice di comunicarvi che, grazie alla flessibilità di SocketCAN, anche il CAN FD si usa in maniera molto semplificata. Vediamo come: non so se ricordate, ma nel <a href="https://artcprogramming.blogspot.com/2023/05/guardians-of-can-bus-come-usare-il-can.html" rel="noopener" target="_blank"><strong>primo articolo</strong></a> del ciclo avevo mostrato come è descritto (nell'<em>header can.h</em>) un <em>frame</em> CAN di SocketCAN, ve lo ripropongo:</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">/**</span></div><div><span style="color: silver; font-style: italic;"> * struct can_frame - basic CAN frame structure</span></div><div><span style="color: silver; font-style: italic;"> * </span><span style="color: yellow; font-weight: bold;">@can_id:</span><span style="color: silver; font-style: italic;"> the CAN ID of the frame and CAN_*_FLAG flags, see above.</span></div><div><span style="color: silver; font-style: italic;"> * </span><span style="color: yellow; font-weight: bold;">@can_dlc:</span><span style="color: silver; font-style: italic;"> the data length field of the CAN frame</span></div><div><span style="color: silver; font-style: italic;"> * </span><span style="color: yellow; font-weight: bold;">@data:</span><span style="color: silver; font-style: italic;"> the CAN frame payload.</span></div><div><span style="color: silver; font-style: italic;"> */</span></div><div><span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">can_frame</span> <span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">canid_t</span> can_id<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> /* 32 bit CAN_ID + EFF/RTR/ERR flags */</span></div><div> __u8 can_dlc<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> /* data length code: 0 .. 8 */</span></div><div> __u8 data<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">8</span><span style="color: yellow; font-weight: bold;">]</span> __attribute__<span style="color: yellow; font-weight: bold;">((</span>aligned<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">8</span><span style="color: yellow; font-weight: bold;">)));</span></div><div><span style="color: yellow; font-weight: bold;">};</span></div></div></pre><p>Ecco, questa sopra è una versione abbastanza datata (<strong>Linux 2.6</strong>) che non trattava ancora CAN FD. In <strong>Linux 3.7</strong> troviamo la prima versione "moderna" che include il CAN FD:</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">/**</span></div><div><span style="color: silver; font-style: italic;"> * struct can_frame - basic CAN frame structure</span></div><div><span style="color: silver; font-style: italic;"> * </span><span style="color: yellow; font-weight: bold;">@can_id:</span><span style="color: silver; font-style: italic;"> CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition</span></div><div><span style="color: silver; font-style: italic;"> * </span><span style="color: yellow; font-weight: bold;">@can_dlc:</span><span style="color: silver; font-style: italic;"> frame payload length in byte (0 .. 8) aka data length code</span></div><div><span style="color: silver; font-style: italic;"> * N.B. the DLC field from ISO 11898-1 Chapter 8.4.2.3 has a 1:1</span></div><div><span style="color: silver; font-style: italic;"> * mapping of the 'data length code' to the real payload length</span></div><div><span style="color: silver; font-style: italic;"> * </span><span style="color: yellow; font-weight: bold;">@data:</span><span style="color: silver; font-style: italic;"> CAN frame payload (up to 8 byte)</span></div><div><span style="color: silver; font-style: italic;"> */</span></div><div><span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">can_frame</span> <span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">canid_t</span> can_id<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> /* 32 bit CAN_ID + EFF/RTR/ERR flags */</span></div><div> __u8 can_dlc<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> /* frame payload length in byte (0 .. CAN_MAX_DLEN) */</span></div><div> __u8 data<span style="color: yellow; font-weight: bold;">[</span>CAN_MAX_DLEN<span style="color: yellow; font-weight: bold;">]</span> __attribute__<span style="color: yellow; font-weight: bold;">((</span>aligned<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">8</span><span style="color: yellow; font-weight: bold;">)));</span></div><div><span style="color: yellow; font-weight: bold;">};</span></div><br /><div><span style="color: silver; font-style: italic;">/**</span></div><div><span style="color: silver; font-style: italic;"> * struct canfd_frame - CAN flexible data rate frame structure</span></div><div><span style="color: silver; font-style: italic;"> * </span><span style="color: yellow; font-weight: bold;">@can_id:</span><span style="color: silver; font-style: italic;"> CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition</span></div><div><span style="color: silver; font-style: italic;"> * </span><span style="color: yellow; font-weight: bold;">@len:</span><span style="color: silver; font-style: italic;"> frame payload length in byte (0 .. CANFD_MAX_DLEN)</span></div><div><span style="color: silver; font-style: italic;"> * </span><span style="color: yellow; font-weight: bold;">@flags:</span><span style="color: silver; font-style: italic;"> additional flags for CAN FD</span></div><div><span style="color: silver; font-style: italic;"> * @__res0: reserved / padding</span></div><div><span style="color: silver; font-style: italic;"> * @__res1: reserved / padding</span></div><div><span style="color: silver; font-style: italic;"> * </span><span style="color: yellow; font-weight: bold;">@data:</span><span style="color: silver; font-style: italic;"> CAN FD frame payload (up to CANFD_MAX_DLEN byte)</span></div><div><span style="color: silver; font-style: italic;"> */</span></div><div><span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">canfd_frame</span> <span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">canid_t</span> can_id<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> /* 32 bit CAN_ID + EFF/RTR/ERR flags */</span></div><div> __u8 len<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> /* frame payload length in byte */</span></div><div> __u8 flags<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> /* additional flags for CAN FD */</span></div><div> __u8 __res0<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> /* reserved / padding */</span></div><div> __u8 __res1<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> /* reserved / padding */</span></div><div> __u8 data<span style="color: yellow; font-weight: bold;">[</span>CANFD_MAX_DLEN<span style="color: yellow; font-weight: bold;">]</span> __attribute__<span style="color: yellow; font-weight: bold;">((</span>aligned<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">8</span><span style="color: yellow; font-weight: bold;">)));</span></div><div><span style="color: yellow; font-weight: bold;">};</span></div></div></pre><p>Poi, in <strong>Linux 5.11,</strong> il <em>can_frame</em> è diventato così (mentre il <em>canfd_frame</em> è rimasto invariato):</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">/**</span></div><div><span style="color: silver; font-style: italic;"> * struct can_frame - Classical CAN frame structure (aka CAN 2.0B)</span></div><div><span style="color: silver; font-style: italic;"> * </span><span style="color: yellow; font-weight: bold;">@can_id:</span><span style="color: silver; font-style: italic;"> CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition</span></div><div><span style="color: silver; font-style: italic;"> * </span><span style="color: yellow; font-weight: bold;">@len:</span><span style="color: silver; font-style: italic;"> CAN frame payload length in byte (0 .. 8)</span></div><div><span style="color: silver; font-style: italic;"> * </span><span style="color: yellow; font-weight: bold;">@can_dlc:</span><span style="color: silver; font-style: italic;"> deprecated name for CAN frame payload length in byte (0 .. 8)</span></div><div><span style="color: silver; font-style: italic;"> * </span><span style="color: yellow; font-weight: bold;">@__pad:</span><span style="color: silver; font-style: italic;"> padding</span></div><div><span style="color: silver; font-style: italic;"> * @__res0: reserved / padding</span></div><div><span style="color: silver; font-style: italic;"> * @len8_dlc: optional DLC value (9 .. 15) at 8 byte payload length</span></div><div><span style="color: silver; font-style: italic;"> * len8_dlc contains values from 9 .. 15 when the payload length is</span></div><div><span style="color: silver; font-style: italic;"> * 8 bytes but the DLC value (see ISO 11898-1) is greater then 8.</span></div><div><span style="color: silver; font-style: italic;"> * CAN_CTRLMODE_CC_LEN8_DLC flag has to be enabled in CAN driver.</span></div><div><span style="color: silver; font-style: italic;"> * </span><span style="color: yellow; font-weight: bold;">@data:</span><span style="color: silver; font-style: italic;"> CAN frame payload (up to 8 byte)</span></div><div><span style="color: silver; font-style: italic;"> */</span></div><div><span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">can_frame</span> <span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">canid_t</span> can_id<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> /* 32 bit CAN_ID + EFF/RTR/ERR flags */</span></div><div> <span style="color: yellow; font-weight: bold;">union</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: silver; font-style: italic;"> /* CAN frame payload length in byte (0 .. CAN_MAX_DLEN)</span></div><div><span style="color: silver; font-style: italic;"> * was previously named can_dlc so we need to carry that</span></div><div><span style="color: silver; font-style: italic;"> * name for legacy support</span></div><div><span style="color: silver; font-style: italic;"> */</span></div><div> __u8 len<span style="color: yellow; font-weight: bold;">;</span></div><div> __u8 can_dlc<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> /* deprecated */</span></div><div> <span style="color: yellow; font-weight: bold;">};</span></div><div> __u8 __pad<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> /* padding */</span></div><div> __u8 __res0<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> /* reserved / padding */</span></div><div> __u8 len8_dlc<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> /* optional DLC for 8 byte payload length (9 .. 15) */</span></div><div> __u8 data<span style="color: yellow; font-weight: bold;">[</span>CAN_MAX_DLEN<span style="color: yellow; font-weight: bold;">]</span> __attribute__<span style="color: yellow; font-weight: bold;">((</span>aligned<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">8</span><span style="color: yellow; font-weight: bold;">)));</span></div><div><span style="color: yellow; font-weight: bold;">};</span></div></div></pre><p>Come si nota il campo <em>can_dlc</em> è stato deprecato (ma non cancellato) e il nome è stato allineato a quello usato nel CAN FD, proprio per permettere la scrittura semplificata di Software per entrambi i tipi senza troppi cambi!. Insomma, alla fine della fiera, ora è possibile riscrivere gli esempi del primo articolo (<em>cansend.c</em> e <em>canrecv.c</em>) per usare "Classic" o FD definendo una semplice variabile di compilazione (che ho chiamato CAN_FD). Cominciamo con <em>canfdsend.c</em>, vai col codice!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdlib.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">string.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">errno.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">unistd.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">net/if.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/ioctl.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/socket.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">linux/can.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">linux/can/raw.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// definizione del tipo CanFrame</span></div><div><span style="color: yellow; font-weight: bold;">#ifdef</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">CAN_FD</span></div><div><span style="color: yellow; font-weight: bold;">typedef</span> <span style="color: yellow; font-weight: bold;">struct</span> canfd_frame CanFrame<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // CAN FD</span></div><div><span style="color: yellow; font-weight: bold;">#else</span></div><div><span style="color: yellow; font-weight: bold;">typedef</span> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">can_frame</span> <span style="color: yellow; font-weight: bold;">CanFrame</span><span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // CAN classic</span></div><div><span style="color: yellow; font-weight: bold;">#endif</span></div><br /><div><span style="color: silver; font-style: italic;">// canfdsend - funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// creo il socket</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> sockfd<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">((</span>sockfd <span style="color: yellow; font-weight: bold;">=</span> socket<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">PF_CAN</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">SOCK_RAW</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">CAN_RAW</span><span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore socket()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore socket() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// set degli attributi di i/o</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">ifreq</span> ifr<span style="color: yellow; font-weight: bold;">;</span></div><div> memset<span style="color: yellow; font-weight: bold;">(&</span>ifr<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>ifr<span style="color: yellow; font-weight: bold;">));</span></div><div> snprintf<span style="color: yellow; font-weight: bold;">(</span>ifr<span style="color: yellow; font-weight: bold;">.</span>ifr_name<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>ifr<span style="color: yellow; font-weight: bold;">.</span>ifr_name<span style="color: yellow; font-weight: bold;">),</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">vcan0</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>ioctl<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">SIOCGIFINDEX</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>ifr<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore ioctl()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore ioctl() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">#ifdef</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">CAN_FD</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// set del modo CAN_FD</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> enable_canfd <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span> setsockopt<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> SOL_CAN_RAW<span style="color: yellow; font-weight: bold;">,</span> CAN_RAW_FD_FRAMES<span style="color: yellow; font-weight: bold;">,</span></div><div> <span style="color: yellow; font-weight: bold;">&</span>enable_canfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>enable_canfd<span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore setsockopt()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore setsockopt() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span>errno<span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> EXIT_FAILURE<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">#endif</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// set attributi dell'indirizzo</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">sockaddr_can</span> addr<span style="color: yellow; font-weight: bold;">;</span></div><div> memset<span style="color: yellow; font-weight: bold;">(&</span>addr<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>addr<span style="color: yellow; font-weight: bold;">));</span></div><div> addr<span style="color: yellow; font-weight: bold;">.</span>can_family <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">AF_CAN</span><span style="color: yellow; font-weight: bold;">;</span></div><div> addr<span style="color: yellow; font-weight: bold;">.</span>can_ifindex <span style="color: yellow; font-weight: bold;">=</span> ifr<span style="color: yellow; font-weight: bold;">.</span>ifr_ifindex<span style="color: yellow; font-weight: bold;">;</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// assegna l'indirizzo al socket</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>bind<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">(struct</span> <span style="color: yellow; font-weight: bold;">sockaddr</span> <span style="color: yellow; font-weight: bold;">*)&</span>addr<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>addr<span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore bind()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore bind() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// compongo il frame</span></div><div> <span style="color: yellow; font-weight: bold;">CanFrame</span> frame<span style="color: yellow; font-weight: bold;">;</span></div><div> memset<span style="color: yellow; font-weight: bold;">(&</span>frame<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">CanFrame</span><span style="color: yellow; font-weight: bold;">));</span></div><div> frame<span style="color: yellow; font-weight: bold;">.</span>can_id <span style="color: yellow; font-weight: bold;">=</span> <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">100</span><span style="color: #b5cea8; font-weight: bold;">U</span><span style="color: yellow; font-weight: bold;">;</span></div><div> frame<span style="color: yellow; font-weight: bold;">.</span>len <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">8</span><span style="color: yellow; font-weight: bold;">;</span></div><div> snprintf<span style="color: yellow; font-weight: bold;">(</span>frame<span style="color: yellow; font-weight: bold;">.</span>data<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>frame<span style="color: yellow; font-weight: bold;">.</span>data<span style="color: yellow; font-weight: bold;">),</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">0123456</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// invio il frame</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>write<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>frame<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">CanFrame</span><span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">!=</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">CanFrame</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore write()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore write() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_SUCCESS</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>ed ora passiamo a <em>canfdrecv.c</em>!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdlib.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">string.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">errno.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">unistd.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">net/if.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/ioctl.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/socket.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">linux/can.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">linux/can/raw.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// definizione del tipo CanFrame</span></div><div><span style="color: yellow; font-weight: bold;">#ifdef</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">CAN_FD</span></div><div><span style="color: yellow; font-weight: bold;">typedef</span> <span style="color: yellow; font-weight: bold;">struct</span> canfd_frame CanFrame<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // CAN FD</span></div><div><span style="color: yellow; font-weight: bold;">#else</span></div><div><span style="color: yellow; font-weight: bold;">typedef</span> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">can_frame</span> <span style="color: yellow; font-weight: bold;">CanFrame</span><span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // CAN classic</span></div><div><span style="color: yellow; font-weight: bold;">#endif</span></div><br /><div><span style="color: silver; font-style: italic;">// canfdrecv - funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// creo il socket</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> sockfd<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">((</span>sockfd <span style="color: yellow; font-weight: bold;">=</span> socket<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">PF_CAN</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">SOCK_RAW</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">CAN_RAW</span><span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore socket()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore socket() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// set degli attributi di i/o</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">ifreq</span> ifr<span style="color: yellow; font-weight: bold;">;</span></div><div> memset<span style="color: yellow; font-weight: bold;">(&</span>ifr<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>ifr<span style="color: yellow; font-weight: bold;">));</span></div><div> snprintf<span style="color: yellow; font-weight: bold;">(</span>ifr<span style="color: yellow; font-weight: bold;">.</span>ifr_name<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>ifr<span style="color: yellow; font-weight: bold;">.</span>ifr_name<span style="color: yellow; font-weight: bold;">),</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">vcan0</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>ioctl<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">SIOCGIFINDEX</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>ifr<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore ioctl()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore ioctl() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">#ifdef</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">CAN_FD</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// set del modo CAN_FD</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> enable_canfd <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span> setsockopt<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> SOL_CAN_RAW<span style="color: yellow; font-weight: bold;">,</span> CAN_RAW_FD_FRAMES<span style="color: yellow; font-weight: bold;">,</span></div><div> <span style="color: yellow; font-weight: bold;">&</span>enable_canfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>enable_canfd<span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore setsockopt()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore setsockopt() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span>errno<span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> EXIT_FAILURE<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">#endif</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// set attributi dell'indirizzo</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">sockaddr_can</span> addr<span style="color: yellow; font-weight: bold;">;</span></div><div> memset<span style="color: yellow; font-weight: bold;">(&</span>addr<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>addr<span style="color: yellow; font-weight: bold;">));</span></div><div> addr<span style="color: yellow; font-weight: bold;">.</span>can_family <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">AF_CAN</span><span style="color: yellow; font-weight: bold;">;</span></div><div> addr<span style="color: yellow; font-weight: bold;">.</span>can_ifindex <span style="color: yellow; font-weight: bold;">=</span> ifr<span style="color: yellow; font-weight: bold;">.</span>ifr_ifindex<span style="color: yellow; font-weight: bold;">;</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// assegna l'indirizzo al socket</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>bind<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">(struct</span> <span style="color: yellow; font-weight: bold;">sockaddr</span> <span style="color: yellow; font-weight: bold;">*)&</span>addr<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>addr<span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore bind()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore bind() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// ricevo il frame</span></div><div> <span style="color: yellow; font-weight: bold;">CanFrame</span> frame<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>read<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>frame<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">CanFrame</span><span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore read()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore read() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// mostro il frame ricevuto</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">id:0x%x dlc:%d data: </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> frame<span style="color: yellow; font-weight: bold;">.</span>can_id<span style="color: yellow; font-weight: bold;">,</span> frame<span style="color: yellow; font-weight: bold;">.</span>len<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">for</span> <span style="color: yellow; font-weight: bold;">(int</span> i <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span> i <span style="color: yellow; font-weight: bold;"><</span> frame<span style="color: yellow; font-weight: bold;">.</span>len<span style="color: yellow; font-weight: bold;">;</span> i<span style="color: yellow; font-weight: bold;">++)</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%c </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> frame<span style="color: yellow; font-weight: bold;">.</span>data<span style="color: yellow; font-weight: bold;">[</span>i<span style="color: yellow; font-weight: bold;">]);</span></div><br /><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_SUCCESS</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Visto come è facile? Nel codice ci sono gli appositi <em>"#ifdef CAN_FD"</em> che decidono che parte di codice usare. Quindi per ottenere la versione "Classic" si può compilare con:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>aldo@Linux $ gcc canfdsend.c <span style="color: yellow; font-weight: bold;">-</span>o canfdsend</div><div>aldo@Linux $ gcc canfdrecv.c <span style="color: yellow; font-weight: bold;">-</span>o canfdrecv</div></div></pre><p>Mentre, per ottenere la versione FD si deve compilare con:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>aldo@Linux $ gcc canfdsend.c <span style="color: yellow; font-weight: bold;">-</span>o canfdsend <span style="color: yellow; font-weight: bold;">-</span>DCAN_FD</div><div>aldo@Linux $ gcc canfdrecv.c <span style="color: yellow; font-weight: bold;">-</span>o canfdrecv <span style="color: yellow; font-weight: bold;">-</span>DCAN_FD</div></div></pre><p>E i cambi nel codice sono veramente limitatissimi, grazie alla flessibilità dell'interfaccia SocketCAN: basta definire un nuovo tipo (che ho chiamato <em>CanFrame</em>) da usare al posto della <em>struct can_frame</em> (o della <em>struct canfd_frame</em>), così i cambi nel codice sono minimi, anche grazie al fatto che il campo <em>can_dlc</em> ora si chiama <em>len</em> in entrambe le strutture, e quindi, in pratica, l'unico vero cambio nel codice è questo:</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">#ifdef</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">CAN_FD</span></div><div><span style="color: silver; font-style: italic;">// set del modo CAN_FD</span></div><div><span style="color: yellow; font-weight: bold;">int</span> enable_canfd <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span> setsockopt<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> SOL_CAN_RAW<span style="color: yellow; font-weight: bold;">,</span> CAN_RAW_FD_FRAMES<span style="color: yellow; font-weight: bold;">,</span></div><div> <span style="color: yellow; font-weight: bold;">&</span>enable_canfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>enable_canfd<span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore setsockopt()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore setsockopt() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span>errno<span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> EXIT_FAILURE<span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><div><span style="color: yellow; font-weight: bold;">#endif</span></div></div></pre><p>che forza l'uso, nel <em>socket</em> aperto precedentemente, dei <em>frame</em> di tipo FD. Insomma, come avevo promesso è veramente semplice scrivere codice <em>"compatibile"</em> con entrambi i tipi <em>"Classic"</em> e FD. E il CAN FD extended? Vabbé, ve lo lascio come utile esercizio, basti sapere che è sufficiente modificare il codice per FD con le minime modifiche che avevo mostrato nello <a href="https://artcprogramming.blogspot.com/2023/07/guardians-of-can-bus-come-usare-il-can.html" rel="noopener" target="_blank"><strong>scorso articolo</strong></a> al riguardo del CAN extended.</p><p>Ok, penso che con questo possa bastare. Il nostro viaggio nei misteri del CAN bus e del suo uso attraverso SocketCAN è terminato, ora possiamo qualificarci come <strong>Guardians of the CAN bus</strong>! Giuro che non ci sarà (<em>per il momento...</em>) una quinta parte, quindi nel prossimo articolo parleremo d'altro. E non trattenete il fiato nell'attesa, mi raccomando!</p><p>Ciao, e al prossimo post!</p></div>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-30786341817739510152023-07-15T18:24:00.001+02:002023-07-15T18:37:45.882+02:00Guardians of the CAN bus come usare il CAN bus in C - pt.3<blockquote><b>Mantis:</b> <i>È sbagliato manipolare i sentimenti degli amici.</i><br /><b>Drax</b><b>:</b> <i>E quella volta che mi hai fatto innamorare del mio calzino?</i><br /><b>Mantis</b><b>:</b> <i>Beh, è stato divertente.</i><br /></blockquote><p>Ed eccoci alla terza parte di <strong>Guardians of the CAN bus</strong>, un ciclo di articoli ispirati al bellissimo <a href="https://letterboxd.com/aldoz/film/guardians-of-the-galaxy-vol-3/" rel="noopener" target="_blank"><strong>Guardiani della Galassia Vol.3</strong></a> (ripassatevi <strong><a href="https://artcprogramming.blogspot.com/2023/05/guardians-of-can-bus-come-usare-il-can.html" rel="noopener" target="_blank">la prima</a></strong> e <strong><a href="https://artcprogramming.blogspot.com/2023/06/guardians-of-can-bus-come-usare-il-can.html" rel="noopener" target="_blank">la seconda</a></strong> parte, please...). Nel dialogo qui sopra c'è tutta la vena comica del film, che viaggia sempre in parallelo alla vena seria e drammatica. Un po' come il <strong>CAN bus</strong>, che è così ben studiato ed efficace che rende divertente scrivere Software che lo usa, salvo quando devi risolvere un problema molto complicato e allora entri nella fase <i>seria e drammatica</i>... ma siamo qui per questo! In questa terza parte scopriremo gli ultimi segreti che ci mancano per diventare dei veri e propri <strong>Guardians of The CAN bus</strong>!</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVyF2yi2OpLjl-xT7vkKt3NucbmZoJUsQ8iZkcKqTcdR_3Njp1z7CwSOuWhgHLG-sIu5nAWvRagCWS7Rf85ID4-WUP1lvKPq1etOpsE3z_pcmqbHqhJs0Vchha57mDqDqV7yA2_i1HDzrj4_QYsRuRAtBJSZMMEat24S-9CkW_ySRl1L30Y76vvY2mxoNj/s1204/guardiansofthecanbus.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="601" data-original-width="1204" height="293" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVyF2yi2OpLjl-xT7vkKt3NucbmZoJUsQ8iZkcKqTcdR_3Njp1z7CwSOuWhgHLG-sIu5nAWvRagCWS7Rf85ID4-WUP1lvKPq1etOpsE3z_pcmqbHqhJs0Vchha57mDqDqV7yA2_i1HDzrj4_QYsRuRAtBJSZMMEat24S-9CkW_ySRl1L30Y76vvY2mxoNj/w588-h293/guardiansofthecanbus.jpg" width="588" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...peró il CAN bus è ancora più divertente!...</td></tr></tbody></table><p>Nelle parti <a href="https://artcprogramming.blogspot.com/2023/05/guardians-of-can-bus-come-usare-il-can.html" rel="noopener" target="_blank"><strong>una</strong></a> e <a href="https://artcprogramming.blogspot.com/2023/06/guardians-of-can-bus-come-usare-il-can.html" rel="noopener" target="_blank"><strong>due</strong></a> abbiamo affrontato un bel po' di argomenti: <strong>la struttura del CAN bus e dei frame, il protocollo SocketCAN, esempi elementari di send e receive, il can_frame di SocketCAN, le can-utils, il setup di un CAN device sotto Linux, il sistema di filtraggio</strong>... E cosa ci manca allora? Direi che per completare l'opera bisogna descrivere: <strong>il trattamento degli errori, il CAN extended e, dulcis in fundo, il CAN FD.</strong> Uh, è molta roba e il tempo stringe: cominciamo!</p><p>E cominceremo dal <strong>trattamento degli errori</strong>: nel caso che qualcosa vada male nel protocollo è prevista la generazione di messaggi di errore che, opportunamente decodificati, conducono alla identificazione del problema: ossia, la mia funzione di ricezione riceverà de messaggi auto-generati dal controller che sta nel dispositivo fisico con cui comunico, e questi messaggi mi diranno cosa c'è che non va. È un sistema di auto-diagnostica molto efficace, quindi. Vediamo allora una versione di <strong>canrecv.c</strong> che realizza questo punto: vai col codice di <strong>canrecverr.c</strong>!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdlib.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">string.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">errno.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">unistd.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">net/if.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/ioctl.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/socket.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">linux/can.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">linux/can/raw.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">linux/can/error.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// canrecverr - funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// creo il socket</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> sockfd<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">((</span>sockfd <span style="color: yellow; font-weight: bold;">=</span> socket<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">PF_CAN</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">SOCK_RAW</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">CAN_RAW</span><span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore socket()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore socket() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// set degli attributi di i/o</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">ifreq</span> ifr<span style="color: yellow; font-weight: bold;">;</span></div><div> memset<span style="color: yellow; font-weight: bold;">(&</span>ifr<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>ifr<span style="color: yellow; font-weight: bold;">));</span></div><div> snprintf<span style="color: yellow; font-weight: bold;">(</span>ifr<span style="color: yellow; font-weight: bold;">.</span>ifr_name<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>ifr<span style="color: yellow; font-weight: bold;">.</span>ifr_name<span style="color: yellow; font-weight: bold;">),</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">vcan0</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>ioctl<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">SIOCGIFINDEX</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>ifr<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore ioctl()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore socket() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// set attributi dell'indirizzo</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">sockaddr_can</span> addr<span style="color: yellow; font-weight: bold;">;</span></div><div> memset<span style="color: yellow; font-weight: bold;">(&</span>addr<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>addr<span style="color: yellow; font-weight: bold;">));</span></div><div> addr<span style="color: yellow; font-weight: bold;">.</span>can_family <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">AF_CAN</span><span style="color: yellow; font-weight: bold;">;</span></div><div> addr<span style="color: yellow; font-weight: bold;">.</span>can_ifindex <span style="color: yellow; font-weight: bold;">=</span> ifr<span style="color: yellow; font-weight: bold;">.</span>ifr_ifindex<span style="color: yellow; font-weight: bold;">;</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// assegna l'indirizzo al socket</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>bind<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">(struct</span> <span style="color: yellow; font-weight: bold;">sockaddr</span> <span style="color: yellow; font-weight: bold;">*)&</span>addr<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>addr<span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore bind()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore bind() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// set errori da testare (in questo caso 3 ma se ne possono aggiungere a piacere)</span></div><div> <span style="color: yellow; font-weight: bold;">can_err_mask_t</span> err_mask <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">CAN_ERR_TX_TIMEOUT</span> <span style="color: yellow; font-weight: bold;">|</span> <span style="color: white; font-weight: bold;">CAN_ERR_LOSTARB</span> <span style="color: yellow; font-weight: bold;">|</span> <span style="color: white; font-weight: bold;">CAN_ERR_PROT</span><span style="color: yellow; font-weight: bold;">);</span></div><div> setsockopt<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">SOL_CAN_RAW</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: #b5cea8;">CAN_RAW_ERR_FILTER</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>err_mask<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>err_mask<span style="color: yellow; font-weight: bold;">));</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// ricevo il frame</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">can_frame</span> frame<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>read<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>frame<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(struct</span> <span style="color: yellow; font-weight: bold;">can_frame</span><span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore read()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore read() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// analizzo se è un error frame</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">((</span>frame<span style="color: yellow; font-weight: bold;">.</span>can_id <span style="color: yellow; font-weight: bold;">&</span> <span style="color: white; font-weight: bold;">CAN_ERR_FLAG</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">!=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: messaggio di errore\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">]);</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// mostro il frame ricevuto</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">id:0x%x dlc:%d data: </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> frame<span style="color: yellow; font-weight: bold;">.</span>can_id<span style="color: yellow; font-weight: bold;">,</span> frame<span style="color: yellow; font-weight: bold;">.</span>can_dlc<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">for</span> <span style="color: yellow; font-weight: bold;">(int</span> i <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span> i <span style="color: yellow; font-weight: bold;"><</span> frame<span style="color: yellow; font-weight: bold;">.</span>can_dlc<span style="color: yellow; font-weight: bold;">;</span> i<span style="color: yellow; font-weight: bold;">++)</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%c </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> frame<span style="color: yellow; font-weight: bold;">.</span>data<span style="color: yellow; font-weight: bold;">[</span>i<span style="color: yellow; font-weight: bold;">]);</span></div><br /><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_SUCCESS</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Visto? Usando la onnipresente <em>system-call </em> <a href="https://man7.org/linux/man-pages/man2/setsockopt.2.html" rel="noopener" target="_blank"><strong>setsockopt(2)</strong></a> si attiva la gestione degli errori usando il flag <strong>CAN_RAW_ERR_FILTER</strong> e passandole la maschera <em>err_mask</em> degli errori che vogliamo vedere: io ne ho messi tre, ma se ne possono mettere altri a piacere, o addirittura aggiungerli tutti assegnando a <em>err_mask </em>la maschera speciale <strong>CAN_ERR_MASK</strong>. Per attivare questa gestione l'unico altro cambio da effettuare nel codice è il test della presenza del <em>flag</em> <strong>CAN_ERR_FLAG</strong> nel <strong>can_id</strong> del messaggio ricevuto: se il <em>flag</em> è preesente non è un messaggio normale ma è un messaggio di errore da analizzare per estrarre l'informazione contenuta. L'analisi dell'errore si fa cercando nel <em>can_id</em> quali sono i bit attivi, seguendo questo esempio:</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// errore: "TX timeout (by netdevice driver)"</span></div><div><span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>frame<span style="color: yellow; font-weight: bold;">-></span>can_id <span style="color: yellow; font-weight: bold;">&</span> CAN_ERR_TX_TIMEOUT<span style="color: yellow; font-weight: bold;">)</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">TX timeout (by netdevice driver)</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: silver; font-style: italic;">// errore: "arbitration lost in bit: (data[0])"</span></div><div><span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>frame<span style="color: yellow; font-weight: bold;">-></span>can_id <span style="color: yellow; font-weight: bold;">&</span> CAN_ERR_LOSTARB<span style="color: yellow; font-weight: bold;">)</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">arbitration lost in bit: 0x%02x</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> frame<span style="color: yellow; font-weight: bold;">-></span>data<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">]);</span></div><br /><div><span style="color: silver; font-style: italic;">// errore: "protocol violations: type (data[2]) location (data[3])"</span></div><div><span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>frame<span style="color: yellow; font-weight: bold;">-></span>can_id <span style="color: yellow; font-weight: bold;">&</span> CAN_ERR_PROT<span style="color: yellow; font-weight: bold;">)</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">protocol violations: type 0x%02x location 0x%02x</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> frame<span style="color: yellow; font-weight: bold;">-></span>data<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">2</span><span style="color: yellow; font-weight: bold;">],</span> frame<span style="color: yellow; font-weight: bold;">-></span>data<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">3</span><span style="color: yellow; font-weight: bold;">]);</span></div></div></pre><p>L'esempio evidentemente non è completo, visto che gli errori trattabili sono veramente molti, ma per completarlo basta aiutarsi con le molte <em>#define</em> contenute in <em>linux/can/error.h</em>, è una attività relativemente semplice. Notare che la gestione degli errori è compatibile con quella dei filtri: nessuno impedisce di usarle (con oculatezza) allo stesso tempo.</p><p>Chiuso il capitolo errori possiamo passare al <strong>CAN extended</strong>, identificato dal protocollo <strong>CAN2.0(B)</strong> (mentre il CAN standard è <strong>CAN2.0(A)</strong>). Come si può intuire dal nome è una versione (quasi identica) del bus che usa un <strong>can_id</strong> esteso, e il risultato lo possiamo vedere nella seguente figura, dove <em>(a)</em> el il tipo normale e<em> (b)</em> è l'extended:</p><p><img alt="Frame--Standard-Extended" class="alignnone wp-image-7002 size-full" height="193" src="https://italiancoders.it/wp-content/uploads/2023/07/Frame-Standard-Extended.png" width="586" /></p><p>Con l'extended, praticamente, possiamo trasferire gli stessi dati, ma abbiamo una capacità di indirizzamento superiore. Dal punto di vista che ci interessa, quello del codice, questo cambio implica poche differenze visto che il <em>"lavoro sporco"</em> lo effettua il protocollo <strong>SocketCAN</strong> che, come gia visto nella <strong><a href="https://artcprogramming.blogspot.com/2023/05/guardians-of-can-bus-come-usare-il-can.html" rel="noopener" target="_blank">primo articolo</a> </strong>del ciclo, usa un <strong>can_id di 32 bit</strong>, quindi è compatibile con il <strong>CAN extended</strong>. La differenza fondamentale è nell'uso delle maschere definite nell'<em>header can.h</em>, di cui vi passo un estratto (utile anche per il punto precedente, quello degli errori):</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">/* special address description flags for the CAN_ID */</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">CAN_EFF_FLAG</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">80000000</span><span style="color: #b5cea8; font-weight: bold;">U</span><span style="color: silver; font-style: italic;"> /* EFF/SFF is set in the MSB */</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">CAN_RTR_FLAG</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">40000000</span><span style="color: #b5cea8; font-weight: bold;">U</span><span style="color: silver; font-style: italic;"> /* remote transmission request */</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">CAN_ERR_FLAG</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">20000000</span><span style="color: #b5cea8; font-weight: bold;">U</span><span style="color: silver; font-style: italic;"> /* error message frame */</span></div><br /><div><span style="color: silver; font-style: italic;">/* valid bits in CAN ID for frame formats */</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">CAN_SFF_MASK</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">000007FF</span><span style="color: #b5cea8; font-weight: bold;">U</span><span style="color: silver; font-style: italic;"> /* standard frame format (SFF) */</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">CAN_EFF_MASK</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">1FFFFFFF</span><span style="color: #b5cea8; font-weight: bold;">U</span><span style="color: silver; font-style: italic;"> /* extended frame format (EFF) */</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">CAN_ERR_MASK</span><span style="color: #569cd6;"> </span><span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">1FFFFFFF</span><span style="color: #b5cea8; font-weight: bold;">U</span><span style="color: silver; font-style: italic;"> /* omit EFF, RTR, ERR flags */</span></div></div></pre><p>quindi, ad esempio, nel codice per la gestione degli errori visto sopra (<em>canrecverr.c</em>) si dovrebbe cambiare una piccola parte del codice per usare <strong>CAN extended</strong> (vi mostro solo il frammento modificato):</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// set di 2 filtri (ma possono essere di più) e attivazione con setsockopt(2)</span></div><div><span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">can_filter</span> rfilter<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">2</span><span style="color: yellow; font-weight: bold;">];</span></div><div>rfilter<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">]</span>.can_id <span style="color: yellow; font-weight: bold;">=</span> <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">100</span><span style="color: yellow; font-weight: bold;">;</span></div><div>rfilter<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">]</span>.can_id <span style="color: yellow; font-weight: bold;">|=</span> <span style="color: white; font-weight: bold;">CAN_EFF_FLAG</span><span style="color: yellow; font-weight: bold;">;</span></div><div>rfilter<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">]</span>.can_mask <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">CAN_EFF_FLAG</span> <span style="color: yellow; font-weight: bold;">|</span> <span style="color: white; font-weight: bold;">CAN_RTR_FLAG</span> <span style="color: yellow; font-weight: bold;">|</span> <span style="color: white; font-weight: bold;">CAN_EFF_MASK</span><span style="color: yellow; font-weight: bold;">);</span></div><div>rfilter<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">]</span>.can_id <span style="color: yellow; font-weight: bold;">=</span> <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">200</span><span style="color: yellow; font-weight: bold;">;</span></div><div>rfilter<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">]</span>.can_id <span style="color: yellow; font-weight: bold;">|=</span> <span style="color: white; font-weight: bold;">CAN_EFF_FLAG</span><span style="color: yellow; font-weight: bold;">;</span></div><div>rfilter<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">]</span>.can_mask <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">CAN_EFF_FLAG</span> <span style="color: yellow; font-weight: bold;">|</span> <span style="color: white; font-weight: bold;">CAN_RTR_FLAG</span> <span style="color: yellow; font-weight: bold;">|</span> <span style="color: white; font-weight: bold;">CAN_EFF_MASK</span><span style="color: yellow; font-weight: bold;">);</span></div><div>setsockopt<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> SOL_CAN_RAW<span style="color: yellow; font-weight: bold;">,</span> CAN_RAW_FILTER<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>rfilter<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>rfilter<span style="color: yellow; font-weight: bold;">));</span></div></div></pre><p>E, analogamente, quando inviamo o riceviamo un messaggio in modo <strong>CAN extended</strong> dobbiamo fare questo:</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// preparo la spedizione di un messaggio</span></div><div>...</div><div>send_frame.can_id <span style="color: yellow; font-weight: bold;">=</span> <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">100</span><span style="color: yellow; font-weight: bold;">;</span></div><div>send_frame.can_id <span style="color: yellow; font-weight: bold;">|=</span> <span style="color: white; font-weight: bold;">CAN_EFF_FLAG</span><span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // per usare CAN extended</span></div><div>...</div><br /><div><span style="color: silver; font-style: italic;">// esamino un messaggio ricevuto</span></div><div>...</div><div>printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">recvd: 0x%03X\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> frame<span style="color: yellow; font-weight: bold;">-></span>can_id <span style="color: yellow; font-weight: bold;">&</span> <span style="color: white; font-weight: bold;">CAN_EFF_MASK</span><span style="color: yellow; font-weight: bold;">);</span></div><div>...</div></div></pre><p>Tutto qua!</p><p>E adesso dovremmo concludere in bellezza con il <strong>CAN FD</strong>. Dovremmo, ma l'atmosfera pre-vacanziera mi sta travolgendo, e poi non voglio buttare troppa carne al fuoco a Luglio inoltrato (<em>scrivo questo pensiero in tempo reale: mentre scrivevo quello che avete appena letto sopra non avevo ancora questa intenzione. Giuro!</em>)... quasi quasi lo rimando al prossimo articolo. Si, lo rimando... termineremo con una quarta parte in Agosto, tutta sul <strong>CAN FD</strong>, <em>sperando che qualcuno la legga in spiaggia, ah ah ah.</em></p><p>Ciao, e al prossimo post!</p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-5056488540502949322023-06-19T21:57:00.001+02:002023-06-19T21:57:31.969+02:00Guardians of the CAN bus come usare il CAN bus in C - pt.2<blockquote><b>Nebula:</b> <i>Drax, siediti! [a Drax che è disteso]</i><br /><b>Drax</b><b>:</b> <i>È qui per questo!</i><br /><b>Star-Lord</b><b>:</b> <i>Drax, si chiama divano. Non è un letto.</i><br /><b>Drax</b><b>:</b> <i>Perché è così lungo allora?</i><br /><b>Star-Lord</b><b>: </b><i>Ma che...</i><br /><b>Drax</b><b>:</b><b> </b><i>Beh, trovo difficile credere che non abbia scopi multipli. È così allettante.</i></blockquote><p>Ed eccoci alla seconda parte di <strong>Guardians of the CAN bus</strong>, un ciclo di articoli ispirati al bellissimo <a href="https://letterboxd.com/aldoz/film/guardians-of-the-galaxy-vol-3/" rel="noopener" target="_blank"><strong>Guardiani della Galassia Vol.3</strong></a> (ripassatevi <strong><a href="https://artcprogramming.blogspot.com/2023/05/guardians-of-can-bus-come-usare-il-can.html" rel="noopener" target="_blank">la prima parte</a></strong>, please...). Nel dialogo qui sopra si può evidenziare lo spirito del film, con <strong>Drax il Distruttore</strong> sempre pronto a scatenare equivoci esilaranti... Eppure non è affatto un film comico, è molto più profondo, esattamente come questo articolo che potrebbe sembrare semi-serio ma è (spero) una concreta guida introduttiva ai misteri del <strong>CAN bus</strong> (<em>e vabbé, io spero che lo sia...</em>). In questa seconda parte parleremo di <strong>utilities</strong>, <strong>setup </strong>e <strong>filtri</strong>... uh, molta roba, cominciamo allora!</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEig9g2628651ih-5_FHJKQXAAIumzrTEKMW3lwEJaK6NSh4Lso2HXMWe5QCbbtgnRm6FdEhMpMhX9H_b8OC903nbu0g0iJaKpYk3H6Bh2abKakfyrIRSzrApbE9TcSlI8g0kTY633p2T3aQL7iTAdtgXKXTG93NGhl7uGTxiaVsViUyqkcQsNYVv0U0AQ/s1280/guardians-of-the-canbus.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="640" data-original-width="1280" height="295" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEig9g2628651ih-5_FHJKQXAAIumzrTEKMW3lwEJaK6NSh4Lso2HXMWe5QCbbtgnRm6FdEhMpMhX9H_b8OC903nbu0g0iJaKpYk3H6Bh2abKakfyrIRSzrApbE9TcSlI8g0kTY633p2T3aQL7iTAdtgXKXTG93NGhl7uGTxiaVsViUyqkcQsNYVv0U0AQ/w589-h295/guardians-of-the-canbus.jpg" width="589" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...secondo me il CAN bus si usa meglio da distesi...</td></tr></tbody></table><p>Come promesso <a href="https://artcprogramming.blogspot.com/2023/05/guardians-of-can-bus-come-usare-il-can.html" rel="noopener" target="_blank"><strong>nell'articolo precedente</strong></a> il primo argomento che tratteremo riguarda la maniera di eseguire e testare Software per <strong>CAN bus</strong> (come i due esempi proposti nella prima parte) su una macchina <strong>Linux</strong>. In questo siamo abbastanza fortunati: il <strong>CAN bus</strong> oltre ad essere perfettamente integrato (attraverso <strong>SocketCAN</strong>) nel <em>kernel</em> <strong>Linux</strong>, è anche ben supportato a livello utente (anzi, a livello programmatore), visto che fornisce un utilissimo set di utilità, le <strong>can-utils</strong>, pensate proprio per il <em>testing</em>, e dispone anche di un dispositivo virtuale che è perfetto per provare il Software sviluppato anche senza disporre di un dispositivo fisico <em>"reale"</em>. Con le <em>can-utils</em> possiamo fare un sacco di operazioni, tra cui queste:</p><ul><li><em>candump:</em> visualizza, filtra e registra i dati CAN su file</li><li><em>canplayer:</em> riproduce i file di log del CAN.</li><li><em>cansend:</em> invia un singolo frame.</li><li><em>cangen:</em> genera un traffico CAN casuale.</li><li><em>cansequence:</em> invia e controlla una sequenza di frame CAN con payload crescente.</li><li><em>cansniffer:</em> visualizza le differenze di contenuto dei dati CAN.</li></ul><p>E queste sono solo le funzioni principali! Ce ne sono anche molte secondarie! Ma, visto che in rete ci sono già delle buone guide descrittive di <strong>can-utils</strong>, non voglio riscoprire l'acqua calda, e quindi vi consiglio di leggere, ad esempio, <a href="https://github.com/linux-can/can-utils" rel="noopener" target="_blank"><strong>direttamente la descrizione su GitHub</strong></a>.</p><p>E passiamo al lato pratico: una volta scritto il codice vorremo provarlo, no? (<em>credo che questa sia sempre una attività raccomandabile...</em>) E allora dobbiamo chiedere al nostro amico Linux di attivare il dispositivo CAN, che può essere uno vero o, come detto sopra, uno virtuale che si comporta esattamente come uno reale (questo <em>virtual device</em> è un vero gioiellino che aiuta moltissimo il lavoro di programmazione). Per attivare la comunicazione CAN possiamo usare un semplice <em>Bash Script</em> di <em>setup</em> come il seguente:</p><pre class="EnlighterJSRAW" data-enlighter-language="bash"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">#!/bin/bash</span></div><br /><div><span style="color: silver; font-style: italic;"># aggiunge il nuovo device</span></div><div>sudo <span style="color: white; font-weight: bold;">ip</span> <span style="color: white; font-weight: bold;">link</span> <span style="color: white; font-weight: bold;">add</span> <span style="color: white; font-weight: bold;">dev</span> <span style="color: white; font-weight: bold;">vcan0</span> <span style="color: white; font-weight: bold;">type</span> <span style="color: white; font-weight: bold;">vcan</span></div><br /><div><span style="color: silver; font-style: italic;"># abilita il nuovo device</span></div><div>sudo <span style="color: white; font-weight: bold;">ip</span> <span style="color: white; font-weight: bold;">link</span> <span style="color: white; font-weight: bold;">set</span> <span style="color: white; font-weight: bold;">vcan0</span> <span style="color: white; font-weight: bold;">up</span></div></div></pre><p>Come indicato nei commenti (che <a href="https://artcprogramming.blogspot.com/2012/08/no-comment.html" rel="noopener" target="_blank"><strong>devono essere sempre presenti e chiari</strong></a> sia nel <strong>C</strong> che nel <em>Bash)</em> il primo comando aggiunge un <i>device</i> di tipo rete (stiamo usando SocketCAN) che abbiamo chiamato <em>vcan0</em> ed è di tipo virtuale (è un <em>"vcan"</em>). Il seguente comando usa anche lui l'interfaccia di rete e dice semplicemente a Linux di attivare il <i>device</i>. Tutto qua!</p><p>È semplicissimo, no? E se il dispositivo è <em>"reale" </em>che faremo? Useremo un tipo <em>"can"</em> invece di <em>"vcan"</em> e, per coerenza, lo chiameremo <em>can0</em> invece di <em>vcan0.</em> Ricordatevi che <em>vcan0</em> e <em>can0</em> <strong>sono solo nomi</strong> (e possiamo usarne anche <em>"di fantasia")</em>, bisogna solo tenere presente che il nostro codice dovrà, poi, riferirsi al nome assegnato in <em>setup</em> al dispositivo<em>.</em> E il <em>numero 0</em>? Si mette per ricordare che possiamo trattare più <em>bus</em> alla volta, quindi mettere un numero progressivo è una buona idea.</p><p>Adesso che abbiamo installato e attivato il nostro <em>device CAN</em> possiamo compilare ed eseguire gli esempi dell'altra volta (<em>canrecv.c</em> e <em>cansend.c</em>), ottenendo questi risultati:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>aldo@Linux $ gcc canrecv.c <span style="color: yellow; font-weight: bold;">-</span>o canrecv</div><div>aldo@Linux $ .<span style="color: yellow; font-weight: bold;">/</span>canrecv</div><div>id:<span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">100</span> dlc:<span style="color: white; font-weight: bold;">8</span> data: <span style="color: white; font-weight: bold;">0</span> <span style="color: white; font-weight: bold;">1</span> <span style="color: white; font-weight: bold;">2</span> <span style="color: white; font-weight: bold;">3</span> <span style="color: white; font-weight: bold;">4</span> <span style="color: white; font-weight: bold;">5</span> <span style="color: white; font-weight: bold;">6</span></div><div>aldo@Linux $</div></div></pre><p>Qui sopra <em>canrecv</em> si era messo in attesa, e il risultato (<code class="EnlighterJSRAW" data-enlighter-language="generic">id:0x100 dlc:8 data: 0 1 2 3 4 5 6</code>) è apparso, ovviamente, solo quando in un altro terminale ho eseguito <em>cansend</em> :</p><pre class="EnlighterJSRAW" data-enlighter-language="raw"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>aldo@Linux $ gcc cansend.c <span style="color: yellow; font-weight: bold;">-</span>o cansend</div><div>aldo@Linux $ .<span style="color: yellow; font-weight: bold;">/</span>cansend</div><div>aldo@Linux $</div></div></pre><p>sono soddisfazioni...</p><p>E adesso possiamo passare al prossimo punto, che, direi, si potrebbe chiamare <em>"Funzioni avanzate di SocketCAN: filtri ed errori".</em> L'argomento che mi preme trattare per primo è quello dei filtri, ossia di come limitare la ricezione dei messaggi a <em>"ricevo solo i can_id che mi interessano"</em>, anche perché dobbiamo ricordare che <strong>CAN bus</strong> è, per l'appunto, un <em>bus,</em> quindi potrebbe esserci un certo affollamento di dispositivi e, quindi, di messaggi che circolano.</p><p>Il metodo di filtraggio è relativamente semplice, e si basa sull'attributo <em>CAN_RAW_FILTER</em> del <em>socket</em> in uso, e si deve settare usando la <em>system-call</em> <a href="https://man7.org/linux/man-pages/man2/setsockopt.2.html" rel="noopener" target="_blank"><strong>setsockopt(2)</strong></a> a cui bisogna passare la lista dei filtri da applicare, usando la apposita struttura <em>can_filter </em>descritta in uno degli <em>header file</em> di SocketCAN, e cioè <em>linux/can.h.</em> Ma forse è meglio fornire un semplice esempio facendo cantare il codice: di seguito vi mostro <em>canrecvfilt.c</em>, che, come indica il nome, è una <em>canrecv.c</em> con i filtri. Vai col codice!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdlib.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">string.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">errno.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">unistd.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">net/if.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/ioctl.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/socket.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">linux/can.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">linux/can/raw.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// canrecvfilt - funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// creo il socket</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> sockfd<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">((</span>sockfd <span style="color: yellow; font-weight: bold;">=</span> socket<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">PF_CAN</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">SOCK_RAW</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">CAN_RAW</span><span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore socket()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore socket() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// set degli attributi di i/o</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">ifreq</span> ifr<span style="color: yellow; font-weight: bold;">;</span></div><div> memset<span style="color: yellow; font-weight: bold;">(&</span>ifr<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>ifr<span style="color: yellow; font-weight: bold;">));</span></div><div> snprintf<span style="color: yellow; font-weight: bold;">(</span>ifr<span style="color: yellow; font-weight: bold;">.</span>ifr_name<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>ifr<span style="color: yellow; font-weight: bold;">.</span>ifr_name<span style="color: yellow; font-weight: bold;">),</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">vcan0</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>ioctl<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">SIOCGIFINDEX</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>ifr<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore ioctl()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore socket() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// set attributi dell'indirizzo</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">sockaddr_can</span> addr<span style="color: yellow; font-weight: bold;">;</span></div><div> memset<span style="color: yellow; font-weight: bold;">(&</span>addr<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>addr<span style="color: yellow; font-weight: bold;">));</span></div><div> addr<span style="color: yellow; font-weight: bold;">.</span>can_family <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">AF_CAN</span><span style="color: yellow; font-weight: bold;">;</span></div><div> addr<span style="color: yellow; font-weight: bold;">.</span>can_ifindex <span style="color: yellow; font-weight: bold;">=</span> ifr<span style="color: yellow; font-weight: bold;">.</span>ifr_ifindex<span style="color: yellow; font-weight: bold;">;</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// assegna l'indirizzo al socket</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>bind<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">(struct</span> <span style="color: yellow; font-weight: bold;">sockaddr</span> <span style="color: yellow; font-weight: bold;">*)&</span>addr<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>addr<span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore bind()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore bind() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// set di 2 filtri (ma possono essere di più) e attivazione con setsockopt(2)</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">can_filter</span> rfilter<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">2</span><span style="color: yellow; font-weight: bold;">];</span></div><div> rfilter<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">].</span>can_id <span style="color: yellow; font-weight: bold;">=</span> <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">100</span><span style="color: yellow; font-weight: bold;">;</span></div><div> rfilter<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">].</span>can_mask <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">CAN_SFF_MASK</span><span style="color: yellow; font-weight: bold;">;</span></div><div> rfilter<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">].</span>can_id <span style="color: yellow; font-weight: bold;">=</span> <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">200</span><span style="color: yellow; font-weight: bold;">;</span></div><div> rfilter<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">].</span>can_mask <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">CAN_SFF_MASK</span><span style="color: yellow; font-weight: bold;">;</span></div><div> setsockopt<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">SOL_CAN_RAW</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: #b5cea8;">CAN_RAW_FILTER</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>rfilter<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>rfilter<span style="color: yellow; font-weight: bold;">));</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// ricevo il frame</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">can_frame</span> frame<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>read<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>frame<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(struct</span> <span style="color: yellow; font-weight: bold;">can_frame</span><span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore read()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore read() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// mostro il frame ricevuto</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">id:0x%x dlc:%d data: </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> frame<span style="color: yellow; font-weight: bold;">.</span>can_id<span style="color: yellow; font-weight: bold;">,</span> frame<span style="color: yellow; font-weight: bold;">.</span>can_dlc<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">for</span> <span style="color: yellow; font-weight: bold;">(int</span> i <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span> i <span style="color: yellow; font-weight: bold;"><</span> frame<span style="color: yellow; font-weight: bold;">.</span>can_dlc<span style="color: yellow; font-weight: bold;">;</span> i<span style="color: yellow; font-weight: bold;">++)</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%c </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> frame<span style="color: yellow; font-weight: bold;">.</span>data<span style="color: yellow; font-weight: bold;">[</span>i<span style="color: yellow; font-weight: bold;">]);</span></div><br /><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_SUCCESS</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Visto? È identica alla <em>canrecv.c</em> dello scorso articolo però ha alcune linee aggiunte tra la <a href="https://man7.org/linux/man-pages/man2/bind.2.html" rel="noopener" target="_blank"><strong>bind(2)</strong></a> e la <a href="https://man7.org/linux/man-pages/man2/read.2.html" rel="noopener" target="_blank"><strong>read(2)</strong></a> (<em>ma non era obbligatorio metterle proprio li, eh!</em>): si riempie un <em>array</em> di filtri (in questo caso ne ho messi 2 ma se ne possono aggiungere a piacere) e si passa <em>l'array</em> alla <strong>setsockopt(2)</strong>. Ogni struttura <em>can_filter</em> dell'array contiene il <em>can_id</em> che si vuole far passare e la maschera da applicare al filtro (serve per evitare di usare anche i bit non significativi per il filtraggio): nell'esempio ho usato <em>CAN_SFF_MASK</em>, che è la maschera di default del <em>Standard CAN</em>, mentre per il <em>Extended CAN</em> bisogna usare un altra maschera (ma questo lo vedremo nella terza parte del ciclo di articoli).</p><p>Per verificare il funzionamento dell'esempio basta compilare (ed eseguire) <em>canrecvfilt.c</em> e modificare <em>cansend.c</em> per inviare, ad esempio, un messaggio con <em>can_id=0x300</em>, e, magicamente, succederà che la <em>canrecvfilt</em> non riceve nulla! Poi si può provare a spedire messaggi con <em>can_id=0x100</em> o <em>0x200</em> per verificare, invece, la corretta ricezione dei messaggi. È semplicissimo!</p><p>L'esempio che ho scritto è del tipo <em>"classico"</em> , ma sono possibili anche delle varianti: ad esempio si può filtrare al contrario, e cioè: <em>"questi can_id non li voglio ricevere"</em>, ma questi sono dettagli che vi lascio il piacere di approfondire a parte.</p><p>Ok, la seconda parte del ciclo la chiudiamo qui. Nella terza parte (<em>ebbene si! Ci sarà una terza parte, e l'ho deciso in corsa...</em>) parleremo della <strong>gestione degli errori</strong> e, poi, di <strong>Extended CAN</strong> e <strong>CAN FD</strong> ovvero le due varianti del tipo <em>"Classical"</em>: sono meno usate, ma hanno caratteristiche interessanti, e possono essere necessarie in alcuni progetti, quindi è meglio essere preparati all'uopo. Tenetevi pronti ma non siate impazienti: sicuramente il nuovo articolo non arriverà domani (<em>e neanche dopodomani, ah ah ah</em>).</p><p>Ciao, e al prossimo post!</p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-6320146409990813802023-05-26T19:58:00.004+02:002023-06-18T18:42:44.753+02:00Guardians of the CAN bus come usare il CAN bus in C - pt.1<blockquote><b>Drax:</b> <i>E uccideremo chiunque si metta sulla nostra strada!</i><br /><b>Star-Lord</b><b>:</b> <i>No! non uccideremo nessuno.</i><br /><b>Drax</b><b>:</b> <i>Uccidiamo un po' di gente...</i><br /><b>Star-Lord</b><b>:</b> <i>Non uccidiamo un po' di gente.</i><br /><b>Drax</b><b>: </b><i>Uccidiamo solo una persona. Una stupida persona che nessuno ama...</i><br /><b>Star-Lord</b><b>:</b><b> </b><i>Ora stai solo rendendo la cosa triste...</i></blockquote><p>In questo articolo prendo spunto dal bellissimo <a href="https://letterboxd.com/aldoz/film/guardians-of-the-galaxy-vol-3/" rel="noopener" target="_blank"><strong>Guardiani della Galassia Vol.3</strong></a> che ho visto da poco e con gran piacere. Questo gruppo di guerrieri abituati a grandi battaglie interstellari spesso si perdono (con notevole humor) in discussioni surreali come quella sopra, che rendono divertente un film dalla forte carica drammatica: un mix che il bravissimo <a href="https://letterboxd.com/director/james-gunn/" rel="noopener" target="_blank"><strong>James Gunn</strong></a> maneggia alla perfezione. </p><p>E cosa centra con tutto questo il <em>Controller Area Network</em> (<strong>CAN bus</strong> per gli amici) che è il protagonista del post di oggi? Centra, centra... il <strong>CAN bus</strong> è un protocollo che unisce una apparente semplicità (e un bus seriale su doppino intrecciato + GND) a una molteplicità di prestazioni tipiche dei protocolli sofisticati (tipo <em>Ethernet</em>): è orientato ai messaggi, è insensibile ai disturbi elettromagnetici (è un ottimo bus di campo), è multiplexato (più dispositivi sullo stesso bus), è protetto dal <em>Data Collision</em>, è veloce (1 Mbit/s)... insomma, il <strong>CAN bus</strong> è un vero Guardiano della Galassia! Tra l'altro è nato per uso <em>Automotive</em>, che è uno degli usi più restrittivi esistenti, dove <em>"l'affidabilità è prima di tutto"</em>: e grazie a queste caratteristiche l'uso si è allargato anche in molti altri settori. <em>Viva il CAN bus!</em></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0fhwRxKIjAuBej_Eitxf74PvrM5upBLQ56x9Ff4COy2ZvIn1OjaZfTuJnFnMlEfjte51t6fhxGlst3wL75PL4RDt8_zdqcP30Br8lyZ_1Ls8M-6wUW2d3rURFvXJBAkgUzb1-N7Bz5fRpeeJCBRrmOI0bnPkZATWbUPyUN9VWTB6u9hS11x7boE2dWg/s1200/Guardians_of_the_Galaxy_vol_3.0.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="675" data-original-width="1200" height="331" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0fhwRxKIjAuBej_Eitxf74PvrM5upBLQ56x9Ff4COy2ZvIn1OjaZfTuJnFnMlEfjte51t6fhxGlst3wL75PL4RDt8_zdqcP30Br8lyZ_1Ls8M-6wUW2d3rURFvXJBAkgUzb1-N7Bz5fRpeeJCBRrmOI0bnPkZATWbUPyUN9VWTB6u9hS11x7boE2dWg/w588-h331/Guardians_of_the_Galaxy_vol_3.0.jpg" width="588" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...qualcuno si è ricordato di portare il CAN bus?...</td></tr></tbody></table><div><br /></div><div>E qui ci vorrebbe una breve descrizione di come funziona il <strong>CAN bus</strong> a livello elettrico, però dubito che riuscirei a farne una veramente esaustiva, visto che non è proprio il mio campo (<em>a meno di non fare copia-e-incolla, ah ah ah</em>), per cui vi rimando alle decine di descrizioni che si trovano in rete (<a href="https://www.csselectronics.com/pages/can-bus-simple-intro-tutorial" rel="noopener" target="_blank"><strong>ad esempio questa</strong></a>). </div><div><br /></div><div>Quello che conta per noi è come si può rappresentare questo protocollo con il <em>Software</em> e, osservando la disposizione dei dati <em>"elettrici"</em>, salta all'occhio che il tipo di trasmissione è basato sul concetto di <em>"frame"</em>, quindi di piccoli pacchetti di dati che contengono un identificatore unico <strong>ID</strong> di 11 bit che permette di <em>"dare un nome"</em> al pacchetto, un campo <strong>DLC</strong> che specifica la lunghezza del campo dati contenuto nel pacchetto e, infine, il vero e proprio campo <strong>Data</strong> di 0-8 byte che è l'unico campo a larghezza variabile (controllata dal campo <strong>DLC</strong>). Gli altri bit del pacchetto di dati non è necessario descriverli in questa sede, comunque tra di loro troviamo alcuni nomi famigliari come il <strong>SOF</strong>, il <strong>EOF</strong>, il <strong>CRC</strong>, ecc. Vi mostro una immagine qualsiasi delle mille che si trovano in rete:</div><div><p><img alt="" class="wp-image-6938 size-full" height="184" src="https://italiancoders.it/wp-content/uploads/2023/05/can_frame.jpeg" width="595" /></p></div><p>Ecco, come ben visibile nel disegno, il <em>frame</em> ha una piccola sezione dati, di massimo 8 byte (per la versione classica, mentre per la versione estesa <strong>CAN FD</strong> sono 64 byte), e quindi rispetto a altri protocolli come <em>Ethernet</em>, il formato dati del <strong>CAN bus</strong> è più, per modo di dire <em>"ristretto"</em>, basandosi su pacchetti di dimensioni ridotte (i <em>frame,</em> per l'appunto) che permettono di scambiare pochi dati alla volta, quindi non è fatto per trasmettere rapidamente <em>"qualunque cosa"</em> (pensate a un un <em>file transfer</em> con TCP/IP sotto <em>Ethernet),</em> ma piccoli gruppi di dati in maniera super-sicura e super-efficiente: è un vero bus industriale, perfetto per trasferire misure di sensori, <em>setpoint,</em> comandi, ecc.</p><p>E come si usa il <strong>CAN bus</strong> a livello <em>Software?</em> Riferendoci alla struttura del <em>frame</em> descritta sopra si può dire che un driver Software per CAN può limitarsi a isolare le tre parti <em>"utili"</em> del frame, e cioè: <strong>ID</strong>, <strong>DLC</strong> e <strong>Data</strong>, che sono sufficienti per gestire la comunicazione. Sotto <strong>Linux</strong> funziona così, e abbiamo a disposizione ben due maniere di operare (e due driver):</p><ul><li><strong>SocketCAN</strong> che è, come dice il nome, un <em>network driver</em> inserito nella famiglia <em>BSD socket.</em></li><li><strong>can4linux</strong> che è un classico <em>device driver</em> a caratteri.</li></ul><p>In questo articolo parleremo di <strong>SocketCAN</strong>, che ha vari vantaggi, tra cui:</p><ul><li>È inserito di default nel <em>Kernel</em> Linux, quindi è perfettamente integrato nel sistema.</li><li>Permette di usare la classica interfaccia <em>socket,</em> che è veramente il prezzemolino della programmazione <em>low-level</em>.</li><li>È quello che conosco meglio, perché lo uso normalmente, mentre l'altro no... (<em>e questo è l'attributo fondamentale, ah ah ah).</em></li></ul><p>E veniamo a <strong>SocketCAN</strong>: considerato il tipo di protocollo <em>socket-like</em> c'è da aspettarsi che il <em>Software</em> di comunicazione abbia un aspetto familiare, tipo i notissimi <em>Client/Server TCP o UDP </em>(<a href="https://artcprogramming.blogspot.com/2016/07/udphunter-come-scriver-un-udp-server-in.html" rel="noopener" target="_blank"><strong>tipo quelli visti qui</strong></a> e <a href="https://artcprogramming.blogspot.com/2016/08/udphunter-come-scrivere-un-udp-client.html" rel="noopener" target="_blank"><strong>qui</strong></a>), e in effetti è così, a parte un piccolo dettaglio: qui non ci sono <em>Client</em> e <em>Server,</em> tutti i dispositivi leggono e scrivono senza bisogno di instaurare una connessione, quindi è come se fossero tutti <em>Server.</em> Per mostrarvi un caso reale semplificato ho scritto due semplici esempi specializzati, in cui uno scrive (<em>cansend.c</em>) e l'altro legge (<em>canrecv.c</em>). E vediamo gli esempi... vai col codice!</p><div><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div style="line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdlib.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">string.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">errno.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">unistd.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">net/if.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/ioctl.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/socket.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">linux/can.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">linux/can/raw.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// cansend - funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// creo il socket</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> sockfd<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">((</span>sockfd <span style="color: yellow; font-weight: bold;">=</span> socket<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">PF_CAN</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">SOCK_RAW</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">CAN_RAW</span><span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore socket()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore socket() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// set degli attributi di i/o</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">ifreq</span> ifr<span style="color: yellow; font-weight: bold;">;</span></div><div> memset<span style="color: yellow; font-weight: bold;">(&</span>ifr<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>ifr<span style="color: yellow; font-weight: bold;">));</span></div><div> snprintf<span style="color: yellow; font-weight: bold;">(</span>ifr<span style="color: yellow; font-weight: bold;">.</span>ifr_name<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>ifr<span style="color: yellow; font-weight: bold;">.</span>ifr_name<span style="color: yellow; font-weight: bold;">),</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">vcan0</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>ioctl<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">SIOCGIFINDEX</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>ifr<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore ioctl()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore socket() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// set attributi dell'indirizzo</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">sockaddr_can</span> addr<span style="color: yellow; font-weight: bold;">;</span></div><div> memset<span style="color: yellow; font-weight: bold;">(&</span>addr<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>addr<span style="color: yellow; font-weight: bold;">));</span></div><div> addr<span style="color: yellow; font-weight: bold;">.</span>can_family <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">AF_CAN</span><span style="color: yellow; font-weight: bold;">;</span></div><div> addr<span style="color: yellow; font-weight: bold;">.</span>can_ifindex <span style="color: yellow; font-weight: bold;">=</span> ifr<span style="color: yellow; font-weight: bold;">.</span>ifr_ifindex<span style="color: yellow; font-weight: bold;">;</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// assegna l'indirizzo al socket</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>bind<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">(struct</span> <span style="color: yellow; font-weight: bold;">sockaddr</span> <span style="color: yellow; font-weight: bold;">*)&</span>addr<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>addr<span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore bind()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore bind() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><div style="line-height: 13px;"><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// compongo il frame</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">can_frame</span> frame<span style="color: yellow; font-weight: bold;">;</span></div><div> memset<span style="color: yellow; font-weight: bold;">(&</span>frame<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(struct</span> <span style="color: yellow; font-weight: bold;">can_frame</span><span style="color: yellow; font-weight: bold;">));</span></div><div> frame<span style="color: yellow; font-weight: bold;">.</span>can_id <span style="color: yellow; font-weight: bold;">=</span> <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">100</span><span style="color: #b5cea8; font-weight: bold;">U</span><span style="color: yellow; font-weight: bold;">;</span></div><div> frame<span style="color: yellow; font-weight: bold;">.</span>can_dlc <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">8</span><span style="color: yellow; font-weight: bold;">;</span></div><div> snprintf<span style="color: yellow; font-weight: bold;">(</span>frame<span style="color: yellow; font-weight: bold;">.</span>data<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>frame<span style="color: yellow; font-weight: bold;">.</span>data<span style="color: yellow; font-weight: bold;">),</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">0123456</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><br /></div></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// invio il frame</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>write<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>frame<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(struct</span> <span style="color: yellow; font-weight: bold;">can_frame</span><span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">!=</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(struct</span> <span style="color: yellow; font-weight: bold;">can_frame</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore write()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore write() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_SUCCESS</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></div></pre><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div style="line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdlib.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">string.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">errno.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">unistd.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">net/if.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/ioctl.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/socket.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">linux/can.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">linux/can/raw.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// canrecv - funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// creo il socket</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> sockfd<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">((</span>sockfd <span style="color: yellow; font-weight: bold;">=</span> socket<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">PF_CAN</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">SOCK_RAW</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">CAN_RAW</span><span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore socket()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore socket() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// set degli attributi di i/o</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">ifreq</span> ifr<span style="color: yellow; font-weight: bold;">;</span></div><div> memset<span style="color: yellow; font-weight: bold;">(&</span>ifr<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>ifr<span style="color: yellow; font-weight: bold;">));</span></div><div> snprintf<span style="color: yellow; font-weight: bold;">(</span>ifr<span style="color: yellow; font-weight: bold;">.</span>ifr_name<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>ifr<span style="color: yellow; font-weight: bold;">.</span>ifr_name<span style="color: yellow; font-weight: bold;">),</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">vcan0</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>ioctl<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">SIOCGIFINDEX</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>ifr<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore ioctl()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore socket() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// set attributi dell'indirizzo</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">sockaddr_can</span> addr<span style="color: yellow; font-weight: bold;">;</span></div><div> memset<span style="color: yellow; font-weight: bold;">(&</span>addr<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>addr<span style="color: yellow; font-weight: bold;">));</span></div><div> addr<span style="color: yellow; font-weight: bold;">.</span>can_family <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">AF_CAN</span><span style="color: yellow; font-weight: bold;">;</span></div><div> addr<span style="color: yellow; font-weight: bold;">.</span>can_ifindex <span style="color: yellow; font-weight: bold;">=</span> ifr<span style="color: yellow; font-weight: bold;">.</span>ifr_ifindex<span style="color: yellow; font-weight: bold;">;</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// assegna l'indirizzo al socket</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>bind<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">(struct</span> <span style="color: yellow; font-weight: bold;">sockaddr</span> <span style="color: yellow; font-weight: bold;">*)&</span>addr<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>addr<span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore bind()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore bind() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// ricevo il frame</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">can_frame</span> frame<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>read<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>frame<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(struct</span> <span style="color: yellow; font-weight: bold;">can_frame</span><span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore read()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore read() (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// mostro il frame ricevuto</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">id:0x%x dlc:%d data: </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> frame<span style="color: yellow; font-weight: bold;">.</span>can_id<span style="color: yellow; font-weight: bold;">,</span> frame<span style="color: yellow; font-weight: bold;">.</span>can_dlc<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">for</span> <span style="color: yellow; font-weight: bold;">(int</span> i <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span> i <span style="color: yellow; font-weight: bold;"><</span> frame<span style="color: yellow; font-weight: bold;">.</span>can_dlc<span style="color: yellow; font-weight: bold;">;</span> i<span style="color: yellow; font-weight: bold;">++)</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%c </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> frame<span style="color: yellow; font-weight: bold;">.</span>data<span style="color: yellow; font-weight: bold;">[</span>i<span style="color: yellow; font-weight: bold;">]);</span></div><br /><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div> close<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">EXIT_SUCCESS</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></div></pre></div><p>Visto? Sembrano due <em>Server UDP</em>, ma ancora più semplificati. Compilando ed eseguendo (<em>poi vedremo come...</em>) si nota che <em>cansend</em> si blocca in attesa che <em>canrecv</em> gli invii un <em>frame,</em> e una volta ricevuto lo mostra ed esce. Nella realtà un applicazione <strong>SocketCAN</strong> tipicamente scrive e legge, per cui è opportuno gestire azioni di <em>read/write</em> in maniera <em>non-blocking</em>, ma questi dettagli dipendono molto dal tipo di oggetto che si vuole realizzare. Ovviamente per testare questi programmi bisogna disporre di un <strong>CAN bus</strong> attivo nel sistema: nel prossimo articolo vedremo come si può (sotto Linux) lavorare e testare il <em>Sotfware</em> pur non disponendo di veri dispositivi <em>Hardware</em> CAN da provare (<em>giurin giuretta che sarà veramente il tema del prossimo articolo</em>). Già che ci siamo vi mostro anche un piccolo estratto dell'<em>header can.h</em> di Linux, dove si nota come è stato tradotto in <em>Sofware</em> il formato del <em>frame Hardware:</em></p><div><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">/**</span></div><div><span style="color: silver; font-style: italic;"> * struct can_frame - basic CAN frame structure</span></div><div><span style="color: silver; font-style: italic;"> * </span><span style="color: yellow; font-weight: bold;">@can_id:</span><span style="color: silver; font-style: italic;"> the CAN ID of the frame and CAN_*_FLAG flags, see above.</span></div><div><span style="color: silver; font-style: italic;"> * </span><span style="color: yellow; font-weight: bold;">@can_dlc:</span><span style="color: silver; font-style: italic;"> the data length field of the CAN frame</span></div><div><span style="color: silver; font-style: italic;"> * </span><span style="color: yellow; font-weight: bold;">@data:</span><span style="color: silver; font-style: italic;"> the CAN frame payload.</span></div><div><span style="color: silver; font-style: italic;"> */</span></div><div><span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">can_frame</span> <span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">canid_t</span> can_id<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> /* 32 bit CAN_ID + EFF/RTR/ERR flags */</span></div><div> __u8 can_dlc<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> /* data length code: 0 .. 8 */</span></div><div> __u8 data<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">8</span><span style="color: yellow; font-weight: bold;">]</span> __attribute__<span style="color: yellow; font-weight: bold;">((</span>aligned<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">8</span><span style="color: yellow; font-weight: bold;">)));</span></div><div><span style="color: yellow; font-weight: bold;">};</span></div></div></pre></div><p>Visto? Vengono trattati solo i campi <strong>ID</strong>, <strong>DLC</strong> e <strong>Data</strong> del <em>frame,</em> e sono stati rinominati, rispettivamente, <em>can_id</em>, <em>can_dlc</em> e <em>data</em>. Il campo <em>can_id</em> è lungo 32 bit, di cui 11 sono dedicati al vero e proprio ID del frame, mentre gli altri hanno usi un po' speciali (gestione degli errori, ecc.).</p><p>Ok, per oggi può bastare. Nella seconda parte dell'articolo vi spiegherò come testare gli esempi mostrati usando un device CAN virtuale, e se avanza tempo magari parlerò anche dei filtri e degli errori. E, come vi ho già raccomandato altre volte, non trattenete il respiro nell'attesa! (<em>che potrebbe risultare dannoso per la salute, ah ah a</em><i>h</i>).</p><p>Ciao, e al prossimo post! </p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-16086777905778615482023-04-26T19:39:00.004+02:002024-03-07T10:50:37.370+01:00The Big Select come usare la select(2) in C - pt.2<blockquote><b>Big Lebowski:</b> <i>Cos'è... cos'è che fa di un uomo un uomo, signor Lebowski?</i><br /><b>Drugo:</b> <i>Non... non lo so, signore.</i><br /><b>Big Lebowski</b><b>:</b> <i>Essere pronti a fare ciò che è più giusto. A qualunque costo. Non è questo che fa di un uomo un uomo?</i><br /><b>Drugo</b><b>:</b> <i>Sì, quello e un paio di testicoli.</i></blockquote><p>Nel capolavoro <strong><a href="https://letterboxd.com/aldoz/film/the-big-lebowski/1/" rel="noopener" target="_blank">The Big Lebowski</a></strong>, il Lebowski <em>"grande"</em> diceva al <em>Drugo</em> che un vero uomo fa sempre la cosa giusta, a qualunque costo (<em>e vabbè, poi il Drugo con il suo solito acume aggiungeva un piccolo dettaglio...</em>). Riportando questo al nostro caso, e cioè all'uso della ottima e indispensabile <em>system-call</em> <a href="https://man7.org/linux/man-pages/man2/select.2.html" rel="noopener" target="_blank"><strong>select(2)</strong></a>, potrebbe sembrare che usarla non sia proprio la cosa più giusta, visto che, come anticipato <a href="https://artcprogramming.blogspot.com/2023/03/the-big-select-come-usare-la-select2-in.html" rel="noopener" target="_blank"><strong>nello scorso articolo</strong></a>, ci sono delle controindicazioni. Ok, è venuto il momento di verificare se è il caso di usarla o no!</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCLkmhMNb6zLK9RhkOu3Lx0Ecr9iAWv9Nu6CotqxUehvgr6kJUC0B0nKpikgCJuqr1WhU8GYbUpXXpFtYEQlp3ulmer0qkkSSG3s8C6jyrClRezy7xNoxqeXQDCDRGCGF7a-g2E7qIElTWsGqfJGOhcNEqr-eiDRXKjqbWm46z_sSTHiTP8e1Z3xZIcw/s1000/the-big-lebowski2.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="563" data-original-width="1000" height="328" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCLkmhMNb6zLK9RhkOu3Lx0Ecr9iAWv9Nu6CotqxUehvgr6kJUC0B0nKpikgCJuqr1WhU8GYbUpXXpFtYEQlp3ulmer0qkkSSG3s8C6jyrClRezy7xNoxqeXQDCDRGCGF7a-g2E7qIElTWsGqfJGOhcNEqr-eiDRXKjqbWm46z_sSTHiTP8e1Z3xZIcw/w582-h328/the-big-lebowski2.jpg" width="582" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...cioè, spiegami bene 'sta storia delle controindicazioni...</td></tr></tbody></table><p>Allora, il manuale della <strong>select(2)</strong> inizia la descrizione con un <em>Warning</em>, il che dovrebbe preoccuparci:</p><blockquote><pre>WARNING: select() can monitor only file descriptors numbers that
are less than FD_SETSIZE (1024) — an unreasonably low limit for
many modern applications — and this limitation will not change.
All modern applications should instead use poll(2) or epoll(7),
which do not suffer this limitation.</pre></blockquote><p>e certo, se lo dice il <strong>Linux Programmer's Manual</strong> c'è poco da dubitare. E poi, chi siamo noi per dubitare del manuale? Eppure io, in questo caso particolare, <em>dubito</em>, e non perché voglia mettere in dubbio quanto sopra (<em>e ci mancherebbe! Il manuale è la bibbia del programmatore Linux!</em>), ma perché mi sembra che il <em>Warning</em> sia strettamente riferito a un dettaglio implementativo di <strong>Linux</strong> (e, infatti, il manuale <strong>POSIX</strong> della <a href="https://www.man7.org/linux/man-pages/man3/pselect.3p.html" rel="noopener" target="_blank"><strong>pselect(3p)</strong></a> non ne parla) e, soprattutto, la frase <em>"an unreasonably low limit for many modern applications"</em> è un po' eccessiva: secondo me le applicazioni che devono maneggiare più di 1024 <em>file descriptors</em> alla volta sono l'eccezione e non la regola. L'importante è ricordarsene al momento opportuno, e se necessario usare, come consigliato, <a href="https://man7.org/linux/man-pages/man2/poll.2.html" rel="noopener" target="_blank"><strong>poll(2)</strong></a> o <a href="https://man7.org/linux/man-pages/man7/epoll.7.html" rel="noopener" target="_blank"><strong>epoll(7)</strong></a>.</p><p>E a proposito della <em>system-call</em> <strong>poll(2)</strong> si può aggiungere che ha, esattamente come la <strong>select(2)</strong>, una <em>"versione p"</em> che si chiama <strong>ppoll(2)</strong>, che differisce dalla versione <em>"normale"</em> più o meno come la <strong>select(2)</strong> differisce dalla <strong>pselect(2)</strong>:</p><ul><li>Usa una <em>struct timespec</em> per il <em>timeout</em> mentre la <strong>poll(2)</strong> usa un <em>int</em> (in millisecondi).</li><li>Il timeout ha un comportamento differente: il valore del timeout viene mantenuto costante durante l'attività della <strong>ppoll(2)</strong>, mentre potrebbe venire aggiornato (decrementato) durante l'azione della<strong> poll(2)</strong>.</li><li>Ha un ulteriore argomento <em>sigmask</em> che, come indica il nome, permette di personalizzare i segnali POSIX durante l'esecuzione della <strong>ppoll(2)</strong>: rimpiazza la sigmask del processo con la nuova <em>sigmask</em> e poi reinstalla quella originale al termine dell'attività della chiamata.</li></ul><p>Se avete scritto del <em>Software</em> che usa la <strong>select(2)</strong> e volete convertirlo rapidamente alla <strong>poll(2)</strong> potete seguire questo semplice esempio (adattandolo alle esigenze del caso):</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">////////////////////////////////////////////////////////////////////////////////</span></div><div>VERSIONE CON LA SELECT</div><div><span style="color: silver; font-style: italic;">////////////////////////////////////////////////////////////////////////////////</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// sorvegliamo stdin (fd 0) per verificare se viene scritto qualcosa</span></div><div> fd_set rfds<span style="color: yellow; font-weight: bold;">;</span></div><div> FD_ZERO<span style="color: yellow; font-weight: bold;">(&</span>rfds<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // azzero il set</span></div><div> FD_SET<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>rfds<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // aggiungo stdin (il fd 0) al set</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// set del timeout a 5 secondi</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> timeval tv<span style="color: yellow; font-weight: bold;">;</span></div><div> tv.tv_sec <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">5</span><span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // set di 5 sec</span></div><div> tv.tv_usec <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // set di 0 usec (utile per aggiungere frazioni di secondo)</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// chiamo select(2)</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> retval <span style="color: yellow; font-weight: bold;">=</span> select<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>rfds<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">NULL,</span> <span style="color: yellow; font-weight: bold;">NULL,</span> <span style="color: yellow; font-weight: bold;">&</span>tv<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// uso retval</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// ...</span></div><br /><div><span style="color: silver; font-style: italic;">////////////////////////////////////////////////////////////////////////////////</span></div><div>VERSIONE CON LA POLL</div><div><span style="color: silver; font-style: italic;">////////////////////////////////////////////////////////////////////////////////</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// sorvegliamo stdin (fd 0) per verificare se viene scritto qualcosa</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> pollfd rfds<span style="color: yellow; font-weight: bold;">;</span></div><div> rfds.fd <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // aggiungo stdin (il fd 0) al set</span></div><div> rfds.events <span style="color: yellow; font-weight: bold;">=</span> POLLIN<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // per verificare se ci sono dati da leggere</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// set del timeout a 5 secondi</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> timeout <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">5000</span><span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // set di 5000 ms</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// chiamo poll(2)</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> retval <span style="color: yellow; font-weight: bold;">=</span> poll<span style="color: yellow; font-weight: bold;">(&</span>rfds<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">,</span> timeout<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// uso retval</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// ...</span></div></div></pre><p>facile, no?</p><p>La <a href="https://man7.org/linux/man-pages/man2/poll.2.html" rel="noopener" target="_blank"><strong>epoll(7)</strong></a>, invece, è tutta un'altra storia: è una vera e propria API presente solo su Linux che permette, usando le varie funzioni a disposizione (<a href="https://man7.org/linux/man-pages/man2/epoll_create.2.html" rel="noopener" target="_blank"><strong>epoll_create(2)</strong></a>, <strong>epoll_create1(2)</strong>, <a href="https://man7.org/linux/man-pages/man2/epoll_ctl.2.html" rel="noopener" target="_blank"><strong>epoll_ctl(2)</strong></a>, <a href="https://man7.org/linux/man-pages/man2/epoll_wait.2.html" rel="noopener" target="_blank"><strong>epoll_wait(2)</strong></a>) un uso veramente molto sofisticato (ma, ahimè, abbastanza complicato) della gestione degli eventi di I/O: da usare proprio per esigenze particolari, direi.</p><p>E veniamo al famoso <strong>10000</strong>, il numero scelto, non a caso, nell'articolo precedente per anticipare i problemi della <strong>select(2)</strong>: visto il limite di 1024 <em>file descriptors</em>, <strong>la select(2)</strong> non è la <em>system-call</em> più adatta a evitare il famigerato <strong>C10K problem</strong>, che descrive esattamente il caso di un <em>mega-server</em> che deve trattare moltissime connessioni contemporanee: l'autore del <a href="http://www.kegel.com/c10k.html" rel="noopener" target="_blank"><strong>primo articolo</strong></a> che trattò l'argomento scelse a titolo esemplificativo il numero 10000 (che, ai tempi, nel 1999, era un numero enorme per <em>l'Hardware</em> disponibile, mentre ora questo numero sembra persino piccolissimo per i <em>mega-server</em> attuali). Ecco spiegato il mistero del 10000 (e leggetevi l'articolo citato sopra, è molto interessante).</p><p>Ed ora, come promesso nella prima parte dell'articolo, vi propongo un uso un po' originale della <strong>select(2)</strong>: non so se ricordate il <a href="https://artcprogramming.blogspot.com/2021/01/per-un-pugno-di-ipc-considerazioni.html" rel="noopener" target="_blank"><strong>ciclo di articoli sulle POSIX IPC</strong></a>, dove nel <a href="https://artcprogramming.blogspot.com/2021/02/per-qualche-ipc-in-piu-considerazioni.html" rel="noopener" target="_blank"><strong>capitolo riservato alle Message Queue</strong></a> avevo mostrato due programmi <em>reader</em> e <em>writer</em> (in pratica un <em>server</em> e un <em>client</em>) che usavano le funzioni <a href="https://man7.org/linux/man-pages/man3/mq_send.3.html" rel="noopener" target="_blank"><strong>mq_send(3)</strong></a> e <a href="https://man7.org/linux/man-pages/man3/mq_receive.3.html" rel="noopener" target="_blank"><strong>mq_receive(3)</strong></a>. Queste due funzioni hanno delle versioni <em>"con timeout"</em>, che si chiamano, rispettivamente, <strong>mq_timedend(3)</strong> e <strong>mq_timedrecv(3</strong>). Queste versioni sono, in alcuni casi, molto utili, e sono assenti nelle analoghe chiamate <strong>send(2)</strong> e <strong>recv(2)</strong> della classica interfaccia <strong>BSD Socket</strong>. Per colmare questa mancanza ho scritto due nuove funzioni, <strong>timedSend()</strong> e <strong>timedRecv()</strong> che usano, ovviamente, la <strong>select(2)</strong> per la gestione del <em>timeout.</em> Le due nuove funzioni hanno gli stessi parametri delle versioni <em>"normali"</em> con l'aggiunta di un ulteriore parametro<em> "timeout_ms"</em> che serve a impostare, per l'appunto, il <em>timeout</em>. E allora vediamole, 'ste funzioni: vai col codice!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">errno.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/select.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/types.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/socket.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">timedSendRecv.h</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: #569cd6;"> </span><span style="color: silver; font-style: italic;">// contiene solo i prototipi</span></div><br /><div><span style="color: silver; font-style: italic;">// timedRecv - una recv(2) con timeout</span></div><div><span style="color: yellow; font-weight: bold;">ssize_t</span> timedRecv<span style="color: yellow; font-weight: bold;">(int</span> sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">void</span> <span style="color: yellow; font-weight: bold;">*</span>buf<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">size_t</span> len<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">int</span> flags<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">unsigned</span> <span style="color: yellow; font-weight: bold;">int</span> timeout_ms<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// test se timeout_ms è maggiore di 0</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>timeout_ms<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// timeout maggiore di 0: set del timeout della select(2)</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">timeval</span> tv<span style="color: yellow; font-weight: bold;">;</span></div><div> tv<span style="color: yellow; font-weight: bold;">.</span>tv_sec <span style="color: yellow; font-weight: bold;">=</span> timeout_ms <span style="color: yellow; font-weight: bold;">/</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">;</span></div><div> tv<span style="color: yellow; font-weight: bold;">.</span>tv_usec <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">(</span>timeout_ms <span style="color: yellow; font-weight: bold;">%</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">*</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">;</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// eseguo select(2) e (eventualmente) la recv(2)</span></div><div> <span style="color: yellow; font-weight: bold;">fd_set</span> readfds<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: white; font-weight: bold;">FD_ZERO</span><span style="color: yellow; font-weight: bold;">(&</span>readfds<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: white; font-weight: bold;">FD_SET</span><span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>readfds<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> rc <span style="color: yellow; font-weight: bold;">=</span> select<span style="color: yellow; font-weight: bold;">(</span>sockfd <span style="color: yellow; font-weight: bold;">+</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>readfds<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>tv<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>rc <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// select(2) fallita: ritorno errore senza cambiare errno</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>rc<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// dati disponibili: eseguo recv(2)</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> recv<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> buf<span style="color: yellow; font-weight: bold;">,</span> len<span style="color: yellow; font-weight: bold;">,</span> flags<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// timeout scaduto: ritorno errore con errno=ETIMEDOUT (utile per il chiamante)</span></div><div> <span style="color: white; font-weight: bold;">errno</span> <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">ETIMEDOUT</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// timeout_ms è 0: eseguo direttamente la recv(2)</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> recv<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> buf<span style="color: yellow; font-weight: bold;">,</span> len<span style="color: yellow; font-weight: bold;">,</span> flags<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// timedSend - una send(2) con timeout</span></div><div><span style="color: yellow; font-weight: bold;">ssize_t</span> timedSend<span style="color: yellow; font-weight: bold;">(int</span> sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">const</span> <span style="color: yellow; font-weight: bold;">void</span> <span style="color: yellow; font-weight: bold;">*</span>buf<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">size_t</span> len<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">int</span> flags<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">unsigned</span> <span style="color: yellow; font-weight: bold;">int</span> timeout_ms<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// test se timeout_ms è maggiore di 0</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>timeout_ms<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// timeout maggiore di 0: set del timeout della select(2)</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">timeval</span> tv<span style="color: yellow; font-weight: bold;">;</span></div><div> tv<span style="color: yellow; font-weight: bold;">.</span>tv_sec <span style="color: yellow; font-weight: bold;">=</span> timeout_ms <span style="color: yellow; font-weight: bold;">/</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">;</span></div><div> tv<span style="color: yellow; font-weight: bold;">.</span>tv_usec <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">(</span>timeout_ms <span style="color: yellow; font-weight: bold;">%</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">*</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">;</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// eseguo select(2) e (eventualmente) la send(2)</span></div><div> <span style="color: yellow; font-weight: bold;">fd_set</span> writefds<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: white; font-weight: bold;">FD_ZERO</span><span style="color: yellow; font-weight: bold;">(&</span>writefds<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: white; font-weight: bold;">FD_SET</span><span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>writefds<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> rc <span style="color: yellow; font-weight: bold;">=</span> select<span style="color: yellow; font-weight: bold;">(</span>sockfd <span style="color: yellow; font-weight: bold;">+</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>writefds<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>tv<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>rc <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// select(2) fallita: ritorno errore senza cambiare errno</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>rc<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// dati disponibili: eseguo send(2)</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> send<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> buf<span style="color: yellow; font-weight: bold;">,</span> len<span style="color: yellow; font-weight: bold;">,</span> flags<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// timeout scaduto: ritorno errore con errno=ETIMEDOUT (utile per il chiamante)</span></div><div> <span style="color: white; font-weight: bold;">errno</span> <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">ETIMEDOUT</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// timeout_ms è 0: eseguo direttamente la send(2)</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> send<span style="color: yellow; font-weight: bold;">(</span>sockfd<span style="color: yellow; font-weight: bold;">,</span> buf<span style="color: yellow; font-weight: bold;">,</span> len<span style="color: yellow; font-weight: bold;">,</span> flags<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Che ne dite? Sono relativamente semplici, <a href="https://artcprogramming.blogspot.com/2012/08/no-comment.html" rel="noopener" target="_blank"><strong>stra-commentate</strong></a> (non credo che ci sia nulla da aggiungere) e funzionano anche bene! Io le uso da molto tempo anche in progetti reali, e permettono alcuni<em> "giri di codice"</em> interessanti, per esempio quando è necessario fare un <em>loop</em> di lettura non bloccante. <em>Provare per credere!</em></p><p>Ok, per oggi può bastare, e anzi può bastare anche per l'argomento <em>select/poll</em> (su cui si potrebbe scrivere un libro, ma per il momento ci fermiamo qui). Per il prossimo articolo non vi prometto nessun argomento in particolare (che poi non mantengo le promesse e mi devo pure scusare). L'unica cosa che vi prometto è che sarà sicuramente molto interessante! (<em>ehm, che modestia...).</em></p><p>Ciao, e al prossimo post!</p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-87167063718977851942023-03-19T17:06:00.001+01:002023-04-25T16:17:33.857+02:00The Big Select come usare la select(2) in C - pt.1<blockquote><b>Poliziotto:</b> <i>E nella valigetta?</i><br /><b>Drugo:</b> <i>Oh, beh, documenti, solo documenti. Già, solo i miei documenti. Documenti di lavoro.</i><br /><b>Poliziotto</b><b>:</b> <i>Che lavoro fa?</i><br /><b>Drugo</b><b>:</b> <i>Sono disoccupato.</i><br /></blockquote><p>Stavo cercando di ultimare la seconda parte dell'oramai <a href="https://artcprogramming.blogspot.com/2023/01/edge-of-real-time-considerazioni-sulla.html" rel="noopener" target="_blank"><strong>mitico articolo</strong></a> sulla programmazione <em>real-time</em> e mi sono reso conto che in anni e anni di onorata carriera divulgativa (<em>ehm...</em>) non ho mai parlato della <a href="https://man7.org/linux/man-pages/man2/select.2.html" rel="noopener" target="_blank"><strong>select(2)</strong></a>. Non sia mai! La <strong>select(2)</strong> è una funzione così importante che non si può rimandare ulteriormente. L'altro articolo dovrà aspettare ancora un po' (<em>anzi, smetto di parlarne, quando arriverà sarà una sorpresa</em>): oggi si parla di <strong>select(2)</strong> e farò come il <strong>drugo</strong> del <a href="https://letterboxd.com/aldoz/film/the-big-lebowski/1/" rel="noopener" target="_blank"><strong>capolavoro</strong></a> dei <a href="https://letterboxd.com/director/joel-coen/" rel="noopener" target="_blank"><strong>fratelli Coen</strong></a>: lui si che è un tipo concreto (vedi il dialogo qui sopra) uno con degli obiettivi precisi e diretti (<i>si, si, lo so, e i più attenti se ne saranno già accorti: il film e il dialogo li ho già usati per <b><a href="https://artcprogramming.blogspot.com/2016/09/il-grande-lighttpd-come-scrivere-un.html" target="_blank">un altro articolo</a></b>... ma l'ho rivisto da poco (il film) e non ho resistito alla tentazione di ri-utilizzarlo, chiedo venia...</i>).</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghundU7wZnUdZNdMJj9Onh1ytPGL1HGawlqB_1TxTDXR3vAXYpnXXJIugwyZdbPx0sheVBYzob8XOA-rkSoNfb_yd_4KcJ-3KWEpHRZYILuc1VT2utA-VAgnZy6Nbvzck2NNzOfcLPToy0zAOqi_6WCQBSvdvMQsxi-G3nbVe9dcdXqWAqNygXp5W4tA/s900/the-big-lebowski.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="506" data-original-width="900" height="328" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghundU7wZnUdZNdMJj9Onh1ytPGL1HGawlqB_1TxTDXR3vAXYpnXXJIugwyZdbPx0sheVBYzob8XOA-rkSoNfb_yd_4KcJ-3KWEpHRZYILuc1VT2utA-VAgnZy6Nbvzck2NNzOfcLPToy0zAOqi_6WCQBSvdvMQsxi-G3nbVe9dcdXqWAqNygXp5W4tA/w584-h328/the-big-lebowski.jpg" width="584" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...sorseggiavo il mio White Russian e pensavo: "Ma... e la select(2)?"...</td></tr></tbody></table><p>E allora veniamo al dunque: la <strong>select(2</strong>) è una <strong>system-call</strong> importantissima che permette (come dice il manuale) di eseguire il <em>"synchronous I/O multiplexing"</em>, e cioè permette di sorvegliare più canali di I/O alla volta (che tipo di canali? Pensate ai <em>socket,</em> ai file aperti, ecc.) per verificare quando sono pronti per una nuova operazione di <em>read/write</em>. Cioè, in pratica, permette di eseguire in un singolo <em>thread</em> di esecuzione quello che spesso si esegue (in maniera ingiustificata) in <em>multithreading</em> (<em>e scusate se è poco!</em>). Un piccolo esempio: un buon <em>Server TCP</em> che serve 10000 Client: secondo voi è più efficiente e funzionale aprire 10000 <em>thread</em> che aspettano i dati dai <em>Client</em> o usare il <em>multiplexing</em> ? Se qualcuno pensa che è meglio aprire 10000 <em>thread</em> il mio consiglio è:</p><ul><li>mettere le scarpe da <em>running</em> e correre per 10 Km a buon ritmo (un metro per ogni <em>thread...).</em></li><li>dopodiché fare una bella doccia rilassante e ripensare all'argomento con la mente (ora) decisamente più aperta.</li><li>a questo punto, se si preferiscono ancora i 10000 <em>thread</em>, c'è da considerare l'idea di cambiare mestiere.</li></ul><p><strong>Ma, ovviamente, scherzo:</strong> ci sono in giro <em>Server TCP</em> e <em>Web</em> con <em>multithread "spinto",</em> <strong>scritti da gente brava e competente</strong>, in grado di servire ben più di 10000 connessioni alla volta (usando, però, mostruose risorse Hardware di CPU e RAM). In ogni caso io <a href="https://artcprogramming.blogspot.com/2020/11/processi-o-thread-considerazioni-sulla.html" rel="noopener" target="_blank"><strong>continuo a pensare</strong> </a>che il <em>mutithreading</em> viene usato spesso a sproposito per semplice pigrizia progettuale, e quando posso lo evito (<em>ah, dimenticavo: il numero 10000 qui sopra non l'ho scelto a caso, ci ritorneremo nella seconda parte dell'articolo</em>).</p><p>Eppure, nonostante gli evidenti meriti, la<strong> select(2)</strong> è abbastanza misconosciuta, e penso che i motivi siano due:</p><ol><li>Non è immediatamente evidente dove e quando sia utile usarla.</li><li>Non è semplicissima da usare, visto che lavora in simbiosi con ben quattro macro, che preparano l'ambiente di esecuzione e testano i risultati.</li></ol><p>E allora cerchiamo di fare chiarezza, siamo qui per questo! Per quanto riguarda il punto 1 lo abbiamo già descritto sopra, e la parola magica è <em>"multiplexing"</em> (anche se, in realtà, ci sono anche altri usi interessanti che vedremo prossimamente). Una volta chiarito dove e quando usarla si può passare al come, e credo che può tornare utile questa piccola lista che ho scritto, con le descrizioni degli argomenti della select(2) e delle quattro macro abbinate:</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// select() - gestisce il synchronous I/O multiplexing su un set di descrittori di file</span></div><div><span style="color: yellow; font-weight: bold;">int</span> select<span style="color: yellow; font-weight: bold;">(</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> nfds<span style="color: yellow; font-weight: bold;">,</span><span style="color: silver; font-style: italic;"> // fd con il numero più alto (+1) nei 3 set sorvegliati</span></div><div> fd_set <span style="color: yellow; font-weight: bold;">*</span>readfds<span style="color: yellow; font-weight: bold;">,</span><span style="color: silver; font-style: italic;"> // set di fd da sorvegliare per "ready for reading"</span></div><div> fd_set <span style="color: yellow; font-weight: bold;">*</span>writefds<span style="color: yellow; font-weight: bold;">,</span><span style="color: silver; font-style: italic;"> // set di fd da sorvegliare per "ready for writing"</span></div><div> fd_set <span style="color: yellow; font-weight: bold;">*</span>exceptfds<span style="color: yellow; font-weight: bold;">,</span><span style="color: silver; font-style: italic;"> // set di fd da sorvegliare per eventi eccezionali</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">timeval</span> <span style="color: yellow; font-weight: bold;">*</span>timeout<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // tempo di bloccaggio del set durante la sorveglianza</span></div><br /><div><span style="color: silver; font-style: italic;">// FD_CLR() - rimuove il file descriptor fd dal set di descrittori</span></div><div>FD_CLR<span style="color: yellow; font-weight: bold;">(</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> fd<span style="color: yellow; font-weight: bold;">,</span><span style="color: silver; font-style: italic;"> // file descriptor da rimuovere dal set</span></div><div> fd_set <span style="color: yellow; font-weight: bold;">*</span>set<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // set di file descriptor</span></div><br /><div><span style="color: silver; font-style: italic;">// FD_SET() - cerca il file descriptor fd nel set di descrittori</span></div><div>FD_ISSET<span style="color: yellow; font-weight: bold;">(</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> fd<span style="color: yellow; font-weight: bold;">,</span><span style="color: silver; font-style: italic;"> // file descriptor da cercare nel</span></div><div> fd_set <span style="color: yellow; font-weight: bold;">*</span>set<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // set di file descriptor</span></div><br /><div><span style="color: silver; font-style: italic;">// FD_SET() - aggiunge il file descriptor fd al set di descrittori</span></div><div>FD_SET<span style="color: yellow; font-weight: bold;">(</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> fd<span style="color: yellow; font-weight: bold;">,</span><span style="color: silver; font-style: italic;"> // file descriptor da aggiungere al set</span></div><div> fd_set <span style="color: yellow; font-weight: bold;">*</span>set<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // set di file descriptor</span></div><br /><div><span style="color: silver; font-style: italic;">// FD_SET() - rimuove tutti i file descriptor dal set di descrittori</span></div><div>FD_ZERO<span style="color: yellow; font-weight: bold;">(</span></div><div> fd_set <span style="color: yellow; font-weight: bold;">*</span>set<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // set di file descriptor da svuotare</span></div></div></pre><p>E tutto questo lo trovate anche nel manuale, eh! E, sicuramente, con più dettagli e con migliori descrizioni, ma lo specchietto qui sopra è una specie di <em>quick-reference guide</em> per chi non ha voglia di leggersi le mille spiegazioni del manuale (<em>che, in questo caso, sono un po' complesse e magari contribuiscono a far passare la voglia di usare la select(2)...)</em>. Nella stessa pagina del manuale si descrive anche una <em>system-call "gemella"</em>, la <strong>pselect(2)</strong>, che è sostanzialmente identica a parte queste caratteristiche:</p><ul><li>Ha un sesto argomento <em>sigmask</em> che, come indica il nome, permette di personalizzare i <strong><a href="https://man7.org/linux/man-pages/man7/signal.7.html" rel="noopener" target="_blank">segnali POSIX</a> </strong>surante l'esecuzione della <strong>pselect(2)</strong>: rimpiazza la sigmask del processo con la nuova <em>sigmask</em> e poi reinstalla quella originale al termine dell'attività della <strong>pselect(2)</strong>. Questa è, evidentemente, una funzionalità molto utile e interessante.</li><li>Usa una <em>struct timeval</em> per il <em>timeout</em> (invece di una<em> struct timespec</em>): questo cambio tipo è abbastanza irrilevante, ma è associato a un comportamento differente: il valore del <em>timeout</em> viene mantenuto costante durante l'attività della <strong>pselect(2)</strong>, mentre <i>potrebbe</i> venire aggiornato (decrementato) durante l'azione della <strong>select(2)</strong>: anche questo fatto è da tenere in conto scrivendo il codice.</li></ul><p>Un ultimo appunto lo merita il descrittore <em>exceptfds</em>: per <em>"eventi eccezionali"</em> non si intendono gli errori ma, tipicamente, messaggi speciali (<em>"Urgent Messages"</em>) generati da alcuni protocolli: un buon esempio è il messaggio <em>"out-of-band"</em> che si può ricevere su un <em>socket TCP</em> (ma questo è un argomento molto particolare che necessiterebbe un articolo a parte: diciamo che il set <em>exceptfds</em> si usa poco e in casi molto specifici).</p><p>E quindi come si usa la select? Diciamo che per un uso <em>"classico"</em> sono sufficienti questi cinque passi:</p><ol><li>Si definiscono, usando il tipo <em>fd_set</em>, i <em>set</em> di descrittori di file da sorvegliare.</li><li>Si inizializzano i <em>set</em> usando la macro FD_ZERO (per azzerare il set) seguita da FD_SET (per aggiungere un file al <em>set).</em></li><li>Si prepara il <em>timeout</em> riempiendo una <em>struct timeval</em> (ovvero si scrivono i secondi e i microsecondi che compongono il nostro <em>timeout)</em></li><li>Si lancia la <strong>select(2)</strong> con gli argomenti preparati nei punti precedenti.</li><li>Si testa il risultato della <strong>select(2)</strong> per eseguire le varie ed eventuali operazioni che necessitiamo.</li></ol><p>E qui casca a fagiolo un bell'esempio pratico ed elementare, lo stesso presente nel manuale, tradotto e <a href="https://artcprogramming.blogspot.com/2012/08/no-comment.html" rel="noopener" target="_blank"><strong>con qualche commento in più</strong></a> (<em>perché inventarne uno nuovo? Questo è veramente ben fatto</em>). In quest'esempio si sorveglia il <strong>descrittore 0</strong> che non è nient'altro che il famoso <em>standard input "stdin"</em> e, in base all'attività sullo <em>stdin</em> (ossia se scriviamo o no qualcosa sulla tastiera), visualizzeremo il risultato corrispondente: nell'esempio il <em>timeout</em> è di 5 secondi e quindi, se non scriviamo nulla, apparirà dopo 5 secondi la scritta <em>"nessun dato disponibile"</em> , ma se scriviamo qualcosa prima che scada il <em>timeout</em>, apparirà la scritta <em>"ci sono dati disponibili"</em>. Semplicissimo, no? Vai col codice!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdlib.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/select.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(void)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">fd_set</span> rfds<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">timeval</span> tv<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> retval<span style="color: yellow; font-weight: bold;">;</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// sorvegliamo stdin (fd 0) per verificare se viene scritto qualcosa</span></div><div> <span style="color: white; font-weight: bold;">FD_ZERO</span><span style="color: yellow; font-weight: bold;">(&</span>rfds<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // azzero il set</span></div><div> <span style="color: white; font-weight: bold;">FD_SET</span><span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>rfds<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // aggiungo stdin (il fd 0) al set</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// set del timeout a 5 secondi</span></div><div> tv<span style="color: yellow; font-weight: bold;">.</span>tv_sec <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">5</span><span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // set di 5 sec</span></div><div> tv<span style="color: yellow; font-weight: bold;">.</span>tv_usec <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // set di 0 usec (utile per aggiungere frazioni di secondo)</span></div><br /><div> retval <span style="color: yellow; font-weight: bold;">=</span> select<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>rfds<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>tv<span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: silver; font-style: italic;"> /* N.B. da qui in avanti il valore di tv cambia dinamicamente:</span></div><div><span style="color: silver; font-style: italic;"> bisogna tenerlo in conto nel caso di usarlo! */</span></div><br /><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>retval <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// retval == -1 indica che la select() ha fallito</span></div><div> perror<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">select()</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>retval<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: silver; font-style: italic;"> /* retval > 0 indica che qualcuno ha scritto</span></div><div><span style="color: silver; font-style: italic;"> qua si poteva anche usare questo test: FD_ISSET(0, &rfds) > 0 */</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">ci sono dati disponibili!\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// retval == 0 indica che è scaduto il timeout</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">nessun dato disponibile\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div> exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">EXIT_SUCCESS</span><span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Ok, per oggi può bastare. Spero che questa introduzione abbia fatto comprendere la potenza e l'utilità della <em>sistem-call</em> <strong>select(2)</strong> e abbia fatto venire la voglia a qualcuno di usarla in qualche progetto reale. Nella seconda parte dell'articolo parleremo delle criticità della <strong>select(2)</strong> (<em>spoiler: ahimè, ce ne sono! Ad esempio quel 10000 usato qua sopra...</em>), parleremo delle possibili alternative e, <em>dulcis in fundo</em>, proporrò un esempio di uso<em> "non proprio canonico"</em> della <strong>select(2)</strong> che potrebbe interessare a molti. <em>Non state in pena, ci risentiremo presto!</em></p><p>Ciao, e al prossimo post!</p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-487169215482374282023-02-22T19:28:00.002+01:002023-02-23T12:44:39.384+01:00Licorice System pt.2 come scrivere una system(3) con cattura dello stdout in C<blockquote><b>Alana:</b> <i>Lo sapevo! Sapevo che era quello che stavi pensando. Pensi sempre alle cose, pensatore! Tu, pensatore! Tu pensi cose!</i></blockquote><p><a href="https://artcprogramming.blogspot.com/2023/01/edge-of-real-time-considerazioni-sulla.html" rel="noopener" target="_blank"><strong>Nell'ultimo articolo</strong></a> avevo annunciato un seguito incentrato sui metodi di programmazione real-time, ma ho deciso di rimandarlo, perché ho avuto la necessità di aggiungere (<em>per motivi miei esterni al blog</em>) una nuova funzionalità a una versione migliorata della<a href="https://man7.org/linux/man-pages/man3/system.3.html" rel="noopener" target="_blank"><strong> system(3)</strong></a> che avevo proposto <a href="https://artcprogramming.blogspot.com/2022/10/licorice-system-come-scrivere-una.html" rel="noopener" target="_blank"><strong>in un altro articolo</strong></a>, e mi è sembrata una buona idea fare una seconda puntata con un po' di dettagli (spero interessanti). Il fatto è che bisognerebbe sempre essere come il <em>"pensatore"</em> della frase citata qui sopra (tratta dal bel <a href="https://letterboxd.com/aldoz/film/licorice-pizza/" rel="noopener" target="_blank"><strong>Licorice Pizza</strong></a> del Maestro <a href="https://letterboxd.com/director/paul-thomas-anderson/" rel="noopener" target="_blank"><strong>Paul Thomas Anderson</strong></a>). <em>Pensare, pensare, pensare... quello si che è un gran lavoro, e i programmatori ne sanno qualcosa, no?</em></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNB3Nxu3nT6ZgiXSng36SoQQGMsibGrtBut1Qsd9XtLT6JMBDLSARRE_0ZcExrgbSyF3XxFsTyZnnVjprwAeARiQj4ZrIRVaEQvKHwloZBsgG-2MErEhbTq5VVEuyGkdVI_sUnI-B2mN58s54vEpJTWrrEXGKdm-zX7LE9ewhiGQEwBxGP56pywhE67g/s2560/licorice-system.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1252" data-original-width="2560" height="274" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNB3Nxu3nT6ZgiXSng36SoQQGMsibGrtBut1Qsd9XtLT6JMBDLSARRE_0ZcExrgbSyF3XxFsTyZnnVjprwAeARiQj4ZrIRVaEQvKHwloZBsgG-2MErEhbTq5VVEuyGkdVI_sUnI-B2mN58s54vEpJTWrrEXGKdm-zX7LE9ewhiGQEwBxGP56pywhE67g/w561-h274/licorice-system.jpg" width="561" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...ho pensato ripetutamente, ma non ricordo cosa...</td></tr></tbody></table><p>Riepilogando, vi ricordo che questo articolo è, di fatto, la terza parte di <a href="https://italiancoders.it/system-no-grazie-considerazioni-sul-perche-non-usare-la-system3-in-c-e-c/" rel="noopener" target="_blank"><strong>System? No, grazie!</strong></a>, anche se il titolo originale si era già perso nella seconda parte (<em>e, già che ci sono, vi ricordo che di articoli della famiglia "No Grazie!" ne ho scritti altri, e vi invito a leggerli o rileggerli, </em><em><a href="https://artcprogramming.blogspot.com/2020/09/sleep-no-grazie-considerazioni-sul.html" rel="noopener" target="_blank"><strong>qui</strong></a>, <a href="https://artcprogramming.blogspot.com/2019/12/sprintf-no-grazie-considerazioni.html" rel="noopener" target="_blank"><strong>qui</strong>,</a> <a href="https://artcprogramming.blogspot.com/2019/10/debugger-no-grazie-considerazioni.html" rel="noopener" target="_blank"><strong>qui</strong></a> e <a href="https://artcprogramming.blogspot.com/2019/02/variabili-globali-no-grazie-come-non.html" rel="noopener" target="_blank"><strong>qui</strong></a></em>).</p><p>Nella prima parte avevo descritto i mille problemi della <strong>system(3)</strong>, una vera funzione anti-pattern da non usare mai, e nella seconda parte avevo proposto una funzione, la <strong>toutSystem()</strong>, che correggeva vari problemi, tra cui il più grave:</p><blockquote><p>"Quando si chiama la system(3) il programma principale viene sospeso fino al termine del comando invocato, e non c'è nessuna maniera di controllare efficacemente quello che sta succedendo."</p></blockquote><p>e quanto sopra implica anche che, oltre alla mancanza di controllo, potremmo avere la nostra applicazione (o un thread dell'applicazione) completamente bloccata da una <strong>system(3)</strong> che non è ritornata! Con la <strong>toutSystems()</strong> questo problema sparisce, perché si introduce un fondamentale <em>timeout</em> oltre il quale il nostro comando esterno viene bloccato restituendo un adeguato tracciamento dell'errore. <em>Molto bene, no?</em></p><p>Poi, però, ho pensato che alla<strong> toutSystem()</strong> manca qualcosa: Ok, non si blocca e ci permette di conoscere l'esito, buono o cattivo, della nostra esecuzione (attraverso il semplice codice di ritorno) ma... e se avessimo anche bisogno di ricevere dei dati dal comando esterno eseguito? Magari scritti, come è logico aspettarsi, nello <em>standard output (stdout per gli amici)</em>? Si può fare? Ma certo! vai col codice!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div style="line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">_GNU_SOURCE</span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">string.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdlib.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">unistd.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">errno.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">time.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">fcntl.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/wait.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// prototipi locali</span></div><div><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">int</span> toutSystemStdout<span style="color: yellow; font-weight: bold;">(const</span> <span style="color: yellow; font-weight: bold;">char*</span> command<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">unsigned</span> <span style="color: yellow; font-weight: bold;">int</span> timeout_ms<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>dest<span style="color: yellow; font-weight: bold;">,</span></div><div> <span style="color: yellow; font-weight: bold;">size_t</span> n<span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">void</span> mySleep<span style="color: yellow; font-weight: bold;">(unsigned</span> <span style="color: yellow; font-weight: bold;">int</span> milliseconds<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">TOUT_SLEEP</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">500</span><span style="color: silver; font-style: italic;"> // intervallo di sleep per il loop busy wait del timeout</span></div><br /><div><span style="color: silver; font-style: italic;">// funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// test con un programma che scrive sullo stdout e esce</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">main: eseguo toutSystemStdout(\"./test\", 5000, buf, sizeof(buf))\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> buf<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">256</span><span style="color: yellow; font-weight: bold;">];</span><span style="color: silver; font-style: italic;"> // verificare se questo può andare in overflow</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>toutSystemStdout<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">./test</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">5000</span><span style="color: yellow; font-weight: bold;">,</span> buf<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>buf<span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">!=</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: toutSystemStdout: cmd output: %s\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> __func__<span style="color: yellow; font-weight: bold;">,</span> buf<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">else</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: toutSystemStdout error\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> __func__<span style="color: yellow; font-weight: bold;">);</span></div><br /><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// toutSystem() - una system(3) con timeout e catturo dello stdout del comando</span></div><div><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">int</span> toutSystemStdout<span style="color: yellow; font-weight: bold;">(</span></div><div> <span style="color: yellow; font-weight: bold;">const</span> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>command<span style="color: yellow; font-weight: bold;">,</span><span style="color: silver; font-style: italic;"> // il comando shell da eseguire (e.g.: cp -v file1 file2)</span></div><div> <span style="color: yellow; font-weight: bold;">unsigned</span> <span style="color: yellow; font-weight: bold;">int</span> timeout_ms<span style="color: yellow; font-weight: bold;">,</span><span style="color: silver; font-style: italic;"> // timeout in ms: 0 significa senza timeout</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>dest<span style="color: yellow; font-weight: bold;">,</span><span style="color: silver; font-style: italic;"> // buffer destinazione per lo standard output del comando</span></div><div> <span style="color: yellow; font-weight: bold;">size_t</span> n<span style="color: yellow; font-weight: bold;">)</span><span style="color: silver; font-style: italic;"> // size del buffer</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> errmsg_buf<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">256</span><span style="color: yellow; font-weight: bold;">];</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// creo una pipe in modo nonblocking</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> pipefd<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">2</span><span style="color: yellow; font-weight: bold;">];</span><span style="color: silver; font-style: italic;"> // pipefd[0] = lato input della pipe; pipefd[1] = lato output della pipe</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>pipe2<span style="color: yellow; font-weight: bold;">(</span>pipefd<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">O_NONBLOCK</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore pipe</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore pipe: %s\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> __func__<span style="color: yellow; font-weight: bold;">,</span> strerror_r<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">,</span> errmsg_buf<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>errmsg_buf<span style="color: yellow; font-weight: bold;">)));</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// fork + exec + wait</span></div><div> <span style="color: yellow; font-weight: bold;">pid_t</span> pid <span style="color: yellow; font-weight: bold;">=</span> fork<span style="color: yellow; font-weight: bold;">();</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>pid <span style="color: yellow; font-weight: bold;">==</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// figlio</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">//</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// chiudo pipefd[0] che è il lato input/read della pipe</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>pipefd<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">]);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// uso dup2() invece di close()+dup() per evitare eventuali race-condition</span></div><div> dup2<span style="color: yellow; font-weight: bold;">(</span>pipefd<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">],</span> <span style="color: white; font-weight: bold;">STDOUT_FILENO</span><span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // per il stdout</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// chiudo pipefd[1] che è il lato output/write della pipe che già non serve</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>pipefd<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">]);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// eseguo il comando come lo esegue la system(3)</span></div><div> execl<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">/bin/sh</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">sh</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">-c</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> command<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">(char</span> <span style="color: yellow; font-weight: bold;">*)</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// questo viene eseguito solo se fallisce exec (exec non ritorna mai)</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: figlio: processo %d: errore exec: %s\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> __func__<span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">(),</span> strerror_r<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">,</span> errmsg_buf<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>errmsg_buf<span style="color: yellow; font-weight: bold;">)));</span></div><div> exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>pid <span style="color: yellow; font-weight: bold;">></span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// padre</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">//</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// chiudo pipefd[1] che è il lato output/write della pipe</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>pipefd<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">]);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// attesa uscita del figlio</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: padre: processo %d: attesa uscita del figlio\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> __func__<span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">());</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> rc_wait<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> status<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>timeout_ms <span style="color: yellow; font-weight: bold;">></span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span><span style="color: silver; font-style: italic;"> // check timeout</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// busy wait con timeout</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> cnt_wait <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">(</span>read<span style="color: yellow; font-weight: bold;">(</span>pipefd<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> dest<span style="color: yellow; font-weight: bold;">,</span> n<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">!=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div> mySleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">TOUT_SLEEP</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(++</span>cnt_wait <span style="color: yellow; font-weight: bold;">></span> timeout_ms <span style="color: yellow; font-weight: bold;">/</span> <span style="color: white; font-weight: bold;">TOUT_SLEEP</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// figlio non uscito prima del timeout: return errore</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: padre: processo %d: waitpid timeout scaduto\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> __func__<span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">());</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div> rc_wait <span style="color: yellow; font-weight: bold;">=</span> waitpid<span style="color: yellow; font-weight: bold;">(</span>pid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>status<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">WNOHANG</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// wait senza timeout</span></div><div> <span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">(</span>read<span style="color: yellow; font-weight: bold;">(</span>pipefd<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> dest<span style="color: yellow; font-weight: bold;">,</span> n<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">!=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span></div><div> mySleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">TOUT_SLEEP</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div> rc_wait <span style="color: yellow; font-weight: bold;">=</span> waitpid<span style="color: yellow; font-weight: bold;">(</span>pid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>status<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// attesa terminata: analizzo il risultato</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>rc_wait <span style="color: yellow; font-weight: bold;">!=</span> pid<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore waitpid</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: padre: processo %d: errore waitpid (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> __func__<span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">(),</span> strerror_r<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">,</span> errmsg_buf<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>errmsg_buf<span style="color: yellow; font-weight: bold;">)));</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// processo terminato: return risultato</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> result <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">WIFEXITED</span><span style="color: yellow; font-weight: bold;">(</span>status<span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// questo è l'unico risultato accettato come successo</span></div><div> result <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: padre: processo %d: pid %d uscito (status=%d)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> __func__<span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">(),</span> pid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">WEXITSTATUS</span><span style="color: yellow; font-weight: bold;">(</span>status<span style="color: yellow; font-weight: bold;">));</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">WIFSIGNALED</span><span style="color: yellow; font-weight: bold;">(</span>status<span style="color: yellow; font-weight: bold;">))</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: padre: processo %d: pid %d ucciso dal segnale %d\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> __func__<span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">(),</span> pid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">WTERMSIG</span><span style="color: yellow; font-weight: bold;">(</span>status<span style="color: yellow; font-weight: bold;">));</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">WIFSTOPPED</span><span style="color: yellow; font-weight: bold;">(</span>status<span style="color: yellow; font-weight: bold;">))</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: padre: processo %d: pid %d fermato dal segnale %d\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> __func__<span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">(),</span> pid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">WSTOPSIG</span><span style="color: yellow; font-weight: bold;">(</span>status<span style="color: yellow; font-weight: bold;">));</span></div><div> <span style="color: yellow; font-weight: bold;">else</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: padre: processo %d: pid %d con stato sconosciuto (status=%d)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> __func__<span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">(),</span> pid<span style="color: yellow; font-weight: bold;">,</span> status<span style="color: yellow; font-weight: bold;">);</span></div><br /><div> <span style="color: yellow; font-weight: bold;">return</span> result<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore fork</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: processo %d: errore fork: %s\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> __func__<span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">(),</span> strerror_r<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">,</span> errmsg_buf<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>errmsg_buf<span style="color: yellow; font-weight: bold;">)));</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// mySleep() - wrapper per nanosleep()</span></div><div><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">void</span> mySleep<span style="color: yellow; font-weight: bold;">(unsigned</span> <span style="color: yellow; font-weight: bold;">int</span> milliseconds<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">timespec</span> ts<span style="color: yellow; font-weight: bold;">;</span></div><div> ts<span style="color: yellow; font-weight: bold;">.</span>tv_sec <span style="color: yellow; font-weight: bold;">=</span> milliseconds <span style="color: yellow; font-weight: bold;">/</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">;</span></div><div> ts<span style="color: yellow; font-weight: bold;">.</span>tv_nsec <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">(</span>milliseconds <span style="color: yellow; font-weight: bold;">%</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">*</span> <span style="color: white; font-weight: bold;">1000000</span><span style="color: yellow; font-weight: bold;">;</span></div><div> nanosleep<span style="color: yellow; font-weight: bold;">(&</span>ts<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></div></pre><p>Come avrete notato dal codice e, come al solito, dai <a href="https://artcprogramming.blogspot.com/2012/08/no-comment.html" rel="noopener" target="_blank"><strong>commenti prolissi</strong></a>, la nuova funzione che ho originalissimamente chiamato <strong>toutSystemStdout()</strong> mantiene la struttura della versione precedente però con un bel po' di cambi, perché questo di intercettare nel processo padre lo <em>standard output</em> del processo figlio non è esattamente un gioco da ragazzi. Il trucco consiste, fondamentalmente, nell'usare una <em>pipe</em> per mettere in comunicazione i due processi, ma bisogna farlo con un certo stile, se no non funziona nulla. Riassumo i punti più importanti commentati nel codice:</p><ul><li>Si aggiunge un <em>buffer</em> (con la relativa lunghezza) al prototipo della funzione, per salvare i dati scritti nello <em>stdout</em> dal comando esterno eseguito.</li><li>Si crea una <em>pipe</em> (con <a href="https://man7.org/linux/man-pages/man2/pipe.2.html" rel="noopener" target="_blank"><strong>pipe2(2)</strong></a>) di cui verrà usato solo il canale di comunicazione da processo figlio a processo padre. La <em>pipe</em> deve essere <em>nonblocking</em> per rispettare la natura della nostra <em>pseudo-system</em> con <em>timeout</em>.</li><li>I descrittori del canale di scrittura della <em>pipe</em> deve essere duplicato sullo <em>standard output</em>, e bisogna usare <a href="https://man7.org/linux/man-pages/man2/dup.2.html" rel="noopener" target="_blank"><strong>dup2(2) </strong></a>invece della sequenza <a href="https://man7.org/linux/man-pages/man2/close.2.html" rel="noopener" target="_blank"><strong>close(2</strong>)</a>+<strong>dup(2)</strong>: questo perché <strong>dup2(2)</strong> esegue internamente la sequenza in maniera atomica (evitando problemi di <em>race-condition</em>), <em>e voi sapete già quanto ci teniamo alla robustezza della programmazione in ambito multitasking/multithreading, no?</em></li><li>Il ciclo di <em>busy-wait</em> per la gestione del <em>timeout</em> si fa, ora, sulla lettura (con <a href="https://man7.org/linux/man-pages/man2/read.2.html" rel="noopener" target="_blank"><strong>read(2)</strong></a>) dello <em>stdout</em> invece che sul <a href="https://man7.org/linux/man-pages/man2/wait.2.html" rel="noopener" target="_blank"><strong>waitpid(2)</strong></a>: ovviamente se non si ha bisogno di leggere lo <em>stdout</em> del comando esterno è consigliabile usare la normale <strong>toutSystem()</strong>: <em>non esistono funzioni universali, per ogni caso d'uso bisogna usare sempre la funzione più adatta. Ricordatelo!</em></li></ul><p>Ah, prima che mi dimentichi: la funzione qui sopra è un esempio didattico: nella versione di produzione bisognerebbe mettere qualche chiusura e qualche controllo in più: ad esempio ne manca uno (solo possibile, credo) sull'<em>overflow</em> del buffer destinazione dei dati scritti. <em>Ma questo compito ve lo lascio a voi, sbizzarritevi!</em></p><p>Per provare questa nuova funzione ho scritto un piccolissimo programma, <strong>test.c</strong>, da usare come comando esterno: scrive in loop cinque volte <em>"ciao"</em> e poi esce dopo aver eseguito una <a href="https://man7.org/linux/man-pages/man3/sleep.3.html" rel="noopener" target="_blank"><strong>sleep(3)</strong></a> di 4 secondi: la <em>sleep</em> serve per misurare se funziona ancora il <em>timeout</em> (che continua ad essere la parte fondamentale della funzione): chiamando la <strong>toutSystemStdout()</strong> con un <em>timeout</em> di 5 secondi si può giocare con la <strong>sleep(3)</strong> di <strong>test.c</strong> per verificare il buon funzionamento della nuova funzione (a parte verificare che riesca veramente a catturare i dati dello <em>stdout</em> del comando esterno). Il codice di <strong>test.c</strong> è questo:</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">unistd.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char*</span> argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">for</span> <span style="color: yellow; font-weight: bold;">(int</span> i <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span> i <span style="color: yellow; font-weight: bold;"><</span> <span style="color: white; font-weight: bold;">5</span><span style="color: yellow; font-weight: bold;">;</span> i<span style="color: yellow; font-weight: bold;">++)</span></div><div> fprintf<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">stdout</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">ciao %d </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> i<span style="color: yellow; font-weight: bold;">);</span></div><br /><div> sleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">4</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Se eseguiamo il nostro programma di prova della <strong>toutSystemStdout() </strong>con <em>timeout=5sec</em> e <strong>test.c</strong> compilato con <em>sleep=4</em> otteniamo:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>aldo@Linux $ .<span style="color: yellow; font-weight: bold;">/</span>toutSystemStdout</div><div>main: eseguo toutSystemStdout<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">./test</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">5000</span><span style="color: yellow; font-weight: bold;">,</span> buf<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>buf<span style="color: yellow; font-weight: bold;">))</span></div><div>toutSystemStdout: padre: processo <span style="color: white; font-weight: bold;">19866</span>: attesa uscita del figlio</div><div>toutSystemStdout: padre: processo <span style="color: white; font-weight: bold;">19866</span>: pid <span style="color: white; font-weight: bold;">19867</span> uscito <span style="color: yellow; font-weight: bold;">(</span>status<span style="color: yellow; font-weight: bold;">=</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span></div><div>main: toutSystemStdout: cmd output: ciao <span style="color: white; font-weight: bold;">0</span> ciao <span style="color: white; font-weight: bold;">1</span> ciao <span style="color: white; font-weight: bold;">2</span> ciao <span style="color: white; font-weight: bold;">3</span> ciao <span style="color: white; font-weight: bold;">4</span></div></div></pre><p>mentre, se eseguiamo ancora con <em>timeout=5sec</em> e <strong>test.c</strong> compilato con <em>sleep=6sec</em> otteniamo:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>aldo@Linux $ .<span style="color: yellow; font-weight: bold;">/</span>toutSystemStdout</div><div>main: eseguo toutSystemStdout<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">./test</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">5000</span><span style="color: yellow; font-weight: bold;">,</span> buf<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>buf<span style="color: yellow; font-weight: bold;">))</span></div><div>toutSystemStdout: padre: processo <span style="color: white; font-weight: bold;">19880</span>: attesa uscita del figlio</div><div>toutSystemStdout: padre: processo <span style="color: white; font-weight: bold;">19880</span>: waitpid timeout scaduto</div><div>main: toutSystemStdout error</div></div></pre><p>Come volevasi dimostrare: nel primo caso (quello buono) ottengo i dati scritti dal comando test sullo <em>stdout</em>, mentre nel secondo caso ottengo (come sperato) un errore di <em>timeout </em>senza dati. <em>Provare per credere!</em></p><p>E anche per oggi può bastare. nel prossimo articolo torneremo sulla programmazione <em>real-time</em>... <em>o magari no, chissà che non mi tocchi rimandarlo ancora una volta, non si s</em>a mai!</p><p>Ciao, e al prossimo post!</p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-91356417038329802902023-01-25T22:41:00.001+01:002023-01-30T15:38:21.276+01:00Edge of Real-Time considerazioni sulla programmazione real-time - pt.1<blockquote><b>William Cage:</b> <i>Lei è americano?</i><br /><b>Sergente Farrell:</b> <i>No, sono del Kentucky.</i><br /></blockquote><p>Il bel <a href="https://letterboxd.com/aldoz/film/edge-of-tomorrow/2/" rel="noopener" target="_blank"><strong>Edge of Tomorrow</strong></a> è un film sul tempo, dove passato, presente e futuro si accavallano ed entrano in un loop infinito (<em>ma quasi senza paradossi, notevole...</em>), e mi offre un bello spunto per parlare della programmazione <strong>real-time</strong>, un tipo di programmazione dove il tempo gioca un ruolo fondamentale (<em>N.B.: si parla di tempo di esecuzione, non del tempo necessario allo sviluppo di una applicazione: quello è la croce di molti programmatori, ma è tutta un'altra storia... ah ah ah</em>).</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEZKDy_AezfLVsObj5-0VOx-GTKPCp0BAMW3OG4it1Ieayy-T4c7h6jZzqvbPDR9Sb2yv82g9mvsC9keDusDHFPzYhTsKCeGeOB3s3LMzfyY5HNik0HCfJWwfDZ6pkvn95SPmU7r887-bA12qE2cP0uHJAedx3cb4npkhWKFulNaqncJ99Clu76bMJww/s621/edge-of-real-time.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="344" data-original-width="621" height="322" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEZKDy_AezfLVsObj5-0VOx-GTKPCp0BAMW3OG4it1Ieayy-T4c7h6jZzqvbPDR9Sb2yv82g9mvsC9keDusDHFPzYhTsKCeGeOB3s3LMzfyY5HNik0HCfJWwfDZ6pkvn95SPmU7r887-bA12qE2cP0uHJAedx3cb4npkhWKFulNaqncJ99Clu76bMJww/w581-h322/edge-of-real-time.png" width="581" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...grazie al real-time riesco a fare anche questo...</td></tr></tbody></table><p>Veniamo al dunque: cosa è una applicazione <strong>real-time</strong>? Prima di dirlo farò una piccola premessa: in questa parte sto per ripetere concetti stra-conosciuti a chi mastica già l'argomento, ripetendo spiegazioni che usano quasi le stesse frasi (al limite del plagio) e che potete trovare in mille altre pagine sull'argomento: il fatto è che non posso parlare di <strong>real-time</strong> senza introdurlo, ed è impossibile farlo senza che sembri una copia di una descrizione già vista (giuro: ho trovato articoli sul real-time che sembrano fatti col copia-e-incolla, ma è inevitabile: sono quatto argomenti ben precisi e sintetici, ed è difficile descriverli in maniera molto originale). <em>Indi per cui: chi vuole può saltare tutta l'introduzione, anzi può andare direttamente al "Ciao, e al prossimo post!". Saluti e baci.</em></p><p>E torniamo al dunque:</p><blockquote><p><i>"una applicazione real-time è una applicazione che esegue le sue attività con dei tempi garantiti"</i></p></blockquote><p>Questa frase è del sottoscritto, l'ho sfornata proprio ora, ma magari l'ho copiata: il subconscio gioca brutti scherzi e ho letto molta (forse troppa) roba su questo argomento. La frase qua sopra nasconde già una insidia: <em>"ma allora un sistema veloce è un sistema real-time!"</em>. La risposta è <strong>NO</strong>: una applicazione veloce, ben scritta con un linguaggio adatto (<em>in C, no? Se no che ci stiamo a fare qui?</em>) e che gira su un computer molto veloce dotato di un buon sistema operativo (<em>Linux, ovviamente...</em>) potrebbe dare l'impressione di essere <strong>real-time</strong> con la sua grande velocità di risposta, ma in realtà non garantisce <strong>SEMPRE</strong> la velocità che ti aspetti, quindi non è veramente <strong>real-time</strong>. La parte importante della frase qui sopra è <strong>"tempi garantiti"</strong>, magari lunghi, ma garantiti.</p><p>Il caso che ho appena descritto come sbagliato offre lo spunto per il passo successivo: una applicazione <strong>real-time</strong> non è <em>solo</em> una applicazione, ma è un vero e proprio sistema composto da Sistema Operativo + Software + Hardware:</p><ol><li><strong>Sistema Operativo</strong>: deve essere di tipo real-time (RTOS per gli amici).</li><li><strong>Software</strong>: deve essere scritto rispettando lo "<em>stile real-time</em>" previsto dal RTOS prescelto, usando un linguaggio adatto (tipicamente il C, ma se ne usano anche altri).</li><li><strong>Hardware</strong>: deve essere adatto alle esigenze del RTOS prescelto.</li></ol><p>Devo evidenziare una caratteristica della tabellina qua sopra: tutti i punti elencati sono condizioni necessarie ma non sufficienti, nel senso che o sono rispettati tutti e tre o non potremo ottenere prestazioni <strong>real-time</strong>.</p><p>(<em>...ho saltato un caso particolare che non è argomento di questa trattazione: è possibile realizzare sistemi con prestazioni real-time anche programmando Hardware senza sistema operativo (programmazione "bare-metal"), in C o addirittura in Assembler, ma anche questa è un altra storia...</em>)</p><p>Ci manca solo un ultimo punto da descrivere: esiste un solo tipo di <strong>real-time</strong>? No, ce ne sono due, <strong>Hard</strong> e <strong>Soft</strong> (più un terzo, il <strong>Firm</strong>, che praticamente corrisponde col <strong>Soft</strong>, quindi lo saltiamo):</p><ul><li><strong>Hard real-time</strong>: è, in realtà, l'unico che può fregiarsi del titolo <strong>real-time</strong>: un sistema di questo tipo garantisce i tempi previsti <strong>SEMPRE</strong>. In gergo del settore: <em>"non tollera il fallimento di nessuna deadline"</em> dove per <em>deadline</em> si intende il limite temporale massimo oltre il quale una attività <strong>DEVE</strong> essere completata, pena il degrado irrimediabile del sistema controllato (<em>provate a pensare a una centrale nucleare controllata da un sistema non real-time... bum!</em>).</li><li><strong>Soft real-time</strong>: si comporta, <strong>NORMALMENTE</strong>, come un vero sistema <strong>real-time</strong>, ma tollera il fallimento di <strong>QUALCHE</strong> <em>deadline</em> sporadica, con un degrado di funzionamento statisticamente accettabile.</li></ul><p>E adesso non ci sta male una breve panoramica (non esaustiva e, perlopiù, soggettiva: metterò solo quelli che mi piacciono) dei sistemi operativi, di tipo RTOS, disponibili e raccomandabili per realizzare sistemi <strong>Hard real-time</strong>:</p><ul><li><strong>QNX</strong> - Proprietario, POSIX-compliant. È un vero e proprio UNIX real-time, basato su un microkernel real-time.</li><li><strong>LynxOS</strong> - Proprietario, POSIX-compliant. Anche questo è un vero e proprio UNIX real-time, basato su un kernel monolitico real-time.</li><li><strong>VxWorks</strong> - Proprietario, POSIX-compliant. Molto completo, basato su un kernel monolitico real-time.</li><li><strong>RT-Linux</strong> - Open source, POSIX-compliant. È, in parole povere, un microkernel real-time su cui gira un Linux standard come processo a bassa priorità.</li><li><strong>RTAI-Linux</strong> - Open source, POSIX-compliant. È, come RT-Linux, un microkernel real-time su cui gira un Linux standard come processo a bassa priorità.</li><li><strong>NuttX</strong> - Open source, POSIX-compliant. Molto compatto e, quindi, adatto anche a sistemi <em>embedded</em> semplici. Basato su un microkernel real-time.</li></ul><p>E, fuori dalla lista (perché non è un sistema operativo completo), aggiungerei anche il buon <strong>FreeRTOS</strong>, un RTOS compattissimo e leggerissimo (è quasi solo uno <em>scheduler</em>) studiato ad-hoc per sistemi <em>embedded</em> semplici che non necessitano di tutte le funzionalità avanzate fornite dai sistemi POSIX. È una ottima alternativa (ma non è l'unica) alla programmazione embedded <em>"bare-metal"</em>, quella senza sistema operativo.</p><p>Una parentesi a parte la merita il Linux standard, che, dal kernel 2.6 in avanti, ha delle <strong>estensioni</strong> <strong>real-time</strong> che si possono attivare ed usare congiuntamente a una <em>scheduling-policy</em> dello <em>scheduler</em> di tipo SCHED_FIFO o SCHED_RR (al posto di quella di <em>default</em>, che è SCHED_OTHER, come già citato <a href="https://artcprogramming.blogspot.com/2022/12/a-history-of-schedyield-considerazioni.html" rel="noopener" target="_blank"><strong>nel mio articolo sulla sched_yield(2)</strong></a>). In questa maniera Linux standard può eseguire egregiamente attività di tipo <strong>Soft real-time</strong>. Meglio che niente, no?</p><p>E un'altra parentesi bisogna spenderla sulle prestazioni assolute di questi RTOS: anche se nella introduzione dell'articolo ho ricalcato sul fatto che il <strong>real-time</strong> è <em>"rispetto dei tempi"</em> e non necessariamente velocità, tutti gli RTOS descritti sopra sono anche velocissimi, perché, essendo progettati per adempiere ai compiti più svariati, devono per forza tentare di fornire <em>"tempi garantiti"</em> anche quando si ha bisogno di risposte velocissime (avionica, centrali nucleari, ecc.). Quindi, ad esempio, <strong>QNX</strong> e compagni hanno tempi di latenza per le interruzioni e tempi di <em>context-switch</em> dell'ordine dei microsecondi (!). Invece Linux standard in modo <strong>soft real-time</strong> può fornire prestazioni dell'ordine dei millisecondi (<em>e anche in questo caso: meglio che niente, no?</em>).</p><p>Per oggi può bastare, abbiamo analizzato il punto 1 della prima tabella (<em>lo ammetto, ho descritto cose abbastanza semplici da reperire qui e là, ma almeno qua le trovate tutte insieme...</em>). Nella seconda parte (<em>che non necessariamente sarà il prossimo articolo...</em>) parleremo del punto 2 della tabella, <strong>il Software in stile real-time</strong>, che è un argomento un pelino più complicato. <em>E, ancora una volta, non trattenete il respiro nell'attesa!</em></p><p>Ciao, e al prossimo post!</p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com3tag:blogger.com,1999:blog-3955087438777078778.post-89821948725561037402022-12-23T14:25:00.003+01:002022-12-23T14:25:59.591+01:00Buon Natale e Buon Anno!<p> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgi0zS6x8NMdbav0EXJZLK6Ky4FccpZVWYppNHLVNV7HWhojx7MgtZTvYo2eaOFyQsc38mve0FujHBpT9JS5NBKN10J3tuMxFlMP221YYJq5RVMw0a60q8-zTDMLasG9XlHqw7OERw9AVxDoTuslFWK2F02uMqzkxkANxwcArreHo66kea16t97tADU8Q/s640/BuoneFeste.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="427" data-original-width="640" height="378" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgi0zS6x8NMdbav0EXJZLK6Ky4FccpZVWYppNHLVNV7HWhojx7MgtZTvYo2eaOFyQsc38mve0FujHBpT9JS5NBKN10J3tuMxFlMP221YYJq5RVMw0a60q8-zTDMLasG9XlHqw7OERw9AVxDoTuslFWK2F02uMqzkxkANxwcArreHo66kea16t97tADU8Q/w567-h378/BuoneFeste.jpg" width="567" /></a><br /><br /></p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-12301417154376116952022-12-16T19:56:00.002+01:002022-12-16T19:56:49.689+01:00A History of Sched_yield considerazioni sull'uso della sched_yield(2) su Linux<blockquote><b>Joey:</b> <i>Richie, io sono qui per fare pace... dimmi che devo fare per mettere le cose a posto.</i><br /><b>Richie:</b> <i>Una cosa la potresti fare, credo... potresti morire, Joey.</i><br /></blockquote><p>Questo articolo ha un argomento spinoso, esattamente come l'argomento trattato nel bellissimo <a href="https://letterboxd.com/aldoz/film/a-history-of-violence/" rel="noopener" target="_blank"><strong>A History of Violence</strong></a> del Maestro <a href="https://letterboxd.com/director/david-cronenberg/" rel="noopener" target="_blank"><strong>David Cronenberg</strong></a>, caratterizzato da una trama ambigua che è una sequenza di pacifiche e tranquillizzanti scene familiari intervallata da improvvisi momenti di iper-violenza. Qui mi tocca tornare su un argomento che ho trattato solo di striscio, l'uso della system-call <a href="https://man7.org/linux/man-pages/man2/sched_yield.2.html" rel="noopener" target="_blank"><strong>sched_yeld(2)</strong></a>, che ho usato in un esempio <a href="https://artcprogramming.blogspot.com/2020/09/sleep-no-grazie-considerazioni-sul.html" rel="noopener" target="_blank"><strong>di un mio vecchio articolo</strong></a> e, con l'intenzione di essere il più possibile preciso, sono costretto a rivedere un po' l'esempio, dato che quello proposto lì non era valido universalmente, anzi era valido solo per usi particolari. <em>Mi fascio la testa...</em></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGAA3XnmptDdLjJvU0wdSd53TruwUNGaZ0arpPSabsQII5A10clnVLowN3eU3J72poeKU74nsYa76FeUlTRDxbrsrImbfZ8POlbfU9jyHxx4yE1Y-INc0hY8X8WcTF_3uPe-KJoEAvDtRSZxXfSRf6V1VzwVWaRVjgCoYo3eVS4tT9066jKzHyTyCT1g/s1860/A-History-of-sched_yield.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1044" data-original-width="1860" height="330" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGAA3XnmptDdLjJvU0wdSd53TruwUNGaZ0arpPSabsQII5A10clnVLowN3eU3J72poeKU74nsYa76FeUlTRDxbrsrImbfZ8POlbfU9jyHxx4yE1Y-INc0hY8X8WcTF_3uPe-KJoEAvDtRSZxXfSRf6V1VzwVWaRVjgCoYo3eVS4tT9066jKzHyTyCT1g/w587-h330/A-History-of-sched_yield.jpg" width="587" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...sched_yield, o non sched_yield, questo è il dilemma...</td></tr></tbody></table><p>La spinta alla scrittura di questo articolo mi è venuta leggendo un bell'articolo, questo: <a href="https://cpufun.substack.com/p/to-sched_yield-or-not-to-sched_yield" rel="noopener" target="_blank"><strong>To sched_yield() Or Not To sched_yield()?</strong></a> (<em>che ho citato nella didascalia qua sopra, ah ah ah</em>). L'articolo in questione, oltre ad alcune interessanti note teoriche, contiene anche un bel <em>benchmark</em> realizzato su una vera macchina multiprocessore (perfetta per test di <em>multiprocessing</em> e/o <em>multithreading</em> reali), nientepopodimeno che un <a href="https://www.hpe.com/us/en/compute/hpc.html" rel="noopener" target="_blank"><strong>Cray</strong></a> con 256 CPU (<em>con Linux, ovviamente: vi siete mai chiesto perché i super-computer usano quasi sempre Linux, qualche volta UNIX, e quasi mai Windows? Beh, chiedetevelo...</em>). Visto che io non ho a disposizione un mostro del genere non vi proporrò nessun <em>benchmark,</em> quello del'articolo citato è più che sufficiente per mostrare che usare la <strong>sched_yield(2)</strong> non sempre è una buona idea. Per chi non lo ricordasse la <strong>sched_yield(2)</strong> fa questo:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>sched_yield <span style="color: yellow; font-weight: bold;">-</span> cede il processore</div><br /><div>sched_yield<span style="color: yellow; font-weight: bold;">()</span> fa sì che il thread chiamante rinunci alla CPU. Il thread viene</div><div>spostato alla fine della coda per la sua priorità statica e un nuovo thread</div><div>viene eseguito.</div></div></pre><p>E veniamo al dunque: <a href="https://artcprogramming.blogspot.com/2020/09/sleep-no-grazie-considerazioni-sul.html" rel="noopener" target="_blank"><strong>il mio vecchio articolo</strong></a> (in due puntate) parlava più che altro di <em>sleep</em> <em>(se avete voglia potete rileggerlo</em>) e qui vi farò un breve riassunto della solo parte che ci interessa oggi. E allora: avevo proposto una funzione di <em>sleep</em> in millisecondi che, in maniera intelligente, decideva se usare <strong>nanosleep(2)</strong>,<strong> usleep(3)</strong> o <strong>sched_yield(2)</strong>, in base alle esigenze e disponibilità. Vai col codice!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// Sleep() - una sleep in ms</span></div><div><span style="color: yellow; font-weight: bold;">void</span> Sleep<span style="color: yellow; font-weight: bold;">(unsigned</span> <span style="color: yellow; font-weight: bold;">int</span> milliseconds<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// testa il tempo di sleep per intercettare il valore 0</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>milliseconds <span style="color: yellow; font-weight: bold;">></span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// usa nanosleep() o usleep()</span></div><div><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">#if</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">_POSIX_C_SOURCE</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">>=</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">199309</span><span style="color: #b5cea8; font-weight: bold;">L</span><span style="color: yellow; font-weight: bold;">)</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> timespec ts<span style="color: yellow; font-weight: bold;">;</span></div><div> ts<span style="color: yellow; font-weight: bold;">.</span>tv_sec <span style="color: yellow; font-weight: bold;">=</span> milliseconds <span style="color: yellow; font-weight: bold;">/</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">;</span></div><div> ts<span style="color: yellow; font-weight: bold;">.</span>tv_nsec <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">(</span> milliseconds <span style="color: yellow; font-weight: bold;">%</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">*</span> <span style="color: white; font-weight: bold;">1000000</span><span style="color: yellow; font-weight: bold;">;</span></div><div> nanosleep<span style="color: yellow; font-weight: bold;">(&</span>ts<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">NULL);</span></div><div><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">#else</span></div><div> usleep<span style="color: yellow; font-weight: bold;">(</span>milliseconds <span style="color: yellow; font-weight: bold;">*</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">#endif</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// usa sched_yield() come alternativa Linux alla Sleep(0) di Windows</span></div><div> sched_yield<span style="color: yellow; font-weight: bold;">();</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Quindi avevo applicato questa nuova <strong>Sleep()</strong> a un semplicissimo programma con due <em>thread</em> usandola come <em>sleep</em> di un <em>loop</em> infinito (<em>il codice ve lo risparmio, comunque è consultabile nel vecchio articolo</em>). Grazie a questo codice avevo realizzato un piccolo <em>benchmark</em> per provare la famigerata <strong>Sleep(0)</strong> (di origine Windows) e l'effetto reale che si otteneva cambiando il valore di <em>sleep</em> del <em>loop:</em> nella tabella seguente ci sono i risultati originali (con una macchina Linux con un i7 con 4 core e 8 thread). Per i casi inferiori al millisecondo avevo modificato il codice per usare direttamente la obsoleta (ma in questo caso comoda)<strong> usleep(3)</strong>, e i dati si riferiscono all'uso di CPU e al numero <em>"i"</em> di cicli effettuati dai <em>thread:</em></p><pre class="EnlighterJSRAW" data-enlighter-language="raw"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>senza sleep i <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">5314563827</span> CPU <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">100</span><span style="color: yellow; font-weight: bold;">%</span></div><div>Sleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> i <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">18863299</span> CPU <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">100</span><span style="color: yellow; font-weight: bold;">%</span> <span style="color: yellow; font-weight: bold;">(</span>esegue sched_yield<span style="color: yellow; font-weight: bold;">())</span></div><div>Sleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">10</span><span style="color: yellow; font-weight: bold;">)</span> t<span style="color: yellow; font-weight: bold;">=</span><span style="color: #f44747;">10ms</span> i <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">900</span> CPU <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">/</span><span style="color: white; font-weight: bold;">2</span><span style="color: yellow; font-weight: bold;">%</span></div><div>Sleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> t<span style="color: yellow; font-weight: bold;">=</span><span style="color: #f44747;">1ms</span> i <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">9000</span> CPU <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">/</span><span style="color: white; font-weight: bold;">2</span><span style="color: yellow; font-weight: bold;">%</span></div><div>usleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">100</span><span style="color: yellow; font-weight: bold;">)</span> t<span style="color: yellow; font-weight: bold;">=</span><span style="color: #f44747;">100us</span> i <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">62970</span> CPU <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">/</span><span style="color: white; font-weight: bold;">2</span><span style="color: yellow; font-weight: bold;">%</span></div><div>usleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">10</span><span style="color: yellow; font-weight: bold;">)</span> t<span style="color: yellow; font-weight: bold;">=</span><span style="color: #f44747;">10us</span> i <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">145000</span> CPU <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">/</span><span style="color: white; font-weight: bold;">2</span><span style="color: yellow; font-weight: bold;">%</span></div><div>usleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> t<span style="color: yellow; font-weight: bold;">=</span><span style="color: #f44747;">1us</span> i <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">170000</span> CPU <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">/</span><span style="color: white; font-weight: bold;">2</span><span style="color: yellow; font-weight: bold;">%</span></div><div>usleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> t<span style="color: yellow; font-weight: bold;">=</span><span style="color: #f44747;">0us</span> i <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">170000</span> CPU <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">/</span><span style="color: white; font-weight: bold;">2</span><span style="color: yellow; font-weight: bold;">%</span> <span style="color: yellow; font-weight: bold;">(</span>test con usleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">))</span></div></div></pre><p>Ora, citerò direttamente il vecchio articolo, che diceva:</p><p></p><blockquote><p><i>Il primo caso ci mostra perché qui conviene usare la sleep: i nostri due thread funzionano bene (i due contatori avanzano simultaneamente), e la attività è gestita dal thread-scheduler (facile in questo caso: un thread dell'applicazione su un thread della CPU), ma comunque, se non lavorano volontariamente in maniera cooperativa (o gentile, se preferite) e non rilasciano ogni tanto la CPU, finiscono col mangiarsela tutta (due degli otto CPU-thread, nel mio caso), e in un sistema multi-process non è una buona cosa, no?.</i></p><p><i>Esaminiamo, ora, il secondo caso che ci dimostra che la famosa Sleep(0)/sched_yield() di cui abbiamo parlato nell'altro articolo non da proprio dei gran risultati: i thread lavorano meno (guardare il counter) ma la CPU va lo stesso al 100%.</i></p><p><i>Il terzo e il quarto caso ci mostrano che, usando valori dell'ordine di grandezza del time-slice del sistema, la CPU lavora poco e il comportamento è lineare (fino a 1 ms).</i></p><p><i>I casi successivi mantengono il rispetto della CPU ma il comportamento diventa irregolare (i counter non aumentano proporzionalmente alla riduzione della sleep). Conclusione: in questa tipo di architettura Software (che è un buon riferimento, essendo abbastanza comune) è conveniente usare dei tempi paragonabili al time-slice del sistema, ossia tra 1 e 10 ms.</i></p><p><i>L'ultimo caso l'ho aggiunto solo per curiosità, per confermare quanto detto nell'altro articolo: usleep(0) non è equivalente a Sleep(0) (magari qualcuno non ci credeva...) e applica il tempo di sleep minimo possibile, che è di 1 us.</i></p></blockquote><p></p><p>E torniamo al presente: come si può ben notare la <strong>sched_yield(2)</strong> non dava dei gran risultati, però l'avevo lasciata ugualmente nel codice della nuova <em>Sleep()</em> a mo' di <em>"funziona male, ma se proprio volete usarla..."</em>. In realtà più che funzionare male è una di quelle <em>system-call</em> che o si usano bene o non si usano (<em>avevamo visto qualcosa di simile nei <a href="https://artcprogramming.blogspot.com/2022/08/thread-cancel-no-grazie-considerazioni.html" rel="noopener" target="_blank"><strong>miei 2 articoli</strong></a> sulla funzione <strong>pthread_cancel(3)</strong>, ricordate?</em>), quindi ci vuole molta cautela e, alla fine della fiera, è possibile che non si debba mai usare, a parte casi particolarissimi (<em>e questo, scusate l'OT, mi risulta vero anche per la Sleep(0) di Windows</em>). Del resto è ben scritto anche nel manuale della <strong>sched_yield(2)</strong>:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>Chiamate strategiche a sched_yield<span style="color: yellow; font-weight: bold;">()</span> possono migliorare le prestazioni dando ad</div><div>altri thread o processi la possibilità di funzionare quando risorse <span style="color: yellow; font-weight: bold;">(</span>fortemente<span style="color: yellow; font-weight: bold;">)</span></div><div>contese <span style="color: yellow; font-weight: bold;">(</span>e.g.: dei mutex<span style="color: yellow; font-weight: bold;">)</span> sono state rilasciate dal chiamante. Evitate di</div><div>chiamare sched_yield<span style="color: yellow; font-weight: bold;">()</span> inutilmente o in modo inappropriato <span style="color: yellow; font-weight: bold;">(</span>ad esempio<span style="color: yellow; font-weight: bold;">,</span> quando</div><div>le risorse necessarie ad altri thread schedulabili sono ancora in possesso del</div><div>chiamante<span style="color: yellow; font-weight: bold;">),</span> poiché ciò comporterà inutili commutazioni di contesto<span style="color: yellow; font-weight: bold;">,</span> con</div><div>conseguente peggioramento delle prestazioni del sistema.</div></div></pre><p>e, soprattutto, in questo paragrafo (sempre nel manuale):</p><pre class="EnlighterJSRAW" data-enlighter-language="raw"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div style="line-height: 13px;"><div>sched_yield<span style="color: yellow; font-weight: bold;">()</span> è destinato all'uso con politiche di schedulazione in tempo reale</div><div><span style="color: yellow; font-weight: bold;">(</span>ad esempio<span style="color: yellow; font-weight: bold;">,</span> SCHED_FIFO o SCHED_RR<span style="color: yellow; font-weight: bold;">)</span>. L'uso di sched_yield<span style="color: yellow; font-weight: bold;">()</span> con politiche di</div><div>schedulazione non deterministiche come SCHED_OTHER non è specificato e molto</div><div>probabilmente significa che il progetto dell applicazione non è corretto.</div></div></div></pre><p>E, solo come curiosità, vi segnalo anche una nota dal manuale <a href="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux_for_real_time/7/html/reference_guide/chap-system_calls#sched_yield" rel="noopener" target="_blank"><strong>Red Hat Enterprise Linux for Real Time</strong></a> che indica che anche in ambito<em> real-time</em> la <strong>sched_yield(2)</strong> è da usare con parsimonia (per i processi, ma per i <em>thread</em> il discorso dovrebbe essere simile):</p><pre class="EnlighterJSRAW" data-enlighter-language="raw"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div style="line-height: 13px;"><div>La funzione sched_yield è stata originariamente progettata per indurre un</div><div>processore a selezionare un processo diverso da quello in esecuzione. Questo</div><div>tipo di richiesta è incline a fallire quando viene emessa da un'applicazione</div><div>mal scritta.</div><div>Quando la funzione sched_yield<span style="color: yellow; font-weight: bold;">()</span> viene utilizzata all'interno di processi con</div><div>priorità in tempo reale<span style="color: yellow; font-weight: bold;">,</span> può presentare un comportamento inaspettato. Il processo</div><div>che ha chiamato sched_yield viene spostato in coda alla coda dei processi in</div><div>esecuzione con quella priorità. Quando questo accade in una situazione in cui non</div><div>ci sono altri processi in esecuzione con la stessa priorità<span style="color: yellow; font-weight: bold;">,</span> il processo che ha</div><div>chiamato sched_yield continua la sua esecuzione. Se la priorità di questo processo</div><div>è alta<span style="color: yellow; font-weight: bold;">,</span> può potenzialmente creare un busy loop<span style="color: yellow; font-weight: bold;">,</span> rendendo la macchina inutilizzabile.</div><div>In generale<span style="color: yellow; font-weight: bold;">,</span> non utilizzare sched_yield su processi in tempo reale.</div></div></div></pre><p>Ebbene si, il codice che proposi non era realmente universale, perché la <strong>sched_yield(2)</strong> si dovrebbe usare (con molta cautela) solo quando Linux usa uno <em>scheduler</em> di tipo <em>real-time</em> (SCHED_FIFO o SCHED_RR), e questo è un caso particolare, visto che il caso normale (il <em>default)</em> di Linux è l'uso dello <em>scheduler</em> SCHED_OTHER. E quindi? Beh, devo aggiornare l'esempio, che ora si presenta così:</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// Sleep() - una sleep in ms</span></div><div><span style="color: yellow; font-weight: bold;">void</span> Sleep<span style="color: yellow; font-weight: bold;">(unsigned</span> <span style="color: yellow; font-weight: bold;">int</span> milliseconds<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// check se il tempo di sleep è zero</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>milliseconds <span style="color: yellow; font-weight: bold;">==</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span></div><div> milliseconds <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // forza 1ms</span></div><br /><div><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">#if</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">_POSIX_C_SOURCE</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">>=</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">199309</span><span style="color: #b5cea8; font-weight: bold;">L</span><span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// uso nanosleep(2)</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> timespec ts<span style="color: yellow; font-weight: bold;">;</span></div><div> ts<span style="color: yellow; font-weight: bold;">.</span>tv_sec <span style="color: yellow; font-weight: bold;">=</span> milliseconds <span style="color: yellow; font-weight: bold;">/</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">;</span></div><div> ts<span style="color: yellow; font-weight: bold;">.</span>tv_nsec <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">(</span>milliseconds <span style="color: yellow; font-weight: bold;">%</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">*</span> <span style="color: white; font-weight: bold;">1000000</span><span style="color: #b5cea8; font-weight: bold;">L</span><span style="color: yellow; font-weight: bold;">;</span></div><div> nanosleep<span style="color: yellow; font-weight: bold;">(&</span>ts<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">NULL);</span></div><div><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">#else</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// uso usleep(3) - NOTA: POSIX.1-2001 dichiara questa funzione come obsoleta</span></div><div> usleep<span style="color: yellow; font-weight: bold;">(</span>milliseconds <span style="color: yellow; font-weight: bold;">*</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">#endif</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Volendo si potrebbe migliorare questo codice forzando l'uso della <strong>sched_yield(2)</strong> quando i <em>milliseconds</em> sono 0 e lo <em>scheduler</em> (che si può ottenere usando <strong>sched_getscheduler(2)</strong>) è SCHED_FIFO o SCHED_RR, ma, essendo questo un caso molto particolare (e da usare con le molle) non mi sembrava il caso di farlo. Notare che ho forzato l'uso di una <em>sleep</em> minima di<em> 1 ms</em> coerentemente con i risultati del mio <em>benchmark</em> qui sopra, dove è dimostrato che un valore ottimale di <em>sleep</em> minima è quello dell'ordine di grandezza del <em>time-slice </em>del sistema.</p><p>E per oggi può bastare, <em>ho messo i puntini sulle i</em>, anche se mi è costato un poco ammettere di non avere, a suo tempo, fornito un esempio veramente rigoroso. Eh si, <em>Errare Humanum Est...</em></p><p>Ciao, e al prossimo post! </p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-45135826026253816422022-11-28T20:15:00.001+01:002022-11-29T11:34:11.195+01:00Atomichunter come usare le funzioni atomic built-in di GCC in C<blockquote><b>Graham:</b> <i>Non è curioso di sapere se Lei è più furbo di quello che cerchiamo?</i><br /><b>Lecktor:</b> <i>Stai scherzando? Tu sei più furbo, visto che mi hai preso...</i><br /><b>Graham:</b> <i>No, non sono più furbo di Lei.</i><br /><b>Lecktor:</b> <i>E allora come hai fatto a prendermi?</i><br /><b>Graham:</b> <i>Lei era svantaggiato.</i><br /><b>Lecktor:</b> <i>In che senso svantaggiato?</i><br /><b>Graham:</b> <i>Lei è pazzo.</i><br /></blockquote><p>Per parlare delle funzioni <a href="https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/_005f_005fatomic-Builtins.html" rel="noopener" target="_blank"><strong>atomic built-in</strong></a> di <a href="https://gcc.gnu.org/" rel="noopener" target="_blank"><strong>GCC</strong></a> ho deciso di fare un temerario abbinamento con il capolavoro <a href="https://letterboxd.com/aldoz/film/manhunter/1/" rel="noopener" target="_blank"><strong>Manhunter</strong></a> del Maestro <a href="https://letterboxd.com/director/michael-mann/" rel="noopener" target="_blank"><strong>Michael Mann</strong></a>, un film assolutamente imperdibile. Il fatto è che per risolvere un problema pratico (ebbene si, oggi parleremo del mondo reale) a volte un programmatore deve trasformarsi in un cacciatore (fortunatamente non di uomini), e quindi deve investigare, provare, cercare informazioni qui e là per risolvere un caso reale. Si, bisogna fare come il <strong>detective Graham</strong> del film: beh, magari senza arrivare ai suoi estremi, e chi ha visto il film sa di cosa parlo...</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_tY7rTrLlhaGYfhhFFmPk11OvlXKDCWYPj9A-Q-zUd-rLEfYDPwsBuJjCfwB-dxKV0TgdfgYg5mTxdM92zBzNI9qbVt1ylImaAGs91-O0f4WqMFpONKWtZF-0PeoPb3P7pE8-zFe96OA3hvumq4mtfuqt443t5QLPrF4ydSPcq1dJZAbt_ZMxL0aDHA/s640/manhunter2.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="320" data-original-width="640" height="293" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_tY7rTrLlhaGYfhhFFmPk11OvlXKDCWYPj9A-Q-zUd-rLEfYDPwsBuJjCfwB-dxKV0TgdfgYg5mTxdM92zBzNI9qbVt1ylImaAGs91-O0f4WqMFpONKWtZF-0PeoPb3P7pE8-zFe96OA3hvumq4mtfuqt443t5QLPrF4ydSPcq1dJZAbt_ZMxL0aDHA/w586-h293/manhunter2.jpg" width="586" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...sai, ti ho preso grazie alle atomic built-in di GCC...</td></tr></tbody></table><p>Non so se ricordate un mio articolo di un annetto fa, <a href="https://artcprogramming.blogspot.com/2021/09/atomic-endgame-come-quando-e-perche.html" rel="noopener" target="_blank"><strong>Atomic: Endgame</strong></a>, in cui decantavo la bontà del nuovo <strong>type qualifier _Atomic</strong> introdotto con <strong>C11</strong> (<em>e, ovviamente, se non lo ricordate siete tenuti a rileggerlo...</em>). Nell'articolo era <em>(spero)</em> ben descritta la nuova funzionalità e si evidenziava che, grazie a <strong>_Atomic</strong>, la scrittura di applicazioni <em>multi-thread</em> diventava molto più agevole (<em>era ora!</em>). E perché oggi torniamo sull'argomento? E qui sopraggiunge il problema pratico citato sopra: supponiamo (<em>true story</em>) di dover aggiungere una nuova prestazione a una vecchia applicazione su <strong>Linux Embedded</strong> usando la piattaforma (e quindi il compilatore) originale, che, <em>per la legge di Murphy</em>, non supporta <strong>C11</strong> (anzi supporta a stento <strong>C99</strong>): che facciamo? Dovremo mica riscrivere il codice (già scritto e testato su piattaforme più recenti) in modo di usare i classici meccanismi di sincronizzazione delle variabili (<em>mutex</em>, ecc.)? No, ci deve essere una soluzione più semplice!</p><p>E quindi mi sono <em>Graham-izzato</em> (vedi sopra) e ho scoperto che il nostro amato <strong>GCC</strong>, grande amico dei programmatori <b>C</b> (e non solo), ha da molto tempo delle funzioni <em>built-in</em> che hanno anticipato le funzionalità di <strong>_Atomic</strong> ben prima del supporto a <strong>C11</strong>. Anzi, abbiamo ben due famiglie di <em>built-in</em> denominate <strong>__sync_*</strong> e <strong>__atomic_*</strong> , aggiunte con a la seguente sequenza temporale:</p><ul><li>GCC 4.1: <strong>__sync_*</strong><em> built-in</em> (nel 02/2006)</li><li>GCC 4.4: <strong>__sync_*</strong> <em>built-in</em> (nel 04/2009, con supporto <strong>ARM</strong>)</li><li>GCC 4.7: <strong>__atomic_*</strong><em> built-in </em>(nel 03/2012)</li><li>GCC 4.9: <strong>_Atomic </strong>(nel 04/2014, supporto completo)</li></ul><p>Considerando la data in cui <strong>_Atomic</strong> è entrato in <strong>GCC</strong> il supporto a queste operazioni è stato fornito con un buon anticipo, no? Nella lista qui sopra ho anche evidenziato quando è stata aggiunta la compatibilità con <strong>ARM</strong>, perché l'argomento di questo articolo è decisamente indirizzato alla programmazione con <strong>Linux Embedded</strong>, e <strong>ARM</strong> è una piattaforma molto usata in questo ambito. Tra l'altro si può evidenziare che anche le versioni recenti di <strong>GCC</strong> continuano a supportare questi<em> built-in</em>, e quindi un eventuale Software <em>"datato"</em> che le usa si può ancora compilare senza problemi con un <strong>GCC</strong> recente, e senza modificare nulla (<em>questa si che è vera retro-compatibilità! Grazie gcc.gnu.org!</em>).</p><p>Nella mia ricerca ho anche notato che la quantità di informazioni a livello base (tipicamente quella fornita da<strong> gcc.gnu.org</strong>) è notevole in quantità e qualità, però latitano un po' i veri esempi d'uso (<em>il codice!</em>), e quindi ho deciso di contribuire un po' con questo articolo, che interesserà a pochi (credo) ma per quei pochi potrebbe essere una <i>manna dal cielo</i> (<em>vabbé, non esageriamo...</em>).</p><p>Che ho fatto allora? Ho scritto una mini-libreria per semplificare l'uso dell funzioni<em> atomic built-in</em>, e ho strutturato la libreria in maniera che si comporti in modo <em>"intelligente"</em> riconoscendo la versione del <strong>GCC</strong> in uso, per passare automaticamente da <strong>_Atomic</strong> a <strong>__atomic_*</strong> oppure a<strong> __sinc_*</strong>. Ho scritto un<em> header</em>,<em> atoint.h</em> che definisce un nuovo tipo <strong>atoint</strong> (<i>nome originalissimo</i> che sta per <strong>atomic int</strong>). Vediamolo: vai col codice!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">/* atoint.h - header della libreria atoint</span></div><div><span style="color: silver; font-style: italic;"> versioni delle funzioni atomiche:</span></div><div><span style="color: silver; font-style: italic;"> _Atomic: da GCC 4.9 in avanti (40900L)</span></div><div><span style="color: silver; font-style: italic;"> __atomic: da GCC 4.7 in avanti (40700L)</span></div><div><span style="color: silver; font-style: italic;"> __sync con ARM: da GCC 4.4 in avanti (40400L)</span></div><div><span style="color: silver; font-style: italic;"> __sync: da GCC 4.1 in avanti (40100L) */</span></div><br /><div><span style="color: silver; font-style: italic;">// GCC_VERSION contiene la versione di GCC come numero (e.g.: GCC4.9 = 40900)</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">GCC_VERSION</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">__GNUC__</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">*</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">10000</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">+</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">__GNUC_MINOR__</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">*</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">100</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">+</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">__GNUC_PATCHLEVEL__</span><span style="color: yellow; font-weight: bold;">)</span></div><br /><div><span style="color: yellow; font-weight: bold;">#if</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">GCC_VERSION</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">>=</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">40900</span><span style="color: #b5cea8; font-weight: bold;">L</span></div><div><span style="color: silver; font-style: italic;">// da GCC 4.9 in avanti è disponibile _Atomic di C11</span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdatomic.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">typedef</span> <span style="color: yellow; font-weight: bold;">atomic_int</span> <span style="color: yellow; font-weight: bold;">atoint</span><span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // uso atomic_int per atoint</span></div><div><span style="color: yellow; font-weight: bold;">#else</span></div><div><span style="color: silver; font-style: italic;">// _Atomic di C11 non è disponibile</span></div><div><span style="color: yellow; font-weight: bold;">typedef</span> <span style="color: yellow; font-weight: bold;">int</span> atoint<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // uso int per atoint</span></div><div><span style="color: yellow; font-weight: bold;">#endif</span></div><br /><div><span style="color: yellow; font-weight: bold;">#if</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">GCC_VERSION</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">>=</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">40900</span><span style="color: #b5cea8; font-weight: bold;">L</span></div><div><span style="color: silver; font-style: italic;">// uso le funzioni di _Atomic</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">ATOMIC_STORE</span><span style="color: yellow; font-weight: bold;">(</span>ptr<span style="color: yellow; font-weight: bold;">,</span><span style="color: #569cd6;"> </span>val<span style="color: yellow; font-weight: bold;">)</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">atomic_store</span><span style="color: yellow; font-weight: bold;">((</span><span style="color: #569cd6;">ptr</span><span style="color: yellow; font-weight: bold;">),</span><span style="color: #569cd6;"> val</span><span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">ATOMIC_LOAD</span><span style="color: yellow; font-weight: bold;">(</span>ptr<span style="color: yellow; font-weight: bold;">)</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">atomic_load</span><span style="color: yellow; font-weight: bold;">(</span><span style="color: #569cd6;">ptr</span><span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">ATOMIC_INC</span><span style="color: yellow; font-weight: bold;">(</span>ptr<span style="color: yellow; font-weight: bold;">)</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">atomic_fetch_add</span><span style="color: yellow; font-weight: bold;">((</span><span style="color: #569cd6;">ptr</span><span style="color: yellow; font-weight: bold;">),</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">ATOMIC_DEC</span><span style="color: yellow; font-weight: bold;">(</span>ptr<span style="color: yellow; font-weight: bold;">)</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">atomic_fetch_sub</span><span style="color: yellow; font-weight: bold;">((</span><span style="color: #569cd6;">ptr</span><span style="color: yellow; font-weight: bold;">),</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">#elif</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">GCC_VERSION</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">>=</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">40700</span><span style="color: #b5cea8; font-weight: bold;">L</span></div><div><span style="color: silver; font-style: italic;">// uso le funzioni di GCC __atomic prefixed built-in</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">ATOMIC_STORE</span><span style="color: yellow; font-weight: bold;">(</span>ptr<span style="color: yellow; font-weight: bold;">,</span><span style="color: #569cd6;"> </span>val<span style="color: yellow; font-weight: bold;">)</span><span style="color: #569cd6;"> </span>__atomic_store_n<span style="color: yellow; font-weight: bold;">((</span><span style="color: #569cd6;">ptr</span><span style="color: yellow; font-weight: bold;">),</span><span style="color: #569cd6;"> val</span><span style="color: yellow; font-weight: bold;">,</span><span style="color: #569cd6;"> __ATOMIC_SEQ_CST</span><span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">ATOMIC_LOAD</span><span style="color: yellow; font-weight: bold;">(</span>ptr<span style="color: yellow; font-weight: bold;">)</span><span style="color: #569cd6;"> </span>__atomic_load_n<span style="color: yellow; font-weight: bold;">((</span><span style="color: #569cd6;">ptr</span><span style="color: yellow; font-weight: bold;">),</span><span style="color: #569cd6;"> __ATOMIC_SEQ_CST</span><span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">ATOMIC_INC</span><span style="color: yellow; font-weight: bold;">(</span>ptr<span style="color: yellow; font-weight: bold;">)</span><span style="color: #569cd6;"> </span>__atomic_fetch_add<span style="color: yellow; font-weight: bold;">((</span><span style="color: #569cd6;">ptr</span><span style="color: yellow; font-weight: bold;">),</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">,</span><span style="color: #569cd6;"> __ATOMIC_SEQ_CST</span><span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">ATOMIC_DEC</span><span style="color: yellow; font-weight: bold;">(</span>ptr<span style="color: yellow; font-weight: bold;">)</span><span style="color: #569cd6;"> </span>__atomic_fetch_sub<span style="color: yellow; font-weight: bold;">((</span><span style="color: #569cd6;">ptr</span><span style="color: yellow; font-weight: bold;">),</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">,</span><span style="color: #569cd6;"> __ATOMIC_SEQ_CST</span><span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">#elif</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">GCC_VERSION</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">>=</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">40100</span><span style="color: #b5cea8; font-weight: bold;">L</span></div><div><span style="color: silver; font-style: italic;">// TODO: scrivere la versione con __sync prefixed built-in</span></div><div><span style="color: silver; font-style: italic;">// ...</span></div><div><span style="color: yellow; font-weight: bold;">#else</span></div><div><span style="color: yellow; font-weight: bold;">#error</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">Spiacente, il tuo compilatore è troppo vecchio - aggiornalo, per favore.</span></div><div><span style="color: yellow; font-weight: bold;">#endif</span></div><br /><div><span style="color: silver; font-style: italic;">// prototipi globali</span></div><div><span style="color: yellow; font-weight: bold;">atoint</span> atoStore<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">atoint</span> <span style="color: yellow; font-weight: bold;">*</span>val<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">atoint</span> newval<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // store</span></div><div><span style="color: yellow; font-weight: bold;">atoint</span> atoLoad<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">atoint</span> <span style="color: yellow; font-weight: bold;">*</span>val<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // load</span></div><div><span style="color: yellow; font-weight: bold;">atoint</span> atoPreInc<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">atoint</span> <span style="color: yellow; font-weight: bold;">*</span>value<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // prefix increment (++val)</span></div><div><span style="color: yellow; font-weight: bold;">atoint</span> atoPostInc<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">atoint</span> <span style="color: yellow; font-weight: bold;">*</span>value<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // postfix increment (val++) (usa il prefix increment)</span></div><div><span style="color: yellow; font-weight: bold;">atoint</span> atoPreDec<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">atoint</span> <span style="color: yellow; font-weight: bold;">*</span>value<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // prefix decrement (--val)</span></div><div><span style="color: yellow; font-weight: bold;">atoint</span> atoPostDec<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">atoint</span> <span style="color: yellow; font-weight: bold;">*</span>value<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // postfix decrement (val--) (usa il prefix decrement)</span></div></div></pre><p>Come si può ben vedere il file contiene la definizione del nuovo tipo e i prototipi delle funzioni della libreria. E, soprattutto, contiene degli alias (delle <em>#define</em>) delle funzioni che usano le appropriate<em> atomic built-in</em> in base alla versione del compilatore in uso, versione che è contenuta nella macro <strong>GCC_VERSION</strong> (è un numero identificativo abbastanza semplice da calcolare, ad esempio per <strong>GCC 4.9</strong> è 40900). Con l'uso di alcune semplici direttive <em>#if</em> per il preprocessore del <strong>C</strong> possiamo scegliere quali alias attivare automaticamente. In questa versione ho usato come requisito minimo per la famiglia <em>__sync_*</em> il <strong>GCC 4.1</strong> (ver.40100), ma chi ha bisogno del supporto <strong>ARM</strong> dovrebbe usare come requisito minimo il <strong>GCC 4.4</strong> (ver.40400). Usando un compilatore ancora più anziano del requisito minimo, la compilazione esce (miseramente) con questo errore: <em>"Spiacente, il tuo compilatore è troppo vecchio - aggiornalo, per favore.".</em></p><p>Visto che non volevo mettere troppa carne al fuoco ho lasciato come <em>TODO</em> la versione con <em>__sync_*</em> che è un po' più complicata e avrebbe appesantito un po' il codice. Tra l'altro si dovrebbero/potrebbero aggiungere anche altre funzioni oltre a quelle presentate qui (e si può fare: io, mio malgrado, ho dovuto scrivere una versione <i>full-optional</i> perché mi serviva): magari ci torneremo in un prossimo articolo.</p><p>Grazie agli alias delle funzioni la implementazione della libreria è molto semplice e lineare. Vediamola:</p><pre class="EnlighterJSRAW" data-enlighter-language="generic"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// atoint.c - implementazione della libreria atoint</span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">atoint.h</span><span style="color: yellow; font-weight: bold;">"</span></div><br /><div><span style="color: silver; font-style: italic;">// store (assegna il valore)</span></div><div><span style="color: yellow; font-weight: bold;">atoint</span> atoStore<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">atoint</span> <span style="color: yellow; font-weight: bold;">*</span>val<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">atoint</span> newval<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: white; font-weight: bold;">ATOMIC_STORE</span><span style="color: yellow; font-weight: bold;">(</span>val<span style="color: yellow; font-weight: bold;">,</span> newval<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: yellow; font-weight: bold;">*</span>val<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // return del valore nuovo</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// load (legge il valore)</span></div><div><span style="color: yellow; font-weight: bold;">atoint</span> atoLoad<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">atoint</span> <span style="color: yellow; font-weight: bold;">*</span>val<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">ATOMIC_LOAD</span><span style="color: yellow; font-weight: bold;">(</span>val<span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// prefix increment (++val)</span></div><div><span style="color: yellow; font-weight: bold;">atoint</span> atoPreInc<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">atoint</span> <span style="color: yellow; font-weight: bold;">*</span>val<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: white; font-weight: bold;">ATOMIC_INC</span><span style="color: yellow; font-weight: bold;">(</span>val<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // incrementa atomicamente</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: yellow; font-weight: bold;">*</span>val<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // return del valore nuovo</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// postfix increment (val++) (usa il prefix increment)</span></div><div><span style="color: yellow; font-weight: bold;">atoint</span> atoPostInc<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">atoint</span> <span style="color: yellow; font-weight: bold;">*</span>val<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">atoint</span> old <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">*</span>val<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // salva il valore vecchio</span></div><div> atoPreInc<span style="color: yellow; font-weight: bold;">(</span>val<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // incrementa atomicamente</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> old<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // return del valore vecchio</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// prefix decrement (--val)</span></div><div><span style="color: yellow; font-weight: bold;">atoint</span> atoPreDec<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">atoint</span> <span style="color: yellow; font-weight: bold;">*</span>val<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: white; font-weight: bold;">ATOMIC_DEC</span><span style="color: yellow; font-weight: bold;">(</span>val<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // decrementa atomicamente</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: yellow; font-weight: bold;">*</span>val<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // return del valore nuovo</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// postfix decrement (val--) (usa il prefix decrement)</span></div><div><span style="color: yellow; font-weight: bold;">atoint</span> atoPostDec<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">atoint</span> <span style="color: yellow; font-weight: bold;">*</span>val<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">atoint</span> old <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">*</span>val<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // salva il valore vecchio</span></div><div> atoPreDec<span style="color: yellow; font-weight: bold;">(</span>val<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // decrementa atomicamente</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> old<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // return del valore vecchio</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>È veramente molto semplice, no? E grazie ai <a href="https://artcprogramming.blogspot.com/2012/08/no-comment.html" rel="noopener" target="_blank"><strong>molti commenti</strong></a>, credo che non sono necessarie ulteriori spiegazioni.</p><p>E, <em>dulcis in fundo</em>, bisogna provare se la libreria funziona, no? Si, ci vuole un bel programma di test, vai col codice!</p><pre class="EnlighterJSRAW" data-enlighter-language="generic"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">pthread.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">atoint.h</span><span style="color: yellow; font-weight: bold;">"</span></div><br /><div><span style="color: silver; font-style: italic;">// prototipi locali</span></div><div><span style="color: yellow; font-weight: bold;">void*</span> updateCnt<span style="color: yellow; font-weight: bold;">(void*</span> arg<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: silver; font-style: italic;">// variabili globali (per un semplice test si possono usare!)</span></div><div><span style="color: yellow; font-weight: bold;">atoint</span> ato_cnt<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // un int atomico usato come contatore</span></div><div><span style="color: yellow; font-weight: bold;">int</span> cnt<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // un int normale usato come contatore</span></div><br /><div><span style="color: silver; font-style: italic;">// funzione main</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(void)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// init contatori</span></div><div> atoStore<span style="color: yellow; font-weight: bold;">(&</span>ato_cnt<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">);</span></div><div> cnt <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// avvio 10 thread</span></div><div> <span style="color: yellow; font-weight: bold;">pthread_t</span> tid<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">10</span><span style="color: yellow; font-weight: bold;">];</span></div><div> <span style="color: yellow; font-weight: bold;">for</span> <span style="color: yellow; font-weight: bold;">(int</span> n <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span> n <span style="color: yellow; font-weight: bold;"><</span> <span style="color: white; font-weight: bold;">10</span><span style="color: yellow; font-weight: bold;">;</span> <span style="color: yellow; font-weight: bold;">++</span>n<span style="color: yellow; font-weight: bold;">)</span></div><div> pthread_create<span style="color: yellow; font-weight: bold;">(&</span>tid<span style="color: yellow; font-weight: bold;">[</span>n<span style="color: yellow; font-weight: bold;">],</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>updateCnt<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// attendo la fine dei 10 thread</span></div><div> <span style="color: yellow; font-weight: bold;">for(int</span> n <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span> n <span style="color: yellow; font-weight: bold;"><</span> <span style="color: white; font-weight: bold;">10</span><span style="color: yellow; font-weight: bold;">;</span> <span style="color: yellow; font-weight: bold;">++</span>n<span style="color: yellow; font-weight: bold;">)</span></div><div> pthread_join<span style="color: yellow; font-weight: bold;">(</span>tid<span style="color: yellow; font-weight: bold;">[</span>n<span style="color: yellow; font-weight: bold;">],</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// i risultati!</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">Il contatore atomico vale: %u\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> ato_cnt<span style="color: yellow; font-weight: bold;">);</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">Il contatore normale vale: %u\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> cnt<span style="color: yellow; font-weight: bold;">);</span></div><br /><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// updateCnt() - funzione di update dei counter eseguita da ogni thread</span></div><div><span style="color: yellow; font-weight: bold;">void*</span> updateCnt<span style="color: yellow; font-weight: bold;">(void*</span> arg<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// incremento i counter per 1000 volte</span></div><div> <span style="color: yellow; font-weight: bold;">for</span> <span style="color: yellow; font-weight: bold;">(int</span> n <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span> n <span style="color: yellow; font-weight: bold;"><</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">;</span> <span style="color: yellow; font-weight: bold;">++</span>n<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// uso prefix increment</span></div><div> atoPreInc<span style="color: yellow; font-weight: bold;">(&</span>ato_cnt<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">++</span>cnt<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>I più attenti avranno notato che è esattamente lo stesso programma di test mostrato nell'articolo <a href="https://artcprogramming.blogspot.com/2021/09/atomic-endgame-come-quando-e-perche.html" rel="noopener" target="_blank"><strong>Atomic: Endgame</strong></a>, solo che in questo caso si usa il nuovo tipo <strong>atoint</strong> invece del tipo standard <strong>atomic_int</strong>: se il test funzionasse potremmo affermare che la libreria <strong>atoint</strong> è un buon sostituto della versione standard. Ci riusciremo? Proviamo: prima di tutto se stiamo usando un <strong>GCC</strong> recente (cosa probabile) dobbiamo forzare l'uso della versione senza<strong> _Atomic</strong> (ad esempio <em>"truccando" </em> la <em>define</em> di <strong>GCC_VERSION</strong>), poi possiamo compilare ed eseguire. Il risultato è questo:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>aldo@Linux $ .<span style="color: yellow; font-weight: bold;">/</span>test</div><div>Il contatore atomico vale: <span style="color: white; font-weight: bold;">10000</span></div><div>Il contatore normale vale: <span style="color: white; font-weight: bold;">4902</span></div></div></pre><p>Ma funziona! Il contatore atomico <strong>atoint</strong> ha contato esattamente tutti gli incrementi e vale 10000 mentre il contatore normale ha perso molti degli incrementi a causa della concorrenza, e, quindi, vale molto meno (e ripetendo l'esecuzione si ottengono sempre valori inferiori a 10000). Se poi vogliamo fare anche <em>"la prova del 9"</em> possiamo togliere il trucco che forza la versione antica e ricompilare con il nostro <strong>GCC</strong> recente: con <strong>_Atomic</strong>, il risultato dovrebbe essere lo stesso (e se non lo è sarebbe il caso di dire <em>"Houston, abbiamo un problema."</em>).</p><p>E, se avete voglia di sperimentare, potete modificare leggermente il codice per usare anche le tre funzioni inutilizzate (<strong>atoPostInc()</strong>, <strong>atoPreDec()</strong> e <strong>atoPostDec(</strong>)), vi assicuro che funzionano bene.</p><p>Ok, personalmente a questo punto sono soddisfatto e per oggi possiamo concludere qui. La <strong>libreria atoint</strong> funziona e spero vivamente che possa risultare utile a molti lettori. Nel prossimo articolo cambieremo argomento, ma, come promesso sopra, prima o poi torneremo su questa storia per completare la trattazione... <em>e non trattenete il respiro nell'attesa, mi raccomando!</em></p><p>Ciao, e al prossimo post!</p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com2tag:blogger.com,1999:blog-3955087438777078778.post-42455612553516606592022-10-26T20:41:00.003+02:002022-10-27T09:28:51.969+02:00Licorice System come scrivere una system(3) con timeout in C<blockquote><b>Gary:</b> <i>Signore e signori posso avere la vostra attenzione? Lasciate che vi presenti la futura signora Alana Valentine.</i><br /><b>Alana:</b> <i>Idiota.</i><br /></blockquote><p>Questo articolo è la seconda parte (non prevista, devo ammetterlo) di <a href="https://artcprogramming.blogspot.com/2022/06/system-no-grazie-considerazioni-sul.html" rel="noopener" target="_blank"><strong>System? No, grazie!</strong></a>, quindi dovrebbe ripetere lo stesso titolo; però poco tempo fa ho visto il bel <a href="https://letterboxd.com/aldoz/film/licorice-pizza/" rel="noopener" target="_blank"><strong>Licorice Pizza</strong></a> del Maestro <a href="https://letterboxd.com/director/paul-thomas-anderson/" rel="noopener" target="_blank"><strong>Paul Thomas Anderson</strong></a> e non ho resistito alla tentazione di agganciarlo a questo post. E, a maggior ragione, l'ho fatto anche perché questo secondo articolo non è più un <strong>"No grazie!"</strong> ma è una variazione sul tema con una proposta interessante. Interessante come il film in oggetto, pieno di frasi lapidarie come quella vista sopra. Un piccolo gioiellino che ci ha regalato (<em>grazie Paul!</em>) un P.T.Anderson in veste leggera e spensierata, ma sempre a modo suo (<em>ah, divagando: ne ho scritti altri di “No Grazie!” e vi invito a leggerli o rileggerli, </em><em><a href="https://artcprogramming.blogspot.com/2020/09/sleep-no-grazie-considerazioni-sul.html" rel="noopener" target="_blank"><strong>qui</strong></a>, <a href="https://artcprogramming.blogspot.com/2019/12/sprintf-no-grazie-considerazioni.html" rel="noopener" target="_blank"><strong>qui</strong>,</a> <a href="https://artcprogramming.blogspot.com/2019/10/debugger-no-grazie-considerazioni.html" rel="noopener" target="_blank"><strong>qui</strong></a> e <a href="https://artcprogramming.blogspot.com/2019/02/variabili-globali-no-grazie-come-non.html" rel="noopener" target="_blank"><strong>qui</strong></a></em>).</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7mAQEZgFtoa04O-hEQEW5UAvbQwVxjitXybuDavQ31pgvkEORXl32k1AH9hNiMCt9qCtLW8ImgXPj2ys2fZ2pGeLZlBClTyggW9CxiU-nKaywlFuF5vsAv3Rq5Sx_MK6jYUmKcMyAMoE4aGZnkVe0eCuZv9QqYtfgQWvuNWw2UjYSk08y69BjW66Qhg/s828/licoricepizza.jpeg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="460" data-original-width="828" height="328" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7mAQEZgFtoa04O-hEQEW5UAvbQwVxjitXybuDavQ31pgvkEORXl32k1AH9hNiMCt9qCtLW8ImgXPj2ys2fZ2pGeLZlBClTyggW9CxiU-nKaywlFuF5vsAv3Rq5Sx_MK6jYUmKcMyAMoE4aGZnkVe0eCuZv9QqYtfgQWvuNWw2UjYSk08y69BjW66Qhg/w589-h328/licoricepizza.jpeg" width="589" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...corri, se no scade il timeout della nuova system...</td></tr></tbody></table><p>E allora: immagino che tutti avete ben chiari i difetti della <a href="https://man7.org/linux/man-pages/man3/system.3.html" rel="noopener" target="_blank"><strong>system(3)</strong></a> che ne fanno una funzione <em>anti-pattern</em> (<em>e se non li avete chiari correte a rileggere <strong><a href="https://artcprogramming.blogspot.com/2022/06/system-no-grazie-considerazioni-sul.html" rel="noopener" target="_blank">l'articolo</a></strong>, svelti, prima che scappi!</em>). Come ricorderete avevo elencato una lunga serie di problemi, indicando questo come il più grave:</p><blockquote><p><i>"Quando si chiama la system(3) il programma principale viene sospeso fino al termine del comando invocato, e non c'è nessuna maniera di controllare efficacemente quello che sta succedendo."</i></p></blockquote><p>e dopo calcavo la mano in questa maniera:</p><blockquote><p><i>"Ah, solo come curiosità: nella lista dei problemi qui sopra quello che mi molesta di più è il numero 3: una applicazione che usa system(3) non ha maniera di controllare quello che sta succedendo con i programmi esterni invocati, e deve sospendersi per aspettare che finiscano l'esecuzione: ma questa vi sembra la descrizione di una buona applicazione da mandare in produzione? E vabbè, continuiamo cosi, facciamoci del male..."</i></p></blockquote><p>Per cui, di cosa parleremo oggi? Ma di una <strong>system(3)</strong> con <em>timeout</em>, proprio quello che ci serve! In realtà l'idea originale era di fare una versione che risolvesse anche tutti gli altri problemi della lista (e in effetti ne ho scritta una) ma non volevo gettare troppa carne al fuoco, e quindi, per il momento, vi propongo questa che risolve il problema più importante (<em>e scusate se è poco!</em>).</p><p>Ok, è venuto il momento di far cantare il codice, <a href="https://artcprogramming.blogspot.com/2012/08/no-comment.html" rel="noopener" target="_blank"><strong>che è pieno di commenti</strong></a> (che dovrebbero essere sufficienti a spiegare il tutto), ma comunque aggiungerò anche qualche nota in coda. Vai col codice!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">_GNU_SOURCE</span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">string.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdlib.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">unistd.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">errno.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">time.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/wait.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// prototipi locali</span></div><div><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">int</span> toutSystem<span style="color: yellow; font-weight: bold;">(const</span> <span style="color: yellow; font-weight: bold;">char*</span> command<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">unsigned</span> <span style="color: yellow; font-weight: bold;">int</span> timeout_ms<span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">void</span> mySleep<span style="color: yellow; font-weight: bold;">(unsigned</span> <span style="color: yellow; font-weight: bold;">int</span> milliseconds<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">TOUT_SLEEP</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">500</span><span style="color: silver; font-style: italic;"> // intervallo di sleep per il loop busy wait del timeout</span></div><br /><div><span style="color: silver; font-style: italic;">// funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char*</span> argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// test con uno shell-script ma funziona con qualsiasi comando disponibile</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">main: eseguo toutSystem(\"./script.sh > out.txt\", 5000)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> toutSystem<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">./script.sh > out.txt</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">5000</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// toutSystem() - una system(3) con timeout</span></div><div><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">int</span> toutSystem<span style="color: yellow; font-weight: bold;">(</span></div><div> <span style="color: yellow; font-weight: bold;">const</span> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>command<span style="color: yellow; font-weight: bold;">,</span><span style="color: silver; font-style: italic;"> // il comando shell da eseguire (e.g.: cp -v file1 file2)</span></div><div> <span style="color: yellow; font-weight: bold;">unsigned</span> <span style="color: yellow; font-weight: bold;">int</span> timeout_ms<span style="color: yellow; font-weight: bold;">)</span><span style="color: silver; font-style: italic;"> // timeout per waitpid(2) in ms: 0 significa senza timeout</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> errmsg_buf<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">256</span><span style="color: yellow; font-weight: bold;">];</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// fork + exec + wait</span></div><div> <span style="color: yellow; font-weight: bold;">pid_t</span> pid <span style="color: yellow; font-weight: bold;">=</span> fork<span style="color: yellow; font-weight: bold;">();</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>pid <span style="color: yellow; font-weight: bold;">==</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// figlio: eseguo il comando</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: figlio: processo %d: eseguo il comando\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> __func__<span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">());</span></div><div> execl<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">/bin/sh</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">sh</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">-c</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> command<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">(char</span> <span style="color: yellow; font-weight: bold;">*)</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// questo viene eseguito solo se fallisce exec (exec non ritorna mai)</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: figlio: processo %d: errore exec: %s\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> __func__<span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">(),</span> strerror_r<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">,</span> errmsg_buf<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>errmsg_buf<span style="color: yellow; font-weight: bold;">)));</span></div><div> exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>pid <span style="color: yellow; font-weight: bold;">></span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// padre: attesa uscita del figlio</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: padre: processo %d: attesa uscita del figlio\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> __func__<span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">());</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> rc_wait<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> status<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>timeout_ms <span style="color: yellow; font-weight: bold;">></span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span><span style="color: silver; font-style: italic;"> // check timeout</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// busy wait con timeout</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> cnt_wait <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">((</span>rc_wait <span style="color: yellow; font-weight: bold;">=</span> waitpid<span style="color: yellow; font-weight: bold;">(</span>pid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>status<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">WNOHANG</span><span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">==</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div> mySleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">TOUT_SLEEP</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(++</span>cnt_wait <span style="color: yellow; font-weight: bold;">></span> timeout_ms <span style="color: yellow; font-weight: bold;">/</span> <span style="color: white; font-weight: bold;">TOUT_SLEEP</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// figlio non uscito prima del timeout: return errore</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: padre: processo %d: waitpid timeout scaduto\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> __func__<span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">());</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// wait senza timeout</span></div><div> rc_wait <span style="color: yellow; font-weight: bold;">=</span> waitpid<span style="color: yellow; font-weight: bold;">(</span>pid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>status<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// figlio uscito: return risultato</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>rc_wait <span style="color: yellow; font-weight: bold;">!=</span> pid<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// waitpid error</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: padre: processo %d: errore waitpid (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> __func__<span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">(),</span> strerror_r<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">,</span> errmsg_buf<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>errmsg_buf<span style="color: yellow; font-weight: bold;">)));</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// processo terminato: return risultato</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> result <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">WIFEXITED</span><span style="color: yellow; font-weight: bold;">(</span>status<span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// questo è l'unico risultato accettato come successo</span></div><div> result <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: padre: processo %d: pid %d uscito (status=%d)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> __func__<span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">(),</span> pid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">WEXITSTATUS</span><span style="color: yellow; font-weight: bold;">(</span>status<span style="color: yellow; font-weight: bold;">));</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">WIFSIGNALED</span><span style="color: yellow; font-weight: bold;">(</span>status<span style="color: yellow; font-weight: bold;">))</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: padre: processo %d: pid %d ucciso dal segnale %d\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> __func__<span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">(),</span> pid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">WTERMSIG</span><span style="color: yellow; font-weight: bold;">(</span>status<span style="color: yellow; font-weight: bold;">));</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">WIFSTOPPED</span><span style="color: yellow; font-weight: bold;">(</span>status<span style="color: yellow; font-weight: bold;">))</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: padre: processo %d: pid %d fermato dal segnale %d\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> __func__<span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">(),</span> pid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">WSTOPSIG</span><span style="color: yellow; font-weight: bold;">(</span>status<span style="color: yellow; font-weight: bold;">));</span></div><div> <span style="color: yellow; font-weight: bold;">else</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: padre: processo %d: pid %d con stato sconosciuto (status=%d)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> __func__<span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">(),</span> pid<span style="color: yellow; font-weight: bold;">,</span> status<span style="color: yellow; font-weight: bold;">);</span></div><br /><div> <span style="color: yellow; font-weight: bold;">return</span> result<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore fork</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: errore fork: %s\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> __func__<span style="color: yellow; font-weight: bold;">,</span> strerror_r<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">,</span> errmsg_buf<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>errmsg_buf<span style="color: yellow; font-weight: bold;">)));</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// mySleep() - wrapper per nanosleep()</span></div><div><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">void</span> mySleep<span style="color: yellow; font-weight: bold;">(unsigned</span> <span style="color: yellow; font-weight: bold;">int</span> milliseconds<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">timespec</span> ts<span style="color: yellow; font-weight: bold;">;</span></div><div> ts<span style="color: yellow; font-weight: bold;">.</span>tv_sec <span style="color: yellow; font-weight: bold;">=</span> milliseconds <span style="color: yellow; font-weight: bold;">/</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">;</span></div><div> ts<span style="color: yellow; font-weight: bold;">.</span>tv_nsec <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">(</span>milliseconds <span style="color: yellow; font-weight: bold;">%</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">*</span> <span style="color: white; font-weight: bold;">1000000</span><span style="color: yellow; font-weight: bold;">;</span></div><div> nanosleep<span style="color: yellow; font-weight: bold;">(&</span>ts<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Come avrete notato la nuova funzione, che ho battezzato <strong>toutSystem()</strong> (<em>un nome originalissimo...</em>) è scritta sulla falsariga dell'esempio con <a href="https://man7.org/linux/man-pages/man2/fork.2.html" rel="noopener" target="_blank"><strong>fork(2)</strong></a> + <a href="https://man7.org/linux/man-pages/man3/exec.3.html" rel="noopener" target="_blank"><strong>exec(3)</strong></a> + <a href="https://man7.org/linux/man-pages/man2/wait.2.html" rel="noopener" target="_blank"><strong>wait(2)</strong></a> del precedente articolo, che era una delle soluzioni proposte come buona alternativa alla <strong>system(3)</strong> e che fa al caso nostro per sviluppare questa nuova versione.</p><p>Il funzionamento è (relativamente) semplice: la funzione esegue<strong> fork(2)</strong> e si sdoppia in <em>padre</em> e <em>figlio</em>. Il <em>figlio</em> esegue il comando <em><command></em> esattamente come lo fa, internamente, la <strong>system(3)</strong>, per cui usa <strong>execl(3)</strong> per creare una <em>sub-shell</em> di esecuzione (e fino a qui siamo abbastanza in linea con la <strong>system(3)</strong> e con alcuni dei i suoi difetti, <em>sigh</em>). La parte buona, però la esegue il <em>padre </em>che, invece di limitarsi ad aspettare l'uscita del <em>figlio</em>, esegue un <em>loop</em> in stile <em>busy wait</em> usando in maniera <em>"intelligente"</em> <strong>waitpid(3)</strong> per un tempo mai superiore ai millisecondi indicati dall'argomento <<em>timeout_ms>. </em>Alla fine del <em>loop</em> si testa il risultato dell'attesa per decidere se ritornare un errore o un esito, e il risultato che ci preme è stato conseguito: la <strong>toutSystem()</strong> non può bloccare indefinitamente il nostro programma (al contrario di quello che può fare la <em>famigerata</em> <strong>system(3)</strong>), ma lo blocca <strong>al massimo</strong> per la durata del <em>timeout.</em> E, comunque, ho lasciato la possibilità di simulare il comportamento <em>"classico" </em>(ossia: attesa indefinita) usando zero come <em>timeout</em>.</p><p>L'esempio qui sopra contiene anche un <strong>main()</strong> di prova, così gli increduli potranno compilare e verificare direttamente il funzionamento. Si può eseguire qualsiasi comando: io nell'esempio ho eseguito uno <em>shell script</em> che ho scritto proprio per verificare l'efficacia del <em>timeout</em>. Lo <em>script</em> (<em>che poteva essere anche un semplice codice <strong>C</strong> compilato, eh!</em>) è questo:</p><pre class="EnlighterJSRAW" data-enlighter-language="shell"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">#!/bin/bash</span></div><br /><div><span style="color: yellow; font-weight: bold;">for</span> i <span style="color: yellow; font-weight: bold;">in</span> 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15</div><div><span style="color: yellow; font-weight: bold;">do</span></div><div> echo <span style="color: yellow; font-weight: bold;">$</span>i</div><div> sleep 1</div><div><span style="color: yellow; font-weight: bold;">done</span></div></div></pre><p>Lo <em>script</em> scrive per 15 secondi un numero progressivo nel file <em>"out.txt"</em> su cui è rediretto il comando di <strong>toutSystem()</strong> nel<strong> main()</strong>: <em>toutSystem("./script.sh > out.txt", 5000)</em>. Se compilate ed eseguite l'output sarà questo:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>main: eseguo toutSystem<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">./script.sh > out.txt</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">5000</span><span style="color: yellow; font-weight: bold;">)</span></div><div>toutSystem: padre: processo <span style="color: white; font-weight: bold;">16463</span>: attesa uscita del figlio</div><div>toutSystem: figlio: processo <span style="color: white; font-weight: bold;">16464</span>: eseguo il comando</div><div>toutSystem: padre: processo <span style="color: white; font-weight: bold;">16463</span>: waitpid timeout scaduto</div></div></pre><p>che indica che la nostra chiamata a <strong>toutSystem()</strong> è uscita per <em>timeout</em> dopo 5 secondi (lo <em>script</em> eseguito lavora per 15 secondi, quindi è ancora attivo in quel momento), e difatti noterete che il file <em>"out.txt"</em> si riempirà 10 secondi dopo l'uscita del programma principale. <em>Provare per credere!</em></p><p>Ok, per oggi può bastare: la<strong> toutSystem()</strong> è già perfettamente utilizzabile nella forma proposta, ed è una ottima alternativa all'uso della<strong> system(3)</strong> che, non mi stanco mai di dirlo, non si dovrebbe mai usare. Vi consiglio, come utile esercitazione, di provare a modificare la <strong>toutSystem()</strong> per ovviare anche agli altri problemi elencati nell'articolo <a href="https://artcprogramming.blogspot.com/2022/06/system-no-grazie-considerazioni-sul.html" rel="noopener" target="_blank"><strong>System? No, grazie!</strong></a>. Io l'ho fatto e vi assicuro che non è un lavoro molto complicato (<em>e prossimamente vi mostrerò la mia soluzione, promesso</em>).</p><p>Ciao, e al prossimo post!</p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-64175465624419914942022-09-26T18:53:00.001+02:002022-09-26T18:57:39.566+02:00Thread Cancel? No, grazie! considerazioni sul perché non usare la pthread_cancel(3) in C (e C++) - pt.2<blockquote><b>[il gruppo alla ricerca di una via d'uscita si accorge di non essere sulla Terra guardando il cielo]</b><br /><b>Royce:</b> <i>Abbiamo bisogno di un nuovo piano...</i><br /></blockquote><p>Come promesso <a href="https://artcprogramming.blogspot.com/2022/08/thread-cancel-no-grazie-considerazioni.html" rel="noopener" target="_blank"><strong>nella prima parte dell'articolo</strong></a> è venuto il momento di dare qualche consiglio su come fermare un thread <strong>senza</strong> <strong>usare</strong> la <a href="https://man7.org/linux/man-pages/man3/pthread_cancel.3.html" rel="noopener" target="_blank"><strong>pthread_cancel(3)</strong></a>. Là il film collegato era il mitico <a href="https://letterboxd.com/aldoz/film/predator/" rel="noopener" target="_blank"><strong>Predator</strong></a>, e in questo, per rimanere in tema, ho scelto il buon <a href="https://letterboxd.com/aldoz/film/predators/" rel="noopener" target="_blank"><strong>Predators</strong></a> che, pur non essendo all'altezza del primo della saga, ne è un buon seguito (al contrario di <a href="https://letterboxd.com/aldoz/film/predator-2/" rel="noopener" target="_blank"><strong>Predator 2</strong></a> su cui è meglio sorvolare). In <strong>Predators</strong> i protagonisti sono alle prese con una missione quasi impossibile (<em>e mi scuso per lo spoiler: tornare sulla terra</em>), una missione complicata come fermare efficacemente un <em>thread</em> usando la <strong>pthread_cancel(3)</strong>... Ma niente paura, qui vedremo come si può fare!</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKfmHbAnxXmGSxExfXMhzM0pkElYhw-StfWijl4J4xK9IR07MxtpYLzJC7d2bgQ85Bs4XOl_t-HMfBraF33OSA84uXg9MOO2aPhGsxYuYq2-QRumAYK_v9hnMSutMU0fcjpOQ1Td7-b6gw7QAWz2uoD3dNwk9GxxF_XUvKHiPvqttEUCiAOsOMjNk2WQ/s1920/predators.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="816" data-original-width="1920" height="248" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKfmHbAnxXmGSxExfXMhzM0pkElYhw-StfWijl4J4xK9IR07MxtpYLzJC7d2bgQ85Bs4XOl_t-HMfBraF33OSA84uXg9MOO2aPhGsxYuYq2-QRumAYK_v9hnMSutMU0fcjpOQ1Td7-b6gw7QAWz2uoD3dNwk9GxxF_XUvKHiPvqttEUCiAOsOMjNk2WQ/w585-h248/predators.jpg" width="585" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...ve l'avevo detto che andava a finire così usando la pthread_cancel(3)...</td></tr></tbody></table><p>E allora, veniamo al dunque: immagino che tutti conosciate il principio del <strong>Rasodio di Occam</strong>, che più o meno dice:</p><blockquote><pre>E’ inutile fare con più ciò che può essere fatto con meno.</pre></blockquote><p>Ebbene si, applicando questo fantastico principio al nostro problema possiamo dire: <em>"perché per fermare un thread devo usare la (demenziale) combinazione di pthread_setcancelstate(3) + pthread_setcanceltype(3) + pthread_key_create(3) + pthread_cleanup_push(3) + pthread_cleanup_pop(3) + pthread_getspecific(3) + pthread_setspecific(3) + pthread_cancel(3) + pthread_join(3) quando posso lasciare a lui l'incarico di fermarsi bene?".</em> Uhm, detto così sembra facile... e in effetti lo è! Il trucco consiste nel progettare adeguatamente la funzione eseguita dal <em>thread</em> in maniera che abbia dei punti di uscita <em>"puliti"</em>, e senza usare nessuna funzione accessoria della <em>libpthread</em>, che, come abbiamo visto, sono (per questa particolare situazione) molte, macchinose e anche complicate da usare.</p><p>Ok, bando alle ciance, facciamo parlare il codice: ecco un esempio molto semplice di uscita controllata che ho ottenuto applicando pochi piccoli cambi al codice di <em>threaderrcancel.c</em> che, nello scorso articolo, <strong>chiudeva male il thread</strong>. Vai col codice!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; line-height: 13px;"><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: silver; font-style: italic;">/ threadstop.c -esempio di stop thread</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdlib.h</span><span style="color: yellow; font-weight: bold;">></span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">pthread.h</span><span style="color: yellow; font-weight: bold;">></span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: silver; font-style: italic;">// prototipi globali</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">void*</span> myThread<span style="color: yellow; font-weight: bold;">(void</span> <span style="color: yellow; font-weight: bold;">*</span>arg<span style="color: yellow; font-weight: bold;">);</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">void</span> mySleep<span style="color: yellow; font-weight: bold;">(unsigned</span> <span style="color: yellow; font-weight: bold;">int</span> milliseconds<span style="color: yellow; font-weight: bold;">);</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: silver; font-style: italic;">// main() - funzione main()</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char*</span> argv<span style="color: yellow; font-weight: bold;">[])</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">{</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// creo il thread con un argomento "stop"</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">int</span> stop <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">pthread_t</span> tid<span style="color: yellow; font-weight: bold;">;</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> pthread_create<span style="color: yellow; font-weight: bold;">(&</span>tid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>myThread<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>stop<span style="color: yellow; font-weight: bold;">);</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// aspetto 2 secondi e poi stop del thread</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> mySleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">2000</span><span style="color: yellow; font-weight: bold;">);</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> stop <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// join del thread</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> pthread_join<span style="color: yellow; font-weight: bold;">(</span>tid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">);</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// esco</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: esco\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">]);</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">}</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: silver; font-style: italic;">// myThread() - thread routine</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">void*</span> myThread<span style="color: yellow; font-weight: bold;">(void</span> <span style="color: yellow; font-weight: bold;">*</span>arg<span style="color: yellow; font-weight: bold;">)</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">{</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// recast dell'argomento di stop thread</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">int</span> <span style="color: yellow; font-weight: bold;">*</span>stop <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">(int</span> <span style="color: yellow; font-weight: bold;">*)</span>arg<span style="color: yellow; font-weight: bold;">;</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// loop del thread</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">thread partito\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">(*</span>stop <span style="color: yellow; font-weight: bold;">==</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// il thread fa cose...</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// ...</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// malloc sul buffer</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>p <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">(char</span> <span style="color: yellow; font-weight: bold;">*)</span>malloc<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">100</span><span style="color: yellow; font-weight: bold;">);</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div><span style="color: yellow; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; font-weight: bold;"> </span><span style="color: silver; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><i>// simulo un ritardo perché il thread fa altre cose...</i></span></span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> mySleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">2</span><span style="color: yellow; font-weight: bold;">);</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// free del buffer</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> free<span style="color: yellow; font-weight: bold;">(</span>p<span style="color: yellow; font-weight: bold;">);</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// sleep del thread (10 ms)</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> mySleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">10</span><span style="color: yellow; font-weight: bold;">);</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">}</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// il thread esce</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">thread finito\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> pthread_exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">);</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">}</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: silver; font-style: italic;">// mySleep() - wrapper per nanosleep()</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">void</span> mySleep<span style="color: yellow; font-weight: bold;">(unsigned</span> <span style="color: yellow; font-weight: bold;">int</span> milliseconds<span style="color: yellow; font-weight: bold;">)</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">{</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">timespec</span> ts<span style="color: yellow; font-weight: bold;">;</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> ts<span style="color: yellow; font-weight: bold;">.</span>tv_sec <span style="color: yellow; font-weight: bold;">=</span> milliseconds <span style="color: yellow; font-weight: bold;">/</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">;</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> ts<span style="color: yellow; font-weight: bold;">.</span>tv_nsec <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">(</span>milliseconds <span style="color: yellow; font-weight: bold;">%</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">*</span> <span style="color: white; font-weight: bold;">1000000</span><span style="color: yellow; font-weight: bold;">;</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> nanosleep<span style="color: yellow; font-weight: bold;">(&</span>ts<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">);</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Non so se avete notato: è praticamente lo stesso codice dell'altro con 5 (cinque<em>!</em>) linee modificate. Il semplicissimo trucco consiste nel passare al <em>thread</em> un <em>flag</em> (con il fantasiosissimo nome <em>"stop"</em>) e usarlo adeguatamente nella funzione del <em>thread</em>. Nel nostro esempio la funzione esegue un loop pseudo-infinito, che ha come condizione di chiusura proprio il valore del <em>flag</em> di <em>stop</em>. Quando il <em>main()</em> scrive 1 nel <em>flag</em> (invece di chiamare la <strong>pthread_cancel(3)</strong>) il <em>thread</em> finirà di eseguire il ciclo loop e, invece di eseguire un nuovo ciclo, uscirà chiamando <strong>pthread_exit(3)</strong>. Ok, questo è un esempio molto semplificato, ma con qualche accortezza si può applicare a qualsiasi modello di <em>thread routine</em>. Anzi, possiamo fare un piccolo specchietto dei tre casi più classici di funzione di <em>thread</em>:</p><ol><li><strong>Funzione con loop pseudo-infinito semplice:</strong> il <em>thread</em> esegue poche operazioni <em>self-cleaning</em> in <em>loop</em>. È quello dell'esempio appena mostrato, e con il test del <em>flag</em> di <em>stop</em> nel <em>while</em> siamo a posto così.</li><li><strong>Funzione con loop pseudo-infinito complesso:</strong> il <em>thread</em> esegue molte operazioni nel <em>loop</em>. Oltre al <em>test</em> del <em>flag</em> di <em>stop</em> nel while possiamo aggiungere delle istruzioni di uscita intermedie di questo tipo:<br /><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>...</div><div><span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>stop <span style="color: yellow; font-weight: bold;">==</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// pulisco memoria, lock, ecc.</span></div><div> ...</div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// esco dal loop (ma potrei anche uscire direttamente con pthread_exit(3))</span></div><div> <span style="color: yellow; font-weight: bold;">break;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><div>...</div></div></pre></li><li><strong>Funzione senza loop pseudo-infinito:</strong> il <em>thread</em> esegue alcune attività sequenziali. Possiamo aggiungere dopo ogni attività delle istruzioni di uscita intermedie di questo tipo:<br /><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>...</div><div><span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>stop <span style="color: yellow; font-weight: bold;">==</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// pulisco memoria, lock, ecc.</span></div><div> ...</div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// esco</span></div><div> pthread_exit<span style="color: yellow; font-weight: bold;">(NULL);</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><div>...</div></div></pre></li></ol><p>Qualcuno dirà:<em> "ma le attività di pulizia prima della chiusura sono le stesse viste l'altra volta con <strong>pthread_cleanup_push(3)</strong>, ecc., quindi è la stessa cosa..."</em>. <em>De gustibus</em>: se sembra la stessa cosa ognuno è libero di continuare a impazzire usando la<strong> pthread_cancel(3)</strong> e le altre 1000 funzioni accessorie. A me sembra molto più semplice usare il <em>flag</em> di <em>stop</em> come appena mostrato... <em>ma non voglio certo privare nessuno del piacere di scrivere codice più complicato del necessario (ah ah ah).</em></p><p>E chiudo con un (spero utile) consiglio: è meglio non confidare troppo nella combinazione <strong>pthread_create(3)</strong> +<strong> pthread_join(3)</strong>: se qualcosa va male nello <em>stop</em> del <em>thread</em> (<em>chessoio</em>: una operazione di I/O bloccata nel <em>thread</em>) la <strong>pthread_join(3)</strong> blocca il programma infinitamente. Qualche furbone ovvia al problema eseguendo <strong>pthread_detach(3)</strong> sul <em>thread</em> appena creato, ma questa non è una scelta raccomandabile, perché si perde completamente il controllo dei <em>thread</em> creati e anche la possibilità di un vero <em>stop</em> controllato. Ci sono, invece, due opzioni molto più interessanti: la prima è usare una vera <em>join con timeout</em>, la <strong>pthread_timed_join_np(3)</strong>, che funziona così (vi mostro solo il <em>main()</em>, nel resto del codice non cambia nulla):</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// main() - funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char*</span> argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// creo il thread con un argomento "stop"</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> stop <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">pthread_t</span> tid<span style="color: yellow; font-weight: bold;">;</span></div><div> pthread_create<span style="color: yellow; font-weight: bold;">(&</span>tid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>myThread<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>stop<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// aspetto 2 secondi e poi stop del thread</span></div><div> mySleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">2000</span><span style="color: yellow; font-weight: bold;">);</span></div><div> stop <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// join del thread con timeout di 1 sec</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">timespec</span> timeout<span style="color: yellow; font-weight: bold;">;</span></div><div> clock_gettime<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">CLOCK_REALTIME</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>timeout<span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // prendo il tempo attuale</span></div><div> timeout<span style="color: yellow; font-weight: bold;">.</span>tv_sec <span style="color: yellow; font-weight: bold;">+=</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // il timeout è il tempo attuale più 1 sec</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> retval <span style="color: yellow; font-weight: bold;">=</span> pthread_timedjoin_np<span style="color: yellow; font-weight: bold;">(</span>tid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>timeout<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// esco</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: esco (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span></div><div> retval <span style="color: yellow; font-weight: bold;">==</span> <span style="color: white; font-weight: bold;">ETIMEDOUT</span> <span style="color: yellow; font-weight: bold;">?</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">con timeout</span><span style="color: yellow; font-weight: bold;">"</span> <span style="color: yellow; font-weight: bold;">:</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">con Ok</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Oppure, se non è disponibile una la <em>join</em> con <em>timeout</em> (e, ahimè, per le interfacce <strong>threads</strong> di C11 e <strong>std::thread</strong> di C++11 è così), si può fare questo (e qui ci sono più cambi, quindi vi mostro quasi tutto il codice, omettendo le parti invariate):</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; line-height: 13px;"><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: silver; font-style: italic;">// struttura per stop controllato</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">typedef</span> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">_stops</span> <span style="color: yellow; font-weight: bold;">{</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">int</span> stop<span style="color: yellow; font-weight: bold;">;</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">int</span> stopped<span style="color: yellow; font-weight: bold;">;</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">}</span> <span style="color: yellow; font-weight: bold;">Stops</span><span style="color: yellow; font-weight: bold;">;</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: silver; font-style: italic;">// prototipi globali</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">void*</span> myThread<span style="color: yellow; font-weight: bold;">(void</span> <span style="color: yellow; font-weight: bold;">*</span>arg<span style="color: yellow; font-weight: bold;">);</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">void</span> mySleep<span style="color: yellow; font-weight: bold;">(unsigned</span> <span style="color: yellow; font-weight: bold;">int</span> milliseconds<span style="color: yellow; font-weight: bold;">);</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: silver; font-style: italic;">// main() - funzione main()</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char*</span> argv<span style="color: yellow; font-weight: bold;">[])</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">{</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// creo il thread con un argomento "stops"</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">Stops</span> stops<span style="color: yellow; font-weight: bold;">;</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> stops<span style="color: yellow; font-weight: bold;">.</span>stop <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> stops<span style="color: yellow; font-weight: bold;">.</span>stopped <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">pthread_t</span> tid<span style="color: yellow; font-weight: bold;">;</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> pthread_create<span style="color: yellow; font-weight: bold;">(&</span>tid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>myThread<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>stops<span style="color: yellow; font-weight: bold;">);</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// aspetto 2 secondi e poi stop del thread</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> mySleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">2000</span><span style="color: yellow; font-weight: bold;">);</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> stops<span style="color: yellow; font-weight: bold;">.</span>stop <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// detach del thread (in chiusura si può fare!)</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> pthread_detach<span style="color: yellow; font-weight: bold;">(</span>tid<span style="color: yellow; font-weight: bold;">);</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// simula una join con timeout</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">int</span> sleep_sum <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">(</span>stops<span style="color: yellow; font-weight: bold;">.</span>stopped <span style="color: yellow; font-weight: bold;">==</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// sleep di attesa</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> mySleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">100</span><span style="color: yellow; font-weight: bold;">);</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">((</span>sleep_sum <span style="color: yellow; font-weight: bold;">+=</span> <span style="color: white; font-weight: bold;">100</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">></span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// timeout scaduto: forzo l'uscita</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">break;</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">}</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">}</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// esco</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: esco (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> sleep_sum <span style="color: yellow; font-weight: bold;">></span> <span style="color: white; font-weight: bold;">1000</span> <span style="color: yellow; font-weight: bold;">?</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">con timeout</span><span style="color: yellow; font-weight: bold;">"</span> <span style="color: yellow; font-weight: bold;">:</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">con Ok</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">}</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: silver; font-style: italic;">// myThread() - thread routine</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">void*</span> myThread<span style="color: yellow; font-weight: bold;">(void</span> <span style="color: yellow; font-weight: bold;">*</span>arg<span style="color: yellow; font-weight: bold;">)</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">{</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// recast degll'argomento</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">Stops</span> <span style="color: yellow; font-weight: bold;">*</span>stops <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">Stops</span> <span style="color: yellow; font-weight: bold;">*)</span>arg<span style="color: yellow; font-weight: bold;">;</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// loop del thread</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">thread partito\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">(</span>stops<span style="color: yellow; font-weight: bold;">-></span>stop <span style="color: yellow; font-weight: bold;">==</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// il thread fa cose...</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// ...</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// malloc sul buffer</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>p <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">(char</span> <span style="color: yellow; font-weight: bold;">*)</span>malloc<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">100</span><span style="color: yellow; font-weight: bold;">);</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div><span style="color: yellow; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; font-weight: bold;"> </span><span style="color: silver; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><i>// simulo un ritardo perché il thread fa altre cose...</i></span></span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> mySleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">2</span><span style="color: yellow; font-weight: bold;">);</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// free del buffer</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> free<span style="color: yellow; font-weight: bold;">(</span>p<span style="color: yellow; font-weight: bold;">);</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// sleep del thread (10 ms)</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> mySleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">10</span><span style="color: yellow; font-weight: bold;">);</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> <span style="color: yellow; font-weight: bold;">}</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// segnala lo stop avvenuto</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> stops<span style="color: yellow; font-weight: bold;">-></span>stopped <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">;</span></div><span style="color: lime; font-family: Consolas, Droid Sans Mono, monospace, monospace;"><span style="font-size: 11px;"><br /></span></span><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// il thread esce</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">thread finito\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"> pthread_exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">);</span></div><div style="color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px;"><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Questo ultimo caso è leggermente più complesso (ripeto: leggermente) perché bisogna passare un parametro complesso al <em>thread</em> (per comandare lo <em>stop</em> e controllare se è avvenuto) e bisogna simulare la <strong>pthread_timed_join_np(3)</strong> usando la <strong>pthread_detach(3)</strong> seguita da un loop di attesa del flag <em>stopped</em>. Come detto sopra non bisognerebbe quasi mai usare <strong>pthread_detach(3)</strong>, ma in questo caso si può fare un eccezione visto che la chiamiamo quando il <em>thread</em> sta già chiudendo la sua attività.</p><p>Per oggi può bastare, nel prossimo articolo cambieremo argomento, non voglio più sentir parlare di <strong>pthread_cancel(3)</strong> per un po' di tempo... <em>anzi, fosse per me la eliminerei, ma questo credo che si era capito (ah ah ah).</em></p><p>Ciao, e al prossimo post!</p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-68203179132012939892022-08-29T20:41:00.004+02:002022-08-29T21:28:52.692+02:00Thread Cancel? No, grazie! considerazioni sul perché non usare la pthread_cancel(3) in C (e C++) - pt.1<blockquote><b>Dutch:</b> <i>Non toccare quell'arma, non ti ha ucciso perché non eri armata...è uno sportivo.</i></blockquote><p>Ok, ci risiamo: ho ottenuto uno spunto da fatti reali (cose di lavoro) e ho ridetto <em>"Ma qui c'è materiale per un articolo!"</em>, per cui oggi parleremo di <a href="https://man7.org/linux/man-pages/man3/pthread_cancel.3.html" rel="noopener" target="_blank"><strong>pthread_cancel(3)</strong></a>. Uhm, e questa volta c'è un film collegato? No, anche questo articolo appartiene alla serie dei serie dei “<em>No Grazie!</em>” (<em>ah: ne ho scritti altri e vi invito a leggerli o rileggerli, <a href="https://artcprogramming.blogspot.com/2022/06/system-no-grazie-considerazioni-sul.html" rel="noopener" target="_blank"><strong>qui,</strong></a> </em><em><a href="https://artcprogramming.blogspot.com/2020/09/sleep-no-grazie-considerazioni-sul.html" rel="noopener" target="_blank"><strong>qui</strong></a>, <a href="https://artcprogramming.blogspot.com/2019/12/sprintf-no-grazie-considerazioni.html" rel="noopener" target="_blank"><strong>qui</strong>,</a> <a href="https://artcprogramming.blogspot.com/2019/10/debugger-no-grazie-considerazioni.html" rel="noopener" target="_blank"><strong>qui</strong></a> e <a href="https://artcprogramming.blogspot.com/2019/02/variabili-globali-no-grazie-come-non.html" rel="noopener" target="_blank"><strong>qui</strong></a></em>) e non farò giochi di parole con il titolo, e ne sceglierò uno a caso tra quelli che ho visto/rivisto ultimamente... si, <a href="https://letterboxd.com/aldoz/film/predator/" rel="noopener" target="_blank"><strong>Predator</strong></a>, va benissimo: è un gran film action di <a href="https://letterboxd.com/director/john-mctiernan/" rel="noopener" target="_blank"><strong>John McTiernan</strong></a>, un autore che ha smesso prematuramente di regalarci grandi opere per colpa di alcune <em>"birichinate"</em> commesse nella sua vita privata... peccato, era un grande. E se proprio vogliamo forzare una attinenza film/articolo possiamo dire: <strong>pthread_cancel(3)</strong> è una di quelle cose che funzionano ma è meglio starne alla larga,<em> è pericolosa come cercare di cancellare un Predator...</em></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRnyr7tMi_8wKB6PzI6GILAbpNeNVAcLLTYrveYvLDxUJP4DMeXy9hPd2MMaFogu2gq28EkaL_tPoo_He1vxboEgqkzQA4mZg7q36lZzk4veffBVtmr2tEYnzTcJSHp-JNHgyLztj2VGhzSVetf3Sdq6OnPmAv-Kqnk2ycBPzi6UEe-tY1rPu5DEDqFA/s1000/predator.jpeg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="567" data-original-width="1000" height="323" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRnyr7tMi_8wKB6PzI6GILAbpNeNVAcLLTYrveYvLDxUJP4DMeXy9hPd2MMaFogu2gq28EkaL_tPoo_He1vxboEgqkzQA4mZg7q36lZzk4veffBVtmr2tEYnzTcJSHp-JNHgyLztj2VGhzSVetf3Sdq6OnPmAv-Kqnk2ycBPzi6UEe-tY1rPu5DEDqFA/w570-h323/predator.jpeg" width="570" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...giuro che non userò mai più la pthread_cancel(3). Te lo giuro...</td></tr></tbody></table><p>Non so se ricordate, ma nell'articolo <a href="https://artcprogramming.blogspot.com/2022/06/system-no-grazie-considerazioni-sul.html" rel="noopener" target="_blank"><strong>System? No, grazie!</strong></a> ho evidenziato i mille problemi dell'uso della <a href="https://man7.org/linux/man-pages/man3/system.3.html" rel="noopener" target="_blank"><strong>system(3)</strong></a>, ma, a parziale scusante (per gli impavidi che la usano) ho anche detto: <em>"system(3), è una funzione della libc usata e abusata: è molto comoda e semplice da usare..."</em>. Ecco, l'argomento di questo articolo, la <strong>pthread_cancel(3)</strong>, non ha neanche questa scusante: se proprio la si vuole usare bisogna usarla bene, e usarla bene è complicatissimo!</p><p>Ok, è ora di entrare nel dettaglio. Cosa è la <strong>pthread_cancel(3)</strong> e cosa permette di fare? Il manuale della <em>glibc</em> titola semplicemente questo:</p><pre>pthread_cancel - send a cancellation request to a thread</pre><p>Ecco, a quanto pare permette inviare una richiesta di cancellazione a un <em>thread</em> (un po' come inviare un <em>sigkill</em> o un <em>sigterm</em> a un processo): sembra facile ma non lo è! E perché non è facile? Perché quando inviamo la richiesta non sappiamo cosa sta facendo il <em>thread</em>: potrebbe essere, ad esempio, in una zona critica eseguendo una attività che, se interrotta bruscamente, potrebbe corrompere la memoria o lasciare il <em>multitreading</em> in uno stato indefinito, ecc. ecc. Insomma:<em> un giochetto da niente</em>. E uno potrebbe dire <em>"Ma sicuramente se ne occupa la libpthread di chiudere bene... si, si, sicuro: la libreria è dotata di una AI che decide cosa è meglio fare per qualunque codice scritto, nel passato, nel presente e nel futuro, non ti preoccupare..."</em></p><p>Ok, scendiamo con i piedi per terra: la povera <a href="https://man7.org/linux/man-pages/man7/pthreads.7.html" rel="noopener" target="_blank"><strong>libpthread</strong></a> non può occuparsi di chiudere bene un <em>thread</em> che può contenere qualsiasi cosa: per farlo dovrebbe fornire degli strumenti specifici, mentre, essendo una (gran) libreria generica, può solo fornire degli strumenti generici per farlo, e il programmatore deve usarli adeguatamente in base al codice che sta scrivendo. E quindi consiglio caldamente di <strong>leggere accuratamente</strong> il manuale della <strong>pthread_cancel(3)</strong> prima di usarla (<em>e, magari, la lettura del manuale vi toglierà ogni residua voglia di usarla</em>). E, già che ci sono, vi evidenzio i 2 punti chiave, estratti direttamente dal manuale:</p><h4 style="text-align: left;">1. tipo di cancellazione (supponendo che il "cancelability state" del thread sia "cancellabile"):</h4><pre><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>A thread s cancellation type<span style="color: yellow; font-weight: bold;">,</span> determined by</div><div>pthread_setcanceltype<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">3</span><span style="color: yellow; font-weight: bold;">),</span> may be either asynchronous or deferred</div><div><span style="color: yellow; font-weight: bold;">(</span>the default <span style="color: yellow; font-weight: bold;">for</span> new threads<span style="color: yellow; font-weight: bold;">)</span>. Asynchronous cancelability means</div><div>that the thread can be canceled at any time <span style="color: yellow; font-weight: bold;">(</span>usually immediately<span style="color: yellow; font-weight: bold;">,</span></div><div>but the system does not guarantee this<span style="color: yellow; font-weight: bold;">)</span>. Deferred cancelability</div><div>means that cancellation will be delayed until the thread next</div><div>calls a function that is a cancellation point. A list of</div><div>functions that are or may be cancellation points is provided in</div><div>pthreads<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">7</span><span style="color: yellow; font-weight: bold;">)</span>.</div></div></pre><h4 style="text-align: left;">2. esecuzione della cancellation request:</h4><pre><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: white; font-weight: bold;">1.</span> Cancellation clean<span style="color: yellow; font-weight: bold;">-</span>up handlers are popped <span style="color: yellow; font-weight: bold;">(</span>in the reverse of</div><div> the order in which they were pushed<span style="color: yellow; font-weight: bold;">)</span> and called. <span style="color: yellow; font-weight: bold;">(</span>See</div><div> pthread_cleanup_push<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">3</span><span style="color: yellow; font-weight: bold;">)</span>.<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: white; font-weight: bold;">2.</span> Thread<span style="color: yellow; font-weight: bold;">-</span>specific data destructors are called<span style="color: yellow; font-weight: bold;">,</span> in an unspecified</div><div> order. <span style="color: yellow; font-weight: bold;">(</span>See pthread_key_create<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">3</span><span style="color: yellow; font-weight: bold;">)</span>.<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: white; font-weight: bold;">3.</span> The thread is terminated. <span style="color: yellow; font-weight: bold;">(</span>See pthread_exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">3</span><span style="color: yellow; font-weight: bold;">)</span>.<span style="color: yellow; font-weight: bold;">)</span></div></div></pre><p>È evidente che bisogna avere ben chiare quali sono le zone critiche del <em>thread</em>, dove sono i <em>cancellation-point</em>, e altre cosucce... E vabbè, ma se proprio la voglio usare che faccio? Ok, è il momento dei consigli:</p><ol><li>In creazione del thread si dovrebbe usare: <strong>pthread_setcancelstate(3)</strong>, <strong>pthread_setcanceltype(3)</strong> e <strong>pthread_create(3)</strong> (e magari anche <strong>pthread_key_create(3)</strong>).</li><li>Durante la vita utile del thread usare:<strong> pthread_cleanup_push(3)</strong> e <strong>pthread_cleanup_pop(3)</strong>.</li><li>Se in creazione abbiamo usato <strong>pthread_key_create(3)</strong>, durante la vita utile del <em>thread</em> si dovrebbe anche usare: <strong>pthread_getspecific(3)</strong> e <strong>pthread_setspecific(3)</strong>.</li><li>In cancellazione del thread usare: <strong>pthread_cancel(3)</strong> e <strong>pthread_join(3)</strong>.</li><li>E se dopo aver letto i punti da 1 a 4 volete ancora usare la <strong>pthread_cancel(3)</strong> <em>prendetevi un momento di riflessione, fatevi una buona dormita, e al risveglio, magari, avrete le idee più chiare...</em></li></ol><p>il quadro esposto sopra è quello minimale, e si può complicare a piacere, ad esempio mischiando sapientemente i punti 2 e 3. Ripeto però: è il quadro minimale, che prevede una buona inizializzazione (punto 1), un buon trattamento delle zone critiche (punti<strong> 2 o 3</strong> o anche punti<strong> 2 e 3</strong>), e una buona gestione della chiusura dei <em>thread</em> (punto 4). Se si gioca al risparmio e si omette qualche passo il disastro è dietro l'angolo. E, se ancora non è chiaro, rispettare queste norme è abbastanza difficile, specialmente se i <em>thread</em> eseguono attività complesse. Ok, nessun problema, seguiamo le istruzioni <em>et voilà!</em>, codice perfetto! Ma...</p><h4>MA, SPESSO, I PROGRAMMATORI SONO PIGRI</h4><p>Ebbene si, ho visto molto codice scritto pigramente, e ve ne fornisco un esempio (molto semplificato, ovviamente). Vai col codice!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// threaderrcancel.c -esempio di cancel thread erronea</span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdlib.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">pthread.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// prototipi globali</span></div><div><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">void*</span> myThread<span style="color: yellow; font-weight: bold;">(void</span> <span style="color: yellow; font-weight: bold;">*</span>arg<span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">void</span> mySleep<span style="color: yellow; font-weight: bold;">(unsigned</span> <span style="color: yellow; font-weight: bold;">int</span> milliseconds<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: silver; font-style: italic;">// main() - funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char*</span> argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// crea il thread</span></div><div> <span style="color: yellow; font-weight: bold;">pthread_t</span> tid<span style="color: yellow; font-weight: bold;">;</span></div><div> pthread_create<span style="color: yellow; font-weight: bold;">(&</span>tid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>myThread<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// aspetto 2 secondi e poi cancello il thread</span></div><div> mySleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">2000</span><span style="color: yellow; font-weight: bold;">);</span></div><div> pthread_cancel<span style="color: yellow; font-weight: bold;">(</span>tid<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// join del thread</span></div><div> pthread_join<span style="color: yellow; font-weight: bold;">(</span>tid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// esco</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: esco\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">]);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// myThread() - thread routine</span></div><div><span style="color: yellow; font-weight: bold;">void*</span> myThread<span style="color: yellow; font-weight: bold;">(void</span> <span style="color: yellow; font-weight: bold;">*</span>arg<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// loop del thread</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">thread partito\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">for</span> <span style="color: yellow; font-weight: bold;">(;;)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// il thread fa cose...</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// ...</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// malloc sul buffer</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>p <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">(char</span> <span style="color: yellow; font-weight: bold;">*)</span>malloc<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">100</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// simulo un ritardo perchè il thread fa altre cose...</span></div><div> mySleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">2</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// free del buffer</span></div><div> free<span style="color: yellow; font-weight: bold;">(</span>p<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// sleep del thread (10 ms)</span></div><div> mySleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">10</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// il thread esce</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">thread finito\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> pthread_exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// mySleep() - wrapper per nanosleep()</span></div><div><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">void</span> mySleep<span style="color: yellow; font-weight: bold;">(unsigned</span> <span style="color: yellow; font-weight: bold;">int</span> milliseconds<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">timespec</span> ts<span style="color: yellow; font-weight: bold;">;</span></div><div> ts<span style="color: yellow; font-weight: bold;">.</span>tv_sec <span style="color: yellow; font-weight: bold;">=</span> milliseconds <span style="color: yellow; font-weight: bold;">/</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">;</span></div><div> ts<span style="color: yellow; font-weight: bold;">.</span>tv_nsec <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">(</span>milliseconds <span style="color: yellow; font-weight: bold;">%</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">*</span> <span style="color: white; font-weight: bold;">1000000</span><span style="color: yellow; font-weight: bold;">;</span></div><div> nanosleep<span style="color: yellow; font-weight: bold;">(&</span>ts<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Ecco, questo è un programma <em>multithread</em> che usa la <strong>pthread_cancel()</strong> in modo pigro, <em>"alla speraindio"</em> (nota a parte: ho usato anche il mio vecchio <a href="https://artcprogramming.blogspot.com/2016/01/mad-sleep-come-scrivere-un-wrapper-per.html" rel="noopener" target="_blank"><strong>wrapper per nanosleep()</strong></a>: direi che è l'unica parte buona di questo programma). Se compilate ed eseguite con l'ottimo analizzatore dinamico <a href="https://valgrind.org/" rel="noopener" target="_blank"><strong>Valgrind</strong></a> (per trovare eventuali <em>memory leak</em>) otterrete (quasi sempre) questo risultato estratto dal <em>logfile</em> del <em>Valgrind</em>:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">10064</span><span style="color: yellow; font-weight: bold;">==</span> HEAP SUMMARY:</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">10064</span><span style="color: yellow; font-weight: bold;">==</span> in use at exit: <span style="color: white; font-weight: bold;">100</span> bytes in <span style="color: white; font-weight: bold;">1</span> blocks</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">10064</span><span style="color: yellow; font-weight: bold;">==</span> total heap usage: <span style="color: white; font-weight: bold;">166</span> allocs<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">165</span> frees<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">18</span><span style="color: yellow; font-weight: bold;">,</span><span style="color: white; font-weight: bold;">906</span> bytes allocated</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">10064</span><span style="color: yellow; font-weight: bold;">==</span> <span style="color: white; font-weight: bold;">100</span> bytes in <span style="color: white; font-weight: bold;">1</span> blocks are definitely lost in loss record <span style="color: white; font-weight: bold;">1</span> of <span style="color: white; font-weight: bold;">1</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">10064</span><span style="color: yellow; font-weight: bold;">==</span> at <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">483B7F3</span>: malloc <span style="color: yellow; font-weight: bold;">(</span>in <span style="color: yellow; font-weight: bold;">/</span>usr<span style="color: yellow; font-weight: bold;">/</span>lib<span style="color: yellow; font-weight: bold;">/</span>x86_64<span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">linux</span><span style="color: yellow; font-weight: bold;">-</span>gnu<span style="color: yellow; font-weight: bold;">/</span>valgrind<span style="color: yellow; font-weight: bold;">/</span>vgpreload_memcheck<span style="color: yellow; font-weight: bold;">-</span>amd64<span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">linux</span>.so<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">10064</span><span style="color: yellow; font-weight: bold;">==</span> by <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">10930A</span>: myThread <span style="color: yellow; font-weight: bold;">(</span>in <span style="color: yellow; font-weight: bold;">/</span>home<span style="color: yellow; font-weight: bold;">/</span>aldo<span style="color: yellow; font-weight: bold;">/</span>Blog<span style="color: yellow; font-weight: bold;">/</span>myblog<span style="color: yellow; font-weight: bold;">/</span>post2022<span style="color: yellow; font-weight: bold;">/</span>post119<span style="color: yellow; font-weight: bold;">-</span>xx082022<span style="color: yellow; font-weight: bold;">/</span>threaderrcancel<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">10064</span><span style="color: yellow; font-weight: bold;">==</span> by <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">486A608</span>: start_thread <span style="color: yellow; font-weight: bold;">(</span>pthread_create.c:<span style="color: white; font-weight: bold;">477</span><span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">10064</span><span style="color: yellow; font-weight: bold;">==</span> by <span style="color: #b5cea8; font-weight: bold;">0x</span><span style="color: white; font-weight: bold;">49A4132</span>: clone <span style="color: yellow; font-weight: bold;">(</span>clone.S:<span style="color: white; font-weight: bold;">95</span><span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">10064</span><span style="color: yellow; font-weight: bold;">==</span> LEAK SUMMARY:</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">10064</span><span style="color: yellow; font-weight: bold;">==</span> definitely lost: <span style="color: white; font-weight: bold;">100</span> bytes in <span style="color: white; font-weight: bold;">1</span> blocks</div></div></pre><p>Cosa è successo? È successo che la brusca interruzione del <em>thread</em> non gli ha dato tempo di eseguire la <a href="https://man7.org/linux/man-pages/man3/free.3.html" rel="noopener" target="_blank"><strong>free(3)</strong></a>. Con un po' di fortuna la cancellazione potrebbe arrivare dopo l'esecuzione della <strong>free(3)</strong>, ma vi sembra serio scrivere un programma basato sulla fortuna? Ok, vediamo allora una versione abbastanza migliorata del programma precedente, con una funzione di <em>cleanup</em> per liberare la memoria in qualunque momento arrivi la cancellazione. Vai col codice!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// threadcancel.c -esempio di cancel thread</span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdlib.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">pthread.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// prototipi globali</span></div><div><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">void*</span> myThread<span style="color: yellow; font-weight: bold;">(void</span> <span style="color: yellow; font-weight: bold;">*</span>arg<span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">void</span> cleanupHandler<span style="color: yellow; font-weight: bold;">(void</span> <span style="color: yellow; font-weight: bold;">*</span>arg<span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">void</span> mySleep<span style="color: yellow; font-weight: bold;">(unsigned</span> <span style="color: yellow; font-weight: bold;">int</span> milliseconds<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: silver; font-style: italic;">// main() - funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char*</span> argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// crea il thread</span></div><div> <span style="color: yellow; font-weight: bold;">pthread_t</span> tid<span style="color: yellow; font-weight: bold;">;</span></div><div> pthread_create<span style="color: yellow; font-weight: bold;">(&</span>tid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>myThread<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// aspetto 2 secondi e poi cancello il thread</span></div><div> mySleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">2000</span><span style="color: yellow; font-weight: bold;">);</span></div><div> pthread_cancel<span style="color: yellow; font-weight: bold;">(</span>tid<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// join del thread</span></div><div> pthread_join<span style="color: yellow; font-weight: bold;">(</span>tid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// esco</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: esco\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">]);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// myThread() - thread routine</span></div><div><span style="color: yellow; font-weight: bold;">void*</span> myThread<span style="color: yellow; font-weight: bold;">(void</span> <span style="color: yellow; font-weight: bold;">*</span>arg<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// per usi particolari posso chiamare (nei punti opportuni) anche:</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">//pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); // default</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">//pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">//pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); // default</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">//pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// loop del thread</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">thread partito\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">for</span> <span style="color: yellow; font-weight: bold;">(;;)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// il thread fa cose...</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// ...</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// malloc sul buffer e pop cleanup di cancellazione</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>p <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">(char</span> <span style="color: yellow; font-weight: bold;">*)</span>malloc<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">100</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: white; font-weight: bold;">pthread_cleanup_push</span><span style="color: yellow; font-weight: bold;">(</span>cleanupHandler<span style="color: yellow; font-weight: bold;">,</span> p<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// simulo un ritardo perchè il thread fa altre cose...</span></div><div> mySleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">2</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// free del buffer e pop cleanup di cancellazione</span></div><div> free<span style="color: yellow; font-weight: bold;">(</span>p<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: white; font-weight: bold;">pthread_cleanup_pop</span><span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// sleep del thread (10 ms)</span></div><div> mySleep<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">10</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// il thread esce</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">thread finito\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> pthread_exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// cleanupHandler() - funzione di cleanup di cancellazione</span></div><div><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">void</span> cleanupHandler<span style="color: yellow; font-weight: bold;">(void</span> <span style="color: yellow; font-weight: bold;">*</span>arg<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">Called clean-up handler\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> free<span style="color: yellow; font-weight: bold;">(</span>arg<span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: silver; font-style: italic;">// mySleep() - wrapper per nanosleep()</span></div><div><span style="color: yellow; font-weight: bold;">static</span> <span style="color: yellow; font-weight: bold;">void</span> mySleep<span style="color: yellow; font-weight: bold;">(unsigned</span> <span style="color: yellow; font-weight: bold;">int</span> milliseconds<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">timespec</span> ts<span style="color: yellow; font-weight: bold;">;</span></div><div> ts<span style="color: yellow; font-weight: bold;">.</span>tv_sec <span style="color: yellow; font-weight: bold;">=</span> milliseconds <span style="color: yellow; font-weight: bold;">/</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">;</span></div><div> ts<span style="color: yellow; font-weight: bold;">.</span>tv_nsec <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">(</span>milliseconds <span style="color: yellow; font-weight: bold;">%</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">*</span> <span style="color: white; font-weight: bold;">1000000</span><span style="color: yellow; font-weight: bold;">;</span></div><div> nanosleep<span style="color: yellow; font-weight: bold;">(&</span>ts<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Ecco, questo va abbastanza meglio, e il <em>Valgrind</em> ci mostra (sempre) questo:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">10086</span><span style="color: yellow; font-weight: bold;">==</span> HEAP SUMMARY:</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">10086</span><span style="color: yellow; font-weight: bold;">==</span> in use at exit: <span style="color: white; font-weight: bold;">0</span> bytes in <span style="color: white; font-weight: bold;">0</span> blocks</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">10086</span><span style="color: yellow; font-weight: bold;">==</span> total heap usage: <span style="color: white; font-weight: bold;">168</span> allocs<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">168</span> frees<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">19</span><span style="color: yellow; font-weight: bold;">,</span><span style="color: white; font-weight: bold;">106</span> bytes allocated</div><div><span style="color: yellow; font-weight: bold;">==</span><span style="color: white; font-weight: bold;">10086</span><span style="color: yellow; font-weight: bold;">==</span> All heap blocks were freed <span style="color: yellow; font-weight: bold;">--</span> no leaks are possible</div></div></pre><p>Ok, di nuovo sembra facile... ma provate a scrivere varie funzioni di <em>cleanup</em> (mantenendole con <strong>pthread_cleanup_push(3)</strong> e <strong>pthread_cleanup_pop(3)</strong>) per un <em>thread</em> che esegue molte attività complesse e <em>che chiama funzioni esterne che chiamano altre funzioni esterne che chiamano...</em> Insomma, è molto complicato, e lo sarebbe ancora di più usando la gestione delle <em>thread key</em> con <strong>pthread_key_create(3) </strong>(gestione che, tra l'altro, è la più indicata per liberare la memoria, visto che, in realtà, le funzioni <em>pthread</em> di <em>push</em> e <em>pop</em> sono più adatte per gestire il reset di stati, lock, ecc.).</p><p>E quale è la conclusione allora? Semplicissimo; <strong>pthread_cancel(3)</strong> non si usa! Perché è difficilissimo usarla bene e, anche mettendocela tutta, potrebbe sfuggirci qualche dettaglio che può causare dei veri disastri a <em>run-time</em>. La <em>libpthread </em>fornisce questa funzione perché è una libreria molto completa che cerca di coprire tutti gli aspetti del <em>multithreading,</em> ma la fornisce sottintendendo <em>"Usatela a vostro rischio e pericolo...".</em></p><p>E concludo con una domanda: <em>"Ma secondo voi, perché i nuovi supporti built-in ai thread in <strong>C11</strong> (threads) e <strong>C++11</strong> (std::thread), pur essendo spesso basati sulla onnipresente <strong>libpthread</strong> (che da sotto esegue in maniera trasparente il lavoro sporco) non implementano una funzione di cancel? Sarà mica perché fa solo danni?". </em>E con questo spunto di riflessione vi saluto e vi aspetto per la seconda parte dell'articolo, dove vedremo la maniera migliore di fermare un <em>thread</em> (<em>spoiler: è molto più semplice di quello che ci si può aspettare!</em>).</p><p>Ciao, e al prossimo post! </p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-77536534411150434592022-07-11T18:05:00.002+02:002022-07-12T10:02:08.271+02:00ZeroMQ Festival come si usa ZeroMQ in C - pt.2<blockquote><b>Sue:</b> <i>Hai sentito che il film di Philippe ha vinto il primo premio al Festival di Colonia, oggi?.</i><br /><b>Mort:</b> <i>Molto eccitante. Ma Eichmann non era di lì?</i><br /></blockquote><p>E va bene, questa volta ci siamo, è il turno della seconda parte di <a href="https://letterboxd.com/aldoz/film/rifkins-festival/" rel="noopener" target="_blank"><strong>Rifkin's Festival</strong></a> (<em>oops:</em> <strong>ZeroMQ Festival</strong>), come promesso <a href="https://artcprogramming.blogspot.com/2022/05/zeromq-festival-come-si-usa-zeromq-in-c.html" rel="noopener" target="_blank"><strong>nella prima parte</strong></a> dell'articolo. Qui sopra vi ho proposto un commento secco e lapidario di Mort (<a href="https://letterboxd.com/actor/wallace-shawn/" rel="noopener" target="_blank"><strong>Wallace Shawn</strong></a>), il protagonista alter-ego di <a href="https://letterboxd.com/director/woody-allen/" rel="noopener" target="_blank"><strong>Woody Allen</strong></a> in questo bel film, un commento che ben introduce questo articolo: dopo aver parlato dell'interfaccia a <strong>ZeroMQ</strong> di "<em>basso livello</em>", e cioè la libreria <em>libzmq</em>, è il momento di parlare dell'interfaccia ad "<em>alto livello</em>", la <strong>CZMQ</strong> (<em>libczmq</em>). Grazie a <strong>CZMQ</strong> è possibile scrivere codice di rete sintetico e compatto (<em>anzi, secco e lapidario</em>) anche per problemi complessi, problemi che richiederebbero la scrittura di codice lungo e complicato anche aiutandosi con <strong>ZeroMQ</strong> <em>low-level</em> (e lunghissimo e complicatissimo usando, ad esempio, i classici <strong>BSD Socket</strong>).</p><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: left;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4Z0lTY8W0BIRP1w6VxN9tAgqSyJjAIxrdOOsjw6SQFSOnoojhSza9XKaHuaNPhVAmM6A7FWYzWeaDdpKWYsUJ1ePSHvERODT8Ejp7gfjJHiOn2ckTVvToWOPyp3Aqb4iykvGuIiWIrdn2Vq_jtx-2MI-cxvrFPNVaPDftuRjia3wmv81TbLzLp6AcXA/s1366/zeromqfestival2.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1030" data-original-width="1366" height="434" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4Z0lTY8W0BIRP1w6VxN9tAgqSyJjAIxrdOOsjw6SQFSOnoojhSza9XKaHuaNPhVAmM6A7FWYzWeaDdpKWYsUJ1ePSHvERODT8Ejp7gfjJHiOn2ckTVvToWOPyp3Aqb4iykvGuIiWIrdn2Vq_jtx-2MI-cxvrFPNVaPDftuRjia3wmv81TbLzLp6AcXA/w578-h434/zeromqfestival2.jpg" width="578" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...crepi l'avarizia, oggi ti offrirò un menu high-level...</td></tr></tbody></table><p style="text-align: left;">Allora: <strong>CZMQ</strong> è una interfaccia <strong>C</strong> ma è strutturata come se fosse una libreria di classi (nel manuale il termine "<em>class</em>" viene usato frequentemente). Comunque niente paura per chi non è avvezzo al <strong>C++</strong>: è una interfaccia <strong>C</strong> al 100%. E questa è la descrizione che danno gli autori stessi della libreria nel <strong><a href="http://api.zeromq.org/czmq3-0:czmq" rel="noopener" target="_blank">manuale</a></strong>:</p><p></p><blockquote><p><i>CZMQ ha questi obiettivi:</i></p><p></p><ul style="text-align: left;"><li><i>"wrappare" l'API core di ØMQ in una semantica che sia naturale e porti ad applicazioni più brevi e leggibili.</i></li><li><i>Nascondere le differenze tra le versioni di ØMQ.</i></li><li><i>Fornire uno spazio per lo sviluppo di semantiche API più sofisticate.</i></li></ul></blockquote><p>E quali sono, in pratica, i vantaggi dell'uso di CZMQ? Facciamo un caso reale: se, per esempio, volessimo risolvere un problema complicato come scrivere un <em>Load Balancing Broker</em> che segua lo schema di funzionamento seguente (copio una descrizione di questo oggetto da <a href="https://zguide.zeromq.org/" rel="noopener" target="_blank"><strong>0MQ - The Guide</strong></a>):</p><p style="text-align: center;"><img alt="lbbroker" class="alignnone wp-image-6719 size-full" height="384" src="https://italiancoders.it/wp-content/uploads/2022/07/lbbroker.png" width="333" /></p><p>grazie a <strong>CZMQ</strong> potremmo farlo in maniera abbastanza sintetica e leggibile. Mentre se provassimo a farlo con altri strumenti ci renderemmo subito conto della grande differenza a livello di lunghezza e complicazione. Per quanto riguarda la realizzazione pratica di questo <strong>LBB</strong> (visto che non amo fare il copia-incolla di cose non mie) vi passo direttamente due link dove viene descritto il codice corrispondente, un doppio codice scritto usando le due librerie (<em>low</em> e <em>high-level</em>): se gli date un occhiata noterete che con <strong>CZMQ</strong> è decisamente più semplificato e leggibile. Ah, e vi consiglio un utile esercizio: provate a riscrivere (o, perlomeno, a immaginare) lo stesso codice scritto con i <strong>BSD Socket</strong>... <em>tremo solo a pensarci</em>. Ecco i link: <a href="https://zguide.zeromq.org/docs/chapter3/#A-Load-Balancing-Message-Broker" rel="noopener" target="_blank"><strong>versione con lzmq</strong></a> e <a href="https://zguide.zeromq.org/docs/chapter3/#The-CZMQ-High-Level-API" rel="noopener" target="_blank"><strong>versione con CZMQ</strong></a>.</p><p>E adesso? Ma non posso chiudere un articolo senza proporre nessun codice mio! Quindi ho pensato che per ben descrivere la differenza di approccio tra <em>low-level</em> e <em>high-level</em> è meglio partire dalle basi, e allora vi propongo un esempio semplicissimo di codice <em>Client/Server</em> scritto in tre versioni, <strong>BSD Socket</strong>, <strong>ZeroMQ low-level</strong> e <strong>CZMQ</strong>.</p><p>Ho scritto tutti gli esempi a mo' di codice di test (e non di produzione), quindi sono (<em>ahimè</em>) completamente assenti le importantissime istruzioni di trattamento degli errori (praticamente sono gli scheletri funzionali da completare per entrare in produzione): questo con l'obiettivo di non sviare l'attenzione dal flusso base del codice, quello che ci mostra come si lavora con una libreria rispetto a un'altra. Tutti gli esempi sono composti da due file,<em> client.c</em> e<em> server.c</em>, e sono perfettamente compilabili e funzionanti. Ok, cominciamo con i <strong>BSD Socket</strong>, vai col codice!</p><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px; white-space: pre;"><div><span style="color: silver; font-style: italic;">// client.c - un semplice client con BSD Socket</span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">string.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">unistd.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">arpa/inet.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// main() - funzione main</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(void)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// creo il socket</span></div><div> printf <span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">Connessione al server...\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> cli_sock <span style="color: yellow; font-weight: bold;">=</span> socket<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">AF_INET</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">SOCK_STREAM</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">IPPROTO_TCP</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// preparo la struttura sockaddr_in per il server remoto</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">sockaddr_in</span> server<span style="color: yellow; font-weight: bold;">;</span></div><div> memset<span style="color: yellow; font-weight: bold;">(&</span>server<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(struct</span> <span style="color: yellow; font-weight: bold;">sockaddr_in</span><span style="color: yellow; font-weight: bold;">));</span></div><div> server<span style="color: yellow; font-weight: bold;">.</span>sin_family <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">AF_INET</span><span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // set famiglia di indirizzi</span></div><div> server<span style="color: yellow; font-weight: bold;">.</span>sin_addr<span style="color: yellow; font-weight: bold;">.</span>s_addr <span style="color: yellow; font-weight: bold;">=</span> inet_addr<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">127.0.0.1</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;">// set indirizzo del server</span></div><div> server<span style="color: yellow; font-weight: bold;">.</span>sin_port <span style="color: yellow; font-weight: bold;">=</span> htons<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">5555</span><span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // set port del server</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// connetto il socket al server remoto</span></div><div> connect<span style="color: yellow; font-weight: bold;">(</span>cli_sock<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">(struct</span> <span style="color: yellow; font-weight: bold;">sockaddr</span> <span style="color: yellow; font-weight: bold;">*)&</span>server<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>server<span style="color: yellow; font-weight: bold;">));</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// loop di invio</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> cnt <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">(</span>cnt<span style="color: yellow; font-weight: bold;">++</span> <span style="color: yellow; font-weight: bold;"><</span> <span style="color: white; font-weight: bold;">10</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// invio la richiesta</span></div><div> send<span style="color: yellow; font-weight: bold;">(</span>cli_sock<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">Ping</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">5</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// ricevo la risposta</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> string<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">10</span><span style="color: yellow; font-weight: bold;">];</span></div><div> recv<span style="color: yellow; font-weight: bold;">(</span>cli_sock<span style="color: yellow; font-weight: bold;">,</span> string<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>string<span style="color: yellow; font-weight: bold;">),</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">);</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">risposta ricevuta: %s\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> string<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// disconnetto</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>cli_sock<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// server.c - un semplice server con BSD Socket</span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">string.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">unistd.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">arpa/inet.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// main() - funzione main</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(void)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// creo il socket TCP</span></div><div> printf <span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">Avvio server...\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> srv_sock <span style="color: yellow; font-weight: bold;">=</span> socket<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">AF_INET</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">SOCK_STREAM</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">IPPROTO_TCP</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// preparo la struttura sockaddr_in per questo server</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">sockaddr_in</span> server<span style="color: yellow; font-weight: bold;">;</span></div><div> memset<span style="color: yellow; font-weight: bold;">(&</span>server<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(struct</span> <span style="color: yellow; font-weight: bold;">sockaddr_in</span><span style="color: yellow; font-weight: bold;">));</span></div><div> server<span style="color: yellow; font-weight: bold;">.</span>sin_family <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">AF_INET</span><span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // set famiglia di indirizzi</span></div><div> server<span style="color: yellow; font-weight: bold;">.</span>sin_addr<span style="color: yellow; font-weight: bold;">.</span>s_addr <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">INADDR_ANY</span><span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // set indirizzo del server</span></div><div> server<span style="color: yellow; font-weight: bold;">.</span>sin_port <span style="color: yellow; font-weight: bold;">=</span> htons<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">5555</span><span style="color: yellow; font-weight: bold;">);</span><span style="color: silver; font-style: italic;"> // set port del server</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// associo l'indirizzo del server al socket e start ascolto</span></div><div> bind<span style="color: yellow; font-weight: bold;">(</span>srv_sock<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">(struct</span> <span style="color: yellow; font-weight: bold;">sockaddr</span> <span style="color: yellow; font-weight: bold;">*)&</span>server<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>server<span style="color: yellow; font-weight: bold;">));</span></div><div> listen<span style="color: yellow; font-weight: bold;">(</span>srv_sock<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">10</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// accetto connessioni da un client entrante</span></div><div> <span style="color: yellow; font-weight: bold;">socklen_t</span> socksize <span style="color: yellow; font-weight: bold;">=</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(struct</span> <span style="color: yellow; font-weight: bold;">sockaddr_in</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">sockaddr_in</span> client<span style="color: yellow; font-weight: bold;">;</span><span style="color: silver; font-style: italic;"> // struttura sockaddr_in per il client remoto</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> cli_sock <span style="color: yellow; font-weight: bold;">=</span> accept<span style="color: yellow; font-weight: bold;">(</span>srv_sock<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">(struct</span> <span style="color: yellow; font-weight: bold;">sockaddr</span> <span style="color: yellow; font-weight: bold;">*)&</span>client<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>socksize<span style="color: yellow; font-weight: bold;">);</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>srv_sock<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// loop di ricezione</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> string<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">10</span><span style="color: yellow; font-weight: bold;">];</span></div><div> <span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">(</span>recv<span style="color: yellow; font-weight: bold;">(</span>cli_sock<span style="color: yellow; font-weight: bold;">,</span> string<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>string<span style="color: yellow; font-weight: bold;">),</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">!=</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">richiesta ricevuta: %s\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> string<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// invio la risposta</span></div><div> send<span style="color: yellow; font-weight: bold;">(</span>cli_sock<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">Pong</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">5</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// disconnetto</span></div><div> close<span style="color: yellow; font-weight: bold;">(</span>cli_sock<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>E passiamo ora al codice in versione <em>libzmq</em> (<strong>ZeroMQ</strong> <em>low-level</em>):</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// client.c - un semplice client ZeroMQ con libzmq</span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">zmq.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// main() - funzione main</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(void)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// creo il context e il requester</span></div><div> printf <span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">Connessione al server...\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">void</span> <span style="color: yellow; font-weight: bold;">*</span>context <span style="color: yellow; font-weight: bold;">=</span> zmq_ctx_new<span style="color: yellow; font-weight: bold;">();</span></div><div> <span style="color: yellow; font-weight: bold;">void</span> <span style="color: yellow; font-weight: bold;">*</span>requester <span style="color: yellow; font-weight: bold;">=</span> zmq_socket<span style="color: yellow; font-weight: bold;">(</span>context<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">ZMQ_REQ</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// connetto il requester al responder</span></div><div> zmq_connect<span style="color: yellow; font-weight: bold;">(</span>requester<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">tcp://localhost:5555</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// loop di invio</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> cnt <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">(</span>cnt<span style="color: yellow; font-weight: bold;">++</span> <span style="color: yellow; font-weight: bold;"><</span> <span style="color: white; font-weight: bold;">10</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// invio la richiesta</span></div><div> zmq_send<span style="color: yellow; font-weight: bold;">(</span>requester<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">Ping</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">5</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// ricevo la risposta</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> string<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">10</span><span style="color: yellow; font-weight: bold;">];</span></div><div> zmq_recv<span style="color: yellow; font-weight: bold;">(</span>requester<span style="color: yellow; font-weight: bold;">,</span> string<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>string<span style="color: yellow; font-weight: bold;">),</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">);</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">risposta ricevuta: %s\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> string<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// disconnetto</span></div><div> zmq_close <span style="color: yellow; font-weight: bold;">(</span>requester<span style="color: yellow; font-weight: bold;">);</span></div><div> zmq_ctx_destroy <span style="color: yellow; font-weight: bold;">(</span>context<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// server.c - un semplice server ZeroMQ con libzmq</span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">zmq.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// main() - funzione main</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(void)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// creo il context e il responder</span></div><div> printf <span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">Avvio server...\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">void</span> <span style="color: yellow; font-weight: bold;">*</span>context <span style="color: yellow; font-weight: bold;">=</span> zmq_ctx_new<span style="color: yellow; font-weight: bold;">();</span></div><div> <span style="color: yellow; font-weight: bold;">void</span> <span style="color: yellow; font-weight: bold;">*</span>responder <span style="color: yellow; font-weight: bold;">=</span> zmq_socket<span style="color: yellow; font-weight: bold;">(</span>context<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">ZMQ_REP</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// associo l'indirizzo del responder al contesto</span></div><div> zmq_bind<span style="color: yellow; font-weight: bold;">(</span>responder<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">tcp://*:5555</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// loop di ricezione</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> string<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">10</span><span style="color: yellow; font-weight: bold;">];</span></div><div> <span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">(</span>zmq_recv<span style="color: yellow; font-weight: bold;">(</span>responder<span style="color: yellow; font-weight: bold;">,</span> string<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">10</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">!=</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">richiesta ricevuta: %s\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> string<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// invio la risposta</span></div><div> zmq_send<span style="color: yellow; font-weight: bold;">(</span>responder<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">Pong</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">5</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// disconnetto</span></div><div> zmq_close<span style="color: yellow; font-weight: bold;">(</span>responder<span style="color: yellow; font-weight: bold;">);</span></div><div> zmq_ctx_destroy<span style="color: yellow; font-weight: bold;">(</span>context<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div><br /></div></pre><p>E già questo codice dimostra che <strong>ZeroMQ</strong> è una libreria che permette di scrivere codice di rete molto semplificato. Ma con <strong>CZMQ</strong> possiamo snellirlo ancora di più (ma senza aspettarci miracoli: la <em>libzmq</em> già di per sé un passo avanti rispetto al vero <em>low-level</em>, che è rappresentato dai <strong>BSD Socket</strong>). Vai di nuovo col codice!</p><pre class="EnlighterJSRAW" data-enlighter-language="generic"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// client.c - un semplice client ZeroMQ con CZMQ</span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">czmq.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// main() - funzione main</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(void)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// create requester</span></div><div> printf <span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">Connessione al server...\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">zsock_t</span> <span style="color: yellow; font-weight: bold;">*</span>requester <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">zsock_new_req</span><span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">tcp://localhost:5555</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// loop di invio</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> cnt <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">(</span>cnt<span style="color: yellow; font-weight: bold;">++</span> <span style="color: yellow; font-weight: bold;"><</span> <span style="color: white; font-weight: bold;">10</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// invio la richiesta</span></div><div> zstr_send<span style="color: yellow; font-weight: bold;">(</span>requester<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">Ping</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// ricevo la risposta</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>string<span style="color: yellow; font-weight: bold;">;</span></div><div> string <span style="color: yellow; font-weight: bold;">=</span> zstr_recv<span style="color: yellow; font-weight: bold;">(</span>requester<span style="color: yellow; font-weight: bold;">);</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">risposta ricevuta: %s\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> string<span style="color: yellow; font-weight: bold;">);</span></div><div> zstr_free<span style="color: yellow; font-weight: bold;">(&</span>string<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// disconnetto</span></div><div> <span style="color: white; font-weight: bold;">zsock_destroy</span><span style="color: yellow; font-weight: bold;">(&</span>requester<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// server.c - un semplice server ZeroMQ con CZMQ</span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">czmq.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// main() - funzione main</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(void)</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// creo il responder</span></div><div> printf <span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">Avvio server...\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">zsock_t</span> <span style="color: yellow; font-weight: bold;">*</span>responder <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">zsock_new_rep</span><span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">tcp://*:5555</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// loop di ricezione</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>string<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">((</span>string <span style="color: yellow; font-weight: bold;">=</span> zstr_recv<span style="color: yellow; font-weight: bold;">(</span>responder<span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">!=</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">richiesta ricevuta: %s\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> string<span style="color: yellow; font-weight: bold;">);</span></div><div> zstr_free<span style="color: yellow; font-weight: bold;">(&</span>string<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// invio la risposta</span></div><div> zstr_send<span style="color: yellow; font-weight: bold;">(</span>responder<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">Pong</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// disconnetto</span></div><div> <span style="color: white; font-weight: bold;">zsock_destroy</span><span style="color: yellow; font-weight: bold;">(&</span>responder<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">return</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Che ne dite? La differenza tra le tre versioni, pur non essendo eclatante, è evidente, e si nota una filosofia di programmazione abbastanza diversa. E, a parte le considerazioni <em>filosofiche</em>, alla fin fine si può dire che nel percorso da <strong>BSD Socket</strong> a <strong>libzmq</strong> a <strong>CZMQ</strong>, le linee di codice vanno in diminuzione, e questo è il fattore molto importante che stavamo ricercando.</p><p>Ok, per oggi può bastare: <strong>ZeroMQ</strong> passa, a pieni voti, l'esame di "<em>ottima libreria per Software di rete</em>". Lo passa bene già con la libreria <em>low-level</em> e <em>"più meglio"</em> (<em>ah ah ah</em>) con <strong>CZMQ</strong>, la libreria<em> high-level</em>. Ma, nonostante questo, vi invito a non dimenticare che, sotto sotto (anche sotto <strong>ZeroMQ</strong>!), ci sono i mitici e insostituibili <strong>BSD Socket</strong> che possono essere ancora usati direttamente per scrivere ottimo e soddisfacente codice di rete, magari solo un po' più complicato (da scrivere e da leggere). <em>De gustibus...</em></p><p>Ciao, e al prossimo post!</p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-26957463198911118182022-06-28T19:32:00.005+02:002022-08-29T16:08:29.790+02:00System? No, grazie! considerazioni sul perché non usare la system(3) in C (e C++)<blockquote><b>[Frase sulla t-shirt regalata a N.Van Orton/M.Douglas]:</b> <i>Sono stato drogato e lasciato per morto in Messico, e tutto quello che ho ottenuto è questa stupida maglietta.</i></blockquote><p>Ebbene si, l'ho fatto un altra volta: <a href="https://artcprogramming.blogspot.com/2022/05/zeromq-festival-come-si-usa-zeromq-in-c.html" rel="noopener" target="_blank"><strong>nell'ultimo articolo</strong></a> avevo promesso una imminente seconda parte, e invece oggi parleremo d'altro (<em>ma niente paura, la seconda parte su <strong>ZeroMQ</strong> arriverà presto! Fidatevi!</em>). Succede che, come al solito, ottengo uno spunto da fatti reali (di solito per cose che faccio sul lavoro) e dico "<em>Ma qui c'è materiale per un articolo!</em>", per cui oggi parleremo di <a href="https://man7.org/linux/man-pages/man3/system.3.html" rel="noopener" target="_blank"><strong>system(3)</strong></a>. Uhm, ma abbiamo un film collegato? Mah, visto che appartiene alla serie dei "<em>No Grazie!</em>" (<em>ah: ne ho scritti altri e vi invito a leggerli o rileggerli, <a href="https://artcprogramming.blogspot.com/2020/09/sleep-no-grazie-considerazioni-sul.html" rel="noopener" target="_blank"><strong>qui</strong></a>, <a href="https://artcprogramming.blogspot.com/2019/12/sprintf-no-grazie-considerazioni.html" rel="noopener" target="_blank"><strong>qui</strong>,</a> <a href="https://artcprogramming.blogspot.com/2019/10/debugger-no-grazie-considerazioni.html" rel="noopener" target="_blank"><strong>qui</strong></a> e <a href="https://artcprogramming.blogspot.com/2019/02/variabili-globali-no-grazie-come-non.html" rel="noopener" target="_blank"><strong>qui</strong></a></em>) non farò giochi di parole con il titolo, e quindi ne sceglierò uno a caso tra quelli che ho visto/rivisto ultimamente... si, <a href="https://letterboxd.com/aldoz/film/the-game/" rel="noopener" target="_blank"><strong>The Game</strong></a>, va benissimo: è un bel film del grande <a href="https://letterboxd.com/director/david-fincher/" rel="noopener" target="_blank"><strong>David Fincher</strong></a>, e non può mancare nella lista di un Cinefilo. Anzi, una attinenza film/articolo c'è: <a href="https://man7.org/linux/man-pages/man3/system.3.html" rel="noopener" target="_blank"><strong>system(3)</strong></a> è una funzione<em> anti-pattern</em>, quindi chi la usa troppo può ridursi come il protagonista della foto qui sotto...</p><p></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinOSXEEoKPaz5VyDGicse74u38n18f1vrjItQkSs5TczkgEr8LGcW1TyybwKCTximlh5ikKowFTv7DDayDFfbDdQyExmkwJUTAl8nrmUmWfts--44Bf1iRJ25tDirPKS1Jbk64ZcdvO0aBe0H0ApJMyPlSpNY_LNbDEh9H9da1tfK_dOmWvvkI2xYzuw/s1300/The-Game2.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="629" data-original-width="1300" height="285" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinOSXEEoKPaz5VyDGicse74u38n18f1vrjItQkSs5TczkgEr8LGcW1TyybwKCTximlh5ikKowFTv7DDayDFfbDdQyExmkwJUTAl8nrmUmWfts--44Bf1iRJ25tDirPKS1Jbk64ZcdvO0aBe0H0ApJMyPlSpNY_LNbDEh9H9da1tfK_dOmWvvkI2xYzuw/w587-h285/The-Game2.jpg" width="587" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...guardate come mi sono ridotto usando la system(3)...</td></tr></tbody></table><p></p><p><strong>system(3)</strong>, è una funzione della <em>libc</em> usata e abusata: è molto comoda e semplice da usare e permette di fare cose, anche complicate, sfruttando applicazioni esterne: proprio questo è, allo stesso tempo, il suo maggior pregio e difetto. Chiariamolo subito: se avete bisogno di scrivere rapidamente una applicazione, diciamo, "<em>sperimentale</em>", o un programma di test (o <em>testing</em>) ad uso interno, o volete semplicemente verificare (rapidamente) se una idea è realizzabile, nessuno vi può criticare se usate la<strong> system(3)</strong>. Ma per una applicazione reale che andrà in produzione è corretto usarla? E vabbè, la risposta è una sola:</p><h2 style="text-align: center;">NO, ASSOLUTAMENTE NO!</h2><p>Ok, è ora di entrare nel dettaglio. Cosa è la <strong>system(3)</strong> e cosa permette di fare? Il manuale della <em>glibc</em> titola semplicemente questo:</p><p><i></i></p><blockquote><i>system - execute a shell command</i></blockquote><p>quindi ci permette di chiamare, dal nostro programma, un comando esterno del sistema operativo (o meglio: un comando nostro o del sistema operativo chiamabile tramite la <em>Shell</em>).</p><p></p><blockquote>(<em>...apro una parentesi: come al solito l'articolo è focalizzato sui sistemi POSIX, ma le note che seguono relative all'uso, pregi e difetti della system(3) sono perfettamente adattabili anche a Windows, dove la stessa funzione è disponibile, con l'unica differenza che usa CMD.EXE invece della UNIX Shell o della Linux Bash.</em><i> Chiudo la parentesi...</i>)</blockquote><p></p><p>Per cui, ad esempio, se vogliamo che il nostro programma mostri che file sono contenuti in una <em>directory</em> che si chiama "<em>test</em>" possiamo, molto semplicemente, usare <strong>system(3)</strong> per chiamare il comando "<em>ls ./test</em>" usando <a href="https://man7.org/linux/man-pages/man1/ls.1.html" rel="noopener" target="_blank"><strong>ls(1)</strong></a> di Linux. Facilissimo, no? Vediamo un piccolo esempio di codice che vi farà gioire (<em>spoiler: e subito dopo ve lo smonterò!</em>):</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdlib.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// main() - funzione main</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// uso system(3) per leggere il contenuto della directory ./test</span></div><div> system<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">ls ./test</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div> exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">EXIT_SUCCESS</span><span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Ok, <strong>system(3)</strong> è indubbiamente comoda da usare, ma si porta dietro una lista di problemi così lunga che si potrebbe scriverne in un libro. Vediamone alcuni:</p><ol><li>Non è per nulla portabile: non è possibile garantire al 100% che su tutti i sistemi dove verrà installata la mia applicazione i comandi da eseguire con <strong>system(3)</strong> siano disponibili.</li><li>Apre, <strong>come minimo</strong>, due processi supplementari: su <strong>Linux</strong> invoca <a href="https://www.man7.org/linux/man-pages/man1/bash.1.html" rel="noopener" target="_blank"><strong>bash(1)</strong></a> che, a sua volta, invoca il comando da eseguire (<em>alla faccia dell'efficienza</em>).</li><li> Quando si chiama la <strong>system(3)</strong> il programma principale viene sospeso fino al termine del comando invocato, e non c'è nessuna maniera di controllare efficacemente quello che sta succedendo.</li><li>Questo, poi, lo dice direttamente il manuale della <strong>system(3)</strong>: "<em>During execution of the command, SIGCHLD will be blocked, and SIGINT and SIGQUIT will be ignored, in the process that calls system().</em>", e questo non è una semplice descrizione di funzionamento, è la descrizione di un possibile problema.</li><li>Anche questo, poi, lo dice direttamente il manuale della <strong>system(3)</strong>: "<em>Do not use system() from a privileged program (a set-user-ID or set-group-ID program, or a program with capabilities)</em>". La sicurezza del sistema può essere molto compromessa se la stringa di comando viene ad una fonte esterna e può essere manipolata: il grande pericolo si chiama <em>Shell Command Injection</em>.</li><li>Un altro buco di sicurezza è possibile se il sistema non è completamente sicuro (cosa frequente...) e un utente malintenzionato può, in qualche maniera, alterare il <em>path</em> di ricerca degli eseguibili o sostituire un eseguibile con un altro con lo stesso nome.</li><li>Non c'è alcun modo di leggere direttamente l'output del comando eseguito nel programma che invoca la <strong>system(3)</strong>.</li><li>Problemi vari ed eventuali: la <strong>system(3)</strong> in un loop non controllato, la <strong>system(3)</strong> e la <em>thread-safety</em> problematica, la <strong>system(3)</strong> per eseguire un comando in <em>background</em>, ecc. Una lunga lista che vi risparmio.</li></ol><p>Insomma, è un vero disastro. Come dite? Ah si, sento alcuni che dicono "<em>A me i problemi della <strong>system(3)</strong> non interessano, tanto io uso la <a href="https://man7.org/linux/man-pages/man3/popen.3.html" rel="noopener" target="_blank"><strong>popen(3)</strong></a>!</em>". Ok, ho delle cattive notizie: almeno la metà dei problemi elencati li ha anche la <strong>popen(3)</strong>, che forse non è un disastro ma, direi, è un mezzo disastro,<em> sai che consolazione...</em></p><p>Ah, solo come curiosità: nella lista dei problemi qui sopra quello che mi molesta di più è il <strong>numero 3</strong>: una applicazione che usa <strong>system(3)</strong> non ha maniera di controllare quello che sta succedendo con i programmi esterni invocati, e deve sospendersi per aspettare che finiscano l'esecuzione: ma questa vi sembra la descrizione di una buona applicazione da mandare in produzione? <em>E vabbè, continuiamo cosi, facciamoci del male...</em></p><p>Quindi: che bisogna fare se abbiamo una attività buona per la <strong>system(3)</strong> ma non vogliamo usarla visto che siamo programmatori consapevoli? Abbiamo due soluzioni:</p><ol><li><em>La soluzione semplice ma accettabile:</em> usare <a href="https://man7.org/linux/man-pages/man2/fork.2.html" rel="noopener" target="_blank"><strong>fork(2)</strong></a> + <a href="https://man7.org/linux/man-pages/man3/exec.3.html" rel="noopener" target="_blank"><strong>execv(3)</strong></a> + <a href="https://man7.org/linux/man-pages/man2/wait.2.html" rel="noopener" target="_blank"><strong>waitpid(2)</strong></a>: è un po' una emulazione, un po' laboriosa, della <strong>system(3)</strong>, che però evita i problemi elencati sopra: non usa la <em>Shell</em>, ci permette di mantenere il controllo sull'esecuzione, ci permette di evitare i problemi di <em>shell-iniection</em>, ecc. ecc.</li><li><em>La soluzione complicata ma impeccabile:</em> scrivere nel nostro programma il codice che vorremmo far eseguire esternamente con la<strong> system(3)</strong>: in alcuni casi potrebbe risultare abbastanza laborioso, ma avremo tutto dentro il nostro codice, quindi avremo il controllo totale sia dell'esecuzione che della implementazione.</li></ol><p>Che dite? Volete un esempio dei due casi? Magari con lo stesso tema di eseguire/emulare un comando "<em>ls ./test</em>"? Eccoli!</p><h3 style="text-align: left;">Caso 1: fork(2) + execv(3) + waitpid(2)</h3><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">string.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdlib.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">unistd.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/wait.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">errno.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char*</span> argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">pid_t</span> pid <span style="color: yellow; font-weight: bold;">=</span> fork<span style="color: yellow; font-weight: bold;">();</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>pid <span style="color: yellow; font-weight: bold;">==</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// sono il figlio</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>pathname <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">/usr/bin/ls</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>newargv<span style="color: yellow; font-weight: bold;">[]</span> <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">{</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">ls</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">./test</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span> <span style="color: yellow; font-weight: bold;">};</span></div><div> execv<span style="color: yellow; font-weight: bold;">(</span>pathname<span style="color: yellow; font-weight: bold;">,</span> newargv<span style="color: yellow; font-weight: bold;">);</span></div><div> exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">);</span> <span style="color: silver; font-style: italic;">// exec non ritorna mai</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>pid <span style="color: yellow; font-weight: bold;">></span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// sono il padre</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> status<span style="color: yellow; font-weight: bold;">;</span></div><div> waitpid<span style="color: yellow; font-weight: bold;">(</span>pid<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>status<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">);</span></div><div> exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">EXIT_SUCCESS</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore fork()</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">error: %s\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><h3 style="text-align: left;">Caso 2: implementazione interna</h3><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdlib.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">dirent.h</span><span style="color: yellow; font-weight: bold;">></span></div><br /><div><span style="color: silver; font-style: italic;">// main() - funzione main</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// apro la directory</span></div><div> <span style="color: yellow; font-weight: bold;">DIR</span> <span style="color: yellow; font-weight: bold;">*</span>dir <span style="color: yellow; font-weight: bold;">=</span> opendir<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">./test</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>dir<span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// leggo una entry alla volta</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">dirent</span> <span style="color: yellow; font-weight: bold;">*</span>dir_ent<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">((</span>dir_ent <span style="color: yellow; font-weight: bold;">=</span> readdir<span style="color: yellow; font-weight: bold;">(</span>dir<span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">!=</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// mostro solo se è un file</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>dir_ent<span style="color: yellow; font-weight: bold;">-></span>d_type <span style="color: yellow; font-weight: bold;">==</span> <span style="color: white; font-weight: bold;">DT_REG</span><span style="color: yellow; font-weight: bold;">)</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> dir_ent<span style="color: yellow; font-weight: bold;">-></span>d_name<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// chiudo la directory e la linea</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><div> closedir<span style="color: yellow; font-weight: bold;">(</span>dir<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div> exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">EXIT_SUCCESS</span><span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Che ne dite? Entrambi i casi sono interessanti, funzionali e non usano la <strong>system(3)</strong>! Se volete testarli basta creare (nella <em>directory</em> dove avete compilato il programma) una <em>directory</em> test con dentro qualche file (io ci ho copiato i sorgenti mostrati in questo articolo). Poi eseguite i programmi, <em>et voilà</em>!</p><p>E, già che ci sono, posso mostrarvi dei risultati reali: ho eseguito direttamente (con <em>bash</em>) il comando "<em>ls ./test</em>" e poi ho eseguito e i tre programmi di test (quello con <strong>system(3) </strong>e i due senza). Il risultato è stato il seguente:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>aldo@Linux $ ls .<span style="color: yellow; font-weight: bold;">/</span>test<span style="color: yellow; font-weight: bold;">/</span></div><div>forkexecwait.c readdir.c system.c</div><div>aldo@Linux $ .<span style="color: yellow; font-weight: bold;">/</span>system</div><div>forkexecwait.c readdir.c system.c</div><div>aldo@Linux $ .<span style="color: yellow; font-weight: bold;">/</span>forkexecwait</div><div>forkexecwait.c readdir.c system.c</div><div>aldo@Linux $ .<span style="color: yellow; font-weight: bold;">/</span>readdir</div><div>readdir.c forkexecwait.c system.c</div></div></pre><p>che era esattamente quello aspettato. Provare per credere!</p><p>Ok, per oggi può bastare: spero di avervi convinto che <strong>system(3)</strong> è una funzione <em>anti-pattern</em>, e di più non posso aggiungere. Nella seconda parte dell'articolo penso che tratteremo usi di ZeroMQ più sofisticati e usando un approccio high-level... <strong>oops!</strong> Ma questo è quello che avevo scritto alla fine <b><a href="https://artcprogramming.blogspot.com/2022/05/zeromq-festival-come-si-usa-zeromq-in-c.html" target="_blank">dello scorso articolo</a></b>! <em>Speriamo, almeno questa volta, di riuscire a mantenere la promessa...</em></p><p>Ciao, e al prossimo post! </p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-3955087438777078778.post-20320450546142799812022-05-30T22:28:00.001+02:002022-06-27T11:27:22.099+02:00ZeroMQ Festival come si usa ZeroMQ in C - pt.1<blockquote><b>Sue:</b> <i>Lo hai sentito parlare del suo prossimo film? Vuole tentare di far riconciliare Arabi e Israeliani.</i><br /><b>Mort:</b> <i>Sono contento che si dedichi alla fantascienza.</i><br /></blockquote><p><a href="https://letterboxd.com/aldoz/film/rifkins-festival/" rel="noopener" target="_blank"><strong>Rifkin's Festival</strong></a> è un bel film del Maestro <a href="https://letterboxd.com/director/woody-allen/" rel="noopener" target="_blank"><strong>Woody Allen</strong></a> appartenente alla serie di quelli diretti da lui e interpretati da un suo alter-ego (in questo caso il bravissimo <a href="https://letterboxd.com/actor/wallace-shawn/" rel="noopener" target="_blank"><strong>Wallace Shawn</strong></a>). È un ode al Cinema (con la C maiuscola), è pieno di citazioni dotte, è brillante. Ed è brillante come l'argomento di questo articolo, la libreria <a href="https://zeromq.org/" rel="noopener" target="_blank"><strong>ZeroMQ</strong></a>, che è un festival di sorprese, di ecletticitá e di prestazioni, una libreria <em>"Super"</em> che si distingue nel mondo della messaggistica (<em>e no: non sono un azionista di ZeroMQ, che, tra l'altro, è open-source e gratuita. Ne parlo bene solo perché merita</em>).</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_rxZ5o7mIZGNEkA6fRAcEO89VGFmoCVZKVqZm09fGLW-_zav18JcBTb1WTmAnJIpe0JDb1hvJDfLJabneukqEJRcnQ_f2inUglJzaj7e8LPI2Cw6EfWHCwUn2ENzdWy8bSU42PICbJ35TXIoiVjf9BCC7ZpKO4m68XSwwOYtBjkB4omSEWGnxaUsWkA/s934/Rifkins-Festival-2.png" style="margin-left: auto; margin-right: auto;"><img alt="ZeroMQ Festival" border="0" data-original-height="615" data-original-width="934" height="382" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_rxZ5o7mIZGNEkA6fRAcEO89VGFmoCVZKVqZm09fGLW-_zav18JcBTb1WTmAnJIpe0JDb1hvJDfLJabneukqEJRcnQ_f2inUglJzaj7e8LPI2Cw6EfWHCwUn2ENzdWy8bSU42PICbJ35TXIoiVjf9BCC7ZpKO4m68XSwwOYtBjkB4omSEWGnxaUsWkA/w580-h382/Rifkins-Festival-2.png" width="580" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">...e nella prossima scena useremo ZeroMQ...</td></tr></tbody></table><p>Veniamo al dunque: cos'è esattamente <strong>ZeroMQ</strong>? Visto che non amo reinventare la ruota lo faccio dire ai suoi stessi creatori/curatori, che nella loro pagina Web la descrivono così:</p><p></p><blockquote><p><i>ZeroMQ (scritto anche ØMQ, 0MQ o ZMQ) è una libreria di messaggistica asincrona ad alte prestazioni, destinata all'uso in applicazioni distribuite o concorrenti. Fornisce una coda di messaggi, ma a differenza dei middleware orientati ai messaggi, un sistema ZeroMQ può funzionare senza un broker di messaggi dedicato.</i></p><p><i>ZeroMQ supporta gli schemi di messaggistica più comuni (pub/sub, request/reply, client/server e altri) su una varietà di transports (TCP, in-process, inter-process, multicast, WebSocket e altri), rendendo la messaggistica inter-process semplice come quella inter-thread. In questo modo il codice rimane chiaro, modulare ed estremamente facile da scalare.</i></p></blockquote><p>Ecco, è evidente che la descrizione qui sopra è di parte (<em>"Ogne scarrafone è bell' a mamma soja"</em>) e quindi, per definizione, poco attendibile. Però io non sono di parte e vi assicuro che è tutto vero! <strong>ZeroMQ</strong> è un unico pacchetto che permette di scrivere codice di messaggistica di tutti i tipi fondamentali: <em>inter-process</em>, <em>inter-thread</em> e <em>inter-network,</em> e senza scomodare le librerie specializzate (come le <strong>POSIX IPC</strong> o i <strong>BSD Socket</strong>). E, oltretutto, <strong>ZeroMQ</strong> è leggera, ed è (relativamente) semplice da usare. E ha altissime prestazioni (veramente molto alte, grazie all'architettura <em>brokerless</em> e a un codice interno genialmente progettato). <strong>ZeroMQ</strong> non è un prodotto di nicchia semisconosciuto: è usato in vari progetti da una impressionante lista di aziende (come Samsung, AT&T, Spotify, Microsoft e molte altre).</p><p><strong>ZeroMQ</strong> è multi-piattaforma ed è veramente multi-linguaggio, nel senso che sono disponibili un sacco (<em>ma veramente un sacco!</em>) di interfacce; ad esempio abbiamo: <strong>C</strong>, <strong>C++</strong>,<strong> C#</strong>, <strong>Java</strong>, <strong>Python</strong>, <strong>Ruby</strong>, <strong>Dart</strong>, <strong>Go</strong>, <strong>NodeJS</strong> e molti altri. Ovviamente in questo articolo ci soffermeremo sul suo uso in <strong>C</strong>, dove abbiamo a disposizione due librerie: <strong>libzmq</strong> e <strong>CZMQ</strong>. E perché due librerie? Perché <strong>ZeroMQ</strong> è un progetto molto versatile e con due personalità: con <em>libzmq</em> si può scrivere codice <em>"classico"</em> usando lo stile <em>low-level</em> che si usa per programmare, ad esempio, con i <em>BSD Sockets</em>; invece con <em>CZMQ</em> si può scrivere codice di tipo <em>high-level</em> usando funzioni semplici che eseguono attività molto complesse (e che sarebbero complicate da scrivere in modo <em>low-level</em>).</p><p><em></em></p><blockquote><em>(...apro una doverosa parentesi: per chi non l'avesse ancora capito, io sono un super-fan del C, e quindi, anche se mi costa un po' dirlo, lo dico: ZeroMQ è stato scritto usando il <a href="https://starwars.fandom.com/it/wiki/Lato_Oscuro_della_Forza" rel="noopener" target="_blank"><strong>lato oscuro della forza</strong></a>, il C++ (che uso moltissimo anch'io, e anche abbastanza bene, nonostante non sia esattamente il mio linguaggio preferito...). E qui viene il bello: nonostante gli ottimi risultati ottenuti, il creatore del progetto (il bravissimo Martin Sústrik) si è pentito della scelta, affermando che sarebbe venuto meglio in C (l'ha detto lui! Io non centro nulla!). I motivi li ha spiegati <a href="https://250bpm.com/blog:4/" rel="noopener" target="_blank"><strong>qui</strong></a> (leggere con attenzione, è molto interessante). Chiudo la doverosa parentesi...).</em></blockquote><p></p><p>Ok, e a questo punto come si procede? Nella pagina Web di <strong>ZeroMQ</strong> ci sono un sacco di esempi base, e sarebbe troppo facile fare il <em>copia-incolla</em> di qualcuno. Piuttosto mi interessa l'idea di fare un bel <em>benchmark</em> (che, indirettamente, fornisce già un esempio base). Poi, nel prossimo articolo (<em>spoiler: ci sarà una seconda parte</em>) potremo vedere un esempio d'uso un po' più interessante. Per il <em>benchmark</em> ho deciso di seguire, pari pari, il mio vecchio ciclo di articoli sulla <em>POSIX IPC</em>, sia perché l'argomento è (quasi) lo stesso, sia per dimostrare che <strong>ZeroMQ</strong> è perfettamente integrabile nello stesso codice che usai a suo tempo (e, come detto sopra, <strong>ZeroMQ</strong> funziona anche in modo inter-process). Ovviamente userò la libreria <em>low-level</em> per ottenere dei sorgenti quasi sovrapponibili a quelli del ciclo <em>POSIX IPC</em> .</p><p>E allora, sulla falsariga dei vecchi articoli (che potete trovare <a href="https://artcprogramming.blogspot.com/2021/01/per-un-pugno-di-ipc-considerazioni.html" rel="noopener" target="_blank"><strong>qui</strong>,</a> <a href="https://artcprogramming.blogspot.com/2021/02/per-qualche-ipc-in-piu-considerazioni.html" rel="noopener" target="_blank"><strong>qui</strong></a> e <a href="https://artcprogramming.blogspot.com/2021/03/il-buono-il-brutto-lipc-considerazioni.html" rel="noopener" target="_blank"><strong>qui</strong>),</a> useremo i seguenti listati:</p><ol><li>Il <strong>main</strong> di un processo padre (<em>processes.c</em>) che crea ed segue due processi figli con <strong>fork</strong> + <strong>exec</strong>. I due processi figli si chiameranno <strong>writer</strong> e <strong>reader</strong>.</li><li>Il <strong>main</strong> del processo <strong>writer</strong> (<em>writer.c</em>).</li><li>Il <strong>main</strong> del processo <strong>reader</strong> (<em>reader.c</em>).</li><li>Un <strong>header file</strong> (data.h).</li></ol><p>Ok, cominciamo da <em>processes.c</em>: vai col codice!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// processes.c - main processo padre</span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdlib.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">string.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">errno.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">unistd.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/wait.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">data.h</span><span style="color: yellow; font-weight: bold;">"</span></div><br /><div><span style="color: silver; font-style: italic;">// funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char*</span> argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// crea i processi figli</span></div><div> <span style="color: yellow; font-weight: bold;">pid_t</span> pid1<span style="color: yellow; font-weight: bold;">,</span> pid2<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">(</span>pid1 <span style="color: yellow; font-weight: bold;">=</span> fork<span style="color: yellow; font-weight: bold;">())</span> <span style="color: yellow; font-weight: bold;">&&</span> <span style="color: yellow; font-weight: bold;">(</span>pid2 <span style="color: yellow; font-weight: bold;">=</span> fork<span style="color: yellow; font-weight: bold;">());</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// test pid processi</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>pid1 <span style="color: yellow; font-weight: bold;">==</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// sono il figlio 1</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">sono il figlio 1 (%d): eseguo il nuovo processo\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">());</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>pathname <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">reader</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>newargv<span style="color: yellow; font-weight: bold;">[]</span> <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">{</span> pathname<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span> <span style="color: yellow; font-weight: bold;">};</span></div><div> execv<span style="color: yellow; font-weight: bold;">(</span>pathname<span style="color: yellow; font-weight: bold;">,</span> newargv<span style="color: yellow; font-weight: bold;">);</span></div><div> exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">);</span> <span style="color: silver; font-style: italic;">// exec non ritorna mai</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>pid2 <span style="color: yellow; font-weight: bold;">==</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// sono il figlio 2</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">sono il figlio 2 (%d): eseguo il nuovo processo\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">());</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>pathname <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">writer</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>newargv<span style="color: yellow; font-weight: bold;">[]</span> <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">{</span> pathname<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span> <span style="color: yellow; font-weight: bold;">};</span></div><div> execv<span style="color: yellow; font-weight: bold;">(</span>pathname<span style="color: yellow; font-weight: bold;">,</span> newargv<span style="color: yellow; font-weight: bold;">);</span></div><div> exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">);</span> <span style="color: silver; font-style: italic;">// exec non ritorna mai</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>pid1 <span style="color: yellow; font-weight: bold;">></span> <span style="color: white; font-weight: bold;">0</span> <span style="color: yellow; font-weight: bold;">&&</span> pid2 <span style="color: yellow; font-weight: bold;">></span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// sono il padre</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">sono il padre (%d): attendo la terminazione dei figli\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">());</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> status<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">pid_t</span> wpid<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">((</span>wpid <span style="color: yellow; font-weight: bold;">=</span> wait<span style="color: yellow; font-weight: bold;">(&</span>status<span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">></span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">sono il padre (%d): figlio %d terminato (%d)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">(),</span> </div><div><span style="color: yellow; font-weight: bold;"> (int)</span>wpid<span style="color: yellow; font-weight: bold;">,</span> status<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// esco</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: processi terminati\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">]);</span></div><div> exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">EXIT_SUCCESS</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">else</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// errore nella fork(): esco</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%s: fork error (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> argv<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">],</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Come avrete notato è identico (come era da sperare) a quello della <em>POSIX IPC Socket</em>, dei vecchi articoli, quindi è il più semplice di tutti i vari <em>processes.</em>c mostrati a suo tempo.</p><p>E ora vediamo l’<em>header file data.h</em> che ci mostra il tipo di dati scambiati e definisce il <em>path</em> usato da <strong>ZeroMQ</strong> per il modo <strong>IPC</strong> (è un file temporaneo che è consigliabile creare in <em>/tmp</em>):</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: yellow; font-weight: bold;">#ifndef</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">DATA_H</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">DATA_H</span></div><br /><div><span style="color: silver; font-style: italic;">// path del file temporaneo per IPC</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">ZEROMQ_PATH</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">ipc:///tmp/0mq.ipc</span><span style="color: yellow; font-weight: bold;">"</span></div><br /><div><span style="color: silver; font-style: italic;">// numero di messaggi da scambiare per il benchmark</span></div><div><span style="color: yellow; font-weight: bold;">#define</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">N_MESSAGES</span><span style="color: #569cd6;"> </span><span style="color: white; font-weight: bold;">2000000</span></div><br /><div><span style="color: silver; font-style: italic;">// struttura Data per i messaggi</span></div><div><span style="color: yellow; font-weight: bold;">typedef</span> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">{</span></div><div> <span style="color: yellow; font-weight: bold;">unsigned</span> <span style="color: yellow; font-weight: bold;">long</span> index<span style="color: yellow; font-weight: bold;">;</span> <span style="color: silver; font-style: italic;">// indice dei dati</span></div><div> <span style="color: yellow; font-weight: bold;">char</span> text<span style="color: yellow; font-weight: bold;">[</span><span style="color: white; font-weight: bold;">1024</span><span style="color: yellow; font-weight: bold;">];</span> <span style="color: silver; font-style: italic;">// testo dei dati</span></div><div><span style="color: yellow; font-weight: bold;">}</span> <span style="color: yellow; font-weight: bold;">Data</span><span style="color: yellow; font-weight: bold;">;</span></div><br /><div><span style="color: yellow; font-weight: bold;">#endif</span> <span style="color: silver; font-style: italic;">/* DATA_H */</span></div></div></pre><p>E adesso siamo pronti per vedere il codice di<em> writer.c</em>:</p><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px; white-space: pre;"><div><span style="color: silver; font-style: italic;">// writer.c - main processo figlio (il Writer (è un Client))</span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdlib.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">string.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">unistd.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">zmq.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">data.h</span><span style="color: yellow; font-weight: bold;">"</span></div><br /><div><span style="color: silver; font-style: italic;">// funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// creo il context, il socket e la connessione</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">processo %d partito\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">());</span></div><div> <span style="color: yellow; font-weight: bold;">void</span> <span style="color: yellow; font-weight: bold;">*</span>context <span style="color: yellow; font-weight: bold;">=</span> zmq_ctx_new<span style="color: yellow; font-weight: bold;">();</span></div><div> <span style="color: yellow; font-weight: bold;">void</span> <span style="color: yellow; font-weight: bold;">*</span>writer <span style="color: yellow; font-weight: bold;">=</span> zmq_socket<span style="color: yellow; font-weight: bold;">(</span>context<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">ZMQ_PUSH</span><span style="color: yellow; font-weight: bold;">);</span></div><div> zmq_connect<span style="color: yellow; font-weight: bold;">(</span>writer<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">ZEROMQ_PATH</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// loop di scrittura messaggi per il reader</span></div><div> <span style="color: yellow; font-weight: bold;">Data</span> my_data<span style="color: yellow; font-weight: bold;">;</span></div><div> my_data<span style="color: yellow; font-weight: bold;">.</span>index <span style="color: yellow; font-weight: bold;">=</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">do</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// test index per forzare l'uscita</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>my_data<span style="color: yellow; font-weight: bold;">.</span>index <span style="color: yellow; font-weight: bold;">==</span> <span style="color: white; font-weight: bold;">N_MESSAGES</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// il processo chiude il socket ed esce per indice raggiunto</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">processo %d terminato (text=%s index=%ld)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> getpid<span style="color: yellow; font-weight: bold;">(),</span> my_data<span style="color: yellow; font-weight: bold;">.</span>text<span style="color: yellow; font-weight: bold;">,</span> my_data<span style="color: yellow; font-weight: bold;">.</span>index<span style="color: yellow; font-weight: bold;">);</span></div><div> zmq_close <span style="color: yellow; font-weight: bold;">(</span>writer<span style="color: yellow; font-weight: bold;">);</span></div><div> zmq_ctx_destroy <span style="color: yellow; font-weight: bold;">(</span>context<span style="color: yellow; font-weight: bold;">);</span></div><div> exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">EXIT_SUCCESS</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// compongo il messaggio e lo invio</span></div><div> my_data<span style="color: yellow; font-weight: bold;">.</span>index<span style="color: yellow; font-weight: bold;">++;</span></div><div> snprintf<span style="color: yellow; font-weight: bold;">(</span>my_data<span style="color: yellow; font-weight: bold;">.</span>text<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span>my_data<span style="color: yellow; font-weight: bold;">.</span>text<span style="color: yellow; font-weight: bold;">),</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">un-messaggio-di-test:%ld</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> my_data<span style="color: yellow; font-weight: bold;">.</span>index<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span> <span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">(</span>zmq_send<span style="color: yellow; font-weight: bold;">(</span>writer<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>my_data<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">Data</span><span style="color: yellow; font-weight: bold;">),</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">!=</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// il processo chiude il socket ed esce per altro motivo (errore)</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">processo %d terminato con errore (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">(),</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> zmq_close<span style="color: yellow; font-weight: bold;">(</span>writer<span style="color: yellow; font-weight: bold;">);</span></div><div> zmq_ctx_destroy<span style="color: yellow; font-weight: bold;">(</span>context<span style="color: yellow; font-weight: bold;">);</span></div><div> exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div><p>Anche questo codice è semplicissimo e auto-esplicativo (specialmente se avete letto i vecchi articoli): dopo aver aperto il <em>context</em> , il <em>socket</em> <strong>ZeroMQ</strong> ed eseguito il <em>bind</em>, esegue un <em>loop</em> di 2000000 (<em>due milioni!</em> Chi offre di più?) di <em>send</em> del messaggio definito in <em>data.h</em>, dopodiché esce. Per ridurre all'osso l’attività della CPU e non falsare i risultati, si potrebbe omettere anche la <a href="https://man7.org/linux/man-pages/man3/printf.3.html" rel="noopener" target="_blank"><strong>snprintf()</strong></a> di ogni messaggio e aggiornare solo il campo index: ho fatto dei test e, come era prevedibile, la <strong>snprintf()</strong> locale è molto più` veloce della scrittura IPC, e quindi, praticamente, i risultati non vengono alterati. Notare che il <em>loop</em> pseudo-infinito prevede una uscita forzata del processo al raggiungimento del numero di scritture prestabilite. In caso di <em>send error</em> si ferma il <em>loop</em> anticipatamente e si va all'uscita generica di errore. La fase di apertura iniziale dell'ambiente <strong>ZeroMQ</strong> (<em>context</em>, <em>socket</em>, <em>bind</em>) l'ho eseguita <em>"secca"</em> senza testare gli errori: <em>è un programma di test, non andrà in produzione.</em></p><p>Ma si, dai, passiamo al <em>reader</em>!</p><pre class="EnlighterJSRAW" data-enlighter-language="c"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div><span style="color: silver; font-style: italic;">// reader.c - main processo figlio (il Reader (è un Server))</span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdio.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">stdlib.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">string.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">unistd.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">zmq.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">time.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;"><</span><span style="color: white; font-weight: bold;">sys/time.h</span><span style="color: yellow; font-weight: bold;">></span></div><div><span style="color: yellow; font-weight: bold;">#include</span><span style="color: #569cd6;"> </span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">data.h</span><span style="color: yellow; font-weight: bold;">"</span></div><br /><div><span style="color: silver; font-style: italic;">// funzione main()</span></div><div><span style="color: yellow; font-weight: bold;">int</span> main<span style="color: yellow; font-weight: bold;">(int</span> argc<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">char</span> <span style="color: yellow; font-weight: bold;">*</span>argv<span style="color: yellow; font-weight: bold;">[])</span></div><div><span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// creo il context, il socket ed eseguo il bind</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">processo %d partito\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">());</span></div><div> <span style="color: yellow; font-weight: bold;">void</span> <span style="color: yellow; font-weight: bold;">*</span>context <span style="color: yellow; font-weight: bold;">=</span> zmq_ctx_new<span style="color: yellow; font-weight: bold;">();</span></div><div> <span style="color: yellow; font-weight: bold;">void</span> <span style="color: yellow; font-weight: bold;">*</span>reader <span style="color: yellow; font-weight: bold;">=</span> zmq_socket<span style="color: yellow; font-weight: bold;">(</span>context<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">ZMQ_PULL</span><span style="color: yellow; font-weight: bold;">);</span></div><div> zmq_bind<span style="color: yellow; font-weight: bold;">(</span>reader<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">ZEROMQ_PATH</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// set clock e time per calcolare il tempo di CPU e il tempo di sistema</span></div><div> <span style="color: yellow; font-weight: bold;">clock_t</span> t_start <span style="color: yellow; font-weight: bold;">=</span> clock<span style="color: yellow; font-weight: bold;">();</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">timeval</span> tv_start<span style="color: yellow; font-weight: bold;">;</span></div><div> gettimeofday<span style="color: yellow; font-weight: bold;">(&</span>tv_start<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// loop di lettura messaggi dal writer</span></div><div> <span style="color: yellow; font-weight: bold;">Data</span> my_data<span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">while</span> <span style="color: yellow; font-weight: bold;">(</span>zmq_recv<span style="color: yellow; font-weight: bold;">(</span>reader<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>my_data<span style="color: yellow; font-weight: bold;">,</span> <span style="color: #569cd6; font-weight: bold;">sizeof</span><span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">Data</span><span style="color: yellow; font-weight: bold;">),</span> <span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">!=</span> <span style="color: yellow; font-weight: bold;">-</span><span style="color: white; font-weight: bold;">1</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// test index per forzare l'uscita</span></div><div> <span style="color: yellow; font-weight: bold;">if</span> <span style="color: yellow; font-weight: bold;">(</span>my_data<span style="color: yellow; font-weight: bold;">.</span>index <span style="color: yellow; font-weight: bold;">==</span> <span style="color: white; font-weight: bold;">N_MESSAGES</span><span style="color: yellow; font-weight: bold;">)</span> <span style="color: yellow; font-weight: bold;">{</span></div><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// get clock e time per calcolare il tempo di CPU e il tempo di sistema</span></div><div> <span style="color: yellow; font-weight: bold;">clock_t</span> t_end <span style="color: yellow; font-weight: bold;">=</span> clock<span style="color: yellow; font-weight: bold;">();</span></div><div> <span style="color: yellow; font-weight: bold;">double</span> t_passed <span style="color: yellow; font-weight: bold;">=</span> <span style="color: yellow; font-weight: bold;">((double)(</span>t_end <span style="color: yellow; font-weight: bold;">-</span> t_start<span style="color: yellow; font-weight: bold;">))</span> <span style="color: yellow; font-weight: bold;">/</span> <span style="color: white; font-weight: bold;">CLOCKS_PER_SEC</span><span style="color: yellow; font-weight: bold;">;</span></div><div> <span style="color: yellow; font-weight: bold;">struct</span> <span style="color: yellow; font-weight: bold;">timeval</span> tv_end<span style="color: yellow; font-weight: bold;">,</span> tv_elapsed<span style="color: yellow; font-weight: bold;">;</span></div><div> gettimeofday<span style="color: yellow; font-weight: bold;">(&</span>tv_end<span style="color: yellow; font-weight: bold;">,</span> <span style="color: white; font-weight: bold;">NULL</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: white; font-weight: bold;">timersub</span><span style="color: yellow; font-weight: bold;">(&</span>tv_end<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>tv_start<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">&</span>tv_elapsed<span style="color: yellow; font-weight: bold;">);</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// il processo chiude il socket ed esce per indice raggiunto</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">reader: ultimo messaggio ricevuto: %s\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> my_data<span style="color: yellow; font-weight: bold;">.</span>text<span style="color: yellow; font-weight: bold;">);</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">processo %d terminato (index=%ld CPU time elapsed: </span><span style="color: yellow; font-weight: bold;">"</span></div><div> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">%.3f - total time elapsed:%ld.%ld)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span></div><div> getpid<span style="color: yellow; font-weight: bold;">(),</span> my_data<span style="color: yellow; font-weight: bold;">.</span>index<span style="color: yellow; font-weight: bold;">,</span> t_passed<span style="color: yellow; font-weight: bold;">,</span> tv_elapsed<span style="color: yellow; font-weight: bold;">.</span>tv_sec<span style="color: yellow; font-weight: bold;">,</span></div><div> tv_elapsed<span style="color: yellow; font-weight: bold;">.</span>tv_usec <span style="color: yellow; font-weight: bold;">/</span> <span style="color: white; font-weight: bold;">1000</span><span style="color: yellow; font-weight: bold;">);</span></div><div> zmq_close<span style="color: yellow; font-weight: bold;">(</span>reader<span style="color: yellow; font-weight: bold;">);</span></div><div> zmq_ctx_destroy<span style="color: yellow; font-weight: bold;">(</span>context<span style="color: yellow; font-weight: bold;">);</span></div><div> exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">EXIT_SUCCESS</span><span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><div> <span style="color: yellow; font-weight: bold;">}</span></div><br /><div><span style="color: yellow; font-weight: bold;"> </span><span style="color: silver; font-style: italic;">// il processo chiude il socket ed esce per altro motivo (errore)</span></div><div> printf<span style="color: yellow; font-weight: bold;">(</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">processo %d terminato con errore (%s)\n</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">,</span> getpid<span style="color: yellow; font-weight: bold;">(),</span> strerror<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">errno</span><span style="color: yellow; font-weight: bold;">));</span></div><div> zmq_close<span style="color: yellow; font-weight: bold;">(</span>reader<span style="color: yellow; font-weight: bold;">);</span></div><div> zmq_ctx_destroy<span style="color: yellow; font-weight: bold;">(</span>context<span style="color: yellow; font-weight: bold;">);</span></div><div> exit<span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">EXIT_FAILURE</span><span style="color: yellow; font-weight: bold;">);</span></div><div><span style="color: yellow; font-weight: bold;">}</span></div></div></pre><p>Il <em>reader</em> è strutturalmente identico (e speculare) al <em>writer</em>, quindi il loop di lettura ha due possibili uscite (forzata e per errore), ed esegue simmetricamente le stesse operazioni, solo che legge invece di scrivere. Ed ha in più la gestione del calcolo dei tempi di esecuzione, che ci servono per effettuare il <em>benchmark</em> che ci eravamo proposti.</p><p>In definitiva si può` affermare che la <strong>IPC</strong> attraverso <strong>ZeroMQ</strong> è veramente semplice da implementare, perché è pura attività di read/write su un file. Tutto Ok, quindi… e i risultati? Il test l’ho effettuato su una macchina Linux (Kernel 5.4.0) con un i7 (4 core/8 thread) con 8GB di RAM. Ok, vediamo come è andata (con la stessa forma del vecchio ciclo di articoli):</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>ZeroMQ <span style="color: yellow; font-weight: bold;">(</span>modo IPC<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">-----------------</span></div><div>sono il padre <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">1896</span><span style="color: yellow; font-weight: bold;">)</span>: attendo la terminazione dei figli</div><div>sono il figlio <span style="color: white; font-weight: bold;">1</span> <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">1897</span><span style="color: yellow; font-weight: bold;">)</span>: eseguo il nuovo processo</div><div>sono il figlio <span style="color: white; font-weight: bold;">2</span> <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">1898</span><span style="color: yellow; font-weight: bold;">)</span>: eseguo il nuovo processo</div><div>processo <span style="color: white; font-weight: bold;">1897</span> partito</div><div>processo <span style="color: white; font-weight: bold;">1898</span> partito</div><div>processo <span style="color: white; font-weight: bold;">1898</span> terminato <span style="color: yellow; font-weight: bold;">(</span>text<span style="color: yellow; font-weight: bold;">=</span>un<span style="color: yellow; font-weight: bold;">-</span>messaggio<span style="color: yellow; font-weight: bold;">-</span>di<span style="color: yellow; font-weight: bold;">-</span>test:<span style="color: white; font-weight: bold;">2000000</span> index<span style="color: yellow; font-weight: bold;">=</span><span style="color: white; font-weight: bold;">2000000</span><span style="color: yellow; font-weight: bold;">)</span></div><div>reader: ultimo messaggio ricevuto: un<span style="color: yellow; font-weight: bold;">-</span>messaggio<span style="color: yellow; font-weight: bold;">-</span>di<span style="color: yellow; font-weight: bold;">-</span>test:<span style="color: white; font-weight: bold;">2000000</span></div><div>processo <span style="color: white; font-weight: bold;">1897</span> terminato <span style="color: yellow; font-weight: bold;">(</span>index<span style="color: yellow; font-weight: bold;">=</span><span style="color: white; font-weight: bold;">2000000</span> CPU time elapsed: <span style="color: white; font-weight: bold;">2.935</span> <span style="color: yellow; font-weight: bold;">-</span> total time elapsed:<span style="color: white; font-weight: bold;">1.759</span><span style="color: yellow; font-weight: bold;">)</span></div><div>sono il padre <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">1896</span><span style="color: yellow; font-weight: bold;">)</span>: figlio <span style="color: white; font-weight: bold;">1897</span> terminato <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span></div><div>sono il padre <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">1896</span><span style="color: yellow; font-weight: bold;">)</span>: figlio <span style="color: white; font-weight: bold;">1898</span> terminato <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span></div><div>.<span style="color: yellow; font-weight: bold;">/</span>processes: processi terminati</div></div></pre><p>Allora: il nostro test mostra che, usando <strong>ZeroMQ</strong> in modo <strong>IPC</strong>, abbiamo scambiato due milioni di messaggi tra i due processi in 1.759 secondi (quindi un messaggio ogni 0,89 us). <strong>ZeroMQ</strong> è velocissimo! A questo punto forse qualcuno noterà la stranezza del CPU time che è molto più alto del total time: non è un errore, è che su una macchina <em>multi-core</em> come quella di test, <strong>ZeroMQ</strong> distribuisce opportunamente il carico su thread diversi della CPU e il tempo calcolato nel codice è la somma dei tempi <em>"reali"</em>.</p><p>Già che c'ero ho pensato che sarebbe stato interessante fare una comparazione diretta con un prodotto similare, quindi ho rieseguito i test con <strong><a href="https://artcprogramming.blogspot.com/2021/02/per-qualche-ipc-in-piu-considerazioni.html" target="_blank">POSIX Message Queue</a></strong>. Ecco i risultati:</p><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>POSIX Message Queue</div><div><span style="color: yellow; font-weight: bold;">-------------------</span></div><div>sono il figlio <span style="color: white; font-weight: bold;">1</span> <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">8824</span><span style="color: yellow; font-weight: bold;">)</span>: eseguo il nuovo processo</div><div>sono il padre <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">8823</span><span style="color: yellow; font-weight: bold;">)</span>: attendo la terminazione dei figli</div><div>sono il figlio <span style="color: white; font-weight: bold;">2</span> <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">8825</span><span style="color: yellow; font-weight: bold;">)</span>: eseguo il nuovo processo</div><div>processo <span style="color: white; font-weight: bold;">8824</span> partito</div><div>processo <span style="color: white; font-weight: bold;">8825</span> partito</div><div>processo <span style="color: white; font-weight: bold;">8825</span> terminato <span style="color: yellow; font-weight: bold;">(</span>text<span style="color: yellow; font-weight: bold;">=</span>un<span style="color: yellow; font-weight: bold;">-</span>messaggio<span style="color: yellow; font-weight: bold;">-</span>di<span style="color: yellow; font-weight: bold;">-</span>test:<span style="color: white; font-weight: bold;">2000000</span> index<span style="color: yellow; font-weight: bold;">=</span><span style="color: white; font-weight: bold;">2000000</span><span style="color: yellow; font-weight: bold;">)</span></div><div>reader: ultimo messaggio ricevuto: un<span style="color: yellow; font-weight: bold;">-</span>messaggio<span style="color: yellow; font-weight: bold;">-</span>di<span style="color: yellow; font-weight: bold;">-</span>test:<span style="color: white; font-weight: bold;">2000000</span></div><div>processo <span style="color: white; font-weight: bold;">8824</span> terminato <span style="color: yellow; font-weight: bold;">(</span>index<span style="color: yellow; font-weight: bold;">=</span><span style="color: white; font-weight: bold;">2000000</span> CPU time elapsed: <span style="color: white; font-weight: bold;">1.881</span> <span style="color: yellow; font-weight: bold;">-</span> total time elapsed:<span style="color: white; font-weight: bold;">1.923</span><span style="color: yellow; font-weight: bold;">)</span></div><div>sono il padre <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">8823</span><span style="color: yellow; font-weight: bold;">)</span>: figlio <span style="color: white; font-weight: bold;">8825</span> terminato <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span></div><div>sono il padre <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">8823</span><span style="color: yellow; font-weight: bold;">)</span>: figlio <span style="color: white; font-weight: bold;">8824</span> terminato <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span></div><div>.<span style="color: yellow; font-weight: bold;">/</span>processes: processi terminati</div></div></pre><p>I risultati assoluti sono simili (come c'era da aspettarsi) con una leggera superiorità di <strong>ZeroMQ</strong>. Il <em>multithreading</em> sembra usato differentemente (pare che la <strong>Message Queue</strong> non lo usa o lo usa in maniera diversa).</p><p>A questo punto, per chiudere in bellezza, ho deciso di sfruttare la versatilità di <strong>ZeroMQ</strong> che permette di cambiare il modo d'uso (con la libreria <em>low-level</em>) con una facilità disarmante, semplicemente giocando con i parametri di <strong>zmq_connect(</strong><strong>)</strong>. Vediamo come: <em>writer</em> e <em>reader</em> IPC usano questo:</p><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px; white-space: pre;"><div>writer:</div><div> zmq_connect<span style="color: yellow; font-weight: bold;">(</span>writer<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">ipc:///tmp/0mq.ipc</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div>reader:</div><div> zmq_bind<span style="color: yellow; font-weight: bold;">(</span>reader<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">ipc:///tmp/0mq.ipc</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div></div><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><span style="font-family: "Times New Roman"; white-space: normal;">Ebbene, per passare al modo TCP è sufficiente fare questo:</span></pre><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px; white-space: pre;"><div>writer:</div><div> zmq_connect<span style="color: yellow; font-weight: bold;">(</span>writer<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">tcp://localhost:5555</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div>reader:</div><div> zmq_bind<span style="color: yellow; font-weight: bold;">(</span>reader<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">tcp://*:5555</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div></div><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><span style="font-family: Times New Roman;"><span style="white-space: normal;">Semplicissimo, no? Così semplice che, in quattro e quattr’otto, ho rifatto i test in modo TCP e ho anche rispolverato il codice del <b><a href="https://artcprogramming.blogspot.com/2021/02/per-qualche-ipc-in-piu-considerazioni.html" target="_blank">POSIX IPC Socket</a></b> per fare un confronto di prestazioni. Vediamo::</span></span></pre><pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>ZeroMQ <span style="color: yellow; font-weight: bold;">(</span>modo TCP<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">-----------------</span></div><div>sono il padre <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">4249</span><span style="color: yellow; font-weight: bold;">)</span>: attendo la terminazione dei figli</div><div>sono il figlio <span style="color: white; font-weight: bold;">1</span> <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">4250</span><span style="color: yellow; font-weight: bold;">)</span>: eseguo il nuovo processo</div><div>sono il figlio <span style="color: white; font-weight: bold;">2</span> <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">4251</span><span style="color: yellow; font-weight: bold;">)</span>: eseguo il nuovo processo</div><div>processo <span style="color: white; font-weight: bold;">4250</span> partito</div><div>processo <span style="color: white; font-weight: bold;">4251</span> partito</div><div>processo <span style="color: white; font-weight: bold;">4251</span> terminato <span style="color: yellow; font-weight: bold;">(</span>text<span style="color: yellow; font-weight: bold;">=</span>un<span style="color: yellow; font-weight: bold;">-</span>messaggio<span style="color: yellow; font-weight: bold;">-</span>di<span style="color: yellow; font-weight: bold;">-</span>test:<span style="color: white; font-weight: bold;">2000000</span> index<span style="color: yellow; font-weight: bold;">=</span><span style="color: white; font-weight: bold;">2000000</span><span style="color: yellow; font-weight: bold;">)</span></div><div>sono il padre <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">4249</span><span style="color: yellow; font-weight: bold;">)</span>: figlio <span style="color: white; font-weight: bold;">4251</span> terminato <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span></div><div>reader: ultimo messaggio ricevuto: un<span style="color: yellow; font-weight: bold;">-</span>messaggio<span style="color: yellow; font-weight: bold;">-</span>di<span style="color: yellow; font-weight: bold;">-</span>test:<span style="color: white; font-weight: bold;">2000000</span></div><div>processo <span style="color: white; font-weight: bold;">4250</span> terminato <span style="color: yellow; font-weight: bold;">(</span>index<span style="color: yellow; font-weight: bold;">=</span><span style="color: white; font-weight: bold;">2000000</span> CPU time elapsed: <span style="color: white; font-weight: bold;">3.251</span> <span style="color: yellow; font-weight: bold;">-</span> total time elapsed:<span style="color: white; font-weight: bold;">2.389</span><span style="color: yellow; font-weight: bold;">)</span></div><div>sono il padre <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">4249</span><span style="color: yellow; font-weight: bold;">)</span>: figlio <span style="color: white; font-weight: bold;">4250</span> terminato <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span></div><div>.<span style="color: yellow; font-weight: bold;">/</span>processes: processi terminati</div><br /><div>POSIX IPC Socket <span style="color: yellow; font-weight: bold;">(</span>modo UDP<span style="color: yellow; font-weight: bold;">)</span></div><div><span style="color: yellow; font-weight: bold;">---------------------------</span></div><div>sono il padre <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">2068</span><span style="color: yellow; font-weight: bold;">)</span>: attendo la terminazione dei figli</div><div>sono il figlio <span style="color: white; font-weight: bold;">1</span> <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">2069</span><span style="color: yellow; font-weight: bold;">)</span>: eseguo il nuovo processo</div><div>sono il figlio <span style="color: white; font-weight: bold;">2</span> <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">2070</span><span style="color: yellow; font-weight: bold;">)</span>: eseguo il nuovo processo</div><div>processo <span style="color: white; font-weight: bold;">2069</span> partito</div><div>processo <span style="color: white; font-weight: bold;">2070</span> partito</div><div>processo <span style="color: white; font-weight: bold;">2070</span> terminato <span style="color: yellow; font-weight: bold;">(</span>text<span style="color: yellow; font-weight: bold;">=</span>un<span style="color: yellow; font-weight: bold;">-</span>messaggio<span style="color: yellow; font-weight: bold;">-</span>di<span style="color: yellow; font-weight: bold;">-</span>test:<span style="color: white; font-weight: bold;">2000000</span> index<span style="color: yellow; font-weight: bold;">=</span><span style="color: white; font-weight: bold;">2000000</span><span style="color: yellow; font-weight: bold;">)</span></div><div>reader: ultimo messaggio ricevuto: un<span style="color: yellow; font-weight: bold;">-</span>messaggio<span style="color: yellow; font-weight: bold;">-</span>di<span style="color: yellow; font-weight: bold;">-</span>test:<span style="color: white; font-weight: bold;">2000000</span></div><div>processo <span style="color: white; font-weight: bold;">2069</span> terminato <span style="color: yellow; font-weight: bold;">(</span>index<span style="color: yellow; font-weight: bold;">=</span><span style="color: white; font-weight: bold;">2000000</span> CPU time elapsed: <span style="color: white; font-weight: bold;">4.004</span> <span style="color: yellow; font-weight: bold;">-</span> total time elapsed:<span style="color: white; font-weight: bold;">5.197</span><span style="color: yellow; font-weight: bold;">)</span></div><div>sono il padre <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">2068</span><span style="color: yellow; font-weight: bold;">)</span>: figlio <span style="color: white; font-weight: bold;">2070</span> terminato <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span></div><div>sono il padre <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">2068</span><span style="color: yellow; font-weight: bold;">)</span>: figlio <span style="color: white; font-weight: bold;">2069</span> terminato <span style="color: yellow; font-weight: bold;">(</span><span style="color: white; font-weight: bold;">0</span><span style="color: yellow; font-weight: bold;">)</span></div><div>.<span style="color: yellow; font-weight: bold;">/</span>processes: processi terminati</div></div></pre><p>Allora, pare che <strong>ZeroMQ</strong> in modo <strong>TCP</strong> è veloce quasi quanto in modo <strong>IPC</strong> (notevole), e batte nettamente <strong>POSIX</strong> <strong>IPC Socket</strong> in modo <strong>UDP</strong> (un modo più veloce ma meno affidabile di <strong>TCP</strong>)... che dire? Veramente sorprendente.</p><p>E, prima di chiudere questa prima parte, approfitto per far notare che il parametro <em>"type"</em> di <a href="https://linux.die.net/man/3/zmq_socket" rel="noopener" target="_blank"><strong>zmq_socket()</strong></a> ha un uso importante: ad esempio nel nostro semplice <em>benchmark</em> con un <em>writer</em> che scrive (e basta) e un <em>reader</em> che legge (e basta), si usa il modo <em>push-pull</em> (PUSH sul <em>writer</em> e PULL sul <em>reader</em>, attraverso il parametro <em>type</em>). Invece con un più classico sistema <em>client/server,</em> con un <em>client</em> che scrive (e aspetta una risposta) e un server che legge (e invia una risposta), si dovrebbe usare il modo <em>request-reply</em> (ZMQ_REQ sul <em>client</em> e ZMQ_REP sul <em>server</em>, attraverso il parametro <em>type</em>). Quindi il codice del modo <strong>TCP</strong> diventa così:</p><pre class="EnlighterJSRAW" data-enlighter-language="c" data-enlighter-linenumbers="false"><div style="background-color: black; color: lime; font-family: Consolas, "Droid Sans Mono", "monospace", monospace; font-size: 11px; line-height: 13px;"><div>client:</div><div> <span style="color: yellow; font-weight: bold;">void</span> <span style="color: yellow; font-weight: bold;">*</span>requester <span style="color: yellow; font-weight: bold;">=</span> zmq_socket<span style="color: yellow; font-weight: bold;">(</span>context<span style="color: yellow; font-weight: bold;">,</span> ZMQ_REQ<span style="color: yellow; font-weight: bold;">);</span></div><div> zmq_connect<span style="color: yellow; font-weight: bold;">(</span>requester<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">tcp://localhost:5555</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div><br /><div>server:</div><div> <span style="color: yellow; font-weight: bold;">void</span> <span style="color: yellow; font-weight: bold;">*</span>responder <span style="color: yellow; font-weight: bold;">=</span> zmq_socket<span style="color: yellow; font-weight: bold;">(</span>context<span style="color: yellow; font-weight: bold;">,</span> ZMQ_REP<span style="color: yellow; font-weight: bold;">);</span></div><div> <span style="color: yellow; font-weight: bold;">int</span> rc <span style="color: yellow; font-weight: bold;">=</span> zmq_bind<span style="color: yellow; font-weight: bold;">(</span>responder<span style="color: yellow; font-weight: bold;">,</span> <span style="color: yellow; font-weight: bold;">"</span><span style="color: white; font-weight: bold;">tcp://*:5555</span><span style="color: yellow; font-weight: bold;">"</span><span style="color: yellow; font-weight: bold;">);</span></div></div></pre><p>Semplice ed efficace, direi.</p><p>Ok, per oggi può bastare. Nella seconda parte dell'articolo penso che tratteremo usi di <strong>ZeroMQ</strong> più sofisticati e usando un approccio <em>high-level</em>... ma non trattenete il respiro nell'attesa: non sarebbe certo la prima volta (<em>ahimè</em>) che rimando la seconda parte <em>"a più avanti"</em> perché ho qualche argomento che mi preme trattare prima: <em>cose che succedono.</em>..</p><p>Ciao, e al prossimo post!</p>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0