tag:blogger.com,1999:blog-19644741603791617282024-03-13T01:59:36.954+01:00El arte de la programación en CEscribir software es un placer. Un programa no sólo debe funcionar bien y ser eficiente (esto se da por supuesto), sino que también debe ser bello y elegante de leer, comprensible y fácil de mantener, tanto para el autor como para eventuales futuros lectores. Programar bien en C es un arte.Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.comBlogger68125tag:blogger.com,1999:blog-1964474160379161728.post-26934099703374099372018-09-09T23:49:00.001+02:002018-09-10T00:10:04.478+02:00Lost in Translation cómo cerrar un Blog de C<blockquote class="tr_bq">
<b>Bob:</b> <i>Tengo que irme, pero no voy a dejar que eso se interponga entre nosotros.</i></blockquote>
No sé si habéis visto <a href="https://es.wikipedia.org/wiki/Lost_in_Translation" target="_blank"><b>Lost in Traslation</b></a>, la bella película de <a href="https://es.wikipedia.org/wiki/Sofia_Coppola" target="_blank"><b>Sofia Coppola</b></a>. Bien, exactamente como <b>Bob</b>, el protagonista, <i>yo también me he perdido en las traducciones.</i><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggq29VKHAI0yw2SA3H6r3cw6defI0VArDwr09TRc88nR6XzIgg1nS-ODQ7FavPS3oJMHlZToQ37tNnjVEDKbKsEo_rNeqgvTUC_OGbDxf_9v0YAqsZurLJkslQCrume_hUP72NMi3PV0zN/s1600/lostintranslation.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="400" data-original-width="720" height="221" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggq29VKHAI0yw2SA3H6r3cw6defI0VArDwr09TRc88nR6XzIgg1nS-ODQ7FavPS3oJMHlZToQ37tNnjVEDKbKsEo_rNeqgvTUC_OGbDxf_9v0YAqsZurLJkslQCrume_hUP72NMi3PV0zN/s400/lostintranslation.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">...tengo que irme...</td></tr>
</tbody></table>
<b>Me explico:</b> como veis en la descripción del blog (aquí arriba) siempre escribo los post en italiano, que es mi lengua materna, y luego los traduzco al español. Pues, últimamente he notado algo un poco molesto: que el <i>trabajo</i> de la traducción me cuesta más que el de escribir el post. Supongo que esto se debe principalmente a dos factores: <b>1) </b>soy un <i>perfeccionista</i> y <b>2)</b> mi estilo de escritura no es <i>plano y lineal</i>. ¿Entonces qué pasa? Pasa que me cuesta mucho traducir a un <i>español</i> <i>brillante</i> lo que trato de escribir en un <i>italiano brillante</i>. Yo frecuentemente cito en los post dichos populares, frases extrañas, proverbios, etc. Y cuando traduzco, me doy cuenta de que a veces el significado y el ritmo original se pierde, y dado que soy un <i>perfeccionista</i> no consigo aceptarlo.<br />
<br />
<b>Pero aquí viene lo peor:</b> últimamente me he dado cuenta de que, inconscientemente, también me limito en la versión italiana porque pienso "<i>...este párrafo es demasiado largo, mejor corto unas cuantas frases porque si no luego tardaré demasiado en traducir... </i>" o bien "<i>... este doble significado es muy comprensible en italiano pero será muy difícil encontrar un equivalente en español, así que mejor lo quito...</i>". Cuesta de creer pero incluso mientras escribo este post de despedida me está pasando lo mismo.<br />
<br />
<b>Conclusión:</b> después de una larga reflexión, he decidido cerrar la versión en español de mi blog. Pero el blog continúa en italiano, así que si lo que realmente os interesa es sólo el código de los programas y las soluciones que propongo, bueno, eso es <b>Lenguaje C</b> y es universal: seguramente conseguiréis sacar algo interesante incluso si el post está en italiano (<i>o al menos eso espero</i>).<br />
<br />
Saludos a todos, ¡ha sido un placer!Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com2tag:blogger.com,1999:blog-1964474160379161728.post-778992235768630432018-08-11T15:02:00.000+02:002018-08-27T19:53:49.559+02:00La strlen tenía un precio cómo optimizar la strlen en C - pt.2<blockquote class="tr_bq">
<b>cnel. Mortimer:</b> <i>¿Que te pasa, muchacho? </i><br />
<b>El Manco:</b> <i>Nada, viejo. Que no me salia la cuenta. Ajora está bien. </i></blockquote>
<span class="" id="result_box" lang="es" tabindex="-1">Ok, estamos listos para la segunda parte de </span><span class="" id="result_box" lang="es" tabindex="-1"><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="https://es.wikipedia.org/wiki/Per_qualche_dollaro_in_pi%C3%B9" target="_blank"><b>La muerte tenía un precio</b></a></span></span> (<i>oops... ¿pero no era <b><a href="https://artcprogramming-es.blogspot.com/2018/07/la-strlen-tenia-un-precio-como.html" target="_blank">La strlen tenia un precio</a></b>?</i>). Como
bien decía <b>El Manco</b>, un buen programador siempre hace bien las cuentas,
por lo que en este post analizaremos los resultados de un <i>benchmark</i> que
escribí ad hoc para este propósito (<i>... sí, lo sé, también le había prometido una sorpresa... La habrá...</i>).</span> <br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1oXSM0RvNqwG8zhwQpOljI2wyQhVBMExqB7V0L1-lvSxFDuLBDlN4MrzUjO1PCgx4grZyXumG0SRxxGDbMM3eTMZNVgny_bC-8fD7hgKVYiz2rqKHvp3Sucvp2NJThEQeiVWS0C_1ARmc/s1600/perqualchedinpiu.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="285" data-original-width="655" height="173" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1oXSM0RvNqwG8zhwQpOljI2wyQhVBMExqB7V0L1-lvSxFDuLBDlN4MrzUjO1PCgx4grZyXumG0SRxxGDbMM3eTMZNVgny_bC-8fD7hgKVYiz2rqKHvp3Sucvp2NJThEQeiVWS0C_1ARmc/s400/perqualchedinpiu.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">...nada viejo... <span class="short_text" id="result_box" lang="es" tabindex="-1">la strlen es un poco lenta hoy</span>...</td></tr>
</tbody></table>
<span class="" id="result_box" lang="es" tabindex="-1">Entonces,
en el <i>benchmark</i> compararemos los resultados (de velocidad, por supuesto)
de las cuatro versiones de </span><a href="http://man7.org/linux/man-pages/man3/strlen.3.html" target="_blank"><b>strlen()</b></a><span class="" id="result_box" lang="es" tabindex="-1"> vistos en el último post, o sea: </span><span class="" id="result_box" lang="es" tabindex="-1"><a href="https://it.wikipedia.org/wiki/Il_linguaggio_C" target="_blank"><b>K&R</b></a>, <a href="https://www.freebsd.org/it/" target="_blank"><b>FreeBSD</b></a>, <a href="https://www.gnu.org/software/libc/" target="_blank"><b>glibc</b></a>, y <a href="https://www.musl-libc.org/" target="_blank"><b>musl libc</b></a> y nuestro punto de
referencia serà, evidentemente, la <b>strlen(</b><b>)</b> por defecto del
sistema, también porque uno de nuestros objetivos es averiguar qué usa
<b>Linux</b>, que es, como siempre, el nuestro sistema de referencia. Veamos el código del </span><span class="" id="result_box" lang="es" tabindex="-1"><i>benchmark</i>:</span> <br />
<pre style="background: #ffffff; color: black;"><span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdint.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">limits.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdlib.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdio.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">time.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">string.h</span><span style="color: maroon;">></span>
<span style="color: dimgrey;">// prototipos locales</span>
<span style="color: #603000;">size_t</span> strlenKaR<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>s<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">size_t</span> strlenFreeBSD<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>str<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">size_t</span> strlenGlibc<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>str<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">size_t</span> strlenMusl<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>s<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">int</span> <span style="color: #400000;">main</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> argc<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">char</span><span style="color: #808030;">*</span> argv<span style="color: #808030;">[</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">static</span> <span style="color: maroon; font-weight: bold;">char</span> s<span style="color: #808030;">[</span><span style="color: #008c00;">1000000000</span><span style="color: #808030;">]</span><span style="color: purple;">;</span>
<span style="color: #603000;">clock_t</span> t_start<span style="color: purple;">;</span>
<span style="color: #603000;">size_t</span> result<span style="color: purple;">;</span>
<span style="color: #603000;">clock_t</span> t_end<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">double</span> t_passed<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">int</span> i<span style="color: purple;">;</span>
<span style="color: dimgrey;">// escribo el string de test</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span>i <span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span> i <span style="color: #808030;"><</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span>s<span style="color: #808030;">)</span><span style="color: purple;">;</span> i<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>i <span style="color: #808030;">%</span> <span style="color: #008c00;">2</span><span style="color: #808030;">)</span>
s<span style="color: #808030;">[</span>i<span style="color: #808030;">]</span> <span style="color: #808030;">=</span> <span style="color: #0000e6;">'a'</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">else</span>
s<span style="color: #808030;">[</span>i<span style="color: #808030;">]</span> <span style="color: #808030;">=</span> <span style="color: #0000e6;">'u'</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
s<span style="color: #808030;">[</span>i <span style="color: #808030;">-</span> <span style="color: #008c00;">1</span><span style="color: #808030;">]</span> <span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// terminador</span>
<span style="color: dimgrey;">// ejecuta con strlen() de default y calcula tiempo</span>
t_start <span style="color: #808030;">=</span> <span style="color: #603000;">clock</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
result <span style="color: #808030;">=</span> <span style="color: #603000;">strlen</span><span style="color: #808030;">(</span>s<span style="color: #808030;">)</span><span style="color: purple;">;</span>
t_end <span style="color: #808030;">=</span> <span style="color: #603000;">clock</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
t_passed <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">double</span><span style="color: #808030;">)</span><span style="color: #808030;">(</span>t_end <span style="color: #808030;">-</span> t_start<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">/</span> CLOCKS_PER_SEC<span style="color: purple;">;</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">strlen(s) = %zu - </span><span style="color: #0000e6;">Tiempo transcurrido<span style="color: #808030;"></span>: </span><span style="color: #007997;">%f</span><span style="color: #0000e6;"> segundos</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> result<span style="color: #808030;">,</span> t_passed<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">ejecuta</span> con strlenKaR() </span><span style="color: dimgrey;">y calcula tiempo</span>
t_start <span style="color: #808030;">=</span> <span style="color: #603000;">clock</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
result <span style="color: #808030;">=</span> strlenKaR<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span><span style="color: purple;">;</span>
t_end <span style="color: #808030;">=</span> <span style="color: #603000;">clock</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
t_passed <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">double</span><span style="color: #808030;">)</span><span style="color: #808030;">(</span>t_end <span style="color: #808030;">-</span> t_start<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">/</span> CLOCKS_PER_SEC<span style="color: purple;">;</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">strlenKaR(s) = %zu - </span><span style="color: #0000e6;">Tiempo transcurrido<span style="color: #808030;"></span>: </span><span style="color: #007997;">%f</span><span style="color: #0000e6;"> </span><span style="color: #0000e6;">segundos</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> result<span style="color: #808030;">,</span> t_passed<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">ejecuta</span> con strlenFreeBSD() </span><span style="color: dimgrey;">y calcula tiempo</span>
t_start <span style="color: #808030;">=</span> <span style="color: #603000;">clock</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
result <span style="color: #808030;">=</span> strlenFreeBSD<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span><span style="color: purple;">;</span>
t_end <span style="color: #808030;">=</span> <span style="color: #603000;">clock</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
t_passed <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">double</span><span style="color: #808030;">)</span><span style="color: #808030;">(</span>t_end <span style="color: #808030;">-</span> t_start<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">/</span> CLOCKS_PER_SEC<span style="color: purple;">;</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">strlenFreeBSD(s) = %zu - </span><span style="color: #0000e6;">Tiempo transcurrido<span style="color: #808030;"></span>: </span><span style="color: #007997;">%f</span><span style="color: #0000e6;"> </span><span style="color: #0000e6;">segundos</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> result<span style="color: #808030;">,</span> t_passed<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">ejecuta</span> con strlenGlibc() </span><span style="color: dimgrey;">y calcula tiempo</span>
t_start <span style="color: #808030;">=</span> <span style="color: #603000;">clock</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
result <span style="color: #808030;">=</span> strlenGlibc<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span><span style="color: purple;">;</span>
t_end <span style="color: #808030;">=</span> <span style="color: #603000;">clock</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
t_passed <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">double</span><span style="color: #808030;">)</span><span style="color: #808030;">(</span>t_end <span style="color: #808030;">-</span> t_start<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">/</span> CLOCKS_PER_SEC<span style="color: purple;">;</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">strlenGlibc(s) = %zu - </span><span style="color: #0000e6;">Tiempo transcurrido<span style="color: #808030;"></span>: </span><span style="color: #007997;">%f</span><span style="color: #0000e6;"> </span><span style="color: #0000e6;">segundos</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> result<span style="color: #808030;">,</span> t_passed<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">ejecuta</span> con strlenMusl() </span><span style="color: dimgrey;">y calcula tiempo</span>
t_start <span style="color: #808030;">=</span> <span style="color: #603000;">clock</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
result <span style="color: #808030;">=</span> strlenMusl<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span><span style="color: purple;">;</span>
t_end <span style="color: #808030;">=</span> <span style="color: #603000;">clock</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
t_passed <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">double</span><span style="color: #808030;">)</span><span style="color: #808030;">(</span>t_end <span style="color: #808030;">-</span> t_start<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">/</span> CLOCKS_PER_SEC<span style="color: purple;">;</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">strlenMusl(s) = %zu - </span><span style="color: #0000e6;">Tiempo transcurrido<span style="color: #808030;"></span>: </span><span style="color: #007997;">%f</span><span style="color: #0000e6;"> </span><span style="color: #0000e6;">segundos</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> result<span style="color: #808030;">,</span> t_passed<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span></pre>
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">Como
habreis visto, hemos creado una cadena enorme (¿y si no que test seria?) y la hemos llenado con una secuencia de "<i>au</i>" (<i>...bueno, se hubiera podido llenar con
cualquier cosa, pero ese día me desperté con</i></span><i> </i><i>un "au" en la cabeza imposible de aguantar...</i>). hemos a<span class="">gregado
el terminador de string requerido y llamado </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><b> </b>
en secuencia </span></span>a las diversas <b>strlen()</b> usando un método confiable (con</span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="http://man7.org/linux/man-pages/man3/clock.3.html" target="_blank"><b> clock()</b></a>) para verificar
el tiempo de actividad real (un método que <a href="https://artcprogramming-es.blogspot.com/2017/05/el-bueno-el-feo-y-el-vla-como-usar-los.html" target="_blank"><b>ya hemos usado en un post anterior)</b></a>.</span> <span class="">Las <b>strlen()</b> son, por
supuesto, <a href="https://artcprogramming-es.blogspot.com/" target="_blank"><b>las de el último post</b></a> (los códigos los podéis encontrar ahí),
renombrados como </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><b>strlenKaR()</b>, <b>strlenFreeBSD()</b>, <b>strlenGlibc() </b>y <b>strlenMusl()</b>, para distinguirlos de la única función no local, que es
la</span> <b>strlen()</b> de librería. <span class="">Como se puede ver en la versión <b>K&R</b> he modificado el prototipo (y la definición) para uniformarlo al de la <b>strlen()</b> </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span class="">estándar</span></span>.</span></span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1">Ahora
veamos los resultados del <i>benchmark</i> con compilación normal (<i>-O0</i>, que es
el valor predeterminado y se puede omitir) y con una optimización
considerable (<i>-O2</i>, que es la que se usa normalmente en casos reales):</span> <br />
<br />
<u><b>usando gcc -O0</b></u><br />
<pre style="background: #ffffff; color: black;">aldo@mylinux<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ <span style="color: #808030;">.</span><span style="color: #808030;">/</span>strlens
strlen<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span> <span style="color: #808030;">=</span> <span style="color: #008c00;">999999999</span> <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">0.054814</span> secondi
strlenKaR<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span> <span style="color: #808030;">=</span> <span style="color: #008c00;">999999999</span> <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">2.173237</span> secondi
strlenFreeBSD<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span> <span style="color: #808030;">=</span> <span style="color: #008c00;">999999999</span> <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">0.308145</span> secondi
strlenGlibc<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span> <span style="color: #808030;">=</span> <span style="color: #008c00;">999999999</span> <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">0.252466</span> secondi
strlenMusl<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span> <span style="color: #808030;">=</span> <span style="color: #008c00;">999999999</span> <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">0.304617</span> secondi
</pre>
<br />
<u><b>usando gcc -O2</b></u><br />
<pre style="background: #ffffff; color: black;">aldo@mylinux<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ <span style="color: #808030;">.</span><span style="color: #808030;">/</span>strlens
strlen<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span> <span style="color: #808030;">=</span> <span style="color: #008c00;">999999999</span> <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">0.164509</span> secondi
strlenKaR<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span> <span style="color: #808030;">=</span> <span style="color: #008c00;">999999999</span> <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">0.395466</span> secondi
strlenFreeBSD<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span> <span style="color: #808030;">=</span> <span style="color: #008c00;">999999999</span> <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">0.091599</span> secondi
strlenGlibc<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span> <span style="color: #808030;">=</span> <span style="color: #008c00;">999999999</span> <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">0.102967</span> secondi
strlenMusl<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span> <span style="color: #808030;">=</span> <span style="color: #008c00;">999999999</span> <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">0.091931</span> secondi
</pre>
<span class="" id="result_box" lang="es" tabindex="-1"><span title="I risultati sono veramente interessanti.">Los resultados son realmente interesantes. </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="I risultati sono veramente interessanti.">La <b>strlenKaR()</b></span><span title="La strlenKaR() è, come ci aspettavamo, molto più lenta delle versioni che usano l'algoritmo descritto in ZeroInWord e, anche compilando con -O2, è più lenta delle altre compilate con -O0, anche se, comunque, il divario tra le"> es, como se esperaba, mucho más lenta que las versiones
que utilizan el algoritmo descrito en </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="La strlenKaR() è, come ci aspettavamo, molto più lenta delle versioni che usano l'algoritmo descritto in ZeroInWord e, anche compilando con -O2, è più lenta delle altre compilate con -O0, anche se, comunque, il divario tra le"><a href="http://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord" target="_blank"><b>ZeroInWord</b></a> e, incluso compilando con
<i>-O2</i>, es más lenta que las otras compilada con <i>-O0</i>, aunque, sin embargo, la
diferencia entre </span><span title="versioni ottimizzate è minore di quello tra le non ottimizzate.">las versiones optimizadas son menores que las diferencias entre las no optimizadas. </span><span title="Ripeto, siamo in un caso limite: la stringa misurata è veramente enorme, e con stringhe "normali" la differenza di prestazioni sarebbe molto più piccola, comunque è evidente che le versioni con algoritmo speciale vanno meglio e sono, giustamente, da preferire, specialmente">Repito,
estamos en un caso extremo: el string analizado es realmente enorme, y
con cadenas "normales" la diferencia de rendimiento sería mucho menor, sin
embargo, está claro que las versiones con algoritmo especial son mejores
y son justamente preferidas, especialmente </span><span title="ricordando che "le super-ottimizzazioni sono sicuramente raccomandabili per le piccole funzioni di uso frequente" (...e questa è una mia auto-citazione dal mio ultimo post...).">recordando
que <b>"</b></span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="ricordando che "le super-ottimizzazioni sono sicuramente raccomandabili per le piccole funzioni di uso frequente" (...e questa è una mia auto-citazione dal mio ultimo post...)."><b><span class="" id="result_box" lang="es" tabindex="-1">las súper-optimizaciones son ciertamente recomendables para
pequeñas funciones de uso frecuente</span>" </b>(<i>...y esta es una miá </i></span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="ricordando che "le super-ottimizzazioni sono sicuramente raccomandabili per le piccole funzioni di uso frequente" (...e questa è una mia auto-citazione dal mio ultimo post...)."><i><span class="short_text" id="result_box" lang="es" tabindex="-1"><span class="">autocitación</span></span> de
mi último post...</i>). </span><span title="Le tre funzioni strlenFreeBSD(), strlenGlibc, e strlenMusl() hanno prestazioni ottime ed analoghe, visto che usano lo stesso algoritmo, e la versione glibc prevale leggermente compilando con -O0, mentre che compilando con -O2 la migliore è la musl, con">Las
tres funciones </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="Le tre funzioni strlenFreeBSD(), strlenGlibc, e strlenMusl() hanno prestazioni ottime ed analoghe, visto che usano lo stesso algoritmo, e la versione glibc prevale leggermente compilando con -O0, mentre che compilando con -O2 la migliore è la musl, con"><b>strlenFreeBSD()</b>, <b>strlenGlibc</b> y <b>strlenMusl()</b> tienen rendimiento </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="Le tre funzioni strlenFreeBSD(), strlenGlibc, e strlenMusl() hanno prestazioni ottime ed analoghe, visto che usano lo stesso algoritmo, e la versione glibc prevale leggermente compilando con -O0, mentre che compilando con -O2 la migliore è la musl, con"><span class="" id="result_box" lang="es" tabindex="-1"><span title="Le tre funzioni strlenFreeBSD(), strlenGlibc, e strlenMusl() hanno prestazioni ottime ed analoghe, visto che usano lo stesso algoritmo, e la versione glibc prevale leggermente compilando con -O0, mentre che compilando con -O2 la migliore è la musl, con">excelentes </span></span>y similares, ya que utilizan el mismo algoritmo, y
la versión de <b>glibc</b> prevalece ligeramente compilando con <i>-O0</i>, mientra que compilando
con <i>-O2</i> la mejor es la </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="Le tre funzioni strlenFreeBSD(), strlenGlibc, e strlenMusl() hanno prestazioni ottime ed analoghe, visto che usano lo stesso algoritmo, e la versione glibc prevale leggermente compilando con -O0, mentre che compilando con -O2 la migliore è la musl, con"><b>musl</b> con </span><span title="la FreeBSD a ruota.">la <b>FreeBSD</b> muy cerca.</span></span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">Y
llegamos a la <b>parte sorprendente</b>: parece que </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class="">la <b>strlen()</b> por defecto es
claramente más rápido que todos los demás compilando con <i>-O0</i> mientras
que pierde la ventaja al compilar con<i> -O2</i>.</span> <span class="">Así que, al
menos sin optimizaciones, en mi sistema <b>Linux</b> </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class="">la<b> strlen()</b> predeterminada no es la de la <b>glibc</b> (como uno esperaría) y es algo diferente.</span> <span class="">¿Y qué es, entonces?</span> <span class=""></span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="short_text" id="result_box" lang="es" tabindex="-1"><span class="">Intentemos </span></span>comentar la línea </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i>"#include <string.h>"</i> y recompilar, y veamos qué nos dice el compilador...</span></span> <br />
<pre style="background: #ffffff; color: black;">aldo@mylinux<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ gcc <span style="color: #808030;">-</span>c <span style="color: #808030;">-</span>O0 strlens<span style="color: #008c00;">.</span>c <span style="color: #808030;">-</span>o strlens<span style="color: #008c00;">.</span>o
strlens<span style="color: #008c00;">.</span>c<span style="color: #808030;">:</span> In function <span style="color: maroon;">'</span><span style="color: #0000e6;">main</span><span style="color: maroon;">'</span><span style="color: #808030;">:</span>
strlens<span style="color: #008c00;">.</span>c<span style="color: #808030;">:</span><span style="color: #008c00;">35</span><span style="color: #808030;">:</span><span style="color: #008c00;">16</span><span style="color: #808030;">:</span> warning<span style="color: #808030;">:</span> implicit declaration of function <span style="color: maroon;">'</span><span style="color: #0000e6;">strlen</span><span style="color: maroon;">'</span>
result <span style="color: #808030;">=</span> strlen<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span><span style="color: #808030;">;</span>
<span style="color: #808030;">^</span>
strlens<span style="color: #008c00;">.</span>c<span style="color: #808030;">:</span><span style="color: #008c00;">35</span><span style="color: #808030;">:</span><span style="color: #008c00;">16</span><span style="color: #808030;">:</span> warning<span style="color: #808030;">:</span> incompatible implicit declaration of built<span style="color: #808030;">-</span>in function <span style="color: maroon;">'</span><span style="color: #0000e6;">strlen</span><span style="color: maroon;">'</span>
strlens<span style="color: #008c00;">.</span>c<span style="color: #808030;">:</span><span style="color: #008c00;">35</span><span style="color: #808030;">:</span><span style="color: #008c00;">16</span><span style="color: #808030;">:</span> note<span style="color: #808030;">:</span> include <span style="color: maroon;">'</span><span style="color: #0000e6;"><string.h></span><span style="color: maroon;">'</span> or provide a declaration of <span style="color: maroon;">'</span><span style="color: #0000e6;">strlen</span><span style="color: maroon;">'</span></pre>
<span class="" id="result_box" lang="es" tabindex="-1">ya està,
parece que </span><span class="" id="result_box" lang="es" tabindex="-1"> la <b>strlen()</b> predeterminada es una función implícita del
compilador </span><span class="" id="result_box" lang="es" tabindex="-1"><a href="https://gcc.gnu.org/" target="_blank"><b>gcc</b></a>, y por lo tanto no se usa la versión de librería y,
además, <b>"...</b></span><span class="" id="result_box" lang="es" tabindex="-1"><b><span class="" id="result_box" lang="es" tabindex="-1">En algunos casos, estas funciones incluso pueden estar <i>inlineadas</i> y/o escritas en <i>ensamblador</i></span></b><b>..."</b> (<i>...y esta es otra autocitación de mi último post...</i>). <span class="">Para confirmar todo esto, he buscado una versión en <b>ensamblador</b> de la <b>strlen()</b> y la he encontrado </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><b><a href="https://stackoverflow.com/questions/34449407/questions-about-the-performance-of-different-implementations-of-strlen" target="_blank">aquí en stackoverflow</a></b>.</span> <span class="">He modificado ligeramente </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span class="">el código </span></span>(el original no devolvia el resultado
correctamente) y he modificado el </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i>benchmark</i> para usarlo.</span> El código es el siguiente:</span> <br />
<br />
<pre style="background: #ffffff; color: black;"><span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">immintrin.h</span><span style="color: maroon;">></span>
<span style="color: dimgrey;">// una posible implementación en assembler de la strlen() implicita en gcc</span>
<span style="color: #603000;">size_t</span> strlenAsm<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span><span style="color: #808030;">*</span> src<span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
<span style="color: #603000;">size_t</span> result <span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">unsigned</span> <span style="color: maroon; font-weight: bold;">int</span> tmp1<span style="color: purple;">;</span>
__m128i zero <span style="color: #808030;">=</span> _mm_setzero_si128<span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: #808030;">,</span> vectmp<span style="color: purple;">;</span>
<span style="color: dimgrey;">// A pointer-increment may perform better than an indexed addressing mode</span>
<span style="color: #004a43;">asm</span>(
<span style="color: #0000e6;">"\n.Lloop:\n\t"</span>
<span style="color: #0000e6;">"movdqu (%[src], %[res]), %[vectmp]\n\t"</span> <span style="color: dimgrey;">// result reg is used as the loop counter</span>
<span style="color: #0000e6;">"pcmpeqb %[zerovec], %[vectmp]\n\t"</span>
<span style="color: #0000e6;">"pmovmskb %[vectmp], %[itmp]\n\t"</span>
<span style="color: #0000e6;">"add $0x10, %[res]\n\t"</span>
<span style="color: #0000e6;">"test %[itmp], %[itmp]\n\t"</span>
<span style="color: #0000e6;">"jz .Lloop\n\t"</span>
<span style="color: #0000e6;">"bsf %[itmp], %[itmp]\n\t"</span>
<span style="color: #0000e6;">"add %q[itmp], %q[res]\n\t"</span> <span style="color: dimgrey;">// q modifier to get quadword register.</span>
<span style="color: #808030;">:</span> <span style="color: #808030;">[</span>res<span style="color: #808030;">]</span> <span style="color: #0000e6;">"+r"</span>(result)<span style="color: #808030;">,</span> <span style="color: #808030;">[</span>vectmp<span style="color: #808030;">]</span> <span style="color: #0000e6;">"=&x"</span> (vectmp)<span style="color: #808030;">,</span> <span style="color: #808030;">[</span>itmp<span style="color: #808030;">]</span> <span style="color: #0000e6;">"=&r"</span> (tmp1)
<span style="color: #808030;">:</span> <span style="color: #808030;">[</span>zerovec<span style="color: #808030;">]</span> <span style="color: #0000e6;">"x"</span> (zero) <span style="color: dimgrey;">// There might already be a zeroed vector reg when inlining</span>
<span style="color: #808030;">,</span> <span style="color: #808030;">[</span>src<span style="color: #808030;">]</span> <span style="color: #0000e6;">"r"</span> (src)
<span style="color: #808030;">,</span> <span style="color: #808030;">[</span>dummy<span style="color: #808030;">]</span> <span style="color: #0000e6;">"m"</span> (<span style="color: #808030;">*</span>(const char (<span style="color: #808030;">*</span>)<span style="color: #808030;">[</span><span style="color: #808030;">]</span>)src) <span style="color: dimgrey;">// this reads the whole object, however</span>
<span style="color: dimgrey;">// long gcc thinks it is not needed</span>
<span style="color: dimgrey;">// because of the dummy input</span>
)<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> result <span style="color: #808030;">-</span> tmp1 <span style="color: #808030;">-</span> <span style="color: #008c00;">1</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span></pre>
<span class="" id="result_box" lang="es" tabindex="-1">y los
resultados se vuelven así (con <i>-O0</i>, y con <i>-O2</i> el resultado de <b>strlenAsm()</b> no cambia ya que, al estar en <b>ensamblador</b>, se compila por separado y
sin optimizaciones, como se puede ver en las siguientes líneas):</span> <br />
<br />
<u><b>usando gcc -O0</b></u><br />
<pre style="background: #ffffff; color: black;">aldo@mylinux<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ gcc <span style="color: #808030;">-</span>c strlenasm<span style="color: #008c00;">.</span>c <span style="color: #808030;">-</span>o strlenasm<span style="color: #008c00;">.</span>o
aldo@mylinux<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ gcc <span style="color: #808030;">-</span>c <span style="color: #808030;">-</span>O0 strlens<span style="color: #008c00;">.</span>c <span style="color: #808030;">-</span>o strlens<span style="color: #008c00;">.</span>o
aldo@mylinux<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ gcc strlens<span style="color: #008c00;">.</span>o strlenasm<span style="color: #008c00;">.</span>o <span style="color: #808030;">-</span>o strlens
aldo@mylinux<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ <span style="color: #808030;">.</span><span style="color: #808030;">/</span>strlens
strlen<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span> <span style="color: #808030;">=</span> <span style="color: #008c00;">999999999</span> <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">0.054087</span> secondi
strlenKaR<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span> <span style="color: #808030;">=</span> <span style="color: #008c00;">999999999</span> <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">2.163937</span> secondi
strlenFreeBSD<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span> <span style="color: #808030;">=</span> <span style="color: #008c00;">999999999</span> <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">0.306909</span><span style="color: #008c00;"></span> secondi
strlenGlibc<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span> <span style="color: #808030;">=</span> <span style="color: #008c00;">999999999</span> <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">0.251383</span> secondi
strlenMusl<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span> <span style="color: #808030;">=</span> <span style="color: #008c00;">999999999</span> <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">0.305544</span> secondi
strlenAsm<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span> <span style="color: #808030;">=</span> <span style="color: #008c00;">999999999</span> <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">0.061378</span> secondi
</pre>
<span class="" id="result_box" lang="es" tabindex="-1">Aquí, el
resultado canta: la versión implícita de <b>gcc</b> está escrita en ensamblador
(y repito: <b>no es en absoluto una opción muy extraña </b></span><span class="" id="result_box" lang="es" tabindex="-1"><b><span class="" id="result_box" lang="es" tabindex="-1">para
pequeñas funciones de librería de uso frecuente</span></b>). Sigue estando
el misterio de por qué compilando con<i> -O2 </i>(mirar los resultados
más arriba) </span><span class="" id="result_box" lang="es" tabindex="-1">la<b> strlen()</b> predeterminada parece que ya no es la versión
implícita, ya que su rendimiento empeora. Este es un misterio que me prometo resolver tarde o temprano.<br /><br /><span class="">(<i>...pero, ya que estamos en Agosto, ciertamente durante las vacaciones lo
olvidaré, generalmente mientras estoy en la playa pienso en otras cosas...</i>)</span></span><br />
<span class="" id="result_box" lang="es" tabindex="-1"><span class=""> </span></span> <br />
¡Hasta el próximo post!Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-44035799719753595312018-07-21T00:05:00.000+02:002018-07-21T00:05:14.340+02:00La strlen tenía un precio cómo optimizar la strlen en C - pt.1<blockquote class="tr_bq">
<b>El Indio:</b><i> ¿Donde vas?</i><br />
<b>El Manco:</b> <i>A dormir. Cuando tengo que disparar la noche antes me acuesto temprano.</i></blockquote>
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">Ya sabéis que </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="https://es.wikipedia.org/wiki/Sergio_Leone" target="_blank"><b>Sergio Leone</b></a> es muy querido en este blog.</span> <span class=""><a href="https://es.wikipedia.org/wiki/Per_qualche_dollaro_in_pi%C3%B9" target="_blank"><b>La muerte tenía un precio</b></a> era el único film que aún no había mencionado de la
<a href="https://es.wikipedia.org/wiki/Trilog%C3%ADa_del_d%C3%B3lar" target="_blank"><b>trilogía del dólar</b></a> (los otros dos están <a href="https://artcprogramming-es.blogspot.com/2017/01/por-un-punado-de-ifdef-como-usar-el.html" target="_blank"><b>aquí</b></a> y <a href="https://artcprogramming-es.blogspot.com/2017/05/el-bueno-el-feo-y-el-vla-como-usar-los.html" target="_blank"><b>aquí</b></a>), así que ha llegado su
momento.</span> <span class="">Hoy vamos a hablar sobre la <a href="http://man7.org/linux/man-pages/man3/strlen.3.html" target="_blank"><b>strlen()</b></a> y
os recomiendo: <i>ir a dormir temprano solo por escribir un buen código a
la mañana siguiente, y no por hacer lo que le sale mejor a a El Manco...</i></span></span> <br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg9vyxiTLL6kHG-7SNXuwoHVMulx6S6lilhbQtLjhpNqKH2NLLvgRfXgU1yzEH-a_3-UXC7LZ-cCF40KMkDbCoC7lDDYXT8KuCoQ1D1_WghLJFVD4WvopLGTvsSI-qpX-q-pF7FdhL-rAY/s1600/perqualchedollaroinpiu.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="460" data-original-width="920" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg9vyxiTLL6kHG-7SNXuwoHVMulx6S6lilhbQtLjhpNqKH2NLLvgRfXgU1yzEH-a_3-UXC7LZ-cCF40KMkDbCoC7lDDYXT8KuCoQ1D1_WghLJFVD4WvopLGTvsSI-qpX-q-pF7FdhL-rAY/s400/perqualchedollaroinpiu.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">...he venido <span class="short_text" id="result_box" lang="es" tabindex="-1">aquí para escribir un buen </span>Software... </td></tr>
</tbody></table>
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">Vamos
al grano: </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class="">la<b> strlen()</b> es una función muy simple, que realiza una tarea
simple (devuelve la longitud de una cadena) y, de hecho, se puede
escribir, literalmente, en cuatro líneas de código.</span> Veamos la versión que se muestra en el mágico </span><span class="" id="result_box" lang="es" tabindex="-1"><a href="https://es.wikipedia.org/wiki/El_lenguaje_de_programaci%C3%B3n_C" target="_blank"><b>K&R</b></a>:</span> <br />
<pre style="background: #ffffff; color: black;"><span style="color: maroon; font-weight: bold;">int</span> <span style="color: #603000;">strlen</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>s<span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>p <span style="color: #808030;">=</span> s<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">while</span> <span style="color: #808030;">(</span><span style="color: #808030;">*</span>p <span style="color: #808030;">!</span><span style="color: #808030;">=</span> <span style="color: #0000e6;">'\0'</span><span style="color: #808030;">)</span>
p<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> p <span style="color: #808030;">-</span> s<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
</pre>
<span class="" id="result_box" lang="es" tabindex="-1">Esta
versión, muy simple y perfecta (<i>¡y solo faltaria, con esos autores legendarios!</i>) funciona muy bien y es satisfactoria en el 99% de los
casos, incluso más si se compila con optimización. Pero... si tuviéramos que hacer un uso intensivo de la <b>strlen()</b>, tal vez para medir enormes cadenas, ¿sería suficiente? <span class="">Bueno,
según los desarrolladores de las diversas </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><b><a href="https://es.wikipedia.org/wiki/Biblioteca_est%C3%A1ndar_de_C" target="_blank">libc</a></b> disponibles, </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><b>strlen() </b></span>debe
implementarse con algoritmos un poco más complicados que el visto
anteriormente, asegurando (efectivamente) un rendimiento muy alto pero pagando el precio de usar un código complicado (y difícil de</span> <span class="">leer) para una operación realmente simple.</span> Se p<span class="">odría
hacer una disquisición muy filosófica sobre el tema y, si vais a volver a
leer un <a href="https://artcprogramming-es.blogspot.com/2012/12/erase-una-vez-la-optimizacion.html" target="_blank"><b>mio antiguo post</b></a>, os quedará claro lo que pienso al respecto.</span></span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1">Pero...
vale, debemos distinguir entre el código de librería (como <b>libc</b>) y
el código de aplicación: se supone que el primero es utilizado por
muchos usuarios para varios proyectos en muchas plataformas, por lo que
se supone, con razón, que tine que estar optimizado al máximo y, por lo tanto, la legibilidad y facilidad de mantenimiento se convierten en problemas secundarios. <span class="">El
código de aplicación, sin embargo, debe estar bien escrito y debe ser
eficiente (<i>¡solo faltaría!</i>), pero cuidando también la legibilidad y
la facilidad de mantenimiento, ya que una aplicación, a menudo, es tocada y
retocada en el tiempo por varios programadores (</span><span class=""><i>...así que mirar lo que está escrito bajo el título del blog aquí arriba: es
la declaración de la filosofía del blog, y cae perfecta en este caso...</i>).</span></span><br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1"><span>Resumiendo, las súper-optimizaciones son ciertamente recomendables para
pequeñas funciones de uso frecuente (como las de la familia </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span><a href="http://man7.org/linux/man-pages/man3/string.3.html" target="_blank"><b>str</b></a>, por
ejemplo), y aún más cuando pueden manejar grandes volúmenes de datos
(como cadenas y/o bloques de memoria muy</span> <span>grandes)</span><span>.</span> <span>En algunos casos, estas funciones incluso pueden estar <i>inlineadas</i> y/o escritas en <i>ensamblador</i>.</span> <span>En cambio, para el "<i>código normal</i>" es mejor seguir las indicaciones de uno<b> mucho mejor que yo</b>, que dijo hace unos años:</span></span> <br />
<blockquote class="tr_bq">
"<i>The
real problem is that programmers have spent far too much time worrying
about efficiency in the wrong places and at the wrong times; premature
optimization is the root of all evil (or at least most of it) in
programming</i>" (<a href="https://es.wikipedia.org/wiki/Donald_Knuth" target="_blank"><b>Donald Knuth</b></a>, "Computer Programming as an Art", 1974).</blockquote>
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">Ok, volvamos al argumento del post: </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class="">la <b>strlen() </b>que usamos todos los días está, casi siempre, súper-optimizada.</span> <span class="">Veamos, por lo tanto, algunos ejemplos reales:</span></span> <br />
<br />
<u><b>implementación en FreeBSD</b></u><br />
<pre style="background: #ffffff; color: black;"><span style="color: dimgrey;">/* Magic numbers for the algorithm */</span>
<span style="color: maroon; font-weight: bold;">static</span> <span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">unsigned</span> <span style="color: maroon; font-weight: bold;">long</span> mask01 <span style="color: #808030;">=</span> <span style="color: green;">0x0101010101010101</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">static</span> <span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">unsigned</span> <span style="color: maroon; font-weight: bold;">long</span> mask80 <span style="color: #808030;">=</span> <span style="color: green;">0x8080808080808080</span><span style="color: purple;">;</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">define</span><span style="color: #004a43;"> LONGPTR_MASK </span><span style="color: #808030;">(</span><span style="color: #004a43;">sizeof</span><span style="color: #808030;">(</span><span style="color: #004a43;">long</span><span style="color: #808030;">)</span><span style="color: #004a43;"> </span><span style="color: #808030;">-</span><span style="color: #004a43;"> 1</span><span style="color: #808030;">)</span>
<span style="color: dimgrey;">// Helper macro to return string length if we caught the zero byte.</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">define</span><span style="color: #004a43;"> testbyte</span><span style="color: #808030;">(</span><span style="color: #004a43;">x</span><span style="color: #808030;">)</span><span style="color: #004a43;"> \</span>
<span style="color: #004a43;"> do </span><span style="color: #808030;">{</span><span style="color: #004a43;"> \</span>
<span style="color: #004a43;"> </span><span style="color: #004a43;">if</span><span style="color: #004a43;"> </span><span style="color: #808030;">(</span><span style="color: #004a43;">p</span><span style="color: #808030;">[</span><span style="color: #004a43;">x</span><span style="color: #808030;">]</span><span style="color: #004a43;"> </span><span style="color: #808030;">=</span><span style="color: #808030;">=</span><span style="color: #004a43;"> </span><span style="color: #0000e6;">'\0'</span><span style="color: #808030;">)</span><span style="color: #004a43;"> \</span>
<span style="color: #004a43;"> return </span><span style="color: #808030;">(</span><span style="color: #004a43;">p </span><span style="color: #808030;">-</span><span style="color: #004a43;"> str </span><span style="color: #808030;">+</span><span style="color: #004a43;"> x</span><span style="color: #808030;">)</span><span style="color: #808030;">;</span><span style="color: #004a43;"> \</span>
<span style="color: #004a43;"> </span><span style="color: #808030;">}</span><span style="color: #004a43;"> while </span><span style="color: #808030;">(</span><span style="color: #004a43;">0</span><span style="color: #808030;">)</span>
<span style="color: #603000;">size_t</span> <span style="color: #603000;">strlen</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>str<span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>p<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">unsigned</span> <span style="color: maroon; font-weight: bold;">long</span> <span style="color: #808030;">*</span>lp<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">long</span> va<span style="color: #808030;">,</span> vb<span style="color: purple;">;</span>
<span style="color: dimgrey;">/* Before trying the hard (unaligned byte-by-byte access) way</span>
<span style="color: dimgrey;"> * to figure out whether there is a nul character, try to see</span>
<span style="color: dimgrey;"> * if there is a nul character is within this accessible word</span>
<span style="color: dimgrey;"> * first.</span>
<span style="color: dimgrey;"> * p and (p & ~LONGPTR_MASK) must be equally accessible since</span>
<span style="color: dimgrey;"> * they always fall in the same memory page, as long as page</span>
<span style="color: dimgrey;"> * boundaries is integral multiple of word size.</span>
<span style="color: dimgrey;"> */</span>
lp <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">unsigned</span> <span style="color: maroon; font-weight: bold;">long</span> <span style="color: #808030;">*</span><span style="color: #808030;">)</span><span style="color: #808030;">(</span><span style="color: #808030;">(</span>uintptr_t<span style="color: #808030;">)</span>str <span style="color: #808030;">&</span> <span style="color: #808030;">~</span>LONGPTR_MASK<span style="color: #808030;">)</span><span style="color: purple;">;</span>
va <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: #808030;">*</span>lp <span style="color: #808030;">-</span> mask01<span style="color: #808030;">)</span><span style="color: purple;">;</span>
vb <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span><span style="color: #808030;">~</span><span style="color: #808030;">*</span>lp<span style="color: #808030;">)</span> <span style="color: #808030;">&</span> mask80<span style="color: #808030;">)</span><span style="color: purple;">;</span>
lp<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>va <span style="color: #808030;">&</span> vb<span style="color: #808030;">)</span>
<span style="color: dimgrey;">/* Check if we have \0 in the first part */</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span>p <span style="color: #808030;">=</span> str<span style="color: purple;">;</span> p <span style="color: #808030;"><</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span><span style="color: #808030;">)</span>lp<span style="color: purple;">;</span> p<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">*</span>p <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #0000e6;">'\0'</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #808030;">(</span>p <span style="color: #808030;">-</span> str<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">/* Scan the rest of the string using word sized operation */</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span><span style="color: purple;">;</span> <span style="color: purple;">;</span> lp<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
va <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: #808030;">*</span>lp <span style="color: #808030;">-</span> mask01<span style="color: #808030;">)</span><span style="color: purple;">;</span>
vb <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span><span style="color: #808030;">~</span><span style="color: #808030;">*</span>lp<span style="color: #808030;">)</span> <span style="color: #808030;">&</span> mask80<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>va <span style="color: #808030;">&</span> vb<span style="color: #808030;">)</span> <span style="color: purple;">{</span>
p <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span><span style="color: #808030;">)</span><span style="color: #808030;">(</span>lp<span style="color: #808030;">)</span><span style="color: purple;">;</span>
testbyte<span style="color: #808030;">(</span><span style="color: #008c00;">0</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
testbyte<span style="color: #808030;">(</span><span style="color: #008c00;">1</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
testbyte<span style="color: #808030;">(</span><span style="color: #008c00;">2</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
testbyte<span style="color: #808030;">(</span><span style="color: #008c00;">3</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
testbyte<span style="color: #808030;">(</span><span style="color: #008c00;">4</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
testbyte<span style="color: #808030;">(</span><span style="color: #008c00;">5</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
testbyte<span style="color: #808030;">(</span><span style="color: #008c00;">6</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
testbyte<span style="color: #808030;">(</span><span style="color: #008c00;">7</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">/* NOTREACHED */</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #808030;">(</span><span style="color: #008c00;">0</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span></pre>
<u><b><u><b>implementación en</b></u> glibc</b></u><br />
<pre style="background: #ffffff; color: black;"><span style="color: dimgrey;">/* Return the length of the null-terminated string STR. Scan for</span>
<span style="color: dimgrey;"> the null terminator quickly by testing four bytes at a time. */</span>
<span style="color: #603000;">size_t</span> strlen<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>str<span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>char_ptr<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">unsigned</span> <span style="color: maroon; font-weight: bold;">long</span> <span style="color: maroon; font-weight: bold;">int</span> <span style="color: #808030;">*</span>longword_ptr<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">unsigned</span> <span style="color: maroon; font-weight: bold;">long</span> <span style="color: maroon; font-weight: bold;">int</span> longword<span style="color: #808030;">,</span> himagic<span style="color: #808030;">,</span> lomagic<span style="color: purple;">;</span>
<span style="color: dimgrey;">/* Handle the first few characters by reading one character at a time.</span>
<span style="color: dimgrey;"> Do this until CHAR_PTR is aligned on a longword boundary. */</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span>char_ptr <span style="color: #808030;">=</span> str<span style="color: purple;">;</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">unsigned</span> <span style="color: maroon; font-weight: bold;">long</span> <span style="color: maroon; font-weight: bold;">int</span><span style="color: #808030;">)</span>char_ptr <span style="color: #808030;">&</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span>longword<span style="color: #808030;">)</span> <span style="color: #808030;">-</span> <span style="color: #008c00;">1</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">!</span><span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span> <span style="color: #808030;">+</span><span style="color: #808030;">+</span>char_ptr<span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">*</span>char_ptr <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #0000e6;">'\0'</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> char_ptr <span style="color: #808030;">-</span> str<span style="color: purple;">;</span>
<span style="color: dimgrey;">/* All these elucidatory comments refer to 4-byte longwords,</span>
<span style="color: dimgrey;"> but the theory applies equally well to 8-byte longwords. */</span>
longword_ptr <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">unsigned</span> <span style="color: maroon; font-weight: bold;">long</span> <span style="color: maroon; font-weight: bold;">int</span> <span style="color: #808030;">*</span><span style="color: #808030;">)</span> char_ptr<span style="color: purple;">;</span>
<span style="color: dimgrey;">/* Bits 31, 24, 16, and 8 of this number are zero. Call these bits</span>
<span style="color: dimgrey;"> the "holes." Note that there is a hole just to the left of</span>
<span style="color: dimgrey;"> each byte, with an extra at the end:</span>
<span style="color: dimgrey;"> bits: 01111110 11111110 11111110 11111111</span>
<span style="color: dimgrey;"> bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD</span>
<span style="color: dimgrey;"> The 1-bits make sure that carries propagate to the next 0-bit.</span>
<span style="color: dimgrey;"> The 0-bits provide holes for carries to fall into. */</span>
himagic <span style="color: #808030;">=</span> <span style="color: green;">0x80808080</span><span style="color: #006600;">L</span><span style="color: purple;">;</span>
lomagic <span style="color: #808030;">=</span> <span style="color: green;">0x01010101</span><span style="color: #006600;">L</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">sizeof</span> <span style="color: #808030;">(</span>longword<span style="color: #808030;">)</span> <span style="color: #808030;">></span> <span style="color: #008c00;">4</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">/* 64-bit version of the magic. */</span>
<span style="color: dimgrey;">/* Do the shift in two steps to avoid a warning if long has 32 bits. */</span>
himagic <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>himagic <span style="color: #808030;"><</span><span style="color: #808030;"><</span> <span style="color: #008c00;">16</span><span style="color: #808030;">)</span> <span style="color: #808030;"><</span><span style="color: #808030;"><</span> <span style="color: #008c00;">16</span><span style="color: #808030;">)</span> <span style="color: #808030;">|</span> himagic<span style="color: purple;">;</span>
lomagic <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>lomagic <span style="color: #808030;"><</span><span style="color: #808030;"><</span> <span style="color: #008c00;">16</span><span style="color: #808030;">)</span> <span style="color: #808030;"><</span><span style="color: #808030;"><</span> <span style="color: #008c00;">16</span><span style="color: #808030;">)</span> <span style="color: #808030;">|</span> lomagic<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">sizeof</span> <span style="color: #808030;">(</span>longword<span style="color: #808030;">)</span> <span style="color: #808030;">></span> <span style="color: #008c00;">8</span><span style="color: #808030;">)</span>
<span style="color: #603000;">abort</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">/* Instead of the traditional loop which tests each character,</span>
<span style="color: dimgrey;"> we will test a longword at a time. The tricky part is testing</span>
<span style="color: dimgrey;"> if *any of the four* bytes in the longword in question are zero. */</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span><span style="color: purple;">;</span><span style="color: purple;">;</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
longword <span style="color: #808030;">=</span> <span style="color: #808030;">*</span>longword_ptr<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span><span style="color: #808030;">(</span>longword <span style="color: #808030;">-</span> lomagic<span style="color: #808030;">)</span> <span style="color: #808030;">&</span> <span style="color: #808030;">~</span>longword <span style="color: #808030;">&</span> himagic<span style="color: #808030;">)</span> <span style="color: #808030;">!</span><span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">/* Which of the bytes was the zero? If none of them were, it was</span>
<span style="color: dimgrey;"> a misfire; continue the search. */</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>cp <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span><span style="color: #808030;">)</span> <span style="color: #808030;">(</span>longword_ptr <span style="color: #808030;">-</span> <span style="color: #008c00;">1</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>cp<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> cp <span style="color: #808030;">-</span> str<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>cp<span style="color: #808030;">[</span><span style="color: #008c00;">1</span><span style="color: #808030;">]</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> cp <span style="color: #808030;">-</span> str <span style="color: #808030;">+</span> <span style="color: #008c00;">1</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>cp<span style="color: #808030;">[</span><span style="color: #008c00;">2</span><span style="color: #808030;">]</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> cp <span style="color: #808030;">-</span> str <span style="color: #808030;">+</span> <span style="color: #008c00;">2</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>cp<span style="color: #808030;">[</span><span style="color: #008c00;">3</span><span style="color: #808030;">]</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> cp <span style="color: #808030;">-</span> str <span style="color: #808030;">+</span> <span style="color: #008c00;">3</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">sizeof</span> <span style="color: #808030;">(</span>longword<span style="color: #808030;">)</span> <span style="color: #808030;">></span> <span style="color: #008c00;">4</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>cp<span style="color: #808030;">[</span><span style="color: #008c00;">4</span><span style="color: #808030;">]</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> cp <span style="color: #808030;">-</span> str <span style="color: #808030;">+</span> <span style="color: #008c00;">4</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>cp<span style="color: #808030;">[</span><span style="color: #008c00;">5</span><span style="color: #808030;">]</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> cp <span style="color: #808030;">-</span> str <span style="color: #808030;">+</span> <span style="color: #008c00;">5</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>cp<span style="color: #808030;">[</span><span style="color: #008c00;">6</span><span style="color: #808030;">]</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> cp <span style="color: #808030;">-</span> str <span style="color: #808030;">+</span> <span style="color: #008c00;">6</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>cp<span style="color: #808030;">[</span><span style="color: #008c00;">7</span><span style="color: #808030;">]</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> cp <span style="color: #808030;">-</span> str <span style="color: #808030;">+</span> <span style="color: #008c00;">7</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: purple;">}</span>
<span style="color: purple;">}</span>
<span style="color: purple;">}</span></pre>
<u><b><u><b>implementación en</b></u> musl libc</b></u><br />
<pre style="background: #ffffff; color: black;"><span style="color: #004a43;">#</span><span style="color: #004a43;">define</span><span style="color: #004a43;"> ALIGN </span><span style="color: #808030;">(</span><span style="color: #004a43;">sizeof</span><span style="color: #808030;">(</span><span style="color: #004a43;">size_t</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">define</span><span style="color: #004a43;"> ONES </span><span style="color: #808030;">(</span><span style="color: #808030;">(</span><span style="color: #004a43;">size_t</span><span style="color: #808030;">)</span><span style="color: #808030;">-</span><span style="color: #004a43;">1</span><span style="color: #808030;">/</span><span style="color: #004a43;">UCHAR_MAX</span><span style="color: #808030;">)</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">define</span><span style="color: #004a43;"> HIGHS </span><span style="color: #808030;">(</span><span style="color: #004a43;">ONES </span><span style="color: #808030;">*</span><span style="color: #004a43;"> </span><span style="color: #808030;">(</span><span style="color: #004a43;">UCHAR_MAX</span><span style="color: #808030;">/</span><span style="color: #004a43;">2</span><span style="color: #808030;">+</span><span style="color: #004a43;">1</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">define</span><span style="color: #004a43;"> HASZERO</span><span style="color: #808030;">(</span><span style="color: #004a43;">x</span><span style="color: #808030;">)</span><span style="color: #004a43;"> </span><span style="color: #808030;">(</span><span style="color: #808030;">(</span><span style="color: #004a43;">x</span><span style="color: #808030;">)</span><span style="color: #808030;">-</span><span style="color: #004a43;">ONES </span><span style="color: #808030;">&</span><span style="color: #004a43;"> </span><span style="color: #808030;">~</span><span style="color: #808030;">(</span><span style="color: #004a43;">x</span><span style="color: #808030;">)</span><span style="color: #004a43;"> </span><span style="color: #808030;">&</span><span style="color: #004a43;"> HIGHS</span><span style="color: #808030;">)</span>
<span style="color: #603000;">size_t</span> strlen<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>s<span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>a <span style="color: #808030;">=</span> s<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: #603000;">size_t</span> <span style="color: #808030;">*</span>w<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span><span style="color: purple;">;</span> <span style="color: #808030;">(</span>uintptr_t<span style="color: #808030;">)</span>s <span style="color: #808030;">%</span> ALIGN<span style="color: purple;">;</span> s<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">!</span><span style="color: #808030;">*</span>s<span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> s<span style="color: #808030;">-</span>a<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span>w <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">void</span> <span style="color: #808030;">*</span><span style="color: #808030;">)</span>s<span style="color: purple;">;</span> <span style="color: #808030;">!</span>HASZERO<span style="color: #808030;">(</span><span style="color: #808030;">*</span>w<span style="color: #808030;">)</span><span style="color: purple;">;</span> w<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: #808030;">)</span>
<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span>s <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">void</span> <span style="color: #808030;">*</span><span style="color: #808030;">)</span>w<span style="color: purple;">;</span> <span style="color: #808030;">*</span>s<span style="color: purple;">;</span> s<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: #808030;">)</span>
<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> s<span style="color: #808030;">-</span>a<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
</pre>
<span class="" id="result_box" lang="es" tabindex="-1"><span>Como habreis
visto, son bastante más complicados que la versión </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span><b>K&R</b>, porque
en lugar de realizar una simple </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span><i>test en 0</i> un byte a la vez, realizan
el test en 4 bytes a la vez (usando un algoritmo como el descrito en </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span><a href="http://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord" target="_blank"><b>ZeroInWord</b></a>): obviamente este sistema multiplica</span> <span>la velocidad de búsqueda del final de la cadena, lo que permite un considerable ahorro de tiempo para cadenas muy grandes.</span> <span>Los
códigos <b>FreeBSD</b> y <b>glibc</b> están bien comentados (he dejado casi todos los
comentarios originales) y, a primera vista, notamos que la versión de
<b>FreeBSD</b> es un poco más compacta que la versión <b>glibc</b>.</span> <span>La
verdadera sorpresa es la versión de <b>musl libc</b> que es realmente
brillante: ¡no es mucho más larga que la implementación de </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span><b>K&R</b>!</span> <span>Acordaros de <b>musl</b> (que ya he mencionado <a href="https://en.wikipedia.org/wiki/Musl" target="_blank"><b>aquí</b></a> hablando sobre los C11 Threads): es verdaderamente
una alternativa notable a <b>glibc</b> para sistemas Linux embedded, ya que
la implementacion compacta y la eficiencia son sus señas de identidad.</span></span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1"><span>Para hoy
puede ser suficiente, en la segunda parte del post os propondré un
programa que he escrito para realizar un <b>benchmark</b> de algunas implementaciones de</span></span><span class="" id="result_box" lang="es" tabindex="-1"><span> <b>strlen()</b>: habrá la que usa por defecto el sistema (<i>Linux,
en mi caso, y uno se esperaría</i></span><i> </i><span><i>el uso de la versión <b>glibc</b>, pero... ¡hay una sorpresa!</i>) y luego, por supuesto, estaran las cuatro de este
post (</span></span><span class="" id="result_box" lang="es" tabindex="-1"><span><b>K&R</b>, <b>FreeBSD</b>, <b>glibc</b> y <b>musl</b>).</span> <span class="">Os anticipo
que los resultados del test son sorprendentes y, para explicarlos, os propondré una sexta versión (aún más sorprendente) de </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span><b>strlen()</b></span></span> que
debería resolver la cuestión de qué es lo que realmente usa Linux cuando
llamamos la </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span><b>strlen()</b></span></span>.</span> <i><span class="">Y, aunque sé que ya estais ansiosos de saber cuál es la sexta versión, </span></i></span><span class="" id="result_box" lang="es"><i>os recomiendo (</i></span><span class="" id="result_box" lang="es"><i><span class="" id="result_box" lang="es"><i>como siempre) </i></span>de no aguantar la respiración en la espera...</i></span> <br />
<br />
¡Hasta el próximo post!<i> </i>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-14975787388321880832018-06-23T19:16:00.001+02:002018-06-24T18:59:22.928+02:00Toma el makefile y corre cómo escribir un makefile universal - pt.2<h2 class="date-header">
</h2>
<blockquote class="tr_bq">
<span class="" id="result_box" lang="es" tabindex="-1"><span class=""><b>Louise</b>: <i>Virgil, vamos a tener un niño.</i><br /><b>Virgil</b>: <i>No exageres...</i><br /><b>Louise</b>: <i>¡No! No. Vamos a ser padres: he ido a ver el medico. Es mi regalo de Navidad.</i><br /><b>Virgil</b>: <i>¡Hubiese preferido una corbata! </i></span></span></blockquote>
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">Para </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="https://es.wikipedia.org/wiki/Take_the_Money_and_Run" target="_blank"><b>Virgil Starkwell</b></a> era suficiente una corbata... para nosotros es suficiente un buen </span></span><b><a href="https://en.wikipedia.org/wiki/Makefile" target="_blank">makefile</a></b><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><b> universal</b></span> ¿Os acordáis de <a href="https://artcprogramming-es.blogspot.com/2017/02/" target="_blank"><b>ese antiguo post</b></a> en el que he propuesto uno? <i><span class="">¿No os acordáis?</span> </i></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i><span class="" id="result_box" lang="es">Volver </span><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es">inmediatamente </span>a leerlo</span> y luego regresar aquí.</i></span></span><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIVN4I7L-YgD83C_FIby63tpBB7MBYF3ZwZ-UYpqqzL3H8CHDwEmyAp_uWmstvAyWmC2Jt9vmYHNvTi0xvpwBtbc9-X8LwNkaCwRDISOhisSyDugUyELxeth2CtPLJh5vRXDBObYq9uR-M/s1600/virgilandlouise.JPG" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="219" data-original-width="397" height="220" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIVN4I7L-YgD83C_FIby63tpBB7MBYF3ZwZ-UYpqqzL3H8CHDwEmyAp_uWmstvAyWmC2Jt9vmYHNvTi0xvpwBtbc9-X8LwNkaCwRDISOhisSyDugUyELxeth2CtPLJh5vRXDBObYq9uR-M/s400/virgilandlouise.JPG" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">....vamos a tener un makefile...</td></tr>
</tbody></table>
<span class="" id="result_box" lang="es" tabindex="-1">Aquí
estamos (<i>y pequeña premisa: algunos puntos de este post están
parcialmente copiados de mi antiguo post... puedo copiar a mi mismo,
¿no?</i>). <span class="">Si habéis releído el post ya sabéis que será
una post rápido,</span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""> y no exactamente sobre el C: he pensado que era hora
de escribir y ofrecer una versión extendida y mejorada del antiguo </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><b>makefile universal</b>: en comparación con el original esto usa más
las variables</span> <span class="">, por lo que el código es más limpio y
legible (<i>¡el estilo, en primer lugar!</i>) y agrega una característica muy
interesante: la creación de una </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="https://en.wikipedia.org/wiki/Library_(computing)#Shared_libraries" target="_blank"><b>shared library</b></a> (<i>una .so, para
los amigos</i>).</span> <span class="">Veamos esquemáticamente cuáles son los
pasos a seguir (en un <b>Linux</b> de la familia Debian) para crear y usar una </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><b>shared-lib</b>:</span></span><br />
<pre style="background: #ffffff; color: black;"><span style="color: #008c00;">1</span><span style="color: #808030;">.</span> crear una directory para compartir la shared<span style="color: #808030;">-</span>lib<span style="color: #808030;">,</span> por ejemplo<span style="color: #808030;">:</span>
<b><span style="color: #808030;">/</span>usr<span style="color: #808030;">/</span>share<span style="color: #808030;">/</span>pluto<span style="color: #808030;">/</span>lib</b>
<span style="color: #008c00;">2</span><span style="color: #808030;">.</span> modificar <span style="color: #808030;">(</span>como veremos luego<span style="color: #808030;">)</span> el makefile para generar la librería
<span style="color: #808030;">(</span>que llamaremos <span style="color: maroon;">"</span><span style="color: #0000e6;">libmyutils.so</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span> y copiarla en <span style="color: maroon;">"</span><span style="color: #0000e6;">/usr/share/pluto/lib</span><span style="color: maroon;">"</span><span style="color: #808030;">.</span>
<span style="color: #008c00;">3</span><span style="color: #808030;">.</span> añadir en <span style="color: maroon;">"</span><span style="color: #0000e6;">/etc/ld.so.conf.d</span><span style="color: maroon;">"</span> un nuevo file <span style="color: maroon;">"</span><span style="color: #0000e6;">libmyutils.conf</span><span style="color: maroon;">"</span> que
contiene las siguientes dos lineas <span style="color: #808030;">(</span>la primera es solo un comentario<span style="color: #808030;">)</span><span style="color: #808030;">:</span>
<b># libmyutils<span style="color: #008c00;">.</span>so default configuration
<span style="color: #808030;">/</span>usr<span style="color: #808030;">/</span>share<span style="color: #808030;">/</span>pluto<span style="color: #808030;">/</span>lib</b>
<span style="color: #008c00;">4</span><span style="color: #808030;">.</span> hacer disponible la nueva shared<span style="color: #808030;">-</span>lib ejecutando<span style="color: #808030;">:</span>
<b>sudo ldconfig</b>
</pre>
<span class="" id="result_box" lang="es" tabindex="-1">Seguimos: supongamos de usar el mismo proyecto de la otra vez (se llamaba <i>pluto</i>). Nuestros
archivos están organizados de manera canónica, esta vez en cuatro
directory (la otra vez había tres): <i>pluto</i>, <i>lib</i>, <i>libmyutils</i> e <i>include</i>. La nueva directory es <i>libmyutils</i>, y contiene las fuentes de la <b>shared-lib</b> que queremos crear. En
la directory <i>pluto</i> encontramos el <b>main()</b> y el <b>makefile</b>, en la
directory <i>lib</i> encontramos las otras fuentes de la aplicación <i>pluto</i> y,
finalmente, en la directory <i>include</i> encontramos los </span><span class="" id="result_box" lang="es" tabindex="-1"><i>header-files</i> comunes a<b> main()</b>, <i>lib</i> y <i>libmyutils</i>. Veamos el nuevo </span><b>makefile</b>:<br />
<pre style="background: #ffffff; color: black;"><span style="color: dimgrey;"># variables</span>
<span style="color: #797997;">CC </span><span style="color: #808030;">=</span><span style="color: #007997;"> gcc</span>
<span style="color: #797997;">CPPFLAGS </span><span style="color: #808030;">=</span><span style="color: #007997;"> -I</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">/</span><span style="color: #007997;">include -g -Wall -Wshadow -pedantic -std=c11</span>
<span style="color: #797997;">CPPFLAGS_UTI </span><span style="color: #808030;">=</span><span style="color: #007997;"> -fpic -I</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">/</span><span style="color: #007997;">include -g -Wall -Wshadow -pedantic -std=c11</span>
<span style="color: #797997;">LDFLAGS </span><span style="color: #808030;">=</span><span style="color: #007997;"> -lmyutils -std=c11</span>
<span style="color: #797997;">LDFLAGS_UTI </span><span style="color: #808030;">=</span><span style="color: #007997;"> -shared </span><span style="color: #007997;"><span style="color: #007997;">-fpic </span>-lcurl -std=c11</span>
<span style="color: dimgrey;"># fuentes, objetos y dependencias</span>
<span style="color: #797997;">SRCS </span><span style="color: #808030;">=</span><span style="color: #007997;"> </span><span style="color: purple;">$(</span><span style="color: #bb7977; font-weight: bold;">wildcard</span><span style="color: #007997;"> </span><span style="color: maroon; font-weight: bold;">*</span><span style="color: #808030;">.</span><span style="color: #007997;">c</span><span style="color: purple;">)</span>
<span style="color: #797997;">SRCS_LIB </span><span style="color: #808030;">=</span><span style="color: #007997;"> </span><span style="color: purple;">$(</span><span style="color: #bb7977; font-weight: bold;">wildcard</span><span style="color: #007997;"> </span><span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">/</span><span style="color: #007997;">lib</span><span style="color: #808030;">/</span><span style="color: maroon; font-weight: bold;">*</span><span style="color: #808030;">.</span><span style="color: #007997;">c</span><span style="color: purple;">)</span>
<span style="color: #797997;">SRCS_LIB_UTI </span><span style="color: #808030;">=</span><span style="color: #007997;"> </span><span style="color: purple;">$(</span><span style="color: #bb7977; font-weight: bold;">wildcard</span><span style="color: #007997;"> </span><span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">/</span><span style="color: #007997;">libmyutils</span><span style="color: #808030;">/</span><span style="color: maroon; font-weight: bold;">*</span><span style="color: #808030;">.</span><span style="color: #007997;">c</span><span style="color: purple;">)</span>
<span style="color: #797997;">OBJS </span><span style="color: #808030;">=</span><span style="color: #007997;"> </span><span style="color: purple;">$(</span><span style="color: #797997;">SRCS</span><span style="color: maroon; font-weight: bold;">:</span><span style="color: #808030;">.</span><span style="color: #007997;">c</span><span style="color: maroon; font-weight: bold;">=</span><span style="color: #808030;">.</span><span style="color: #007997;">o</span><span style="color: purple;">)</span>
<span style="color: #797997;">OBJS_LIB </span><span style="color: #808030;">=</span><span style="color: #007997;"> </span><span style="color: purple;">$(</span><span style="color: #797997;">SRCS_LIB</span><span style="color: maroon; font-weight: bold;">:</span><span style="color: #808030;">.</span><span style="color: #007997;">c</span><span style="color: maroon; font-weight: bold;">=</span><span style="color: #808030;">.</span><span style="color: #007997;">o</span><span style="color: purple;">)</span>
<span style="color: #797997;">OBJS_LIB_UTI </span><span style="color: #808030;">=</span><span style="color: #007997;"> </span><span style="color: purple;">$(</span><span style="color: #797997;">SRCS_LIB_UTI</span><span style="color: maroon; font-weight: bold;">:</span><span style="color: #808030;">.</span><span style="color: #007997;">c</span><span style="color: maroon; font-weight: bold;">=</span><span style="color: #808030;">.</span><span style="color: #007997;">ou</span><span style="color: purple;">)</span>
<span style="color: #797997;">DEPS </span><span style="color: #808030;">=</span><span style="color: #007997;"> </span><span style="color: purple;">$(</span><span style="color: #797997;">SRCS</span><span style="color: maroon; font-weight: bold;">:</span><span style="color: #808030;">.</span><span style="color: #007997;">c</span><span style="color: maroon; font-weight: bold;">=</span><span style="color: #808030;">.</span><span style="color: #007997;">d</span><span style="color: purple;">)</span>
<span style="color: #797997;">DEPS_LIB </span><span style="color: #808030;">=</span><span style="color: #007997;"> </span><span style="color: purple;">$(</span><span style="color: #797997;">SRCS_LIB</span><span style="color: maroon; font-weight: bold;">:</span><span style="color: #808030;">.</span><span style="color: #007997;">c</span><span style="color: maroon; font-weight: bold;">=</span><span style="color: #808030;">.</span><span style="color: #007997;">d</span><span style="color: purple;">)</span>
<span style="color: #797997;">DEPS_LIB_UTI </span><span style="color: #808030;">=</span><span style="color: #007997;"> </span><span style="color: purple;">$(</span><span style="color: #797997;">SRCS_LIB_UTI</span><span style="color: maroon; font-weight: bold;">:</span><span style="color: #808030;">.</span><span style="color: #007997;">c</span><span style="color: maroon; font-weight: bold;">=</span><span style="color: #808030;">.</span><span style="color: #007997;">d</span><span style="color: purple;">)</span>
<span style="color: dimgrey;"># tudos los target</span>
<span style="color: #e34adc;">all</span><span style="color: maroon; font-weight: bold;">:</span><span style="color: #0000e6;"> libmyutils pluto</span>
<span style="color: dimgrey;"># creación del target file ejecutable</span>
<span style="color: #e34adc;">pluto</span><span style="color: maroon; font-weight: bold;">:</span><span style="color: #0000e6;"> </span><span style="color: purple;">$(</span><span style="color: #797997;">OBJS</span><span style="color: purple;">)</span><span style="color: #0000e6;"> </span><span style="color: purple;">$(</span><span style="color: #797997;">OBJS_LIB</span><span style="color: purple;">)</span>
<span style="color: purple;">$(</span><span style="color: #797997;">CC</span><span style="color: purple;">)</span> <span style="color: #797997;">$^</span> -o <span style="color: #797997;">$@</span> <span style="color: purple;">$(</span><span style="color: #797997;">LDFLAGS</span><span style="color: purple;">)</span> -L<span style="color: #808030;">/</span>usr<span style="color: #808030;">/</span>share<span style="color: #808030;">/</span>pluto<span style="color: #808030;">/</span>lib
<span style="color: dimgrey;"># </span><span style="color: dimgrey;"><span style="color: dimgrey;">creación de la </span></span><span style="color: dimgrey;"><span style="color: dimgrey;">shared-lib</span> libmyutils.so</span>
<span style="color: #e34adc;">libmyutils</span><span style="color: maroon; font-weight: bold;">:</span><span style="color: #0000e6;"> </span><span style="color: purple;">$(</span><span style="color: #797997;">OBJS_LIB_UTI</span><span style="color: purple;">)</span>
<span style="color: purple;">$(</span><span style="color: #797997;">CC</span><span style="color: purple;">)</span> <span style="color: #797997;">$^</span> -o libmyutils<span style="color: #808030;">.</span>so <span style="color: purple;">$(</span><span style="color: #797997;">LDFLAGS_UTI</span><span style="color: purple;">)</span>
mv libmyutils<span style="color: #808030;">.</span>so <span style="color: #808030;">/</span>usr<span style="color: #808030;">/</span>share<span style="color: #808030;">/</span>pluto<span style="color: #808030;">/</span>lib
<span style="color: dimgrey;"># </span><span style="color: dimgrey;"><span style="color: dimgrey;"><span style="color: dimgrey;">creación</span></span> de los object file (para la aplicación y la shared-lib)</span>
<span style="color: #004a43;">%</span><span style="color: #808030;">.</span><span style="color: #e34adc;">o</span><span style="color: maroon; font-weight: bold;">:</span><span style="color: #0000e6;"> </span><span style="color: #004a43;">%</span><span style="color: #808030;">.</span><span style="color: #0000e6;">c</span>
<span style="color: purple;">$(</span><span style="color: #797997;">CC</span><span style="color: purple;">)</span> -MMD -MP <span style="color: purple;">$(</span><span style="color: #797997;">CPPFLAGS</span><span style="color: purple;">)</span> -c <span style="color: #797997;">$<</span> -o <span style="color: #797997;">$@</span>
<span style="color: #004a43;">%</span><span style="color: #808030;">.</span><span style="color: #e34adc;">ou</span><span style="color: maroon; font-weight: bold;">:</span><span style="color: #0000e6;"> </span><span style="color: #004a43;">%</span><span style="color: #808030;">.</span><span style="color: #0000e6;">c</span>
<span style="color: purple;">$(</span><span style="color: #797997;">CC</span><span style="color: purple;">)</span> -MMD -MP <span style="color: purple;">$(</span><span style="color: #797997;">CPPFLAGS_UTI</span><span style="color: purple;">)</span> -c <span style="color: #797997;">$<</span> -o <span style="color: #797997;">$@</span>
<span style="color: dimgrey;"># directivas phony</span>
<span style="background: #a8a800; color: black;">.PHONY</span><span style="color: maroon; font-weight: bold;">:</span><span style="color: #0000e6;"> clean</span>
<span style="color: dimgrey;"># limpieza proyecto ($(RM) es de default "rm -f")</span>
<span style="color: #e34adc;">clean</span><span style="color: maroon; font-weight: bold;">:</span>
<span style="color: purple;">$(</span><span style="color: #797997;">RM</span><span style="color: purple;">)</span> <span style="color: purple;">$(</span><span style="color: #797997;">OBJS</span><span style="color: purple;">)</span> <span style="color: purple;">$(</span><span style="color: #797997;">OBJS_LIB</span><span style="color: purple;">)</span> <span style="color: purple;">$(</span><span style="color: #797997;">OBJS_LIB_UTI</span><span style="color: purple;">)</span> <span style="color: purple;">$(</span><span style="color: #797997;">DEPS</span><span style="color: purple;">)</span> <span style="color: purple;">$(</span><span style="color: #797997;">DEPS_LIB</span><span style="color: purple;">)</span> <span style="color: purple;">$(</span><span style="color: #797997;">DEPS_LIB_UTI</span><span style="color: purple;">)</span>
<span style="color: dimgrey;"># creación dependencias</span>
<span style="color: #004a43;">-include</span><span style="color: #0000e6;"> </span><span style="color: purple;">$(</span><span style="color: #797997;">DEPS</span><span style="color: purple;">)</span><span style="color: #0000e6;"> </span><span style="color: purple;">$(</span><span style="color: #797997;">DEPS_LIB</span><span style="color: purple;">)</span><span style="color: #0000e6;"> </span><span style="color: purple;">$(</span><span style="color: #797997;">DEPS_LIB_UTI</span><span style="color: purple;">)</span></pre>
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">Como se puede ver, el nuevo </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><b>makefile</b> presentado es un pariente cercano del
anterior y sigue siendo realmente simple y universal: </span></span>hace todo lo necesario, incluyendo la generación de los files de dependencia de los <i>header</i>, y podemos utilizarlo para cualquier proyecto, sin importar el número de file (las directory <i>lib</i> e <i>include</i> pueden estar vacías o contener cientos de file). Podemos agregar y eliminar <i>fuentes</i> y <i>header</i> y re-compilar sin cambiar una sola línea del <i>makefile</i>, porque el se adapta automáticamente a lo que encuentra en las directory del proyecto: ¿Qué queremos más? <br />
<span class="" id="result_box" lang="es" tabindex="-1"><span class=""> </span></span> <br />
<span class="" id="result_box" lang="es" tabindex="-1">Añado algunos pequeños detalles sobre los bloques (comentados) que componen el </span> <b>makefile</b> <b>universale</b>: <br />
<br />
<b><i># variables</i></b><br />
<span class="" id="result_box" lang="es" tabindex="-1"><span title="Qua si mettono le variabili che vengono usate nel resto del makefile.">Aqui </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="Qua si mettono le variabili che vengono usate nel resto del makefile."><span class="" id="result_box" lang="es" tabindex="-1">están </span> las variables que se utilizan en el resto del </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="Qua si mettono le variabili che vengono usate nel resto del makefile."><b>makefile</b>. </span><span title="Notare che in CPPFLAGS e LDFLAGS sono contenute tutte le opzioni di compilazione e link necessarie nelle fasi di creazione della applicazione, mentre che in CPPFLAGS_UTI e LDFLAGS_UTI sono contenute quelle relative alla shared-lib).">Cabe
destacar que en CPPFLAGS y LDFLAGS están contenidas todas las opciones de
compilación y <i>link</i> necesarias en las etapas de creación de
la aplicación, mientras que en CPPFLAGS_UTI y LDFLAGS_UTI </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="Notare che in CPPFLAGS e LDFLAGS sono contenute tutte le opzioni di compilazione e link necessarie nelle fasi di creazione della applicazione, mentre che in CPPFLAGS_UTI e LDFLAGS_UTI sono contenute quelle relative alla shared-lib)."><span class="" id="result_box" lang="es" tabindex="-1"><span title="Notare che in CPPFLAGS e LDFLAGS sono contenute tutte le opzioni di compilazione e link necessarie nelle fasi di creazione della applicazione, mentre che in CPPFLAGS_UTI e LDFLAGS_UTI sono contenute quelle relative alla shared-lib).">están contenidas</span></span> las
relativas a la </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="Notare che in CPPFLAGS e LDFLAGS sono contenute tutte le opzioni di compilazione e link necessarie nelle fasi di creazione della applicazione, mentre che in CPPFLAGS_UTI e LDFLAGS_UTI sono contenute quelle relative alla shared-lib)."><b>shared-lib</b>). </span><span title="Non mi dilungherò sul significato delle singole opzioni: magari potrebbero essere l'argomento di un prossimo post... comunque le opzioni che ho usato sono decisamente "universali".">No
daré más detalles sobre el significado de las opciones individuales:
<i>tal vez podrían ser el tema de un próximo post...</i> sin embargo,
las opciones que he utilizado son definitivamente "<i>universales</i>". </span><span title="Se si usa qualche libreria esterna si può aggiungere qui: nell'esempio (ricordarsi: è solo un esempio!) ho scritto che pluto usa libmyutils (con il comando -lmyutils in LDFLAGS) e ho scritto che libmyutils usa la libreria open-source libcurl">Si
se utiliza alguna librería externa se puede añadir aquí: en el ejemplo
(<i>recordar: ¡es sólo un ejemplo!</i>) he escrito que <i>pluto</i> utiliza <i>libmyutils</i>
(con el comando<i> -lmyutils</i> en LDFLAGS) y he escrito que <i>libmyutils</i> utiliza la </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="Se si usa qualche libreria esterna si può aggiungere qui: nell'esempio (ricordarsi: è solo un esempio!) ho scritto che pluto usa libmyutils (con il comando -lmyutils in LDFLAGS) e ho scritto che libmyutils usa la libreria open-source libcurl"><span class="" id="result_box" lang="es" tabindex="-1"><span title="Se si usa qualche libreria esterna si può aggiungere qui: nell'esempio (ricordarsi: è solo un esempio!) ho scritto che pluto usa libmyutils (con il comando -lmyutils in LDFLAGS) e ho scritto che libmyutils usa la libreria open-source libcurl">librería</span></span> </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="Se si usa qualche libreria esterna si può aggiungere qui: nell'esempio (ricordarsi: è solo un esempio!) ho scritto che pluto usa libmyutils (con il comando -lmyutils in LDFLAGS) e ho scritto che libmyutils usa la libreria open-source libcurl">pen-source<i> libcurl</i> </span><span title="(con il comando -lcurl in LDFLAGS_UTI).">(con el comando <i>-lcurl </i>en LDFLAGS_UTI). </span><span title="Notare che per compilare e linkare la shared-lib si usano due direttive fondamentali: -fpic (in compilazione e link) e -shared (solo in link).">Tener
en cuenta que para compilar y <i>linkar</i> la <b>shared-lib</b> se utilizan dos
directivas fundamentales:</span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="Notare che per compilare e linkare la shared-lib si usano due direttive fondamentali: -fpic (in compilazione e link) e -shared (solo in link)."><a href="https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html" target="_blank"><b>-fpic</b></a> (en compilación y <i>link</i>) y <b>-<a href="https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html" target="_blank">shared</a></b> (solo en <i>link</i>). </span><span title="E ribadisco: -fpic si usa sia in compilazione che in link: questo è un dettaglio che molte delle guide che si trovano in rete omettono e può essere una possibile causa di strani malfunzionamenti di una shared-lib.">Y
repito: <b>-fpic</b> se usa <b>tanto en compilación como en <i>link</i></b>: este es un
detalle de que muchas de las guías que están en la red omiten y pueden
ser una posible causa de anomalías extrañas de una </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="E ribadisco: -fpic si usa sia in compilazione che in link: questo è un dettaglio che molte delle guide che si trovano in rete omettono e può essere una possibile causa di strani malfunzionamenti di una shared-lib."><b>shared-lib</b>.</span></span> <br />
<br />
<i><b># fuentes, objetos y dependencias</b></i><br />
<span class="" id="result_box" lang="es" tabindex="-1">Aquí están
las directivas que usan internamente el programa <b>make</b> para decidir cómo y
dónde buscar fuentes, objetos y dependencias.</span> <br />
<br />
<i><b># todos los target</b></i><br />
<span class="" id="result_box" lang="es" tabindex="-1"><span class="" id="result_box" lang="es" tabindex="-1">Aquí están</span>
los objetivos de creación: en nuestro caso, la </span><span class="" id="result_box" lang="es" tabindex="-1">libreria <i>libmyutils</i> y la aplicación <i>pluto</i>: el comando <b>make</b> "en solo" ejecuta ambos objetivos (la
palabra clave es "<i>all</i>"), pero se puede omitir esto ejecutando, por
ejemplo, "<i>make libmyutils</i>" que crea solo la </span><span class="" id="result_box" lang="es" tabindex="-1"><b>shared-lib</b>.</span> <br />
<br />
<i><b># creación del target file ejecutable</b></i><br />
<span class="" id="result_box" lang="es" tabindex="-1">Aquí pone el comando para </span><span class="" id="result_box" lang="es" tabindex="-1"><i>linkar</i> los file objetos creados y producir el file ejecutable final. Tener
en cuenta que con la directiva </span><span class="" id="result_box" lang="es" tabindex="-1"><i>-L/usr/share/pluto/lib</i> indicamos
al </span><span class="" id="result_box" lang="es" tabindex="-1"><i>linker</i> donde se encuentra la </span><span class="" id="result_box" lang="es" tabindex="-1">libreria <i>libmyutils.so</i>. También
tener en cuenta que esta directiva solo sirve en el nivel del
<i>linker</i>, mientras que, en el nivel de ejecución de las aplicaciones
que usan la nuestra </span><span class="" id="result_box" lang="es" tabindex="-1"><b>shared-lib</b>, necesitamos los pasos de la
lista descrita al comienzo del post (en particular, los pasos <b>3</b>
y <b>4</b>).</span> <br />
<br />
<i><b># </b></i><i><b><i><b>creación de la shared-lib</b></i> libmyutils.so</b></i><br />
<span class="" id="result_box" lang="es" tabindex="-1">Aquí están las instrucciones para </span><span class="" id="result_box" lang="es" tabindex="-1">la creación de la <b>shared-lib</b> <i>libmyutils.so</i> y par moverla (con el comando "<i>mv</i>") en la directory de destino.</span> <br />
<br />
<i><b># </b><b><b><b>creación</b></b> de los object file (para la aplicacion y la </b><b><b><b>shared-lib</b></b>)</b></i><br />
Aquí se pone el comando para compilar cada fuente y crear el file objeto
correspondiente, activando (a través de las variables definidas
al inicio) todas las opciones del compilador que
necesitamos.<br />
<br />
<b><i># directivas phony</i></b><br />
Aquí se ponen las directivas <i>phony</i> (es un poco largo de explicar: <a href="https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html" target="_blank"><b>mirar en el link, que está muy claro</b></a>).<i> </i><br />
<br />
<b><i># limpieza proyecto ($(RM) es de default "rm -f")</i></b><br />
Aquí se pone el comando de borrado de los objetos para forzar, eventualmente, una siguiente completa re-compilación.<i> </i><br />
<br />
<b><i># creacion dependencias</i></b><br />
<span class="" id="result_box" lang="es"><span class="">Aquí</span> se
pone el comando para generar los file de dependencia que nos permiten
volver a compilar sólo lo que necesita cuando se modifica un <i>header</i> file.<span class=""> </span></span>
<br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1">Creo que
por hoy es suficiente... probad en hacer un pequeño proyecto de prueba (por
ejemplo, usar funciones medio vacías que solo escriben "<i>Hola, soy la
función xyz</i>") y probar el nuevo </span><span class="" id="result_box" lang="es" tabindex="-1"><b>makefile universale</b>: ¡vais a descubrir que
es realmente fácil de usar!</span> <br />
<br />
¡Hasta el próximo post!Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-35672034965962145612018-05-19T01:37:00.001+02:002018-06-22T22:21:09.103+02:00El gran lighttpd cómo escribir un módulo lighttpd en C - pt.4<blockquote class="tr_bq">
<b>Maude</b>: <i>¿Y en que te dedicas en tu tiempo libre?</i><br />
<b>El Nota</b>: <i>Bueno, ya sabes: jugar a los bolos, conducir por ahí, un viaje ácido de vez en cuando...</i></blockquote>
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">Dando por echo
que para escribir un buen Software no hay necesidad de imitar los
hábitos del mítico </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><b>El Nota</b> de </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="https://es.wikipedia.org/wiki/El_gran_Lebowski" target="_blank"><b>El gran Lebowski</b></a>, volvemos al tema
<a href="https://es.wikipedia.org/wiki/Lighttpd" target="_blank"><b>lighttpd</b></a>, porque en el tercer post de la <b>serie</b> </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class="">(<a href="https://artcprogramming-es.blogspot.com.es/2016/09/el-gran-lighttpd-como-escribir-un.html" target="_blank"><b>aquí</b></a>, <a href="https://artcprogramming-es.blogspot.com.es/2016/10/el-gran-lighttpd-como-escribir-un.html" target="_blank"><b>aquí</b></a> y también <a href="https://artcprogramming-es.blogspot.com.es/2016/11/el-gran-lighttpd-como-escribir-un.html" target="_blank"><b>aquí</b></a>) prometí expandir la discusión.</span> <span class="">¡Bien, ha llegado el momento, <i>las promesas se mantienen</i>!</span></span><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEire2Uf-2INIDOYDq4pIyK6XO6IjfQQ_4Ry7cDfkVg5TSQ9z3ICwICecu6k1S1Ydz6AKCYY9qKTaqA4tjURCE8aSWWvuLVRpeLHLAFqWo-EsXjmra9qRThp3UroUGOQ5t3Dp_6hRJ4iFQaU/s1600/lebowski_talk.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="190" data-original-width="350" height="216" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEire2Uf-2INIDOYDq4pIyK6XO6IjfQQ_4Ry7cDfkVg5TSQ9z3ICwICecu6k1S1Ydz6AKCYY9qKTaqA4tjURCE8aSWWvuLVRpeLHLAFqWo-EsXjmra9qRThp3UroUGOQ5t3Dp_6hRJ4iFQaU/s400/lebowski_talk.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">...Bueno, ya sabes: jugar a los bolos, conducir por ahí, un módulo lighttpd de vez en cuando...</td></tr>
</tbody></table>
Pues bien, en "<a href="https://artcprogramming-es.blogspot.com.es/2016/11/el-gran-lighttpd-como-escribir-un.html" target="_blank"><b>El gran lighttpd - pt.3</b></a>" (<i>que acabáis de releer, supongo...</i>) escribimos un agradable módulo elemental para <b>lighttpd</b> (era un clásico "<b><a href="https://es.wikipedia.org/wiki/Hola_mundo" target="_blank">Helloworld</a></b>": escribía solo una presentación en el browser), pero era un buen comienzo para entrar en el mundo de los módulos para <a href="https://es.wikipedia.org/wiki/Servidor_web" target="_blank"><b>Web Servers</b></a>. Vale, ahora vamos a reanudar ese módulo y les vamos a añadir una función que extrae los datos <a href="https://es.wikipedia.org/wiki/Protocolo_de_transferencia_de_hipertexto#POST" target="_blank"><b>POST</b></a> incluidos en una eventual petición <a href="https://es.wikipedia.org/wiki/Protocolo_de_transferencia_de_hipertexto" target="_blank"><b>HTTP</b></a> de tipo <b>POST</b>
que llega al módulo. ¿Por qué he elegido añadir esa funcionalidad?
Bueno, obviamente porque es bastante normal que un módulo cuente con
varios tipos de peticiones (<b>GET</b>, <b>POST</b>, etc.), y luego, en particular, recuerdo que la primera vez que añadí esta funcionalidad en un módulo <b>Apache</b> que escribí, me di cuenta de que no estaban disponibles muchas explicaciones sobre este tema (y esto a pesar de la enorme cantidad de
documentación de <b>Apache</b> disponible respeto a la de <b>lighttpd</b>)... <i><span class="" id="result_box" lang="es" tabindex="-1">os dejo imaginar, por lo tanto, cuánta documentación y cuántos ejemplos
encontré para hacer lo mismo con <b>lighttpd</b>: prácticamente nada.</span></i> <br />
<br />
Pues bien, sin molestarse en repetir todo el código y la forma de generarlo, sólo reescribiremos la función <b>mod_helloworld_uri_handler()</b> y añadiremos una nueva función <b>getPost()</b>. Añado<span class="" id="result_box" lang="es" tabindex="-1">
que, para integrar el código que he escrito, es necesario seguir la guía de
instalación indicada en "</span><span class="" id="result_box" lang="es" tabindex="-1"><b><a href="https://artcprogramming-es.blogspot.com.es/2016/09/el-gran-lighttpd-como-escribir-un.html" target="_blank"><b>El gran lighttpd - pt.1</b></a></b>" y repetir los 10
pasos indicados en "</span><span class="" id="result_box" lang="es" tabindex="-1"><a href="https://artcprogramming-es.blogspot.com.es/2016/10/el-gran-lighttpd-como-escribir-un.html" target="_blank"><b>El gran lighttpd - pt.</b></a><a href="https://artcprogramming.blogspot.com.es/2016/10/il-grande-lighttpd-come-scrivere-un.html" target="_blank"><b>2</b></a>", usando esta vez la versión <b>1.4.45</b> de <b>lighttpd</b> (<i>ojo: usando una versión anterior, el código que estoy a punto de mostrar no funciona</i>). Hecho? entonces</span> ¡Vamos con el código <br />
<pre style="background: #ffffff; color: black;">URIHANDLER_FUNC<span style="color: #808030;">(</span>
mod_helloworld_uri_handler<span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
plugin_data <span style="color: #808030;">*</span>p <span style="color: #808030;">=</span> p_d<span style="color: purple;">;</span>
UNUSED<span style="color: #808030;">(</span>srv<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// test modo (return si error)</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>con<span style="color: #808030;">-</span><span style="color: #808030;">></span>mode <span style="color: #808030;">!</span><span style="color: #808030;">=</span> DIRECT<span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> HANDLER_GO_ON<span style="color: purple;">;</span>
<span style="color: dimgrey;">// test uri path (return si error)</span>
<span style="color: #603000;">size_t</span> s_len <span style="color: #808030;">=</span> buffer_string_length<span style="color: #808030;">(</span>con<span style="color: #808030;">-</span><span style="color: #808030;">></span>uri<span style="color: #808030;">.</span>path<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>s_len <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> HANDLER_GO_ON<span style="color: purple;">;</span>
mod_helloworld_patch_connection<span style="color: #808030;">(</span>srv<span style="color: #808030;">,</span> con<span style="color: #808030;">,</span> p<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// test handler (return si error)</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>con<span style="color: #808030;">-</span><span style="color: #808030;">></span>uri<span style="color: #808030;">.</span>path<span style="color: #808030;">-</span><span style="color: #808030;">></span>ptr <span style="color: #808030;">&</span><span style="color: #808030;">&</span> <span style="color: #603000;">strstr</span><span style="color: #808030;">(</span>con<span style="color: #808030;">-</span><span style="color: #808030;">></span>uri<span style="color: #808030;">.</span>path<span style="color: #808030;">-</span><span style="color: #808030;">></span>ptr<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">helloworld</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// prepara el buffer para la respuesta</span>
buffer <span style="color: #808030;">*</span>buf <span style="color: #808030;">=</span> buffer_init<span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// test método http</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #603000;">strstr</span><span style="color: #808030;">(</span>con<span style="color: #808030;">-</span><span style="color: #808030;">></span>request<span style="color: #808030;">.</span>request<span style="color: #808030;">-</span><span style="color: #808030;">></span>ptr<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">GET</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// método GET: escribe respuesta</span>
buffer_append_string<span style="color: #808030;">(</span>buf<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;"><big>Hello, world!</big></span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">else</span> <span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #603000;">strstr</span><span style="color: #808030;">(</span>con<span style="color: #808030;">-</span><span style="color: #808030;">></span>request<span style="color: #808030;">.</span>request<span style="color: #808030;">-</span><span style="color: #808030;">></span>ptr<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">POST</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// método POST: controla la presencia de post-data</span>
<span style="color: #603000;">size_t</span> len <span style="color: #808030;">=</span> con<span style="color: #808030;">-</span><span style="color: #808030;">></span>request<span style="color: #808030;">.</span>content_length<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>len<span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// post-data presentes; los lee para escribir la respuesta</span>
<span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>data <span style="color: #808030;">=</span> <span style="color: #603000;">malloc</span><span style="color: #808030;">(</span>len <span style="color: #808030;">+</span> <span style="color: #008c00;">1</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>readPost<span style="color: #808030;">(</span>con<span style="color: #808030;">,</span> data<span style="color: #808030;">,</span> len<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// escribe la respuesta</span>
buffer_append_string<span style="color: #808030;">(</span>buf<span style="color: #808030;">,</span> data<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// libera la memoria</span>
<span style="color: #603000;">free</span><span style="color: #808030;">(</span>data<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">else</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// error: mensaje POST sin post-data</span>
buffer_append_string<span style="color: #808030;">(</span>buf<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">mod_helloworld: error: POST sin post-data</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">else</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// error: </span><span style="color: dimgrey;"><span style="color: dimgrey;">mensaje</span> con método no tratado</span>
buffer_append_string<span style="color: #808030;">(</span>buf<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">mod_helloworld error: método no tratado</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">escribe</span> el buffer y lo libera</span>
chunkqueue_append_buffer<span style="color: #808030;">(</span>con<span style="color: #808030;">-</span><span style="color: #808030;">></span>write_queue<span style="color: #808030;">,</span> buf<span style="color: #808030;">)</span><span style="color: purple;">;</span>
buffer_free<span style="color: #808030;">(</span>buf<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// envía el header</span>
response_header_overwrite<span style="color: #808030;">(</span>
srv<span style="color: #808030;">,</span> con<span style="color: #808030;">,</span> CONST_STR_LEN<span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">Content-Type</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: #808030;">,</span> CONST_STR_LEN<span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">text/html</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
con<span style="color: #808030;">-</span><span style="color: #808030;">></span>http_status <span style="color: #808030;">=</span> <span style="color: #008c00;">200</span><span style="color: purple;">;</span>
con<span style="color: #808030;">-</span><span style="color: #808030;">></span>file_finished <span style="color: #808030;">=</span> <span style="color: #008c00;">1</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// handling terminado</span>
<span style="color: maroon; font-weight: bold;">return</span> HANDLER_FINISHED<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// handler no encontrado</span>
<span style="color: maroon; font-weight: bold;">return</span> HANDLER_GO_ON<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">static</span> bool readPost<span style="color: #808030;">(</span>
connection <span style="color: #808030;">*</span>con<span style="color: #808030;">,</span> <span style="color: dimgrey;">// datos conexión</span>
<span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>data<span style="color: #808030;">,</span> <span style="color: dimgrey;">// buffer destinación para post-data</span>
<span style="color: #603000;">size_t</span> len<span style="color: #808030;">)</span> <span style="color: dimgrey;">// longitud data (sin terminador)</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// set valor de return de default</span>
<span style="color: maroon; font-weight: bold;">int</span> retval <span style="color: #808030;">=</span> false<span style="color: purple;">;</span>
<span style="color: dimgrey;">// lee post-data (en el stream de chunks)</span>
<span style="color: #603000;">size_t</span> rpos <span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span>
chunkqueue <span style="color: #808030;">*</span>cq <span style="color: #808030;">=</span> con<span style="color: #808030;">-</span><span style="color: #808030;">></span>read_queue<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span>chunk <span style="color: #808030;">*</span>mychunk <span style="color: #808030;">=</span> cq<span style="color: #808030;">-</span><span style="color: #808030;">></span>first<span style="color: purple;">;</span> mychunk<span style="color: purple;">;</span> mychunk <span style="color: #808030;">=</span> cq<span style="color: #808030;">-</span><span style="color: #808030;">></span>first<span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// calcula el size del buffer correspondiente al chunk de post-data corriente</span>
<span style="color: #603000;">size_t</span> n_tocopy <span style="color: #808030;">=</span> buffer_string_length<span style="color: #808030;">(</span>mychunk<span style="color: #808030;">-</span><span style="color: #808030;">></span>mem<span style="color: #808030;">)</span> <span style="color: #808030;">-</span> mychunk<span style="color: #808030;">-</span><span style="color: #808030;">></span>offset<span style="color: purple;">;</span>
<span style="color: dimgrey;">// test si hay datos a copiar</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>n_tocopy <span style="color: #808030;"><</span><span style="color: #808030;">=</span> <span style="color: #808030;">(</span>len <span style="color: #808030;">-</span> rpos<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// copia un chunk y set de la posición de copia del proximo chunk</span>
<span style="color: #603000;">memcpy</span><span style="color: #808030;">(</span>data <span style="color: #808030;">+</span> rpos<span style="color: #808030;">,</span> mychunk<span style="color: #808030;">-</span><span style="color: #808030;">></span>mem<span style="color: #808030;">-</span><span style="color: #808030;">></span>ptr <span style="color: #808030;">+</span> mychunk<span style="color: #808030;">-</span><span style="color: #808030;">></span>offset<span style="color: #808030;">,</span> n_tocopy<span style="color: #808030;">)</span><span style="color: purple;">;</span>
rpos <span style="color: #808030;">+</span><span style="color: #808030;">=</span> n_tocopy<span style="color: purple;">;</span>
<span style="color: dimgrey;">// ha sido leído (almeno) un chunk de post-data: set retval=true</span>
retval <span style="color: #808030;">=</span> true<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">else</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// buffer overflow: salida forzada del loop</span>
<span style="color: maroon; font-weight: bold;">break</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// indica como leído el chunk de post-data corriente</span>
chunkqueue_mark_written<span style="color: #808030;">(</span>cq<span style="color: #808030;">,</span> chunkqueue_length<span style="color: #808030;">(</span>cq<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// añade el terminadore de string (el buffer es largo len+1)</span>
data<span style="color: #808030;">[</span>len<span style="color: #808030;">]</span> <span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// sale con retval</span>
<span style="color: maroon; font-weight: bold;">return</span> retval<span style="color: purple;">;</span>
<span style="color: purple;">}</span></pre>
Ok, como se nota es <b><a href="http://artcprogramming-es.blogspot.com.es/2012/08/no-comment.html" target="_blank">ampliamente comentado</a> </b> y así se <i>auto-explica</i>, por lo cual no voy a detenerme sobre las instrucciónes y/o grupos de instrucciones (<i>¡leer los comentarios! ¡Están ahí para eso!</i>), pero voy a añadir, solamente, algunos detalles estructurales.<br />
<br />
la función original <b>mod_helloworld_uri_handler</b><b>()</b> sigue siendo una especie de función "<b>Helloworld</b>", que ahora distingue los tipos de petición y, en el caso <b>POST</b>, llama a la función<b> readPost()</b> y escribe como respuesta "<i>Hello, world!</i>" mas los datos. En el caso de peticiones <b>GET</b> se limita a escribir "<i>Hello, world!</i>" y, para otros tipos de peticiones o con peticiones <b>POST</b> sin datos, sale con error. <br />
<br />
La función <b>readPost()</b> es simple, pero no es inmediata: <span class="" id="result_box" lang="es" tabindex="-1">dada la
casi total falta de ejemplos disponibles en la red, he tenido que hacer, prácticamente, </span><span class="" id="result_box" lang="es" tabindex="-1"> <a href="https://es.wikipedia.org/wiki/Ingenier%C3%ADa_inversa" target="_blank"><b>reverse engineering</b></a> sobre los (excelentes) módulos
integrados en la distribución (<i>¡tener el código fuente original
disponible siempre es una gran cosa!</i>) de manera de deducir cómo hacer lo que tenia como objetivo. El
resultado final (<i>modestia aparte</i>) es bueno, tanto en apariencia
(estilo) como en rendimiento (hice algunas pruebas y funciona bien).</span><br />
<br />
¡Ah, el test, se me olvidaba! Testear con peticiones <b>POST</b> no es simple cómo hacerlo con las <b>GET</b>, adonde es suficiente (como dicho en el post anterior) escribir la <a href="https://es.wikipedia.org/wiki/Identificador_de_recursos_uniforme" target="_blank"><b>URI</b></a>
del modulo en la barra de direcciones de <b>Firefox</b> o <b>Chrome</b> <span class="" id="result_box" lang="es">(<i>o cualquier otro navegador menos IE, por favor...</i>)</span> y esperar la
respuesta. Para probar el nuestro nuevo módulo es mejor usar un buen
plugin (como <a href="https://addons.mozilla.org/it/firefox/addon/poster/" target="_blank"><b>Poster</b></a>, por ejemplo) que nos facilitará mucho la tarea. <br />
<br />
Vale, de momento, creo que podemos parar por un tiempo el tema módulos de <b>lighttpd</b>. Juro que en el próximo post voy a hablar de otra cosa, no me gustaría que piensen que el <b>C</b> se utiliza sólo para <b>Web Servers</b>...<br />
<br />
¡Hasta el próximo post!
Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-28118006687399469222018-04-15T19:32:00.001+02:002018-04-21T17:48:34.799+02:00Dawn of the CPU cómo testear el uso de CPU y Memoria en C - pt.2Aquí e<span class="" id="result_box" lang="es" tabindex="-1"><span class="">stamos, después <a href="https://artcprogramming-es.blogspot.com.es/2018/03/dawn-of-cpu-como-testear-el-uso-de-cpu.html" target="_blank"><b>de la presentación en el último post</b></a>, ha llegado
el momento de ver si nuestro sistema de test continuo de <b>CPU</b> y <b>Memoria</b>
funciona.</span> <span class="">Y si funciona bien. Os acordais </span><span class="">el tema, ¿no?</span> <span class="">Zombies de supermercado, <a href="https://es.wikipedia.org/wiki/Dawn_of_the_Dead" target="_blank"><b>Dawn of the Dead</b></a>, </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><b>Dawn of the CPU</b>.</span></span>.. sí, eso.</span></span><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKgBO6FyWH90etInfpT0zTPjCTpy1lMsDmYXbZyxMDFPvJi9nSRSvuWLGSDUdmIAM_INn7aRXYViwbORMxlP99VKZZG_pTcc4KQ8JqUCvixI0WcWUa0_CnZGaTruZjvJLdECYnaTfWmnLX/s1600/dawnofthedead.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="531" data-original-width="1000" height="210" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKgBO6FyWH90etInfpT0zTPjCTpy1lMsDmYXbZyxMDFPvJi9nSRSvuWLGSDUdmIAM_INn7aRXYViwbORMxlP99VKZZG_pTcc4KQ8JqUCvixI0WcWUa0_CnZGaTruZjvJLdECYnaTfWmnLX/s400/dawnofthedead.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">...<span class="" id="result_box" lang="es" tabindex="-1"><span class="">¿pero realmente tenemos que ir armados para comprar algo de RAM?</span></span>...</td></tr>
</tbody></table>
<span class="" id="result_box" lang="es" tabindex="-1">Para probar
el funcionamiento de la función de test descrita en el último post, he escrito un pequeño programa de test (<b>testsys.c</b>) que crea
un </span><span class="" id="result_box" lang="es" tabindex="-1"><i>thread</i> que puede estresar la <b>CPU</b> y la <b>Memoria</b> de un sistema y luego,
directamente en </span><span class="" id="result_box" lang="es" tabindex="-1">el <b>main()</b>, llama en un loop infinito la nuestra función <b>testSys()</b>, enseñando los resultados del test cada dos segundos. <span class="">En
el siguiente código solo falta la función de test: podéis ir a
buscarla en el último post <i>y aprovechar para volver a leer las
partes más importantes (¡bravo quien lo hace!)</i>.</span> ¡<span class="">Vamos con el código!</span></span> <br />
<pre style="background: #ffffff; color: black;"><span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdio.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">pthread.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">string.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">unistd.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">linux/kernel.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdlib.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdio.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">sys/sysinfo.h</span><span style="color: maroon;">></span>
<span style="color: dimgrey;">// struct para los resultados</span>
<span style="color: maroon; font-weight: bold;">typedef</span> <span style="color: maroon; font-weight: bold;">struct</span> <span style="color: purple;">{</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
<span style="color: purple;">}</span> Results<span style="color: purple;">;</span>
<span style="color: dimgrey;">// prototipos locales</span>
<span style="color: maroon; font-weight: bold;">void</span> testSys<span style="color: #808030;">(</span>Results <span style="color: #808030;">*</span>results<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">void</span> <span style="color: #808030;">*</span>tMyThread<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">void</span> <span style="color: #808030;">*</span>arg<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// función main()</span>
<span style="color: maroon; font-weight: bold;">int</span> <span style="color: #400000;">main</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> argc<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>argv<span style="color: #808030;">[</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// init thread</span>
pthread_t tid<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">int</span> error<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>error <span style="color: #808030;">=</span> pthread_create<span style="color: #808030;">(</span><span style="color: #808030;">&</span>tid<span style="color: #808030;">,</span> <span style="color: #7d0045;">NULL</span><span style="color: #808030;">,</span> <span style="color: #808030;">&</span>tMyThread<span style="color: #808030;">,</span> <span style="color: #7d0045;">NULL</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">!</span><span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">: no puedo crear el thread (</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">)</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">,</span> <span style="color: #603000;">strerror</span><span style="color: #808030;">(</span>error<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// llama testSys() para primer set valores estáticos</span>
Results results<span style="color: purple;">;</span>
testSys<span style="color: #808030;">(</span><span style="color: #808030;">&</span>results<span style="color: #808030;">)</span><span style="color: purple;">;</span>
sleep<span style="color: #808030;">(</span><span style="color: #008c00;">2</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// testSys() loop para testear </span><span style="color: dimgrey;"><span class="short_text" id="result_box" lang="es" tabindex="-1"><span class="">repetidamente</span></span> el sistema</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span><span style="color: purple;">;</span><span style="color: purple;">;</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// get valores</span>
testSys<span style="color: #808030;">(</span><span style="color: #808030;">&</span>results<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">cpu: total usage = </span><span style="color: #007997;">%.1f</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> results<span style="color: #808030;">.</span>total_cpu <span style="color: #808030;">/</span> results<span style="color: #808030;">.</span>prec<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">mem: total usage = </span><span style="color: #007997;">%.1f</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> results<span style="color: #808030;">.</span>mem_system <span style="color: #808030;">/</span> results<span style="color: #808030;">.</span>prec<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">cpu: proc usage = </span><span style="color: #007997;">%.1f</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> results<span style="color: #808030;">.</span>proc_cpu <span style="color: #808030;">/</span> results<span style="color: #808030;">.</span>prec<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">mem: proc usage = </span><span style="color: #007997;">%.1f</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> results<span style="color: #808030;">.</span>mem_proc <span style="color: #808030;">/</span> results<span style="color: #808030;">.</span>prec<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">load average: </span><span style="color: #007997;">%.2f</span><span style="color: #0000e6;"> , </span><span style="color: #007997;">%.2f</span><span style="color: #0000e6;"> , </span><span style="color: #007997;">%.2f</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span>
results<span style="color: #808030;">.</span>loads<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span> <span style="color: #808030;">/</span> results<span style="color: #808030;">.</span>loads_prec<span style="color: #808030;">,</span>
results<span style="color: #808030;">.</span>loads<span style="color: #808030;">[</span><span style="color: #008c00;">1</span><span style="color: #808030;">]</span> <span style="color: #808030;">/</span> results<span style="color: #808030;">.</span>loads_prec<span style="color: #808030;">,</span>
results<span style="color: #808030;">.</span>loads<span style="color: #808030;">[</span><span style="color: #008c00;">2</span><span style="color: #808030;">]</span> <span style="color: #808030;">/</span> results<span style="color: #808030;">.</span>loads_prec<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// sleep 2 segundos</span>
sleep<span style="color: #808030;">(</span><span style="color: #008c00;">2</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// exit</span>
<span style="color: #603000;">exit</span><span style="color: #808030;">(</span>EXIT_SUCCESS<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">función</span> de test del sistema</span>
<span style="color: maroon; font-weight: bold;">void</span> testSys<span style="color: #808030;">(</span>
Results <span style="color: #808030;">*</span>results<span style="color: #808030;">)</span> <span style="color: dimgrey;">// </span><span style="color: dimgrey;">destinación de los </span><span style="color: dimgrey;">resultados</span>
<span style="color: purple;">{</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// thread routine</span>
<span style="color: maroon; font-weight: bold;">void</span> <span style="color: #808030;">*</span>tMyThread<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">void</span> <span style="color: #808030;">*</span>arg<span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// alloc memoria</span>
<span style="color: maroon; font-weight: bold;">unsigned</span> <span style="color: maroon; font-weight: bold;">long</span> mem <span style="color: #808030;">=</span> <span style="color: #008c00;">1024</span> <span style="color: #808030;">*</span> <span style="color: #008c00;">1024</span> <span style="color: #808030;">*</span> <span style="color: #008c00;">512</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// 512 mb</span>
<span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>ptr <span style="color: #808030;">=</span> <span style="color: #603000;">malloc</span><span style="color: #808030;">(</span>mem<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// thread loop infinito</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span><span style="color: purple;">;</span><span style="color: purple;">;</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// usa memoria</span>
<span style="color: #603000;">memset</span><span style="color: #808030;">(</span>ptr<span style="color: #808030;">,</span> <span style="color: #008c00;">0</span><span style="color: #808030;">,</span> mem<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// thread sleep</span>
usleep<span style="color: #808030;">(</span><span style="color: #008c00;">10</span><span style="color: #808030;">)</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// NOTA: sleep muy pequeña para forzar mucha actividad de cpu</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #7d0045;">NULL</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span></pre>
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">Como podeis ver, el programa es de una simplicidad desarmante (y con </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"></span></span></span></span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><a href="http://artcprogramming-es.blogspot.com.es/2012/08/no-comment.html" target="_blank"><b><span class="" id="result_box" lang="es" tabindex="-1"><span class="">excelentes</span></span> comentarios</b></a></span></span></span>, como de costumbre).</span> <span class="">Para
estresar la <b>CPU</b> y la </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><b>Memoria</b> he utilizado algunos simples </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span class="">trucos</span></span>: asigno
(con </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="http://man7.org/linux/man-pages/man3/realloc.3.html" target="_blank"><b>malloc()</b></a>) un <i>megabúfer</i> de 512MB, y luego en un loop infinito lo uso
intensamente (con una</span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""> <a href="http://man7.org/linux/man-pages/man3/memset.3.html" target="_blank"><b>memset()</b></a> completa).</span> <span class="">El ciclo
del </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i>thread</i> </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""> una <a href="http://man7.org/linux/man-pages/man3/usleep.3.html" target="_blank"><b>usleep</b></a> muuuuy pequeña (10 us) que carga mucho la
<b>CPU</b> (<i>que nos odiará un poco, pero es el objetivo del nuestro test,
¿no?</i>).</span></span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">Ahora
viene lo bueno: como se ha mencionado varias veces en el post anterior, nuestra referencia es el comando </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="http://man7.org/linux/man-pages/man1/top.1.html" target="_blank"><b>top</b></a> de la familia <b>UNIX</b></span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class="">
así que tenemos que abrir dos terminales, y en una ejecutamos nuestro
programa de test, y en la otra ejecutamos <b>top</b> para poder comparar</span> <span class="">en tiempo real si los resultados coinciden (recordaros de activar la opción <b>"I"</b> de </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><b>top</b></span></span>, como descrito en el otro post).</span> Veamos qué ha pasado en mi ordenador:</span> <br />
<pre style="background: #ffffff; color: black;"><u><b>en la terminal con testsys</b></u>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
load average<span style="color: #808030;">:</span> <span style="color: #008c00;">0.85</span> <span style="color: #808030;">,</span> <span style="color: #008c00;">0.42</span> <span style="color: #808030;">,</span> <span style="color: #008c00;">0.32</span>
cpu<span style="color: #808030;">:</span> total usage <span style="color: #808030;">=</span> <span style="color: #008c00;">12.9</span>
mem<span style="color: #808030;">:</span> total usage <span style="color: #808030;">=</span> <span style="color: #008c00;">47.8</span>
cpu<span style="color: #808030;">:</span> proc usage <span style="color: #808030;">=</span> <span style="color: #008c00;">12.5</span>
mem<span style="color: #808030;">:</span> proc usage <span style="color: #808030;">=</span> <span style="color: #008c00;">6.9</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
<u><b>en la terminal con top</b></u>
top <span style="color: #808030;">-</span> <span style="color: #8745a0;">18:45:16</span> up <span style="color: #008c00;">39</span> min<span style="color: #808030;">,</span> <span style="color: #008c00;">2</span> users<span style="color: #808030;">,</span> load average<span style="color: #808030;">:</span> <span style="color: #008c00;">0,85</span><span style="color: #808030;">,</span> <span style="color: #008c00;">0,42</span><span style="color: #808030;">,</span> <span style="color: #008c00;">0,32</span>
Tasks<span style="color: #808030;">:</span> <span style="color: #008c00;">236</span> total<span style="color: #808030;">,</span> <span style="color: #008c00;">1</span> running<span style="color: #808030;">,</span> <span style="color: #008c00;">235</span> sleeping<span style="color: #808030;">,</span> <span style="color: #008c00;">0</span> stopped<span style="color: #808030;">,</span> <span style="color: #008c00;">0</span> zombie
<span style="color: #808030;">%</span>Cpu<span style="color: #808030;">(</span>s<span style="color: #808030;">)</span><span style="color: #808030;">:</span> <span style="color: #008c00;">12,5</span> us<span style="color: #808030;">,</span> <span style="color: #008c00;">0,1</span> sy<span style="color: #808030;">,</span> <span style="color: #008c00;">0,0</span> ni<span style="color: #808030;">,</span> <span style="color: #008c00;">87,2</span> id<span style="color: #808030;">,</span> <span style="color: #008c00;">0,0</span> wa<span style="color: #808030;">,</span> <span style="color: #008c00;">0,0</span> hi<span style="color: #808030;">,</span> <span style="color: #008c00;">0,1</span> si<span style="color: #808030;">,</span> <span style="color: #008c00;">0,0</span> st
KiB Mem <span style="color: #808030;">:</span> <span style="color: #008c00;">7600656</span> total<span style="color: #808030;">,</span> <span style="color: #008c00;">3962420</span> free<span style="color: #808030;">,</span> <span style="color: #008c00;">1705536</span> used<span style="color: #808030;">,</span> <span style="color: #008c00;">1932700</span> buff<span style="color: #808030;">/</span>cache
KiB Swap<span style="color: #808030;">:</span> <span style="color: #008c00;">0</span> total<span style="color: #808030;">,</span> <span style="color: #008c00;">0</span> free<span style="color: #808030;">,</span> <span style="color: #008c00;">0</span> used<span style="color: #808030;">.</span> <span style="color: #008c00;">5384092</span> avail Mem
PID USER PR NI VIRT RES SHR S <span style="color: #808030;">%</span>CPU <span style="color: #808030;">%</span>MEM TIME<span style="color: #808030;">+</span> COMMAND
<span style="color: #008c00;">5266</span> aldo <span style="color: #008c00;">20</span> <span style="color: #008c00;">0</span> <span style="color: #008c00;">604548</span> <span style="color: #008c00;">524744</span> <span style="color: #008c00;">628</span> S <span style="color: #008c00;">12,5</span> <span style="color: #008c00;">6,9</span> <span style="color: #8745a0;">0:43</span><span style="color: #008c00;">.01</span> testpstat
<span style="color: #008c00;">1380</span> root <span style="color: #008c00;">20</span> <span style="color: #008c00;">0</span> <span style="color: #008c00;">559772</span> <span style="color: #008c00;">94056</span> <span style="color: #008c00;">82400</span> S <span style="color: #008c00;">0,1</span> <span style="color: #008c00;">1,2</span> <span style="color: #8745a0;">0:43</span><span style="color: #008c00;">.89</span> Xorg
<span style="color: #008c00;">1012</span> root <span style="color: #008c00;">20</span> <span style="color: #008c00;">0</span> <span style="color: #008c00;">4396</span> <span style="color: #008c00;">1276</span> <span style="color: #008c00;">1196</span> S <span style="color: #008c00;">0,0</span> <span style="color: #008c00;">0,0</span> <span style="color: #8745a0;">0:00</span><span style="color: #008c00;">.07</span> acpid
<span style="color: #008c00;">2628</span> aldo <span style="color: #008c00;">20</span> <span style="color: #008c00;">0</span> <span style="color: #008c00;">2542528</span> <span style="color: #008c00;">358152</span> <span style="color: #008c00;">128056</span> S <span style="color: #008c00;">0,0</span> <span style="color: #008c00;">4,7</span> <span style="color: #8745a0;">3:18</span><span style="color: #008c00;">.25</span> firefox
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
</pre>
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">dado
que la salida es continua, he seleccionado solo una parte y he agregado las
elipsis, </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="short_text" id="result_box" lang="es" tabindex="-1">en cualquier caso</span> podéis repetir fácilmente el test en el vuestro ordenador
para verificar la consistencia de los resultados: yo diría que el
resultado es más que satisfactorio, ¿no?</span> ¡Misión cumplida!</span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1">¿Qué falta? Ah,
sí, prometí algunas notas sobre los resultados que muestra <b>top</b> (y, consecuentemente,
también el nuestro <b>testsys</b>): en lo que concierne a la <b>CPU</b>, los
comentarios en el codigo de la <b>testSys()</b> son suficientes (descripción de
las cargas medias, opción <b>"I"</b>, etc.). <span class="">Además,
podemos agregar que la línea </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><b>%Cpu(s)</b> mostrada arriba confirma las
fórmulas contenidas (y comentadas) en <b>testSys()</b>: por ejemplo, la suma
de los diversos componentes (</span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class="">user, idle, sys, etc.) vale, como esperado</span>, 100.</span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1"><span title="Per la memoria, invece, il discorso è un po' più complesso, e bisognerebbe dedicargli un post apposito (magari lo farò in futuro): per il momento vi passo un link interessante: understanding-memory-usage-on-linux, e vi">Para
la memoria, sin embargo, el discurso es un poco más complejo, y habria que
dedicar un post dedicado (tal vez lo haré en el futuro): por el
momento os paso un enlace interesante: </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="Per la memoria, invece, il discorso è un po' più complesso, e bisognerebbe dedicargli un post apposito (magari lo farò in futuro): per il momento vi passo un link interessante: understanding-memory-usage-on-linux, e vi"><b><a href="http://virtualthreads.blogspot.com.es/2006/02/understanding-memory-usage-on-linux.html" target="_blank">understanding-memory-usage-on-linux</a></b>, y </span><span title="aggiungo solo una spiegazione semplicissima di una cosa (strana) che a volte succede su sistemi embedded realizzati con BusyBox: in alcuni casi sembra che alcuni processi usino più del 100% della memoria (nella colonna %MEM): questo è dovuto al fatto che si">os añado
sólo una explicación muy simple de algo (extraño) que a veces ocurre en </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="aggiungo solo una spiegazione semplicissima di una cosa (strana) che a volte succede su sistemi embedded realizzati con BusyBox: in alcuni casi sembra che alcuni processi usino più del 100% della memoria (nella colonna %MEM): questo è dovuto al fatto che si"><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="https://es.wikipedia.org/wiki/Sistema_embebido" target="_blank"><b>sistemas embedded</b></a></span></span></span></span> echos con </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="aggiungo solo una spiegazione semplicissima di una cosa (strana) che a volte succede su sistemi embedded realizzati con BusyBox: in alcuni casi sembra che alcuni processi usino più del 100% della memoria (nella colonna %MEM): questo è dovuto al fatto che si"><b><a href="https://es.wikipedia.org/wiki/Busybox" target="_blank">BusyBox</a></b>: en algunos casos parece que algunos
procesos usan más del 100% de la memoria (en la columna <b>%MEM</b>): esto se debe al hecho de que </span><span title="sta usando una vecchia versione di top fornita da BusyBox che calcola %MEM come VSZ/MemTotal invece di RSS/MemTotal: bisogna considerare che RSS è un valore residente mentre VSZ è un valore virtuale, quindi influenzato, ad esempio, da eventuali shared library che">está
utilizando una versión antigua de <b>top</b> proporcionada por
<b>BusyBox</b> que calcula <b>%MEM</b> como <b>VSZ/MemTotal</b> en lugar de <b>RSS/MemTotal</b>:
debemos tener en cuenta que <b>RSS</b> es un valor residente mientras <b>VSZ</b> es
un valor virtual, </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="sta usando una vecchia versione di top fornita da BusyBox che calcola %MEM come VSZ/MemTotal invece di RSS/MemTotal: bisogna considerare che RSS è un valore residente mentre VSZ è un valore virtuale, quindi influenzato, ad esempio, da eventuali shared library che"><span class="short_text" id="result_box" lang="es" tabindex="-1"><span class="">que por lo tanto está influenciado,</span></span> por ejemplo, de eventuales </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="sta usando una vecchia versione di top fornita da BusyBox che calcola %MEM come VSZ/MemTotal invece di RSS/MemTotal: bisogna considerare che RSS è un valore residente mentre VSZ è un valore virtuale, quindi influenzato, ad esempio, da eventuali shared library che"><i>shared library</i> que </span><span title="vengono caricate e condivise tra varie applicazioni, pagine di memoria non usate al momento, ecc., per cui non c'è da stupirsi se il valore supera il 100%.">se
cargan y se comparten entre varias aplicaciones, de páginas de memoria no
utilizadas en ese momento, etc., por lo que no es sorprendente que el
valor exceda el 100%. </span><span title="Comunque le versioni più recenti di top per BusyBox ridefiniscono la colonna %MEM in %VSZ risolvendo così eventuali incomprensioni (oops... si, i significati delle strane sigle qui sopra li trovate nei commenti della testSys(), nel manuale di top e nel">Sin
embargo, las nuevas versiones de <b>top</b> para <b>BusyBox</b> redefinen la columna<b> %MEM</b> en <b>%VSZ </b>solucionando así cualquier malentendido (<i>oops... el
significado de las siglas extrañas anteriores lo vais a encontrar en los
comentarios de <b>testSys()</b>, en el manual de <b>top</b> y </i></span><span title="link che vi ho passato sulla memoria di Linux)."><i>en el enlace que os he pasado sobre la memoria de Linux</i>).</span></span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1">Bueno, yo diría que para este post es suficiente. Os
dejo con una pequeña nota: dado que he utilizado solamente loop infinitos
en el programa de test, podéis modificarlo a vuestro gusto añadiendo
condiciones de salida. <i>O, en alternativa, no tengáis miedo de detenerlo con CTL-C, no creo que Linux se enfade mucho...</i></span> <br />
<br />
¡Hasta el próximo post!
Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-26435065665385237252018-03-18T01:18:00.004+01:002018-08-03T23:16:40.189+02:00Dawn of the CPU cómo testear el uso de CPU y Memoria en C - pt.1<blockquote class="tr_bq">
<div class="post-header">
<i><b>Francine</b>: Pero porque vienen aquí?</i><i><b>Stephen</b>: </i><i>Una especie de instinto primitivo... De memoria, de lo que solían hacer, tal vez este era un lugar importante en sus vidas.</i></div>
</blockquote>
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">En este post, con la excusa de mencionar al legendario <a href="https://es.wikipedia.org/wiki/Dawn_of_the_Dead" target="_blank"><b>Dawn of the Dead</b></a> del
grande <a href="https://es.wikipedia.org/wiki/George_A._Romero" target="_blank"><b>G.A.Romero</b></a>, hablaremos sobre cuándo nuestra PC se convierte en </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><b><a href="https://es.wikipedia.org/wiki/Zombi" target="_blank">zombi</a></b>, volviéndose realmente difícil de usar.</span> <i><span class="">Un zombie de supermercado, capaz de hacer solo la actividad mínima de los buenos tiempos en que estaba vivo...</span></i></span> <br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsM1p6Ml7U_plmNMS1Re34LWGzJ81PkE5Uq-GhwVgH2eRrbtanghBJxf5XK0ugW01qSY8GhlRQmI_HqoWWuvjyV_K1IMFP4qSblMklxEcew55ZLn18tBo6HDd9xJ-xBcmCIckOgVGif74l/s1600/malldotd.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="345" data-original-width="630" height="218" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsM1p6Ml7U_plmNMS1Re34LWGzJ81PkE5Uq-GhwVgH2eRrbtanghBJxf5XK0ugW01qSY8GhlRQmI_HqoWWuvjyV_K1IMFP4qSblMklxEcew55ZLn18tBo6HDd9xJ-xBcmCIckOgVGif74l/s400/malldotd.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">...zombis de supermercado...</td><td class="tr-caption" style="text-align: center;"><br /></td></tr>
</tbody></table>
<span class="" id="result_box" lang="es" tabindex="-1">Entonces,
vayamos al grano: si el vuestro sistema se vuelve no reactivo, lento, las
aplicaciones no se abren inmediatamente... las posibilidades
son 2:</span><br />
<ol>
<li><span class="" id="result_box" lang="es" tabindex="-1"><span class="" id="result_box" lang="es" tabindex="-1">si estáis
usando un sistema de la familia </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class="" id="result_box" lang="es" tabindex="-1"><a href="https://es.wikipedia.org/wiki/Unix" target="_blank"><b>UNIX</b></a> (<b>Linux</b>, <b>FreeBSD</b>, <b>macOS</b>, etc.) y no estáis
pidiendo demasiado al Hardware disponible (<i>como abrir 50 aplicaciones a
la vez en un PC Pentium III de 1999</i>) es probable que una aplicación que
no funciona bien se está comiendo la <b>CPU</b> y/o la <b>Memoria</b>:
intentad descubrir qué aplicación es y matadla (usando el monitor del
sistema o, si sois de la vieja escuela, usando </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class="" id="result_box" lang="es" tabindex="-1"><b><a href="https://es.wikipedia.org/wiki/Top_(Unix)" target="_blank">top</a></b> y <a href="https://es.wikipedia.org/wiki/Kill" target="_blank"><b>kill</b></a> en
una terminal). <i><span class="">Dicho hecho</span></i></span></span>.</li>
<li><span class="" id="result_box" lang="es" tabindex="-1"><span class="">si estáis usando ese sistema </span></span><span class=""><span class="tm-p-em">innominable</span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""> (<i>que comienza con W y termina
con s, ya sabéis</i>), entonces tenéis que superarlo: <i>es su
comportamiento normal, baby...</i> la única solución es pasar a usar algo un
poco</span> más serio (ver el punto 1). <i>¡Masoquistas!</i></span></li>
</ol>
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">Pero
supongamos que (<i>para volver a conectar con el <a href="https://artcprogramming-es.blogspot.com.es/2018/02/the-test-connection-como-testear-la.html" target="_blank"><b>post anterior</b></a> ...
¿no lo habéis leído? ¿Y qué estáis esperando?)</i> Estáis escribiendo una
aplicación para un </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span class=""></span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="https://es.wikipedia.org/wiki/Sistema_embebido" target="_blank"><b>sistema embedded</b></a> </span></span> y la aplicación, que ni siquiera
tendrá una interfaz gráfica (<i>¡ni hablar de usar top + kill</i></span><span class=""><i>!</i>),
debe, sin embargo, controlar el estado del sistema (<b>CPU</b> y <b>Memoria</b>) para
levantar alguna alarma (o encender un LED de error, etc.) en el caso de
que algo salga mal... ¿qué hacer?</span> Vale<span class="">, en este post, propondré un simple monitor de rendimiento que la vuestra aplicación </span></span><i>embedded </i><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span class="">puede llamar </span></span>(con baja frecuencia, por supuesto) para informar de potenciales </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span class="">problemas</span></span>.</span></span> <br />
<br />
(<i>...</i><span class="" id="result_box" lang="es" tabindex="-1"><i>um, lo
siento si insisto pero, volviendo a los dos puntos descritos
anteriormente: el tema ahora es: aplicaciones embedded basadas en UNIX
(entonces </i></span><span class="" id="result_box" lang="es" tabindex="-1"><i><i><a href="https://es.wikipedia.org/wiki/Linux_embebido" target="_blank"><b>Linux Embedded</b></a>, <a href="https://es.wikipedia.org/wiki/QNX" target="_blank"><b>QNX</b></a>, <a href="https://es.wikipedia.org/wiki/LynxOS" target="_blank"><b>LynxOS</b></a>,
etc.</i>.). <span class="">Si, por otro
lado, realmente os queréis lastimar y escribís aplicaciones de este
tipo usando la versión CE del sistema </span></i></span><span class="" id="result_box" lang="es" tabindex="-1"><i><span class=""><span class=""><span class="tm-p-em">innominable</span></span> (W ... s,
¿recordáis?) Bien, ¿qué puedo deciros?</span> </i></span><span class="" id="result_box" lang="es" tabindex="-1"><i><span class="text" data-default-size="13" lang="es">...si te lo has buscado no te quejes</span></i><i>...</i>)</span><br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1">Bueno,
dividiremos el post en dos partes: en esta primera parte
propondré una función (que llamaremos <b>testSys()</b>) que hace todo el
trabajo necesario. <span class="">En la segunda parte haremos un
<b>main()</b>, que simula el de una simple aplicación </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><b>embedded</b>, en la que
introduciremos un <b>thread</b> escrito ad-hoc para </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="short_text" id="result_box" lang="es" tabindex="-1"><span class="">sobrecargar</span></span> el sistema, y nuestro <b>main()</b>, utilizando la <b>testSys()</b>, debería notarlo sin problemas.</span> ¡Vamos con el código!</span> <br />
<pre style="background: #ffffff; color: black;"><span style="color: dimgrey;">// struct para los resultados</span>
<span style="color: maroon; font-weight: bold;">typedef</span> <span style="color: maroon; font-weight: bold;">struct</span> <span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">int</span> total_cpu<span style="color: purple;">;</span> <span style="color: dimgrey;">// cpu total (val.porcentual x prec)</span>
<span style="color: maroon; font-weight: bold;">int</span> proc_cpu<span style="color: purple;">;</span> <span style="color: dimgrey;">// cpu proceso (val.</span><span style="color: dimgrey;"><span style="color: dimgrey;">porcentual</span> x prec)</span>
<span style="color: maroon; font-weight: bold;">int</span> mem_system<span style="color: purple;">;</span> <span style="color: dimgrey;">// mem total (val.</span><span style="color: dimgrey;"><span style="color: dimgrey;">porcentual</span> x prec)</span>
<span style="color: maroon; font-weight: bold;">int</span> mem_proc<span style="color: purple;">;</span> <span style="color: dimgrey;">// mem proceso (val.</span><span style="color: dimgrey;"><span style="color: dimgrey;">porcentual</span> x prec)</span>
<span style="color: maroon; font-weight: bold;">float</span> prec<span style="color: purple;">;</span> <span style="color: dimgrey;">// precisión (10=1dec)</span>
<span style="color: maroon; font-weight: bold;">int</span> loads<span style="color: #808030;">[</span><span style="color: #008c00;">3</span><span style="color: #808030;">]</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// cargas avg (val.</span><span style="color: dimgrey;"><span style="color: dimgrey;">porcentual</span> x loads_prec)</span>
<span style="color: maroon; font-weight: bold;">float</span> loads_prec<span style="color: purple;">;</span> <span style="color: dimgrey;">// precisión para cargas (100=2dec)</span>
<span style="color: purple;">}</span> Results<span style="color: purple;">;</span>
<span style="color: dimgrey;">// función de test del sistema</span>
<span style="color: maroon; font-weight: bold;">void</span> testSys<span style="color: #808030;">(</span>
Results <span style="color: #808030;">*</span>results<span style="color: #808030;">)</span> <span style="color: dimgrey;">// destinación de los </span><span style="color: dimgrey;">resultados</span>
<span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">static</span> <span style="color: maroon; font-weight: bold;">unsigned</span> <span style="color: maroon; font-weight: bold;">long</span> <span style="color: maroon; font-weight: bold;">long</span> total_cpu_last<span style="color: purple;">;</span> <span style="color: dimgrey;">/* system total cpu-time incluido idle-time</span>
<span style="color: dimgrey;"> en jiffies (típicamente centésimos de</span>
<span style="color: dimgrey;"> segundo)) */</span>
<span style="color: maroon; font-weight: bold;">static</span> <span style="color: maroon; font-weight: bold;">unsigned</span> <span style="color: maroon; font-weight: bold;">long</span> <span style="color: maroon; font-weight: bold;">long</span> idle_cpu_last<span style="color: purple;">;</span> <span style="color: dimgrey;">/* system idle cpu-time (in jiffies</span>
<span style="color: dimgrey;"> (tipicamente </span><span style="color: dimgrey;"><span style="color: dimgrey;">centésimos</span> de </span><span style="color: dimgrey;"><span style="color: dimgrey;">segundo</span>)) */</span>
<span style="color: maroon; font-weight: bold;">static</span> <span style="color: maroon; font-weight: bold;">unsigned</span> <span style="color: maroon; font-weight: bold;">long</span> proc_times_last<span style="color: purple;">;</span> <span style="color: dimgrey;">/* cpu-time del proceso corriente</span>
<span style="color: dimgrey;"> (calculado sumando usertime y</span>
<span style="color: dimgrey;"> systemtime del proceso) */</span>
<span style="color: #603000;">FILE</span> <span style="color: #808030;">*</span>fp1<span style="color: purple;">;</span>
<span style="color: #603000;">FILE</span> <span style="color: #808030;">*</span>fp2<span style="color: purple;">;</span>
<span style="color: dimgrey;">// set </span><span style="color: dimgrey;"><span style="color: dimgrey;">resultados</span> de default (así el que llama puede tratar los errores)</span>
results<span style="color: #808030;">-</span><span style="color: #808030;">></span>total_cpu <span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: purple;">;</span>
results<span style="color: #808030;">-</span><span style="color: #808030;">></span>proc_cpu <span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: purple;">;</span>
results<span style="color: #808030;">-</span><span style="color: #808030;">></span>mem_system <span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: purple;">;</span>
results<span style="color: #808030;">-</span><span style="color: #808030;">></span>mem_proc <span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: purple;">;</span>
results<span style="color: #808030;">-</span><span style="color: #808030;">></span>prec <span style="color: #808030;">=</span> <span style="color: #008c00;">10</span><span style="color: #808030;">.</span><span style="color: purple;">;</span>
results<span style="color: #808030;">-</span><span style="color: #808030;">></span>loads<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span> <span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: purple;">;</span>
results<span style="color: #808030;">-</span><span style="color: #808030;">></span>loads<span style="color: #808030;">[</span><span style="color: #008c00;">1</span><span style="color: #808030;">]</span> <span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: purple;">;</span>
results<span style="color: #808030;">-</span><span style="color: #808030;">></span>loads<span style="color: #808030;">[</span><span style="color: #008c00;">2</span><span style="color: #808030;">]</span> <span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: purple;">;</span>
results<span style="color: #808030;">-</span><span style="color: #808030;">></span>loads_prec <span style="color: #808030;">=</span> <span style="color: #008c00;">100</span><span style="color: #808030;">.</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">/* lee /proc/self/stat (estadísticas de proceso di este proceso) y /proc/stat</span>
<span style="color: dimgrey;"> (</span><span style="color: dimgrey;"><span style="color: dimgrey;">estadísticas de procesos</span> de esta maquina) */</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>fp1 <span style="color: #808030;">=</span> <span style="color: #603000;">fopen</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">/proc/self/stat</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">r</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">!</span><span style="color: #808030;">=</span> <span style="color: #7d0045;">NULL</span><span style="color: #808030;">)</span> <span style="color: #808030;">&</span><span style="color: #808030;">&</span>
<span style="color: #808030;">(</span><span style="color: #808030;">(</span>fp2 <span style="color: #808030;">=</span> <span style="color: #603000;">fopen</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">/proc/stat</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">r</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">!</span><span style="color: #808030;">=</span> <span style="color: #7d0045;">NULL</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// lee user time e system time</span>
<span style="color: maroon; font-weight: bold;">unsigned</span> <span style="color: maroon; font-weight: bold;">long</span> utime<span style="color: #808030;">,</span> stime<span style="color: purple;">;</span>
<span style="color: #603000;">fscanf</span><span style="color: #808030;">(</span>fp1<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #007997;">%*d</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%*s</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%*c</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%*d</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%*d</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%*d</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%*d</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%*d</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%*u</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%*u</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%*u</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%*u</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%*u</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%lu</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%lu</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span>
<span style="color: #808030;">&</span>utime<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>stime<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">unsigned</span> <span style="color: maroon; font-weight: bold;">long</span> proc_times_cur <span style="color: #808030;">=</span> utime <span style="color: #808030;">+</span> stime<span style="color: purple;">;</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">lee</span> i valori di user, nice, system, idle, iowait, irq e softirq</span>
<span style="color: maroon; font-weight: bold;">unsigned</span> <span style="color: maroon; font-weight: bold;">long</span> <span style="color: maroon; font-weight: bold;">long</span> user<span style="color: #808030;">,</span> nice<span style="color: #808030;">,</span> <span style="color: #603000;">system</span><span style="color: #808030;">,</span> idle<span style="color: #808030;">,</span> iowait<span style="color: #808030;">,</span> irq<span style="color: #808030;">,</span> softirq<span style="color: purple;">;</span>
<span style="color: #603000;">fscanf</span><span style="color: #808030;">(</span>fp2<span style="color: #808030;">,</span><span style="color: maroon;">"</span><span style="color: #007997;">%*s</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%llu</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%llu</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%llu</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%llu</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%llu</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%llu</span><span style="color: #0000e6;"> </span><span style="color: #007997;">%llu</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> <span style="color: #808030;">&</span>user<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>nice<span style="color: #808030;">,</span> <span style="color: #808030;">&</span><span style="color: #603000;">system</span><span style="color: #808030;">,</span>
<span style="color: #808030;">&</span>idle<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>iowait<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>irq<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>softirq<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">unsigned</span> <span style="color: maroon; font-weight: bold;">long</span> <span style="color: maroon; font-weight: bold;">long</span> total_cpu_cur <span style="color: #808030;">=</span> user <span style="color: #808030;">+</span> nice <span style="color: #808030;">+</span> <span style="color: #603000;">system</span> <span style="color: #808030;">+</span> idle <span style="color: #808030;">+</span> iowait <span style="color: #808030;">+</span>
irq <span style="color: #808030;">+</span> softirq<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">unsigned</span> <span style="color: maroon; font-weight: bold;">long</span> <span style="color: maroon; font-weight: bold;">long</span> idle_cpu_cur <span style="color: #808030;">=</span> idle<span style="color: purple;">;</span>
<span style="color: dimgrey;">/* calcula uso cpu total (%cpu = work_over_period / total_over_period * 100</span>
<span style="color: dimgrey;"> (</span><span style="color: dimgrey;"><span style="color: dimgrey;">adonde</span> work_over_period es (total - idle))</span>
<span style="color: dimgrey;"> NOTA: el campo iowait di /proc/stat es incluido nel calculo, aunque sea un</span>
<span style="color: dimgrey;"> valor poco fiable (pero el error </span><span style="color: dimgrey;"><span style="color: dimgrey;">es</span> trascurable) */</span>
results<span style="color: #808030;">-</span><span style="color: #808030;">></span>total_cpu <span style="color: #808030;">=</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">float</span><span style="color: #808030;">)</span><span style="color: #808030;">(</span><span style="color: #808030;">(</span>total_cpu_cur <span style="color: #808030;">-</span> total_cpu_last<span style="color: #808030;">)</span> <span style="color: #808030;">-</span> <span style="color: #808030;">(</span>idle_cpu_cur <span style="color: #808030;">-</span> idle_cpu_last<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">/</span>
<span style="color: #808030;">(</span>total_cpu_cur <span style="color: #808030;">-</span> total_cpu_last<span style="color: #808030;">)</span> <span style="color: #808030;">*</span> <span style="color: #008c00;">100</span> <span style="color: #808030;">*</span> results<span style="color: #808030;">-</span><span style="color: #808030;">></span>prec<span style="color: purple;">;</span>
<span style="color: dimgrey;">/* calcula uso cpu del proceso ((proc_times2 - proc_times1) * 100 /</span>
<span style="color: dimgrey;"> (float) (total_cpu_usage2 - total_cpu_usage1))</span>
<span style="color: dimgrey;"> NOTA: nel programa "top" este valor es</span><span style="color: dimgrey;"> multiplicado por NPROCESSORS (usar</span>
<span style="color: dimgrey;"> el comando interactivo "I" para deshabilitar esta característica) */</span>
results<span style="color: #808030;">-</span><span style="color: #808030;">></span>proc_cpu <span style="color: #808030;">=</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">float</span><span style="color: #808030;">)</span><span style="color: #808030;">(</span>proc_times_cur <span style="color: #808030;">-</span> proc_times_last<span style="color: #808030;">)</span> <span style="color: #808030;">/</span>
<span style="color: #808030;">(</span>total_cpu_cur <span style="color: #808030;">-</span> total_cpu_last<span style="color: #808030;">)</span> <span style="color: #808030;">*</span> <span style="color: #008c00;">100</span> <span style="color: #808030;">*</span> results<span style="color: #808030;">-</span><span style="color: #808030;">></span>prec<span style="color: purple;">;</span>
<span style="color: dimgrey;">// salva valores y cierra files</span>
total_cpu_last <span style="color: #808030;">=</span> total_cpu_cur<span style="color: purple;">;</span>
idle_cpu_last <span style="color: #808030;">=</span> idle_cpu_cur<span style="color: purple;">;</span>
proc_times_last <span style="color: #808030;">=</span> proc_times_cur<span style="color: purple;">;</span>
<span style="color: #603000;">fclose</span><span style="color: #808030;">(</span>fp1<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">fclose</span><span style="color: #808030;">(</span>fp2<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">lee</span> /proc/self/statm (</span><span style="color: dimgrey;"><span style="color: dimgrey;">estadísticas</span> de memoria de este proceso)</span>
<span style="color: maroon; font-weight: bold;">struct</span> sysinfo si<span style="color: purple;">;</span> <span style="color: dimgrey;">// destinazione per dati ottenuti dalla system call sysinfo()</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span><span style="color: #808030;">(</span>fp1 <span style="color: #808030;">=</span> <span style="color: #603000;">fopen</span><span style="color: #808030;">(</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">/proc/self/statm</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">r</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">!</span><span style="color: #808030;">=</span> <span style="color: #7d0045;">NULL</span><span style="color: #808030;">)</span> <span style="color: #808030;">&</span><span style="color: #808030;">&</span> <span style="color: #808030;">(</span>sysinfo<span style="color: #808030;">(</span><span style="color: #808030;">&</span>si<span style="color: #808030;">)</span> <span style="color: #808030;">!</span><span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">/* lee el resident set size corriente (physical memory use) medido en bytes.</span>
<span style="color: dimgrey;"> NOTA: nel programa "top" el valor llamado RES depende del valor del RSS</span>
<span style="color: dimgrey;"> (resident set size) de las estructuras internas de Linux */</span>
<span style="color: maroon; font-weight: bold;">long</span> resident<span style="color: purple;">;</span>
<span style="color: #603000;">fscanf</span><span style="color: #808030;">(</span>fp1<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #007997;">%*s</span><span style="color: #007997;">%ld</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> <span style="color: #808030;">&</span>resident<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">long</span> res <span style="color: #808030;">=</span> resident <span style="color: #808030;">*</span> <span style="color: #808030;">(</span><span style="color: #603000;">size_t</span><span style="color: #808030;">)</span>sysconf<span style="color: #808030;">(</span>_SC_PAGESIZE<span style="color: #808030;">)</span> <span style="color: #808030;">/</span> <span style="color: #008c00;">1024</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// code+data</span>
<span style="color: dimgrey;">// calcula valores de referencia</span>
<span style="color: maroon; font-weight: bold;">unsigned</span> <span style="color: maroon; font-weight: bold;">long</span> totalram <span style="color: #808030;">=</span> si<span style="color: #808030;">.</span>totalram <span style="color: #808030;">*</span> si<span style="color: #808030;">.</span>mem_unit<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">unsigned</span> <span style="color: maroon; font-weight: bold;">long</span> freeram <span style="color: #808030;">=</span> si<span style="color: #808030;">.</span>freeram <span style="color: #808030;">*</span> si<span style="color: #808030;">.</span>mem_unit<span style="color: purple;">;</span>
results<span style="color: #808030;">-</span><span style="color: #808030;">></span>mem_system <span style="color: #808030;">=</span>
<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">float</span><span style="color: #808030;">)</span><span style="color: #808030;">(</span>totalram <span style="color: #808030;">-</span> freeram<span style="color: #808030;">)</span> <span style="color: #808030;">/</span> totalram <span style="color: #808030;">*</span> <span style="color: #008c00;">100</span> <span style="color: #808030;">*</span> results<span style="color: #808030;">-</span><span style="color: #808030;">></span>prec<span style="color: purple;">;</span>
<span style="color: dimgrey;">/* calcula %mem: nel programa "top" este valor </span><span style="color: dimgrey;"><span style="color: dimgrey;">es </span>calculado como: %mem =</span>
<span style="color: dimgrey;"> RES / totphisicalmem (adonde RES es</span><span style="color: dimgrey;"> CODE + DATA (</span><span style="color: dimgrey;"><span style="color: dimgrey;">adonde</span> DATA </span><span style="color: dimgrey;"><span style="color: dimgrey;">es </span>data + stack)) */</span>
results<span style="color: #808030;">-</span><span style="color: #808030;">></span>mem_proc <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">float</span><span style="color: #808030;">)</span>res <span style="color: #808030;">/</span> <span style="color: #808030;">(</span>totalram <span style="color: #808030;">/</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">float</span><span style="color: #808030;">)</span><span style="color: #008c00;">1024</span><span style="color: #808030;">)</span> <span style="color: #808030;">*</span> <span style="color: #008c00;">100</span> <span style="color: #808030;">*</span> results<span style="color: #808030;">-</span><span style="color: #808030;">></span>prec<span style="color: purple;">;</span>
<span style="color: #603000;">fclose</span><span style="color: #808030;">(</span>fp1<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// lee cpu loadavg</span>
<span style="color: maroon; font-weight: bold;">double</span> loads<span style="color: #808030;">[</span><span style="color: #008c00;">3</span><span style="color: #808030;">]</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">cargas</span> avg de CPU a 1, 5, e 15 minutos</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>getloadavg<span style="color: #808030;">(</span>loads<span style="color: #808030;">,</span> <span style="color: #008c00;">3</span><span style="color: #808030;">)</span> <span style="color: #808030;">!</span><span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// copia las cargas en los resultados</span>
results<span style="color: #808030;">-</span><span style="color: #808030;">></span>loads<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span> <span style="color: #808030;">=</span> loads<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span> <span style="color: #808030;">*</span> results<span style="color: #808030;">-</span><span style="color: #808030;">></span>loads_prec<span style="color: purple;">;</span>
results<span style="color: #808030;">-</span><span style="color: #808030;">></span>loads<span style="color: #808030;">[</span><span style="color: #008c00;">1</span><span style="color: #808030;">]</span> <span style="color: #808030;">=</span> loads<span style="color: #808030;">[</span><span style="color: #008c00;">1</span><span style="color: #808030;">]</span> <span style="color: #808030;">*</span> results<span style="color: #808030;">-</span><span style="color: #808030;">></span>loads_prec<span style="color: purple;">;</span>
results<span style="color: #808030;">-</span><span style="color: #808030;">></span>loads<span style="color: #808030;">[</span><span style="color: #008c00;">2</span><span style="color: #808030;">]</span> <span style="color: #808030;">=</span> loads<span style="color: #808030;">[</span><span style="color: #008c00;">2</span><span style="color: #808030;">]</span> <span style="color: #808030;">*</span> results<span style="color: #808030;">-</span><span style="color: #808030;">></span>loads_prec<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: purple;">}</span></pre>
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">¿Que pensáis de eso?</span> <span class="">Puse </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><a href="http://artcprogramming-es.blogspot.com.es/2012/08/no-comment.html" target="_blank"><b>tantos comentarios</b></a>,</span> que casi no queda nada por decir.</span> <span class="">¿Y qué puedo añadir?</span> <span class="">De
los comentarios se desprende que los datos obtenidos utilizan el mismo
sistema de cálculo que usa <b>top</b> (que es una referencia casi obligatoria)
y, en particular, nos referimos a </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><b>top</b></span></span> con la opción <i>"I"</i>
(</span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i>Irix mode Off</i>) habilitada: esto evita</span> <span class="">,
simplemente, tener indicaciones (extrañas a primera vista, pero
correctas) como <i>"% CPU = 800"</i> que corresponde a un consumo de 100% de
<b>CPU</b> en una máquina con <i>8 núcleos</i>: con la opción <i>"I"</i> estamos seguros de
que el</span> <span class="">el valor máximo indicado será del 100%,
independientemente de la cantidad de núcleos disponibles (para que no
se pueda malinterpretar).</span></span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">La
función <b>testSys()</b> escribe los resultados en una estructura <b>TestResults</b>
pasada como argumento: esto nos permite presentar y/o procesar datos
</span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span class="">de manera adecuada </span></span>en el nivel del llamante.</span> <span class="">En
el ejemplo que veremos proximamente, el <b>main()</b> llama <b>testSys()</b> y
enseña los resultados, para que se pueda verificar si coinciden con
los de <b>top</b> (que podemos ejecutar simultáneamente en otra
terminal).</span> <span class="">En un caso real los datos deberían, en vez, procesarse, por ejemplo comparándolos con valores de referencia para generar alarmas.</span> <span class="">Precisamente
por esta razón los datos se registran como <i>int</i> multiplicados por un
factor de precisión apropiado: esta es una forma normal y clásica de
procesar datos que nacen <i>float</i>, ya que la comparación (y cualquier otra
operación) entre valores enteros es mucho más simple de</span> <span class="">realizar y permite una mayor flexibilidad de uso.</span></span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">Bueno, para hoy puede ser suficiente.</span> <span class="">Como
prometido en el próximo post os mostraré un ejemplo de uso,
algunos resultados de pruebas reales y un pequeño estudio sobre la
interpretación de datos, especialmente los de la Memoria.</span></span> <br />
<br />
¡Hasta el próximo post!
Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-57187573271217325552018-02-17T00:08:00.004+01:002018-03-09T22:30:53.894+01:00The Test Connection cómo testear la conectividad en C<span class="" id="result_box" lang="es" tabindex="-1">La </span><span class="" id="result_box" lang="es" tabindex="-1">Test Connection de este post es un poco menos peligrosa que la </span><span class="" id="result_box" lang="es" tabindex="-1"><a href="https://es.wikipedia.org/wiki/The_French_Connection" target="_blank"><b>French Connection</b></a> de la cual habla la obra maestra de </span><span class="" id="result_box" lang="es" tabindex="-1"><a href="https://es.wikipedia.org/wiki/William_Friedkin" target="_blank"><b>W.Friedkin</b></a>. </span><span class="" id="result_box" lang="es" tabindex="-1"><span class="">Pero sigue siendo una actividad muy importante en algunos tipos de aplicaciones, <i>entonces ni hablar de subestimarla</i></span></span><i>.</i><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTcQWkazeAIvQujulg6OsSNq87Kbo5YAurMbajftPnPI66-9VxkXCJSRkh33AMXNrgx70AHC8DtrQZkVb7GqWTb0MHr1Y7rLlaZI78So03m9xtMx8Z22Y1NE9bBh3xW4pOZeW2T4vYEjhO/s1600/french-connection.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="338" data-original-width="628" height="215" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTcQWkazeAIvQujulg6OsSNq87Kbo5YAurMbajftPnPI66-9VxkXCJSRkh33AMXNrgx70AHC8DtrQZkVb7GqWTb0MHr1Y7rLlaZI78So03m9xtMx8Z22Y1NE9bBh3xW4pOZeW2T4vYEjhO/s400/french-connection.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">...caras de conectividad avanzada...</td></tr>
</tbody></table>
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">Cuando
utilizamos un computer interactivamente y queremos verificar el
estado de la red, tenemos muchas formas sencillas de hacerlo: por
ejemplo, con un buen </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="http://man7.org/linux/man-pages/man8/ping.8.html" target="_blank"><b>ping</b></a> a <i>google.com </i>sabemos si estamos conectados a
Internet y, si no funciona, podemos utilizar algunas utilidades del
sistema para averiguar lo</span> que esta mal. <span class="">Supongamos,
sin embargo, tener que desarrollar una aplicación para un </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="https://es.wikipedia.org/wiki/Sistema_embebido" target="_blank"><b>sistema embedded</b></a> (<i>¿somos o no somos programadores C?</i>) y esta aplicación está
conectada a la red y, en el caso de que no haya comunicación, debe
informar de alguna manera (<i>yo que se, encender un led</i></span><i> </i><span class=""><i>error, ¡ciertamente no enviando un mensaje en la red!</i>).</span> Ok, y... ¿cómo lo hacemos? <span class="">Veamos, ¡vamos con el código!</span></span> <br />
<pre style="background: #ffffff; color: black;"><span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdio.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">string.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdlib.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">unistd.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">errno.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">ifaddrs.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">net/if.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">sys/ioctl.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">linux/ethtool.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">linux/sockios.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">arpa/inet.h</span><span style="color: maroon;">></span>
<span style="color: maroon; font-weight: bold;">int</span> <span style="color: #400000;">main</span><span style="color: #808030;">(</span><span style="color: #808030;"><span style="color: maroon; font-weight: bold;">int</span> argc<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>argv<span style="color: #808030;">[</span><span style="color: #808030;">]</span>)</span>
<span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">int</span> sock<span style="color: purple;">;</span>
<span style="color: dimgrey;">// test interfaces</span>
<span style="color: dimgrey;">//</span>
<span style="color: dimgrey;">// get de todos los network devices configurados</span>
<span style="color: maroon; font-weight: bold;">struct</span> ifaddrs <span style="color: #808030;">*</span>addrs<span style="color: purple;">;</span>
getifaddrs<span style="color: #808030;">(</span><span style="color: #808030;">&</span>addrs<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// loop sobre los network devices configurados</span>
<span style="color: maroon; font-weight: bold;">int</span> i_alr <span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">struct</span> ifaddrs <span style="color: #808030;">*</span>tmpaddrs <span style="color: #808030;">=</span> addrs<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">while</span> <span style="color: #808030;">(</span>tmpaddrs<span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// test interface</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>tmpaddrs<span style="color: #808030;">-</span><span style="color: #808030;">></span>ifa_addr <span style="color: #808030;">&</span><span style="color: #808030;">&</span> tmpaddrs<span style="color: #808030;">-</span><span style="color: #808030;">></span>ifa_addr<span style="color: #808030;">-</span><span style="color: #808030;">></span>sa_family <span style="color: #808030;">=</span><span style="color: #808030;">=</span> AF_PACKET<span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// abre un socket para el test</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>sock <span style="color: #808030;">=</span> <span style="color: #400000;">socket</span><span style="color: #808030;">(</span>AF_INET<span style="color: #808030;">,</span> SOCK_DGRAM<span style="color: #808030;">,</span> IPPROTO_UDP<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;"><</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// enseña error y continua</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">test interfaces: error socket para la interface </span><span style="color: #007997;">%s</span><span style="color: #0000e6;">: </span><span style="color: #007997;">%s</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span>
tmpaddrs<span style="color: #808030;">-</span><span style="color: #808030;">></span>ifa_name<span style="color: #808030;">,</span> <span style="color: #603000;">strerror</span><span style="color: #808030;">(</span>errno<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">continue</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// prepara los dati para ioctl()</span>
<span style="color: maroon; font-weight: bold;">struct</span> ethtool_value edata<span style="color: purple;">;</span>
edata<span style="color: #808030;">.</span>cmd <span style="color: #808030;">=</span> ETHTOOL_GLINK<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">struct</span> ifreq ifr<span style="color: purple;">;</span>
<span style="color: #603000;">strncpy</span><span style="color: #808030;">(</span>ifr<span style="color: #808030;">.</span>ifr_name<span style="color: #808030;">,</span> tmpaddrs<span style="color: #808030;">-</span><span style="color: #808030;">></span>ifa_name<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span>ifr<span style="color: #808030;">.</span>ifr_name<span style="color: #808030;">)</span> <span style="color: #808030;">-</span> <span style="color: #008c00;">1</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
ifr<span style="color: #808030;">.</span>ifr_data <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span><span style="color: #808030;">)</span><span style="color: #808030;">&</span>edata<span style="color: purple;">;</span>
<span style="color: dimgrey;">// esegue ioctl()</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>ioctl<span style="color: #808030;">(</span>sock<span style="color: #808030;">,</span> SIOCETHTOOL<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>ifr<span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// error ioctl: cierra el socket y continua</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">test interfaces: error ioctl para la interface </span><span style="color: #007997;">%s</span><span style="color: #0000e6;">: </span><span style="color: #007997;">%s</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span>
tmpaddrs<span style="color: #808030;">-</span><span style="color: #808030;">></span>ifa_name<span style="color: #808030;">,</span> <span style="color: #603000;">strerror</span><span style="color: #808030;">(</span>errno<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
close<span style="color: #808030;">(</span>sock<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">continue</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// enseña los resultados y cierra el socket</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">test interfaces: interface </span><span style="color: #007997;">%s</span><span style="color: #0000e6;">: </span><span style="color: #007997;">%s</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span>
tmpaddrs<span style="color: #808030;">-</span><span style="color: #808030;">></span>ifa_name<span style="color: #808030;">,</span> edata<span style="color: #808030;">.</span>data <span style="color: purple;">?</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">OK</span><span style="color: maroon;">"</span> <span style="color: purple;">:</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">NOK</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
close<span style="color: #808030;">(</span>sock<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// pasa al proximo device</span>
tmpaddrs <span style="color: #808030;">=</span> tmpaddrs<span style="color: #808030;">-</span><span style="color: #808030;">></span>ifa_next<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// libera la devices list</span>
freeifaddrs<span style="color: #808030;">(</span>addrs<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// test conectividad</span>
<span style="color: dimgrey;">//</span>
<span style="color: dimgrey;">// abre un socket para el test</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>sock <span style="color: #808030;">=</span> <span style="color: #400000;">socket</span><span style="color: #808030;">(</span>AF_INET<span style="color: #808030;">,</span> SOCK_STREAM<span style="color: #808030;">,</span> IPPROTO_TCP<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;"><</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// error socket</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">test de conectividad: socket error: </span><span style="color: #007997;">%s</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> <span style="color: #603000;">strerror</span><span style="color: #808030;">(</span>errno<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_FAILURE<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// set de un timeout para send() (en este caso en realidad lo usa connect()) para evitar</span>
<span style="color: dimgrey;">// un bloqueo sobre error de conexion)</span>
<span style="color: maroon; font-weight: bold;">struct</span> <span style="color: #603000;">timeval</span> tv<span style="color: purple;">;</span>
tv<span style="color: #808030;">.</span>tv_sec <span style="color: #808030;">=</span> <span style="color: #008c00;">1</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// set timeout en segundos</span>
tv<span style="color: #808030;">.</span>tv_usec <span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// set timeout en usegundos</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #400000;">setsockopt</span><span style="color: #808030;">(</span>sock<span style="color: #808030;">,</span> SOL_SOCKET<span style="color: #808030;">,</span> SO_SNDTIMEO<span style="color: #808030;">,</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">char</span><span style="color: #808030;">*</span><span style="color: #808030;">)</span><span style="color: #808030;">&</span>tv<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span>tv<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;"><</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// error ioctl</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">test de conectividad: setsockopt error: </span><span style="color: #007997;">%s</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> <span style="color: #603000;">strerror</span><span style="color: #808030;">(</span>errno<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
close<span style="color: #808030;">(</span>sock<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_FAILURE<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// NOTE: alternativa (non portable) a el uso de SO_SNDTIMEO:</span>
<span style="color: dimgrey;">//int syn_retries = 1; // send de un total de 1 SYN packets => timeout ~2s</span>
<span style="color: dimgrey;">//if (setsockopt(sock, IPPROTO_TCP, TCP_SYNCNT, &syn_retries, sizeof(syn_retries)) < 0) {</span>
<span style="color: dimgrey;">// ...</span>
<span style="color: dimgrey;">// prepara la estructura sockaddr_in para el server remoto</span>
<span style="color: maroon; font-weight: bold;">struct</span> sockaddr_in server<span style="color: purple;">;</span> <span style="color: dimgrey;">// server (remoto) socket info</span>
<span style="color: #603000;">memset</span><span style="color: #808030;">(</span><span style="color: #808030;">&</span>server<span style="color: #808030;">,</span> <span style="color: #008c00;">0</span><span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span>server<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
server<span style="color: #808030;">.</span>sin_family <span style="color: #808030;">=</span> AF_INET<span style="color: purple;">;</span> <span style="color: dimgrey;">// set familia direcciones</span>
server<span style="color: #808030;">.</span>sin_addr<span style="color: #808030;">.</span>s_addr <span style="color: #808030;">=</span> <span style="color: #400000;">inet_addr</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">216.58.214.174</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// set direccion server</span>
server<span style="color: #808030;">.</span>sin_port <span style="color: #808030;">=</span> <span style="color: #400000;">htons</span><span style="color: #808030;">(</span><span style="color: #008c00;">80</span><span style="color: #808030;">)</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// set numero port server</span>
<span style="color: dimgrey;">// conexion al server remoto</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #400000;">connect</span><span style="color: #808030;">(</span>sock<span style="color: #808030;">,</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">struct</span> <span style="color: #400000;">sockaddr</span> <span style="color: #808030;">*</span><span style="color: #808030;">)</span><span style="color: #808030;">&</span>server<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span>server<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;"><</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// error connect</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">test de conectividad: error connect: </span><span style="color: #007997;">%s</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> <span style="color: #603000;">strerror</span><span style="color: #808030;">(</span>errno<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
close<span style="color: #808030;">(</span>sock<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_FAILURE<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// enseña los resultados ed esce</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">test de conectividad: conectividad OK</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
close<span style="color: #808030;">(</span>sock<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_SUCCESS<span style="color: purple;">;</span>
<span style="color: purple;">}</span><span style="color: purple;"></span></pre>
<span class="" id="result_box" lang="es" tabindex="-1">una
premisa: este código está destinado a aplicaciones </span><span class="" id="result_box" lang="es" tabindex="-1"><a href="https://es.wikipedia.org/wiki/Linux_embebido" target="_blank"><b>Linux Embedded</b></a>, por
lo que todo lo que sigue se refiere a un entorno <b>Linux</b> (<i>bueno, como
siempre, y es inútil explicar otra vez el tema...</i>). El código es </span><span class="" id="result_box" lang="es" tabindex="-1"><a href="http://artcprogramming-es.blogspot.com.es/2012/08/no-comment.html" target="_blank"><b>ampliamente comentado</b></a>, así que no tengo que escribir demasiadas explicaciones. En
este ejemplo, el código de test se escribe directamente en el<b> main() </b>
pero, en un proyecto real, debería transformarse en una función que se
puede llamar periódicamente en la posición más adecuada, tal vez
directamente en el </span><span class="" id="result_box" lang="es" tabindex="-1"><span class="" id="result_box" lang="es" tabindex="-1"><b>main()</b></span> de la aplicación. Dado el tipo
de prueba que se realiza, se debe llamar ocasionalmente, en función de cuanto de inmediata debe ser la señal de alarma que necesitamos. Normalmente,
el uso es de baja frecuencia (<i>hablamos de segundos, no de
milisegundos</i>), de lo contrario, nuestra función consumiría mucho tiempo
de <b>CPU</b> solo para verificar la conectividad (<i>y este no parece ser el
caso</i>).</span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">el test se lleva a cabo en dos fases: test de las interfaces de red y test de conectividad.</span> L<span class="">a
primera comprueba eventuales problemas, digamos, <b>Hardware</b>: si
tengo una conexión WiFi y una Ethernet en la misma máquina podría tener una
conexión a Internet incluso si, por ejemplo, el cable de red está
desconectado, y me interesa informar de esta situación, por lo que
un único test de</span> conexión no sería suficiente. <span class="">La
segunda fase verifica la conectividad real y, en caso de que falte,
podemos saber si no hay conectividad a pesar de que las interfaces de
red están bien conectadas, así de aislar mejor las posibles
causas (en resumen, es un test bastante completo</span><span class="">, pero se puede sofisticar aun más).</span></span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1">La fase de
test de las interfaces se realiza con una función (relativamente
nueva) de la siempre indispensable (en casos como este) </span><span class="" id="result_box" lang="es" tabindex="-1"><a href="http://man7.org/linux/man-pages/man2/ioctl.2.html" target="_blank"><b>ioctl()</b></a>. Gracias
a la función <b>SIOCETHTOOL</b> podemos verificar el funcionamiento de bajo
nivel de cada interfaz, ya que nuestro código incluye un loop con el
que analizamos todas las interfaces que, normalmente, son dos (</span><span class="" id="result_box" lang="es" tabindex="-1"><a href="https://es.wikipedia.org/wiki/Loopback" target="_blank"><b>loopback</b></a> y
Ethernet) y, a veces, más (WiFi, otra tarjeta de red, etc.). La
interfaz de </span><span class="" id="result_box" lang="es" tabindex="-1"><b>loopback</b> está </span><span class="" id="result_box" lang="es" tabindex="-1"><span class="" id="result_box" lang="es" tabindex="-1">siempre presente</span> y, si no queremos
probarla, se puede omitir haciendo un simple test sobre el nombre (que
siempre es <i>"lo"</i>, pero en cualquier caso, el nombre puede verificarse
con antelación, para evitar malentendidos, en el file<span class=""> </span></span><i>/etc/network/interfaces</i>). <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1"><span title="Il test di connettività si basa, invece su una semplice connessione tipo Client verso una direzione "sicura": nell'esempio ho usato IP e Port di google.com, ma si può usare quella che ci sembra più opportuna, ad esempio per un dispositivo">El test de conectividad se basa, en vez, en una simple conexión de tipo <b>Client</b> a una dirección <i>"segura"</i>: En el ejemplo he usado <b>IP</b> y <b>Port</b>
de <i>google.com</i>, pero se puede usar la que parece más apropiada, por ejemplo, para
un dispositivo </span><span title="collegato solo in rete locale si può usare la connessione a un server della rete, oppure ci si può collegare all'indirizzo del gateway locale, ecc.">conectado
solo a la red local, se puede usar la conexión a un servidor en la red, o
bien se puede conectar a la dirección del </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="collegato solo in rete locale si può usare la connessione a un server della rete, oppure ci si può collegare all'indirizzo del gateway locale, ecc."><a href="https://es.wikipedia.org/wiki/Puerta_de_enlace" target="_blank"><b>gateway</b></a> local, etc. </span><span title="Dipende anche se quello che necessitiamo testare è la connettività Internet o una semplice connettività locale.">También depende de si lo que tenemos que probar es conectividad a Internet o una simple conectividad local. </span><span title="Notare che tutto ruota sulla system call connect(), che attua inviando dati e ricevendo una risposta, quindi è un test più che sufficiente (senza ricorrere a un ben più complicato ping).">Tener
en cuenta que todo funciona usando la </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="Notare che tutto ruota sulla system call connect(), che attua inviando dati e ricevendo una risposta, quindi è un test più che sufficiente (senza ricorrere a un ben più complicato ping).">system call <b><a href="http://man7.org/linux/man-pages/man2/connect.2.html" target="_blank">connect()</a></b>, que
implementa el envío de datos y la recepción de una respuesta, por lo que
es un test más que suficiente (sin recurrir a un mucho más
complicado </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="Notare che tutto ruota sulla system call connect(), che attua inviando dati e ricevendo una risposta, quindi è un test più che sufficiente (senza ricorrere a un ben più complicato ping)."><b><span class="" id="result_box" lang="es" tabindex="-1"><span title="Notare che tutto ruota sulla system call connect(), che attua inviando dati e ricevendo una risposta, quindi è un test più che sufficiente (senza ricorrere a un ben più complicato ping).">ping</span></span></b>). </span><span title="Visto che la connect() si blocca per molto tempo quando non riceve subito una risposta ho inserito un opportuno timeout (leggere il commento nel codice) usando setcsockopt() + SO_SNDTIMEO, ma (leggere l'altro commento) si poteva usare anche il flag">Ya que <b>connect()</b> se bloquea durante mucho tiempo cuando no
recibe una respuesta inmediata, he añadido un oportuno timeout (<i>leer
el comentario en el código</i>) utilizando </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span title="Visto che la connect() si blocca per molto tempo quando non riceve subito una risposta ho inserito un opportuno timeout (leggere il commento nel codice) usando setcsockopt() + SO_SNDTIMEO, ma (leggere l'altro commento) si poteva usare anche il flag"><a href="http://man7.org/linux/man-pages/man2/setsockopt.2.html" target="_blank"><b>setcsockopt()</b></a> + <b>SO_SNDTIMEO</b>,
pero (<i>leer el otro comentario</i>) también se puede usar el flag </span><span title="TCP_SYNCNT, che però è una soluzione meno ortodossa e meno portabile."><b>TCP_SYNCNT</b>, que es, pero, una solución menos ortodoxa y menos portátil.</span></span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">En
un normal PC (en lugar de un sistema </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class="">embedded) con una conexión a
Internet a través de Ethernet, el resultado en condiciones normales es
el siguiente:</span></span> <br />
<pre style="background: #ffffff; color: black;">aldo@mylinux<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ <span style="color: #808030;">.</span><span style="color: #808030;">/</span>conntest
test interfaces<span style="color: #808030;">:</span> interface lo<span style="color: #808030;">:</span> OK
test interfaces<span style="color: #808030;">:</span> interface enp3s0<span style="color: #808030;">:</span> OK
test de conectividad<span style="color: #808030;">:</span> conectividad OK
</pre>
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">y, si forzamos la desconexión del Software (utilizando, por ejemplo, el </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="https://it.wikipedia.org/wiki/NetworkManager" target="_blank"><b>NetworkManager</b></a> de <b>Linux</b>) el resultado es:</span></span> <br />
<pre style="background: #ffffff; color: black;">aldo@mylinux<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ <span style="color: #808030;">.</span><span style="color: #808030;">/</span>conntest
test interfaces<span style="color: #808030;">:</span> interface lo<span style="color: #808030;">:</span> OK
test interfaces<span style="color: #808030;">:</span> interface enp3s0<span style="color: #808030;">:</span> OK
test de conectividad<span style="color: #808030;">:</span> error connect<span style="color: #808030;">:</span> Network is unreachable
</pre>
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">mientras que si forzamos la desconexión Hardware (desconectando el cable de red) el resultado es:</span></span> <br />
<pre style="background: #ffffff; color: black;">aldo@mylinux<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ <span style="color: #808030;">.</span><span style="color: #808030;">/</span>conntest
test interfaces<span style="color: #808030;">:</span> interface lo<span style="color: #808030;">:</span> OK
test interfaces<span style="color: #808030;">:</span> interface enp3s0<span style="color: #808030;">:</span> NOK
test de conectividad<span style="color: #808030;">:</span> error connect<span style="color: #808030;">:</span> Network is unreachable
</pre>
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">No está mal, ¿verdad?</span> Una función simple simple pero muy útil. <i><span class="">¡Y por hoy ya està, misión cumplida!</span></i></span> <br />
<br />
¡Hasta el próximo post!
<br />
<span class="short_text" id="result_box" lang="es" tabindex="-1"><span class=""></span></span>Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-19814416479630209942018-01-14T17:26:00.000+01:002018-01-21T01:35:37.845+01:00Remapped File cómo sincronizar un Memory Mapped File en C - pt.2<span class="" id="result_box" lang="es"><span class="">Bueno, creo que es hora de publicar la segunda parte de</span></span><span class="" id="result_box" lang="es"><span class=""> <b>Remapped File</b>.</span> <span class="">Espero
que hayáis recargado bien las pilas durante las vacaciones, tal vez viendo una
gran película como </span></span><span class="" id="result_box" lang="es"><span class=""><a href="https://es.wikipedia.org/wiki/Primer_(pel%C3%ADcula)" target="_blank"><b>Primer</b></a>, <i>que internamente contiene varios remakes de
sí misma</i>: podéis verla tantas veces como queráis y cada vez descubriréis nuevos detalles e, inevitablemente, os perderéis detalles antiguos,
entrando en un loop temporal</span> sin fin como los mismos protagonistas de la película. Os
aseguro que el post de este mes no es tan complicado de entender
como <b>Primer</b>, de la cual se encuentran en la web hasta páginas wiki dedicadas a
las <i>timeline</i> con descripciones gráficas </span><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es">incluidas</span>...</span> <br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcquyzptBujf36NJIetGPPPj6myL7qbQOG5TjnohracX5t0Y1UUZxLroG0VqixwKlHmChAs-T29_usZc0iEPn3kwvcUDICc7chBcSvrlnUoDMQib7ukn2dP6m7supkWzUi77Bs_4wiukYw/s1600/Primer.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="499" data-original-width="918" height="216" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcquyzptBujf36NJIetGPPPj6myL7qbQOG5TjnohracX5t0Y1UUZxLroG0VqixwKlHmChAs-T29_usZc0iEPn3kwvcUDICc7chBcSvrlnUoDMQib7ukn2dP6m7supkWzUi77Bs_4wiukYw/s400/Primer.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">...<span class="sel">¿qué apareció antes, el huevo o la gallina</span>?...</td></tr>
</tbody></table>
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">Entonces, sigamos con nuestra biblioteca para </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="https://es.wikipedia.org/wiki/Comunicaci%C3%B3n_entre_procesos" target="_blank"><b>IPC</b></a>.</span> <span class="">Después
de describir el </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class="">header file (<b>libmmap.h</b>) y un doble ejemplo de uso (<b>datareader.c</b> y <b>datawriter.c</b>), es hora de describir la
implementación real.</span> <span class="">Obviamente quien no ha leído la primera parte debe estar avergonzado e ir a leerla de inmediato, y luego volver aquí.</span></span><br />
<br />
Tornati? Ok, vamos al grano, ¡vamos con el código!<br />
<pre style="background: #ffffff; color: black;"><span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">string.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">sys/mman.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">fcntl.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">unistd.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;">"</span><span style="color: #40015a;">libmmap.h</span><span style="color: maroon;">"</span>
<span style="color: dimgrey;">// memMapOpenMast() - abre un mapped-file como master</span>
ShmData <span style="color: #808030;">*</span>memMapOpenMast<span style="color: #808030;">(</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>shmname<span style="color: #808030;">,</span> <span style="color: dimgrey;">// nombre del mapped file</span>
<span style="color: #603000;">size_t</span> len<span style="color: #808030;">)</span> <span style="color: dimgrey;">// size del campo data a compartir</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// abre un mapped-file (el file "shmname" se crea en /dev/shm)</span>
<span style="color: maroon; font-weight: bold;">int</span> fd<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>fd <span style="color: #808030;">=</span> shm_open<span style="color: #808030;">(</span>shmname<span style="color: #808030;">,</span> O_CREAT<span style="color: #808030;">|</span>O_RDWR<span style="color: #808030;">,</span> S_IRUSR<span style="color: #808030;">|</span>S_IWUSR<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #7d0045;">NULL</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// sale con error</span>
<span style="color: dimgrey;">// corta un mapped file</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>ftruncate<span style="color: #808030;">(</span>fd<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span>ShmData<span style="color: #808030;">)</span> <span style="color: #808030;">+</span> len<span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #7d0045;">NULL</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// </span><span style="color: dimgrey;">sale con error</span>
<span style="color: dimgrey;">// mapea un mapped-file</span>
ShmData <span style="color: #808030;">*</span>shmdata<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>shmdata <span style="color: #808030;">=</span> mmap<span style="color: #808030;">(</span><span style="color: #7d0045;">NULL</span><span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span>ShmData<span style="color: #808030;">)</span> <span style="color: #808030;">+</span> len<span style="color: #808030;">,</span>
PROT_READ <span style="color: #808030;">|</span> PROT_WRITE<span style="color: #808030;">,</span> MAP_SHARED<span style="color: #808030;">,</span> fd<span style="color: #808030;">,</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> MAP_FAILED<span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #7d0045;">NULL</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// </span><span style="color: dimgrey;">sale con error</span>
<span style="color: dimgrey;">// init semaforo</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>sem_init<span style="color: #808030;">(</span><span style="color: #808030;">&</span>shmdata<span style="color: #808030;">-</span><span style="color: #808030;">></span>sem<span style="color: #808030;">,</span> <span style="color: #008c00;">1</span><span style="color: #808030;">,</span> <span style="color: #008c00;">1</span><span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #7d0045;">NULL</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// </span><span style="color: dimgrey;">sale con error</span>
<span style="color: dimgrey;">// init flag de data_ready y longitud</span>
shmdata<span style="color: #808030;">-</span><span style="color: #808030;">></span>data_ready <span style="color: #808030;">=</span> false<span style="color: purple;">;</span>
shmdata<span style="color: #808030;">-</span><span style="color: #808030;">></span>len <span style="color: #808030;">=</span> len<span style="color: purple;">;</span>
<span style="color: dimgrey;">// retorna el descriptor</span>
<span style="color: maroon; font-weight: bold;">return</span> shmdata<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// memMapOpenMast() - abre un mapped-file como slave</span>
ShmData <span style="color: #808030;">*</span>memMapOpenSlav<span style="color: #808030;">(</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>shmname<span style="color: #808030;">,</span> <span style="color: dimgrey;">// nombre del mapped-file</span>
<span style="color: #603000;">size_t</span> len<span style="color: #808030;">)</span> <span style="color: dimgrey;">// size del campo data a compartir</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// abre un mapped-file (il file "shmname" se crea en /dev/shm)</span>
<span style="color: maroon; font-weight: bold;">int</span> fd<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>fd <span style="color: #808030;">=</span> shm_open<span style="color: #808030;">(</span>shmname<span style="color: #808030;">,</span> O_RDWR<span style="color: #808030;">,</span> S_IRUSR<span style="color: #808030;">|</span>S_IWUSR<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #7d0045;">NULL</span><span style="color: purple;">;</span> <span style="color: dimgrey;">//</span><span style="color: dimgrey;"> sale con error</span>
<span style="color: dimgrey;">// mapea un mapped-file</span>
ShmData <span style="color: #808030;">*</span>shmdata<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>shmdata <span style="color: #808030;">=</span> mmap<span style="color: #808030;">(</span><span style="color: #7d0045;">NULL</span><span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span>ShmData<span style="color: #808030;">)</span> <span style="color: #808030;">+</span> len<span style="color: #808030;">,</span>
PROT_READ <span style="color: #808030;">|</span> PROT_WRITE<span style="color: #808030;">,</span> MAP_SHARED<span style="color: #808030;">,</span> fd<span style="color: #808030;">,</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> MAP_FAILED<span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #7d0045;">NULL</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// </span><span style="color: dimgrey;">sale con error</span>
<span style="color: dimgrey;">// init semaforo</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>sem_init<span style="color: #808030;">(</span><span style="color: #808030;">&</span>shmdata<span style="color: #808030;">-</span><span style="color: #808030;">></span>sem<span style="color: #808030;">,</span> <span style="color: #008c00;">1</span><span style="color: #808030;">,</span> <span style="color: #008c00;">1</span><span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #7d0045;">NULL</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// </span><span style="color: dimgrey;">sale con error</span>
<span style="color: dimgrey;">// retorna el descriptor</span>
<span style="color: maroon; font-weight: bold;">return</span> shmdata<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// memMapClose() - cierra un mapped-file</span>
<span style="color: maroon; font-weight: bold;">int</span> memMapClose<span style="color: #808030;">(</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>shmname<span style="color: #808030;">,</span> <span style="color: dimgrey;">// nombre del mapped-file</span>
ShmData <span style="color: #808030;">*</span>shmdata<span style="color: #808030;">)</span> <span style="color: dimgrey;">// pointer al mapped-file</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// elimina semaforo</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>sem_destroy<span style="color: #808030;">(</span><span style="color: #808030;">&</span>shmdata<span style="color: #808030;">-</span><span style="color: #808030;">></span>sem<span style="color: #808030;">)</span> <span style="color: #808030;"><</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// </span><span style="color: dimgrey;">sale con error</span>
<span style="color: dimgrey;">// de-mapea un mapped-file</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>munmap<span style="color: #808030;">(</span>shmdata<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span>ShmData<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;"><</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// </span><span style="color: dimgrey;">sale con error</span>
<span style="color: dimgrey;">// cancela un mapped-file</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>shm_unlink<span style="color: #808030;">(</span>shmname<span style="color: #808030;">)</span> <span style="color: #808030;"><</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// </span><span style="color: dimgrey;">sale con error</span>
<span style="color: dimgrey;">// esce con Ok</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// memMapFlush() - flush de un mapped-file</span>
<span style="color: maroon; font-weight: bold;">int</span> memMapFlush<span style="color: #808030;">(</span>
ShmData <span style="color: #808030;">*</span>shmdata<span style="color: #808030;">)</span> <span style="color: dimgrey;">// pointer al mapped-file</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// sync en disco de un mapped-file</span>
<span style="color: maroon; font-weight: bold;">return</span> msync<span style="color: #808030;">(</span>shmdata<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span>ShmData<span style="color: #808030;">)</span> <span style="color: #808030;">+</span> shmdata<span style="color: #808030;">-</span><span style="color: #808030;">></span>len<span style="color: #808030;">,</span> MS_SYNC<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// memMapRead() - lee datos del mapped-file</span>
<span style="color: maroon; font-weight: bold;">int</span> memMapRead<span style="color: #808030;">(</span>
<span style="color: maroon; font-weight: bold;">void</span> <span style="color: #808030;">*</span>dest<span style="color: #808030;">,</span>
ShmData <span style="color: #808030;">*</span>src<span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// lock memoria</span>
sem_wait<span style="color: #808030;">(</span><span style="color: #808030;">&</span>src<span style="color: #808030;">-</span><span style="color: #808030;">></span>sem<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// test presenciaa datos en el mapped-file</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>src<span style="color: #808030;">-</span><span style="color: #808030;">></span>data_ready<span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// lee datos del mapped-file</span>
<span style="color: #603000;">memcpy</span><span style="color: #808030;">(</span>dest<span style="color: #808030;">,</span> src<span style="color: #808030;">-</span><span style="color: #808030;">></span>data<span style="color: #808030;">,</span> src<span style="color: #808030;">-</span><span style="color: #808030;">></span>len<span style="color: #808030;">)</span><span style="color: purple;">;</span>
src<span style="color: #808030;">-</span><span style="color: #808030;">></span>data_ready <span style="color: #808030;">=</span> false<span style="color: purple;">;</span>
<span style="color: dimgrey;">// unlock memoria y sale</span>
sem_post<span style="color: #808030;">(</span><span style="color: #808030;">&</span>src<span style="color: #808030;">-</span><span style="color: #808030;">></span>sem<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #008c00;">1</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">else</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// unlock memoria y sale</span>
sem_post<span style="color: #808030;">(</span><span style="color: #808030;">&</span>src<span style="color: #808030;">-</span><span style="color: #808030;">></span>sem<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// memMapWrite() - escribe datos en el mapped-file</span>
<span style="color: maroon; font-weight: bold;">void</span> memMapWrite<span style="color: #808030;">(</span>
ShmData <span style="color: #808030;">*</span>dest<span style="color: #808030;">,</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">void</span> <span style="color: #808030;">*</span>src<span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// lock memoria</span>
sem_wait<span style="color: #808030;">(</span><span style="color: #808030;">&</span>dest<span style="color: #808030;">-</span><span style="color: #808030;">></span>sem<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// escribe datos en el mapped-file</span>
<span style="color: #603000;">memcpy</span><span style="color: #808030;">(</span>dest<span style="color: #808030;">-</span><span style="color: #808030;">></span>data<span style="color: #808030;">,</span> src<span style="color: #808030;">,</span> dest<span style="color: #808030;">-</span><span style="color: #808030;">></span>len<span style="color: #808030;">)</span><span style="color: purple;">;</span>
dest<span style="color: #808030;">-</span><span style="color: #808030;">></span>data_ready <span style="color: #808030;">=</span> true<span style="color: purple;">;</span>
<span style="color: dimgrey;">// unlock memoria y sale</span>
sem_post<span style="color: #808030;">(</span><span style="color: #808030;">&</span>dest<span style="color: #808030;">-</span><span style="color: #808030;">></span>sem<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span></pre>
Como se puede ver es bastante simple y conciso, y, como siempre, el código es auto-explicativo, <a href="http://artcprogramming-es.blogspot.com.es/2012/08/no-comment.html" target="_blank"><b>ampliamente comentado</b></a> y los comentarios hablan por sí mismos. <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">Todas
las funciones utilizan, internamente, </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class="">las oportunas <i><a href="https://es.wikipedia.org/wiki/Llamada_al_sistema" target="_blank"><b>system call</b></a></i> <b>Linux/POSIX</b> para procesar el nuestro</span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="https://es.wikipedia.org/wiki/Archivo_proyectado_en_memoria" target="_blank"><b> Memory Mapped File</b></a>.</span> <span class="">En
caso de error en una </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i>system call</i> se devuelve inmediatamente <i>-1</i>, y
esto nos permite, a nivel de aplicación, usar directamente </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="http://man7.org/linux/man-pages/man3/strerror.3.html" target="_blank"><b>strerror()</b></a>
para verificar el error (<i>y este <a href="https://artcprogramming-es.blogspot.com.es/2017/11/strerror-y-sus-hermanas-cual-strerror.html" target="_blank"><b>es un tema </b></a>que mis lectores más leales
deberían conocer bien.</i></span><i>..</i>). <span class="">Como podeis ver, hay dos funciones de </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i>open</i>, una <i>master</i> y una <i>slave</i>: ¿porqué?</span> </span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span class="">Porqué, c</span></span>omo
el tipo de comunicación elegido es (ligeramente) asimétrico, es
necesario que uno de los dos extremos (el </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i>writer</i>) abra el canal,
mientras que el otro (el </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i>reader</i>) acceda al canal cuando lo encuentra creado.</span> <span class="">Entonces ahora se entiende mejor (espero) cómo funcionan las dos funciones descritas en la primera parte del post.</span> <span class="">Este
mecanismo asimétrico recuerda mucho al mecanismo </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i>Client/Server</i> que
se usa con los <i>socket</i> (<i><a href="https://artcprogramming-es.blogspot.com.es/2016/07/udphunter-como-escribir-un-udp-server.html" target="_blank"><b>otro tema</b></a> ya discutido aquí</i>) y, de hecho, esta
librería es una alternativa al <b>IPC</b> clásico con los <i>socket</i>.</span></span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1">Las funciones de</span><span class="" id="result_box" lang="es" tabindex="-1"><i> read</i> y <i>write</i> se limitan en usar </span><span class="" id="result_box" lang="es" tabindex="-1"><a href="http://man7.org/linux/man-pages/man3/memcpy.3.html" target="_blank"><b>memcpy()</b></a> para copiar los datos desde/hacia el </span><span class="" id="result_box" lang="es" tabindex="-1"><i>mapped-file</i>. Y,
como se anticipó en el post anterior, las lecturas y escrituras
utilizan un mecanismo de sincronización (un </span><span class="" id="result_box" lang="es" tabindex="-1"><a href="http://man7.org/linux/man-pages/man7/sem_overview.7.html" target="_blank"><b>POSIX unnamed semaphore</b></a>)
que se inicializa en las funciones de </span><span class="" id="result_box" lang="es" tabindex="-1"><i>open</i>: simplemente quién accede a
los datos pone en <b>rojo</b> (</span><span class="" id="result_box" lang="es" tabindex="-1"><i>lock</i>) el semáforo (entonces quién llega despues se detiene) y cuando termina, lo vuelve a poner en <b>verde</b> (</span><span class="" id="result_box" lang="es" tabindex="-1"><i>unlock</i>).</span> <br />
<br />
<span class="" id="result_box" lang="es"><span class="hps">La función</span> <span class="hps">de </span></span><span class="" id="result_box" lang="es"><span class="hps"><i>flush</i></span> <span class="hps">es</span> <span class="hps">sólo un </span></span><span class="" id="result_box" lang="es"><span class="hps"><a href="https://en.wikipedia.org/wiki/Wrapper_function" target="_blank"><b>wrapper</b></a></span> <span class="hps">para</span> la llamada </span><a href="http://man7.org/linux/man-pages/man2/msync.2.html" target="_blank"><b>msync()</b></a><span class="" id="result_box" lang="es"><span class=""></span> <span class="hps">y,</span> <span class="hps">normalmente,</span> <span class="hps">no es necesario</span> <span class="hps">utilizarla:</span> <span class="hps">con esta librería</span> <span class="hps">queremos tratar a</span> <span class="hps">los archivos</span> <span class="hps">mapeados en memoria</span> <span class="hps">para compartir</span> <span class="hps">datos entre</span> <span class="hps">procesos</span><span class="">, por lo que</span>, <i><span class="hps">no sólo</span> <span class="hps">nos interesa poco</span><span class="hps"> que</span> <span class="hps">el archivo</span> <span class="hps">tenga</span> <span class="hps">una imagen real</span> <span class="hps">en el disco</span></i>, <span class="hps">sino que, por una</span> <span class="hps">simple cuestión de</span> <span class="hps">rendimiento,</span> <i>d<span class="hps">eberíamos</span> <span class="hps">evitar de descargar en el disco </span><span class="hps">todos los cambios realizados</span> <span class="hps">en la memoria</span></i>, si no seria como usar files reales<span class="hps"></span>. <span class="hps">Entonces, ¿de que sirve el <i>flush</i></span><span class="hps">?</span> <span class="hps">Sirve sólo </span><span class="hps">para mantener una <i>eventual</i> versión</span> <span class="hps">real y</span> <span class="hps">actualizada del</span> <span class="hps">file compartido</span><span class="">,</span> en el caso que queremos <span class="hps">tratarlo, también, con</span> <span class="hps">la</span> <span class="hps">clásicas funciones</span> <b><span class="hps">open()</span></b>, <b><span class="hps">close()</span></b><span class="atn">, <b>read(</b></span><b>)</b>, <span class="hps">etc.</span> <span class="hps">Por esto en el file </span><b><span class="hps">msgwriter.c</span></b> <span class="hps">descritos</span> <span class="hps">en el post </span></span><span class="" id="result_box" lang="es"><span class="hps">anterior</span> <span class="hps">la llamada </span><b><span class="hps">memMapFlush</span><span class="hps atn">(</span>)</b> no está utilizada<span class="hps">.</span></span><br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">Y
llegamos a la otra novedad presentada en esta nueva versión de <b>libmmap</b>:
los datos procesados ahora son genéricos, por lo que las funciones de </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i>read</i> y <i>write</i> usan</span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i> void*</i> como argumentos: esto es una gran
ventaja, ya que permite intercambiar datos en <b>IPC</b></span> <span class="">usando cualquier formato;</span> una estructura compleja o una sola variable (<i>¿yo que se?</i>, ¿un <i>int</i>?). <span class="">Por
ejemplo, en el último post he definido un tipo </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><b>Data</b> (una </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i>struct</i> con un campo </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i>text</i>) para usarla en el nivel aplicación
y que se puede pasar como un argumento para las </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i>read</i> y <i>write</i> sin
siquiera hacer </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class="">un <a href="https://es.wikipedia.org/wiki/Conversi%C3%B3n_de_tipos" target="_blank"><b><i>cast</i></b></a>.</span> <span class="">Una gran
flexibilidad, similar a la de las funciones de la </span></span><i><i><a href="https://es.wikipedia.org/wiki/Biblioteca_est%C3%A1ndar_de_C" target="_blank"><b>libc</b></a></i></i><span class="" id="result_box" lang="es" tabindex="-1"><span class=""> como la </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><b>memcpy()</b>, pero
con algo más: el tamaño (e, indirectamente, el tipo) de los datos
intercambiados se pasa, de una vez por todas, durante la fase</span> de <span class=""><i>open</i>
(a través del parámetro <i>len</i>), por lo que las funciones de </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i>read</i> y <i>write</i> no tienen el clásico campo "<i>size_t len</i>" que cualquiera se esperaría.</span></span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1">Solo tenemos que describir una cosa: el truco del "<i>char data[1]</i>" utilizado para hacer que los datos </span><span class="" id="result_box" lang="es" tabindex="-1"><span class="" id="result_box" lang="es" tabindex="-1">compartidos </span>sean genéricos. Este
campo es (como era de esperar) el último de la estructura de datos que
describe el </span><span class="" id="result_box" lang="es" tabindex="-1"><i>mapped-file</i>, y funciona así: cuando creas el file se
pasa, a la <b>memMapOpenMast()</b>, el </span><span class="" id="result_box" lang="es" tabindex="-1"><i>size</i> de los datos a intercambiar (con
un operador </span><span class="" id="result_box" lang="es" tabindex="-1"><a href="https://en.wikipedia.org/wiki/Sizeof" target="_blank"><b>sizeof</b></a>, ver el ejemplo de el último post), y, como podemos ver en el código de la <b>memMapOpenMast()</b>, el </span><span class="" id="result_box" lang="es" tabindex="-1"><i>mapped-file</i> se mapea utilizando la </span><span class="" id="result_box" lang="es" tabindex="-1"><i>system call</i> <a href="http://man7.org/linux/man-pages/man2/mmap.2.html" target="_blank"><b>mmap()</b></a> pasandole un argumento </span><span class="" id="result_box" lang="es" tabindex="-1"><i>length</i> que indica el tamaño del </span><span class="" id="result_box" lang="es" tabindex="-1"><span class="" id="result_box" lang="es" tabindex="-1"></span><span class="" id="result_box" lang="es" tabindex="-1"><i>mapped-file</i></span> en cuestión: en nuestro caso pasamos <i>"sizeof(ShmData) + len"</i>, por lo que el </span><span class="" id="result_box" lang="es" tabindex="-1"><span class="" id="result_box" lang="es" tabindex="-1"><span class="" id="result_box" lang="es" tabindex="-1"><i>mapped-file</i></span> </span> está configurado para
intercambiar datos en su parte variable <i>"char data[1]"</i>, que, de base, es larga <i>un char</i>, pero es, en realidad, larga </span><span class="" id="result_box" lang="es" tabindex="-1"><i>len char</i> una vez que el file ha sido mapeado. <i>Un truquito de nada.</i></span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">Espero que os haya gustado la nueva versión de la </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class="">librería.</span> <span class="">Os
aseguro que, con solo algunas mejoras (<i>como la gestión de <b>todos </b>
los errores internos posibles, añadir <b>otro</b></i></span></span><i><span class="" id="result_box" lang="es"><span class=""><b>
semáforo</b> para administrar el lock en read/write, agregar
mecanismos de acceso </span></span></i><i><span class="" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i><i><b>blocking/nonblocking</b></i></i></span></span>... ¡bien, tal vez es un
poco más que algunas mejoras!</span></span></i><span class="" id="result_box" lang="es"><span class="">)</span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class="">, <i>se podría usar</i> en proyectos
profesionales... <i>¿y os parece poco?</i></span></span> <br />
<br />
¡Hasta el próximo post!Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-6332330014830856002017-12-23T17:03:00.001+01:002017-12-23T17:03:50.812+01:00¡Felices Fiestas!<h3 class="post-title entry-title" itemprop="name">
</h3>
<div class="post-header">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6v-ZLa5L4HEBFQQkk7Rjb-m63JI89a4GLwEXCccuOqNjcba85x9d-Ynp5Ebpm_5Riq7_5dQW_cf2LKFVQkIATmFUL58rLdhm-L82gzzQ_PX3hHs1ifIbd9hirAjeL2NW0E_4zn-3kUZjI/s1600/BuoneFeste2.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="250" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6v-ZLa5L4HEBFQQkk7Rjb-m63JI89a4GLwEXCccuOqNjcba85x9d-Ynp5Ebpm_5Riq7_5dQW_cf2LKFVQkIATmFUL58rLdhm-L82gzzQ_PX3hHs1ifIbd9hirAjeL2NW0E_4zn-3kUZjI/s400/BuoneFeste2.jpg" width="400" /></a></div>
Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-66997287571867723862017-12-09T19:32:00.000+01:002018-01-14T17:34:57.979+01:00Remapped File cómo sincronizar un Memory Mapped File en C - pt.1<span class="" id="result_box" lang="es">Este post es </span><span class="" id="result_box" lang="es">un <a href="https://es.wikipedia.org/wiki/Adaptaci%C3%B3n_(artes_audiovisuales)" target="_blank"><b>remake</b></a>. <span class="">Es
el remake de mi post (en dos episodios) de hace casi dos años, </span></span><span class="" id="result_box" lang="es"><span class=""><a href="https://artcprogramming-es.blogspot.com.es/2016/02/irrational-file-como-cear-un-memory.html" target="_blank"><b>Irrational File</b></a> (<i>venga, venga, ir enseguida a releerlo, ¡no nos hagáis rogar!</i>).</span> <span class="">Normalmente, los remake dejan un
sabor amargo, no añaden nada nuevo y, si el original era una gran
película, difícilmente pueden igualarla.</span> <span class="">Hay
excepciones, sin embargo, como </span></span><span class="" id="result_box" lang="es"><span class=""><a href="https://es.wikipedia.org/wiki/The_Thing_(pel%C3%ADcula_de_1982)" target="_blank"><b>The Thing</b></a> de <a href="https://es.wikipedia.org/wiki/John_Carpenter" target="_blank"><b>J.Carpenter</b></a> (<i>uff, también esta película ya la he
usada...</i>) que es una obra maestra superior al</span></span><span class="" id="result_box" lang="es"><span class=""> (aunque bueno)</span></span><span class="" id="result_box" lang="es"><span class=""> <a href="https://es.wikipedia.org/wiki/The_Thing_from_Another_World" target="_blank"><b>original de 1951</b></a>. Bueno, este post pertenece (espero</span><span class="">) a los remake buenos, porque, como verán, añade mucho al original.</span></span> <br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPdszw1qALca6PaYf9C5LcHXkRhCH35hAoaKc09kCVh3wQQAU7CoQT0uOWQZFZuEsuOvn-TsvvkDFPTBnGO7kV3S44ScWzoNa1GHPszmlzRFhUQOTcBiLllqaVCWCgW5MAoRuXrs4uKoca/s1600/thething2.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="544" data-original-width="1280" height="170" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPdszw1qALca6PaYf9C5LcHXkRhCH35hAoaKc09kCVh3wQQAU7CoQT0uOWQZFZuEsuOvn-TsvvkDFPTBnGO7kV3S44ScWzoNa1GHPszmlzRFhUQOTcBiLllqaVCWCgW5MAoRuXrs4uKoca/s400/thething2.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">...una imagen de The Thing para el remake de Irrational Man: empezamos bien...</td></tr>
</tbody></table>
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">Vale,el post original describía una (simple) </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span class="">librería </span></span>que había
escrito para compartir datos entre procesos (</span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="https://es.wikipedia.org/wiki/Comunicaci%C3%B3n_entre_procesos" target="_blank"><b>IPC</b></a>) utilizando como medio de comunicación </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class="">un <a href="https://es.wikipedia.org/wiki/Archivo_proyectado_en_memoria" target="_blank"><b>Memory Mapped File</b></a>.</span> <span class="">En
una frase del viejo post (que os propongo parcialmente) ya se anunciaba este remake: "</span></span><span class="" id="result_box" lang="es" tabindex="-1"><i><span class="">...para un uso sencillo este método está muy bien, pero para aplicaciones complejas [...] se debería utilizar un sistema más avanzado [...] un mutex o un se<span id="goog_994019230"></span><span id="goog_994019231"></span>máforo (pero eso es otra historia, quizás en el futuro voy a hacer un post sobre el tema)</span></i><span class="">...".</span> <span class="">Entonces ya está: esta es la versión con los accesos sincronizados de
la nuestra antigua <i>libmmap</i>, casi lista para un uso profesional.</span></span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">Repitiendo
la estructura del viejo post (<i>y si no, ¿qué remake sería?</i>) </span></span>he dividido todo en dos partes: en la primera describiré, a modo de especifica funcional, el <i>header file</i><b> </b>(<b>libmmap.h</b>) y un<b> </b>ejemplo de uso<b> </b>(<b>data.h</b>, <b>datareader.c</b> y <b>datawriter.c</b>). En la segunda entrega describiré la implementación real de la <span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span class="">librería</span></span></span></span> (<b>libmmap.c</b>).<br />
<br />
Empezamos: vamos a ver el <i>header-file</i>, <b>libmmap.h</b>:<br />
<pre style="background: #ffffff; color: black;"><span style="color: #004a43;">#</span><span style="color: #004a43;">ifndef</span><span style="color: #004a43;"> LIBMMAP_H</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">define</span><span style="color: #004a43;"> LIBMMAP_H</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">semaphore.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdbool.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">define</span><span style="color: #004a43;"> MAPNAME </span><span style="color: maroon;">"</span><span style="color: #0000e6;">/shmdata</span><span style="color: maroon;">"</span>
<span style="color: dimgrey;">// estructura del mapped-file</span>
<span style="color: maroon; font-weight: bold;">typedef</span> <span style="color: maroon; font-weight: bold;">struct</span> <span style="color: purple;">{</span>
sem_t sem<span style="color: purple;">;</span> <span style="color: dimgrey;">// semáforo dei </span><span style="color: dimgrey;"><span class="short_text" id="result_box" lang="es" tabindex="-1"><span class="">sincronización</span></span> accesos</span>
bool data_ready<span style="color: purple;">;</span> <span style="color: dimgrey;">// flag de data ready (true=ready)</span>
<span style="color: #603000;">size_t</span> len<span style="color: purple;">;</span> <span style="color: dimgrey;">// longitud del campo data</span>
<span style="color: maroon; font-weight: bold;">char</span> data<span style="color: #808030;">[</span><span style="color: #008c00;">1</span><span style="color: #808030;">]</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// datos a compartir</span>
<span style="color: purple;">}</span> ShmData<span style="color: purple;">;</span>
<span style="color: dimgrey;">// prototipos globales</span>
ShmData <span style="color: #808030;">*</span>memMapOpenMast<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>shmname<span style="color: #808030;">,</span> <span style="color: #603000;">size_t</span> len<span style="color: #808030;">)</span><span style="color: purple;">;</span>
ShmData <span style="color: #808030;">*</span>memMapOpenSlav<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>shmname<span style="color: #808030;">,</span> <span style="color: #603000;">size_t</span> len<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">int</span> memMapClose<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>shmname<span style="color: #808030;">,</span> ShmData <span style="color: #808030;">*</span>shmdata<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">int</span> memMapFlush<span style="color: #808030;">(</span>ShmData <span style="color: #808030;">*</span>shmdata<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">int</span> memMapRead<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">void</span> <span style="color: #808030;">*</span>dest<span style="color: #808030;">,</span> ShmData <span style="color: #808030;">*</span>src<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">void</span> memMapWrite<span style="color: #808030;">(</span>ShmData <span style="color: #808030;">*</span>dest<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">void</span> <span style="color: #808030;">*</span>src<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">endif</span><span style="color: #004a43;"> </span><span style="color: dimgrey;">/* LIBMMAP_H */</span>
</pre>
<span class="" id="result_box" lang="es" tabindex="-1">Simple y autoexplicativo, ¿no? la n<span class="">uestra librería utiliza una estructura de datos <i>ShmData</i> para mapear el </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i>mapped-file</i>: aquí notamos de inmediato la primera gran mejora
obtenida: hay un semáforo (</span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class="">un <a href="http://man7.org/linux/man-pages/man7/sem_overview.7.html" target="_blank"><b>POSIX unnamed semaphore</b></a>) para
sincronizar los accesos a la memoria, lo que hace que la nuestra librería
sea adecuada para un verdadero</span> <span class="">uso </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="https://es.wikipedia.org/wiki/Multitarea" target="_blank"><b>multitask</b></a> (y <a href="https://es.wikipedia.org/wiki/Multihilo" target="_blank"><b>multithread</b></a>), que era el primer objetivo programado.</span> <span class="">El
mecanismo de sincronización lo he elegido cuidadosamente entre los
diversos disponibles: tal vez un día escriba una publicación específica
sobre el tema, pero, por el momento, me limitaré a decir que el método
elegido es simple de implementar, muy funcional y</span> <span class="">perfectamente adecuado para el propósito que hay que lograr (</span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i>¿y esto es suficiente, no?</i>).</span></span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">El </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""> flag de <i>data_ready</i> se usa (protegido por el semáforo) para indicar la disponibilidad de nuevos datos a leer.</span> <span class="">Luego
tenemos, </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i>dulcis in fundo</i>, los campos <i>len</i> y <i>data</i> que
nos llevan al segundo objetivo que había establecido: los datos
intercambiados son, ahora, genéricos, con formato y longitud que se
deciden a nivel de aplicación.</span> Nótese, de hecho, que el
campo <i>data</i> es un array de <i>dimensión 1</i>: esto es una especie de truco
(a usar con las debidas precauciones) bastante utilizado en <b>C</b> para
tratar datos genéricos de forma y longitud no disponibles a priori. En
nuestra <i>struct</i> el campo debe colocarse, </span><span class="" id="result_box" lang="es" tabindex="-1"><span class="" id="result_box" lang="es" tabindex="-1">obviamente,</span> como el último
miembro, transformándola así en una especie de estructura de tamaño variable. Sin embargo, en el próximo post veremos mejor cómo funciona todo.</span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1"><b>libmmap.h</b>
termina con los prototipos de las funciones que componen la </span><span class="" id="result_box" lang="es" tabindex="-1">libreria:
tenemos dos funciones de apertura, que nos permiten abrir un </span><span class="" id="result_box" lang="es" tabindex="-1"><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i>mapped-file</i></span></span> en modo </span><span class="" id="result_box" lang="es" tabindex="-1"><i>Master</i> o <i>Slave</i> (en el próximo post veremos
el motivo de esta doble apertura); luego tenemos una función de cierre, una función de </span><span class="" id="result_box" lang="es" tabindex="-1"><i>flush</i> y, por supuesto, dos funciones para escribir y leer datos. Estas
dos últimas funciones confirman el discurso de generalidad descrito
anteriormente: las variables de </span><span class="" id="result_box" lang="es" tabindex="-1"><b>read</b>/<b>write</b> son de tipo </span><span class="" id="result_box" lang="es" tabindex="-1"><i>void*</i>,
por lo tanto, son adecuadas para aceptar cualquier tipo de datos. Como
el formato de los datos a intercambiar se mueve al nivel de la
aplicación, he escrito un </span><span class="" id="result_box" lang="es" tabindex="-1"> <i>header-file</i> (como ejemplo), <b>data.h</b>, que
está incluido en las dos aplicaciones que se comunican:</span><br />
<pre style="background: #ffffff; color: black;"><span style="color: #004a43;">#</span><span style="color: #004a43;">ifndef</span><span style="color: #004a43;"> DATA_H</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">define</span><span style="color: #004a43;"> DATA_H</span>
<span style="color: dimgrey;">// definición estructura data para aplicaciones de ejemplo</span>
<span style="color: maroon; font-weight: bold;">typedef</span> <span style="color: maroon; font-weight: bold;">struct</span> <span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">int</span> type<span style="color: purple;">;</span> <span style="color: dimgrey;">// tipo de datos</span>
<span style="color: maroon; font-weight: bold;">int</span> data_a<span style="color: purple;">;</span> <span style="color: dimgrey;">// un dato (ejemplo)</span>
<span style="color: maroon; font-weight: bold;">int</span> data_b<span style="color: purple;">;</span> <span style="color: dimgrey;">// un otro dato (ejemplo)</span>
<span style="color: maroon; font-weight: bold;">char</span> text<span style="color: #808030;">[</span><span style="color: #008c00;">1024</span><span style="color: #808030;">]</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// testo de los datos</span>
<span style="color: purple;">}</span> Data<span style="color: purple;">;</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">endif</span><span style="color: #004a43;"> </span><span style="color: dimgrey;">/* DATA_H */</span></pre>
<span class="" id="result_box" lang="es" tabindex="-1">Como podéis ver he elegido utilizar una estructura de datos que incluye un campo de
texto, pero se puede intercambiar cualquier cosa, también solo un simple <i>int</i>,
por ejemplo. Ahora vamos a ver la primera aplicación de uso, <b>datawriter.c</b>:</span> <br />
<pre style="background: #ffffff; color: black;"><span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdio.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdlib.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">string.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">errno.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;">"</span><span style="color: #40015a;">libmmap.h</span><span style="color: maroon;">"</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;">"</span><span style="color: #40015a;">data.h</span><span style="color: maroon;">"</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;">"</span><span style="color: #40015a;">mysleep.h</span><span style="color: maroon;">"</span>
<span style="color: dimgrey;">// main del programa de test</span>
<span style="color: maroon; font-weight: bold;">int</span> <span style="color: #400000;">main</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> argc<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>argv<span style="color: #808030;">[</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// abre mapped-file</span>
ShmData <span style="color: #808030;">*</span>shmdata<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>shmdata <span style="color: #808030;">=</span> memMapOpenMast<span style="color: #808030;">(</span>MAPNAME<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span>Data<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// file abierto: start loop de escritura</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> i <span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span> i <span style="color: #808030;"><</span> <span style="color: #008c00;">100</span><span style="color: purple;">;</span> i<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// compone datos para el reader</span>
Data data<span style="color: purple;">;</span>
<span style="color: #603000;">snprintf</span><span style="color: #808030;">(</span>data<span style="color: #808030;">.</span>text<span style="color: #808030;">,</span> sizeof(data<span style="color: #808030;">.</span>text)<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">nuevos datos </span><span style="color: #007997;">%d</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> i<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// escribe datos en el mapped-file</span>
memMapWrite<span style="color: #808030;">(</span>shmdata<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>data<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">he escrito: </span><span style="color: #007997;">%s</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> data<span style="color: #808030;">.</span>text<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// loop sleep</span>
mySleep<span style="color: #808030;">(</span><span style="color: #008c00;">100</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// cierra mapped-file</span>
memMapClose<span style="color: #808030;">(</span>MAPNAME<span style="color: #808030;">,</span> shmdata<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">else</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// sale con error</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">no puedo abrir el file </span><span style="color: #007997;">%s</span><span style="color: #0000e6;"> (</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">)</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> MAPNAME<span style="color: #808030;">,</span> <span style="color: #603000;">strerror</span><span style="color: #808030;">(</span>errno<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_FAILURE<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// esce con Ok</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_SUCCESS<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
</pre>
<span class="" id="result_box" lang="es" tabindex="-1">Simple, ¿no? Abre
el file compartido en la memoria (en modo </span><span class="" id="result_box" lang="es" tabindex="-1"><i>Master</i>) y lo usa para
escribir datos (en loop) para la otra aplicación de test, <b>datareader.c</b>:</span> <br />
<pre style="background: #ffffff; color: black;"><span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdio.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdlib.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">string.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">errno.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;">"</span><span style="color: #40015a;">libmmap.h</span><span style="color: maroon;">"</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;">"</span><span style="color: #40015a;">data.h</span><span style="color: maroon;">"</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;">"</span><span style="color: #40015a;">mysleep.h</span><span style="color: maroon;">"</span>
<span style="color: dimgrey;">// main del programa de test</span>
<span style="color: maroon; font-weight: bold;">int</span> <span style="color: #400000;">main</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> argc<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>argv<span style="color: #808030;">[</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// abre esperando que un writer abra come master el mapped-file</span>
ShmData <span style="color: #808030;">*</span>shmdata<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">while</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>shmdata <span style="color: #808030;">=</span> memMapOpenSlav<span style="color: #808030;">(</span>MAPNAME<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span>Data<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #7d0045;">NULL</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// acepta solo el error de file todavía no existente</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>errno <span style="color: #808030;">!</span><span style="color: #808030;">=</span> <span style="color: #7d0045;">ENOENT</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// sale con error</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;"><span style="color: #0000e6;">no puedo abrir el</span> file </span><span style="color: #007997;">%s</span><span style="color: #0000e6;"> (</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">)</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> MAPNAME<span style="color: #808030;">,</span> <span style="color: #603000;">strerror</span><span style="color: #808030;">(</span>errno<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_FAILURE<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// loop sleep</span>
mySleep<span style="color: #808030;">(</span><span style="color: #008c00;">100</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// file abierto: start loop de lectura</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> i <span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span> i <span style="color: #808030;"><</span> <span style="color: #008c00;">100</span><span style="color: purple;">;</span> i<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// busca datos a leer en el mapped-file</span>
Data data<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>memMapRead<span style="color: #808030;">(</span><span style="color: #808030;">&</span>data<span style="color: #808030;">,</span> shmdata<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// enseña los datos leidos</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">me has escrito: </span><span style="color: #007997;">%s</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> data<span style="color: #808030;">.</span>text<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// loop sleep</span>
mySleep<span style="color: #808030;">(</span><span style="color: #008c00;">100</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// cierra mapped-file y sale con Ok</span>
memMapClose<span style="color: #808030;">(</span>MAPNAME<span style="color: #808030;">,</span> shmdata<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_SUCCESS<span style="color: purple;">;</span>
<span style="color: purple;">}</span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""></span></span>
<span class="" id="result_box" lang="es" tabindex="-1"><span class=""></span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""></span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""></span></span></pre>
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">El </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><span class="" id="result_box" lang="es" tabindex="-1"><span class=""></span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i>reader</i></span></span> es, como se nota, una aplicación especular del <i>writer</i></span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""> (lee en lugar de escribir).</span> <span class="">Notar que, en ambas aplicaciones, se testean los errores en las funciones
de <i>open</i> y se cierra (si necesario) la ejecución enseñando
el error con </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="http://man7.org/linux/man-pages/man3/strerror.3.html" target="_blank"><b>strerror()</b></a>: esto es posible porque (como veremos en el proximo post) las funciones de apertura salen</span> <span class="">en
caso de error de las funciones de la </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="https://it.wikipedia.org/wiki/Libreria_standard_del_C" target="_blank"><b>libc</b></a> que usan internamente, y, en
ese punto, la descripción del error que ha ocurrido está disponible con </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><a href="http://man7.org/linux/man-pages/man3/errno.3.html" target="_blank"><b>errno</b></a> (<i>pero de esto <a href="https://artcprogramming-es.blogspot.com.es/2017/11/strerror-y-sus-hermanas-cual-strerror.html" target="_blank"><b>hemos hablado extensamente</b></a> en los dos últimos
post ¿recuerdan?</i>).</span></span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">¿Usando las dos aplicaciones en dos terminales diferentes qué vamos a ver?</span></span><br />
<pre style="background: #ffffff; color: black;"><u><b>En la terminal <span style="color: #008c00;">1</span><span style="color: #808030;">:</span></b></u>
aldo@mylinux<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ <span style="color: #808030;">.</span><span style="color: #808030;">/</span>datawriter
he escrito<span style="color: #808030;">:</span> nuevos datos <span style="color: #008c00;">1</span>
he escrito<span style="color: #808030;">:</span> nuevos datos <span style="color: #008c00;">2</span>
he escrito<span style="color: #808030;">:</span> nuevos datos <span style="color: #008c00;">3</span>
<span style="color: #808030;">^</span>C
<u><b>En la terminal <span style="color: #008c00;">2</span><span style="color: #808030;">:</span></b></u>
aldo@mylinux<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ <span style="color: #808030;">.</span><span style="color: #808030;">/</span>datareader
me has escrito<span style="color: #808030;">:</span> nuevos datos <span style="color: #008c00;">1</span>
me has escrito<span style="color: #808030;">:</span> nuevos datos <span style="color: #008c00;">2</span>
me has escrito<span style="color: #808030;">:</span> nuevos datos <span style="color: #008c00;">3</span></pre>
<span class="" id="result_box" lang="es" tabindex="-1"><span class="">Para
enviar los loop a dormir he utilizado la función <b>mySleep()</b>, que es
<a href="https://artcprogramming-es.blogspot.com.es/2016/01/mad-sleep-como-escribir-un-wrapper-para.html" target="_blank"><b>una nuestra vieja conocida</b></a>: se puede insertar en una </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class="">libreria
separada o en la misma </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class="">libreria <b>libmmap</b> (yo he utilizado un file separado
<b>mysleep.c</b> con su </span></span><span class="" id="result_box" lang="es" tabindex="-1"><span class=""><i>header-file</i> <b>mysleep.h</b></span> que solo contiene el prototipo). <span class="">En este simple ejemplo, las dos aplicaciones que se comunican pueden
detenerse usando CTRL-C, y (cuando vais a usar la libreria) podrais
verificar que iniciando/detenendo/reiniciando ambas aplicaciones, en
cualquier orden, siempre se vuelven a sincronizar sin problemas.</span></span> <br />
<br />
<span class="" id="result_box" lang="es" tabindex="-1">Por hoy hemos terminado. Esperando
la segunda parte, podríais intentar imaginar cómo será la implementación
de la libreria que os presentaré... <i>pero llega la Navidad e imagino (y
espero) que tengais cosas más interesantes en qué pensar en este período ...</i></span> <br />
<br />
¡Hasta el próximo post!
Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-34330696393914312552017-11-25T19:10:00.002+01:002017-11-27T18:18:58.653+01:00Errno y sus hermanos cómo està implementado errno en CEste post es un necesario addendum al <a href="https://artcprogramming-es.blogspot.com.es/2017/11/strerror-y-sus-hermanas-cual-strerror.html" target="_blank"><b>post anterior</b></a> (<i>que si aún no lo habéis leído deberíais hacerlo en seguida, ya que están estrechamente vinculados</i>). Esta vez hablaremos de <a href="http://man7.org/linux/man-pages/man3/errno.3.html" target="_blank"><b>errno</b></a> y sus hermanos, una familia compleja como la de <a href="https://es.wikipedia.org/wiki/Rocco_y_sus_hermanos" target="_blank"><b>Rocco y sus hermanos</b></a>, una obra maestra del neorrealismo Italiano.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuKllyc3ZyM9Yh8GrQpD8oRuaUEkuCPeDC_8gsJOZxRZ8h4mYOQG9tES2fNqx2dgbv5Ehd8zTgWhqxt4uoa0Pvfd8LRuinqAJx2MAWABCLxXdzgN3sNDiVwiRg7rE6EyG5jEt3ai0uEckg/s1600/AD-Rocco-e-i-suoi-fratelli.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="200" data-original-width="350" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuKllyc3ZyM9Yh8GrQpD8oRuaUEkuCPeDC_8gsJOZxRZ8h4mYOQG9tES2fNqx2dgbv5Ehd8zTgWhqxt4uoa0Pvfd8LRuinqAJx2MAWABCLxXdzgN3sNDiVwiRg7rE6EyG5jEt3ai0uEckg/s400/AD-Rocco-e-i-suoi-fratelli.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Rocco Errno y sus hermanos</td></tr>
</tbody></table>
Vamos al grano: después de leer el último post, los lectores más atentos se habrán preguntado: "<i>Ok, con </i><i><i><a href="http://man7.org/linux/man-pages/man3/strerror.3.html" target="_blank"><b>strerror_r()</b></a></i> podemos manejar las cadenas de error de una manera </i><i><i><a href="https://es.wikipedia.org/wiki/Seguridad_en_hilos" target="_blank"><b>thread-safe</b></a></i>, pero ¿de qué nos sirve si, en la base de todo, hay la variable global <b>errno</b> que realmente no tiene el aire de ser <b>thread-safe</b>?</i>" La pregunta es legítima, y para responder tenemos que retroceder un poco en el tiempo... la historia es análoga y paralela a la de <b>strerror()</b> (<i>¡solo faltaria que no!</i>). Antiguamente <b>errno</b> estaba definido en el <i>header</i> <b>errno.h</b>: <br />
<pre style="background: #ffffff; color: black;"><span style="color: maroon; font-weight: bold;"> extern</span> <span style="color: maroon; font-weight: bold;">int</span> errno<span style="color: purple;">;</span></pre>
y hacia referencia a una simple variable global de la <a href="https://es.wikipedia.org/wiki/Biblioteca_est%C3%A1ndar_de_C" target="_blank"><b>libc</b></a>, exactamente un <i>int</i> llamado <b>errno</b>. Luego vinieron los <i>thread</i>, con el estándar <a href="https://es.wikipedia.org/wiki/POSIX" target="_blank"><b>POSIX</b></a> <b>1003.1c</b> (también conocido como <b>POSIX.1c</b>, pero hace lo mismo), y con él vino también la <b>strerror_r()</b> y, como no, también <b>errno</b> ha conseguido un hermano <b>thread-safe</b>. Como bien se puede leer en el manual de <b>errno</b> (después de <b>POSIX.1c</b>) ahora <b>errno</b> es:<br />
<pre style="background: #ffffff; color: black;"> errno is defined by the ISO C standard to be a modifiable lvalue of
type int<span style="color: #808030;">,</span> and must not be explicitly declared<span style="color: #808030;">;</span> errno may be a macro<span style="color: #808030;">.</span>
errno is thread<span style="color: #808030;">-</span>local<span style="color: #808030;">;</span> setting it in one thread does not affect its
value in any other thread<span style="color: #808030;">.</span></pre>
Entonces la nueva definición de <b>errno</b> ahora está en <i>bits/errno.h</i> (que se incluye desde el clásico <i>errno.h</i>). Simplificando un poco (he omitido algunos detalles para que sea más fácil de leer) la nueva impostacion es:<br />
<pre style="background: #ffffff; color: black;"><u><b>en el header-file errno<span style="color: #808030;">.</span>h</b></u>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">bits/errno.h</span><span style="color: maroon;">></span>
<span style="color: dimgrey;">/* Declare the `errno' variable, unless it's defined as a macro by</span>
<span style="color: dimgrey;"> bits/errno.h. This is the case in GNU, where it is a per-thread</span>
<span style="color: dimgrey;"> variable. This redeclaration using the macro still works, but it</span>
<span style="color: dimgrey;"> will be a function declaration without a prototype and may trigger</span>
<span style="color: dimgrey;"> a -Wstrict-prototypes warning. */</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">ifndef</span><span style="color: #004a43;"> errno</span>
<span style="color: maroon; font-weight: bold;">extern</span> <span style="color: maroon; font-weight: bold;">int</span> errno<span style="color: purple;">;</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">endif</span>
<u><b>en el header-file bits<span style="color: #808030;">/</span>errno<span style="color: #808030;">.</span>h</b></u>
<span style="color: dimgrey;">/* Function to get address of global `errno' variable. */</span>
<span style="color: maroon; font-weight: bold;">extern</span> <span style="color: maroon; font-weight: bold;">int</span> <span style="color: #808030;">*</span>__errno_location <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">void</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">/* When using threads, errno is a per-thread value. */</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">define</span><span style="color: #004a43;"> errno </span><span style="color: #808030;">(</span><span style="color: #808030;">*</span><span style="color: #004a43;">__errno_location </span><span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span>
</pre>
Entonces, en pocas palabras, ahora <b>errno</b> ya no es un <i>int</i> global, sino que es "<i>el contenido de una dirección devuelta por una función global</i>". Obviamente, la variable <i>int</i> a la que apunta este objeto es el nuevo <i>errno</i> local de un <i>thread</i> (es decir, cada <i>thread</i> tiene su propio <b>errno</b>). Un ejemplo (muuuy simplificado) de cómo se podría implementar la <b>__errno_location() </b>es el siguiente: <br />
<pre style="background: #ffffff; color: black;"><span style="color: dimgrey;">// errno local de un thread: es una variable de tipo Thread-local storage (TLS)</span>
<span style="color: maroon; font-weight: bold;">__thread</span> <span style="color: maroon; font-weight: bold;">int</span> th_errno<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">int</span> <span style="color: #808030;">*</span>__errno_location<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">void</span><span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// retorna la dirección de la variable th_errno</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #808030;">&</span>th_errno<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
</pre>
Y, al final de todo, a pesar de los cambios descritos, aún será posible hacer operaciones como estas:<br />
<pre style="background: #ffffff; color: black;"><span style="color: maroon; font-weight: bold;">int</span> my_errno <span style="color: #808030;">=</span> errno<span style="color: purple;">;</span> <span style="color: dimgrey;">// Ok, equivale a: int my_errno = (* __errno_location());</span>
errno <span style="color: #808030;">=</span> <span style="color: #7d0045;">EADDRINUSE</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// Ok, equivale a: (* __errno_location()) = EADDRINUSE;</span>
</pre>
porque, por supuesto, todo ha sido diseñado para ser retro-compatible, y por lo tanto <b>errno</b>, aunque ahora es una macro, todavía tiene que comportarse como si fuera un simple <i>int</i>.<br />
<br />
Y, para terminar a lo grande, no podemos renunciar a un pequeño extracto del estándar <b>POSIX.1c</b>, que señala todo lo que se ha dicho hasta ahora:<br />
<pre style="background: #ffffff; color: black;"><b>Redefinition of errno</b>
In POSIX<span style="color: #008c00;">.1</span><span style="color: #808030;">,</span> errno is defined as an external global variable<span style="color: #808030;">.</span> But this definition
is unacceptable in a multithreaded environment<span style="color: #808030;">,</span> because its use can result in
nondeterministic results<span style="color: #808030;">.</span> The problem is that two or more threads can encounter
errors<span style="color: #808030;">,</span> all causing the same errno to be set<span style="color: #808030;">.</span> Under these circumstances<span style="color: #808030;">,</span> a thread
might end up checking errno after it has already been updated by another thread<span style="color: #808030;">.</span>
To circumvent the resulting nondeterminism<span style="color: #808030;">,</span> POSIX<span style="color: #008c00;">.</span>1c redefines errno as a service
that can access the per<span style="color: #808030;">-</span>thread error number as follows <span style="color: #808030;">(</span>ISO<span style="color: #808030;">/</span>IEC <span style="color: #008c00;">9945</span><span style="color: #808030;">:</span><span style="color: #008c00;">1</span><span style="color: #808030;">-</span><span style="color: #008c00;">1996</span><span style="color: #808030;">,</span> n<span style="color: #008c00;">2.4</span><span style="color: #808030;">)</span><span style="color: #808030;">:</span>
Some functions may provide the error number in a variable accessed through the
symbol errno<span style="color: #808030;">.</span> The symbol errno is defined by including the header <span style="color: #808030;"><</span>errno<span style="color: #008c00;">.</span>h<span style="color: #808030;">></span><span style="color: #808030;">,</span> as
specified by the C Standard <span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span> For each thread of a process<span style="color: #808030;">,</span> the value of errno
shall not be affected by function calls or assignments to errno by other threads<span style="color: #808030;">.</span>
In addition<span style="color: #808030;">,</span> all POSIX<span style="color: #008c00;">.</span>1c functions avoid using errno and<span style="color: #808030;">,</span> instead<span style="color: #808030;">,</span> return the
error number directly as the function return value<span style="color: #808030;">,</span> with a return value of zero
indicating that no error was detected<span style="color: #808030;">.</span> This strategy is<span style="color: #808030;">,</span> in fact<span style="color: #808030;">,</span> being followed
on a POSIX<span style="color: #808030;">-</span>wide basis for all new functions<span style="color: #808030;">.</span><span style="color: #808030;"></span></pre>
Notar que la ultima parte del extracto (de <i>In addition...</i> en adelante) explica el porqué existe la<b> strerror_r()</b> <a href="http://pubs.opengroup.org/onlinepubs/007904875/basedefs/xbd_chap02.html#tag_02_01_04" target="_blank"><b>XSI-compliant</b></a> descrita en el post anterior: ¿Habéis visto? <i>todo se aclara al final...</i> Y con esto también podemos considerar resuelto el misterio del <b>errno thread-safe</b>. ¡Misión cumplida!<br />
<br />
¡Hasta el próximo post!Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-69705299258473869982017-11-11T20:11:00.000+01:002017-11-25T18:50:43.435+01:00Strerror y sus hermanas qué strerror escoger en CEsta es una historia de encuentros extraños y relaciones equivocadas, como en la obra maestra (otra más) del gran <a href="https://es.wikipedia.org/wiki/Woody_Allen" target="_blank"><b>Woody</b></a>, <a href="https://es.wikipedia.org/wiki/Hannah_y_sus_hermanas" target="_blank"><b>"Hannah y sus hermanas"</b></a>. La vida a menudo nos lleva a tomar decisiones importantes, como le sucedió a Hannah, y otras un poco menos importantes como las que veremos en breve... sin embargo, siempre de decisiones se trata.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSvxpme9uJc3KTSL3XW1fO9-UZ9pKqiansfvvijJx5Kb7j4li6uEdHm-14wP5qXLgGOW8Tu10drlmXiMz67inMtrteFQJC5h6eEjp7gr_ex8AY7mEUwVpPLIn7pAOdjyvIqr3bD9F2HgiY/s1600/hannah-e-le-sue-sorelle.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="572" data-original-width="1100" height="207" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSvxpme9uJc3KTSL3XW1fO9-UZ9pKqiansfvvijJx5Kb7j4li6uEdHm-14wP5qXLgGOW8Tu10drlmXiMz67inMtrteFQJC5h6eEjp7gr_ex8AY7mEUwVpPLIn7pAOdjyvIqr3bD9F2HgiY/s400/hannah-e-le-sue-sorelle.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Hannah Strerror y sus hermanas</td></tr>
</tbody></table>
<i>(Abro un paréntesis: en este post hablaremos de la </i><i><i><a href="http://man7.org/linux/man-pages/man3/strerror.3.html" target="_blank"><b>strerror()</b></a></i> y sus variantes. La <b>strerror()</b> es una función de la </i><i><i><a href="https://es.wikipedia.org/wiki/Biblioteca_est%C3%A1ndar_de_C" target="_blank"><b>libc</b></a></i> que, al pasarle un número de error, te devuelve la cadena descriptiva correspondiente. Muchas funciones de librería y system calls en el caso de un error actualizan el valor de una variable global, </i><i><i><a href="http://man7.org/linux/man-pages/man3/errno.3.html" target="_blank"><b>errno</b></a></i>, que contiene, en cualquier momento, el valor del último error de ejecución. Existe otra variable global, <b>_sys_errlist</b>, que contiene las cadenas correspondientes a cada <b>errno</b>, de modo que antes de que otra parte del programa en ejecución modifique el valor de <b>errno</b>, deberíamos ubicar en <b>_sys_errlist</b> la cadena de error que queremos tratar. Como adelantado, esta última operación se puede realizar usando la <b>strerror()</b>, de la que ya hemos hablado <a href="https://artcprogramming-es.blogspot.com.es/2016/05/strerror-simple-como-escribir-una.html" target="_blank"><b>aquí</b></a> de manera indirecta. Cierro el paréntesis) </i><br />
<br />
Se dijo: decisiones. La <b>strerror()</b> tiene muchas personalidades, entonces, ¿cual elijo? ¿la <b>strerror()</b> o la <b>strerror_r()</b>? Y si utilizo esta última, ¿elijo la versión <a href="http://pubs.opengroup.org/onlinepubs/007904875/basedefs/xbd_chap02.html#tag_02_01_04" target="_blank"><b>XSI-compliant</b></a> o la versión <a href="https://es.wikipedia.org/wiki/Glibc" target="_blank"><b>GNU-specific</b></a>? (<i>sin mencionar, luego, las otras variantes, la strerror_l(), la strerror_s(), etc., pero estas son variantes secundarias</i>).<br />
<br />
Comencemos con la primera pregunta: ¿<b>strerror()</b> o <b>strerror_r()</b>? Antiguamente existía solo la primera, pero luego aparecieron los thread y comenzaron los problemas, porque en el código multi-thread hay algunas partes críticas donde solo deberían usarse las funciones <a href="https://es.wikipedia.org/wiki/Seguridad_en_hilos" target="_blank"><b>thread-safe</b></a>. La <b>strerror()</b> no está declarada <b>thread-safe</b> en el estándar, y para entender por qué es suficiente analizar una implementación simplificada (aunque muy similar a las implementaciones reales que podemos encontrar en las diversas <b>libc</b> disponibles). ¡Vamos con el código!<br />
<pre style="background: #ffffff; color: black;"><span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdio.h</span><span style="color: maroon;">></span><span style="color: #004a43;"> </span><span style="color: dimgrey;">// stdio.h incluye sys_errlist.h que declara las variables</span>
<span style="color: dimgrey;">// globales _sys_errlist (array errores) y _sys_nerr (num.errores)</span>
<span style="color: maroon; font-weight: bold;">static</span> <span style="color: maroon; font-weight: bold;">char</span> buf<span style="color: #808030;">[</span><span style="color: #008c00;">256</span><span style="color: #808030;">]</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// buffer global estático para la string a retornar</span>
<span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span><span style="color: #603000;">strerror</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> errnum<span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// test si errnum es un valor valido</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>errnum <span style="color: #808030;"><</span> <span style="color: #008c00;">0</span> <span style="color: #808030;">|</span><span style="color: #808030;">|</span> errnum <span style="color: #808030;">></span><span style="color: #808030;">=</span> _sys_nerr <span style="color: #808030;">|</span><span style="color: #808030;">|</span> _sys_errlist<span style="color: #808030;">[</span>errnum<span style="color: #808030;">]</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #7d0045;">NULL</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// error desconocido: copio en buf un mensaje de error genérico</span>
<span style="color: #603000;">snprintf</span><span style="color: #808030;">(</span>buf<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span>buf<span style="color: #808030;">)</span><span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">Unknown error </span><span style="color: #007997;">%d</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> errnum<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">else</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// error conocido: copio en buf el mensaje correspondiente</span>
<span style="color: #603000;">snprintf</span><span style="color: #808030;">(</span>buf<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span>buf<span style="color: #808030;">)</span><span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #007997;">%s</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> _sys_errlist<span style="color: #808030;">[</span>errnum<span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// retorno buf que ahora contiene el mensaje de error</span>
<span style="color: maroon; font-weight: bold;">return</span> buf<span style="color: purple;">;</span>
<span style="color: purple;">}</span></pre>
es obvio por el código (<a href="https://artcprogramming-es.blogspot.com.es/2012/08/no-comment.html" target="_blank"><b>bien comentado</b></a>, como siempre, así que no tengo que explicarlo línea por línea) que la <b>strerror()</b> no devuelve directamente <i>_sys_errlist [errnum]</i> (y si fuera así sería <b>thread-safe</b>) sino crea un mensaje de error (para tratar eventuales <i>errnum</i> no válidos) usando un buffer global estático <i>buf</i>: entonces si dos thread de una aplicación usan (casi) al mismo tiempo, la <b>strerror()</b> el contenido de <i>buf</i> no será fiable (prevalece el thread que ha escrito por ultimo). <br />
<i><br /></i>
<i>(Otro paréntesis: no es imposible escribir una <b>strerror()</b> que sea <b>thread-safe</b>, y en algunos sistemas sí lo es: pero dado que según el estándar no lo es, no podemos estar seguros de que en el sistema que estamos usando (o en el sistema en que un día se ejecutará la aplicación que estamos escribiendo) no haya una implementación como la que acabamos de describir, así que...)</i><br />
<br />
Entonces, para el software multi-thread, ha nacido la <b>strerror_r()</b> que es <b>thread-safe</b>. ¿Cómo funciona? ¡Vamos con el código!<br />
<pre style="background: #ffffff; color: black;"><span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdio.h</span><span style="color: maroon;">></span><span style="color: #004a43;"> </span><span style="color: dimgrey;">// </span><span style="color: dimgrey;">stdio.h incluye sys_errlist.h que declara las variables</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;">globales _sys_errlist (array errores) y _sys_nerr (num.errores)</span>
<span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>strerror_r<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> errnum<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>buf<span style="color: #808030;">,</span> <span style="color: #603000;">size_t</span> buflen<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// test </span><span style="color: dimgrey;">si errnum es un valor valido</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>errnum <span style="color: #808030;"><</span> <span style="color: #008c00;">0</span> <span style="color: #808030;">|</span><span style="color: #808030;">|</span> errnum <span style="color: #808030;">></span><span style="color: #808030;">=</span> _sys_nerr <span style="color: #808030;">|</span><span style="color: #808030;">|</span> _sys_errlist<span style="color: #808030;">[</span>errnum<span style="color: #808030;">]</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #7d0045;">NULL</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;">error desconocido: copio en buf un mensaje de error genérico</span>
<span style="color: #603000;">snprintf</span><span style="color: #808030;">(</span>buf<span style="color: #808030;">,</span> buflen<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">Unknown error </span><span style="color: #007997;">%d</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> errnum<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">else</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;">error conocido: copio en buf el mensaje correspondiente</span>
<span style="color: #603000;">snprintf</span><span style="color: #808030;">(</span>buf<span style="color: #808030;">,</span> buflen<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #007997;">%s</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> _sys_errlist<span style="color: #808030;">[</span>errnum<span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;">retorno buf que ahora contiene el mensaje de error</span>
<span style="color: maroon; font-weight: bold;">return</span> buf<span style="color: purple;">;</span>
<span style="color: purple;">}</span></pre>
también en este caso se trata de un código simplificado, pero muy cercano a la realidad: el truco es simple, en lugar de usar un buffer global estático (que es el origen de los problemas de la <b>strerror()</b>) el que llama la función debe preocuparse de asignar y pasar un buffer (y su longitud) a la <b>strerror_r()</b>. De esta forma, el buffer que usa <b>strerror_r()</b> es local para el thread que lo llama, y no puede ser sobrescrito por otro thread concurrente. Hemos sacrificado un poco de simplicidad de uso ¡pero hemos conseguido el deseado comportamiento <b>thread-safe</b>! <br />
<br />
Y ahora añadimos una pequeña complicación: la versión de <b>strerror_r()</b> que se acaba de mostrar es la <b>GNU-specific</b>. Pero, desafortunadamente, existe también la <b> XSI-compliant</b>, que es la siguiente:<br />
<pre style="background: #ffffff; color: black;"><span style="color: maroon; font-weight: bold;">int</span> strerror_r<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> errnum<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>buf<span style="color: #808030;">,</span> <span style="color: #603000;">size_t</span> buflen<span style="color: #808030;">)</span><span style="color: purple;">;</span></pre>
Como se puede ver, esta segunda versión no devuelve el buffer con la <i>error-string</i>, y devuelve, en vez, un código de error, y la cadena encontrada hay que buscarla directamente en el buffer que hemos pasado. Con respecto al código de error, es 0 en caso de éxito, y dependiendo de la versión de <b>libc</b> en uso, puede devolver -1 si hay un error (seteando <b>errno</b> con el valor de error específico) o un valor positivo correspondiente a <b>errno</b> (<i>bah, este comportamiento dual no es realmente el máximo en simplicidad de uso...</i>). Para usar esta versión o la <b>GNU-specific</b>, hay que jugar correctamente con los flag <b>_GNU_SOURCE</b>, <b>_POSIX_C_SOURCE</b> y <b>_XOPEN_SOURCE</b> del preprocesador (como se describe en el manual de la <b>strerror()</b>). <br />
<br />
Y ahora estamos listos para la segunda decisión: ¿qué usamos, la <b>GNU-specific</b> o la <b>XSI-compliant</b>? Bueno, yo diría que cuando escribimos código para tratar los códigos de error probablemente no nos interesa tratar también los errores generados en esta fase (<i>y en la siguiente fase, etc., etc., un loop infinito de búsqueda de errores</i>); estamos interesados, en vez, en escribir código lineal y simple... para quitarnos la duda podemos analizar dos pequeños ejemplos de uso: <br />
<u><b>GNU<span style="color: #808030;">-</span>specific</b></u>
<br />
<pre style="background: #ffffff; color: black;"><span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>my_socket <span style="color: #808030;">=</span> <span style="color: #400000;">socket</span><span style="color: #808030;">(</span>AF_INET<span style="color: #808030;">,</span> SOCK_STREAM<span style="color: #808030;">,</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// error socket()</span>
<span style="color: maroon; font-weight: bold;">char</span> errbuf<span style="color: #808030;">[</span>MAX_ERROR_LEN<span style="color: #808030;">]</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// buffer para strerror_r()</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">socket() error (</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">)</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> strerror_r<span style="color: #808030;">(</span>errno<span style="color: #808030;">,</span> errbuf<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span>errbuf<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_FAILURE<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
</pre>
<pre style="background: #ffffff; color: black;"><u><b>XSI<span style="color: #808030;">-</span>compliant</b></u>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>my_socket <span style="color: #808030;">=</span> <span style="color: #400000;">socket</span><span style="color: #808030;">(</span>AF_INET<span style="color: #808030;">,</span> SOCK_STREAM<span style="color: #808030;">,</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// error socket()</span>
<span style="color: maroon; font-weight: bold;">char</span> errbuf<span style="color: #808030;">[</span>MAX_ERROR_LEN<span style="color: #808030;">]</span><span style="color: purple;">;</span> <span style="color: dimgrey;">// buffer para strerror_r()</span>
<span style="color: maroon; font-weight: bold;">int</span> my_error <span style="color: #808030;">=</span> strerror_r<span style="color: #808030;">(</span>errno<span style="color: #808030;">,</span> errbuf<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span>errbuf<span style="color: #808030;">))</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">!</span> my_error<span style="color: #808030;">)</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">socket() error (</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">)</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> errbuf<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">else</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// proceso el error (¿quizás usando otra vez strerror_r()?)</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_FAILURE<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
</pre>
No sé lo que pensáis vosotros, ¡pero yo siempre uso la versión <b>GNU-specific</b>! <i>A vosotros la eleccion...</i> <br />
<br />
¡Hasta el próximo post!
Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-35166963739337399042017-10-07T23:31:00.000+02:002017-10-08T15:55:11.480+02:00Thread Ringers cómo usar los thread en C - pt.3<span class="" id="result_box" lang="es"><span class="">Con este post cerramos (a lo grande, espero) la mini-serie sobre los </span></span><span class="" id="result_box" lang="es"><span class=""><a href="https://es.wikipedia.org/wiki/Hilo_de_ejecuci%C3%B3n" target="_blank"><b>thread</b></a> (</span></span><span class="" id="result_box" lang="es"><span class=""><i><a href="https://es.wikipedia.org/wiki/Dead_Ringers" target="_blank"><b>Dead Ringers</b></a> para los amigos</i>).</span></span><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFufrbcA1ah4tlMk8KFc_BxRX0r1tSawWg9ifK9PVhrA6jw1RdrVvhjelq8SmvDYqaxzzoPu84qjwak55IU3GeLmsBjaNIxIbN1s0Z7oOHJazWtKCNvVKuWAY-_SapnAiKRUT_8QKYlKRf/s1600/dead-main.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="464" data-original-width="825" height="223" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFufrbcA1ah4tlMk8KFc_BxRX0r1tSawWg9ifK9PVhrA6jw1RdrVvhjelq8SmvDYqaxzzoPu84qjwak55IU3GeLmsBjaNIxIbN1s0Z7oOHJazWtKCNvVKuWAY-_SapnAiKRUT_8QKYlKRf/s400/dead-main.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">...<span class="" id="result_box" lang="es"><span class="">exactamente la imagen que te esperas en un blog de programación</span></span>...</td></tr>
</tbody></table>
<span class="" id="result_box" lang="es"><span class="">Después de los
ejemplos básicos de las dos primeras partes de la serie (<i>¿que acabáis de
leer otra vez, verdad? <a href="https://artcprogramming-es.blogspot.com.es/2017/08/thread-ringers-como-usar-los-thread-en.html" target="_blank"><b>aquí</b></a> y <a href="https://artcprogramming-es.blogspot.com.es/2017/09/thread-ringers-como-usar-los-thread-en.html" target="_blank"><b>aquí</b></a></i>), es una buena idea enseñar un ejemplo real de una de las
muchas aplicaciones que pueden tener los <b>thread</b>.</span> Y entre las
muchas, he elegido una que me parece interesante, es decir, un </span><span class="" id="result_box" lang="es"><b>Socket Server multithread</b>, donde cada conexión con un <b>Client</b> remoto se
maneja con un </span><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es"><span class=""><b>thread</b></span></span> separado. Una recomendación: antes de seguir, deberíais leer uno de mis antiguos post, o sea: </span><span class="" id="result_box" lang="es"><a href="https://artcprogramming-es.blogspot.com.es/2015/09/el-server-oscuro-la-leyenda-renace-como.html" target="_blank"><b>El Server oscuro: la leyenda renace</b></a>, que es una introducción ideal al tema actual, ya que
describe (y bien, espero) la funcionalidad y el código de un <b>Socket</b><b> </b><b>Server single-thread</b>. Por
cierto, como se verá en breve, el nuevo código que voy a mostrar está
estrechamente relacionado con el que se muestraba en el antiguo
post.</span> <br />
<br />
Y ahora vamos al grano, ¡vamos con el código!<br />
<br />
<pre style="line-height: 125%; margin: 0;"><span style="color: #999999; font-weight: bold;">#include <stdio.h></span>
<span style="color: #999999; font-weight: bold;">#include <stdlib.h></span>
<span style="color: #999999; font-weight: bold;">#include <string.h></span>
<span style="color: #999999; font-weight: bold;">#include <arpa/inet.h></span>
<span style="color: #999999; font-weight: bold;">#include <errno.h></span>
<span style="color: #999999; font-weight: bold;">#include <pthread.h></span>
<span style="color: #999999; font-weight: bold;">#define BACKLOG 10 </span><span style="color: #999988; font-style: italic;">// para listen()</span>
<span style="color: #999999; font-weight: bold;">#define MYBUFSIZE 1024</span>
<span style="color: #999988; font-style: italic;">// prototipos locales</span>
<span style="color: #445588; font-weight: bold;">void</span> <span style="font-weight: bold;">*</span><span style="color: #990000; font-weight: bold;">connHandler</span>(<span style="color: #445588; font-weight: bold;">void</span> <span style="font-weight: bold;">*</span>conn_sock);
<span style="color: #445588; font-weight: bold;">int</span> <span style="color: #990000; font-weight: bold;">main</span>(<span style="color: #445588; font-weight: bold;">int</span> argc, <span style="color: #445588; font-weight: bold;">char</span> <span style="font-weight: bold;">*</span>argv[])
{
<span style="color: #999988; font-style: italic;">// test argumentos</span>
<span style="font-weight: bold;">if</span> (argc <span style="font-weight: bold;">!=</span> <span style="color: #009999;">2</span>) {
<span style="color: #999988; font-style: italic;">// error args</span>
printf(<span style="color: #bb8844;">"%s: numero argumentos equivocado\n"</span>, argv[<span style="color: #009999;">0</span>]);
printf(<span style="color: #bb8844;">"uso: %s port [i.e.: %s 9999]\n"</span>, argv[<span style="color: #009999;">0</span>], argv[<span style="color: #009999;">0</span>]);
<span style="font-weight: bold;">return</span> EXIT_FAILURE;
}
<span style="color: #999988; font-style: italic;">// crea un socket</span>
<span style="color: #445588; font-weight: bold;">int</span> my_socket;
<span style="font-weight: bold;">if</span> ((my_socket <span style="font-weight: bold;">=</span> socket(AF_INET, SOCK_STREAM, <span style="color: #009999;">0</span>)) <span style="font-weight: bold;">==</span> <span style="font-weight: bold;">-</span><span style="color: #009999;">1</span>) {
<span style="color: #999988; font-style: italic;">// errore socket()</span>
printf(<span style="color: #bb8844;">"%s: could not create socket (%s)\n"</span>, argv[<span style="color: #009999;">0</span>], strerror(errno));
<span style="font-weight: bold;">return</span> EXIT_FAILURE;
}
<span style="color: #999988; font-style: italic;">// prepara </span><span style="color: #999988; font-style: italic;"><span style="color: dimgrey;">la struct sockaddr_in para este server</span></span>
<span style="font-weight: bold;">struct</span> sockaddr_in server; <span style="color: #999988; font-style: italic;">// (local) server socket info</span>
server.sin_family <span style="font-weight: bold;">=</span> AF_INET;
server.sin_addr.s_addr <span style="font-weight: bold;">=</span> INADDR_ANY;
server.sin_port <span style="font-weight: bold;">=</span> htons(atoi(argv[<span style="color: #009999;">1</span>]));
<span style="color: #999988; font-style: italic;">// bind informaciones del server al socket</span>
<span style="font-weight: bold;">if</span> (bind(my_socket, (<span style="font-weight: bold;">struct</span> sockaddr <span style="font-weight: bold;">*</span>)<span style="font-weight: bold;">&</span>server, <span style="font-weight: bold;">sizeof</span>(server)) <span style="font-weight: bold;">==</span> <span style="font-weight: bold;">-</span><span style="color: #009999;">1</span>) {
<span style="color: #999988; font-style: italic;">// error bind()</span>
printf(<span style="color: #bb8844;">"%s: bind failed (%s)"</span>, argv[<span style="color: #009999;">0</span>], strerror(errno));
<span style="font-weight: bold;">return</span> EXIT_FAILURE;
}
<span style="color: #999988; font-style: italic;">// start </span><span style="color: #999988; font-style: italic;"><span style="color: dimgrey;">escucha con una cola de max BACKLOG conexiones</span></span>
<span style="font-weight: bold;">if</span> (listen(my_socket, BACKLOG) <span style="font-weight: bold;">==</span> <span style="font-weight: bold;">-</span><span style="color: #009999;">1</span>) {
<span style="color: #999988; font-style: italic;">// error listen()</span>
printf(<span style="color: #bb8844;">"%s: listen failed (%s)\n"</span>, argv[<span style="color: #009999;">0</span>], strerror(errno));
<span style="font-weight: bold;">return</span> EXIT_FAILURE;
}
<span style="color: #999988; font-style: italic;">// acepta conexiones de un client entrante</span>
printf(<span style="color: #bb8844;">"%s: espera conexiones entrantes...\n"</span>, argv[<span style="color: #009999;">0</span>]);
<span style="color: #445588; font-weight: bold;">pthread_t</span> thread_id;
<span style="color: #445588; font-weight: bold;">socklen_t</span> socksize <span style="font-weight: bold;">=</span> <span style="font-weight: bold;">sizeof</span>(<span style="font-weight: bold;">struct</span> sockaddr_in);
<span style="font-weight: bold;">struct</span> sockaddr_in client; <span style="color: #999988; font-style: italic;">// (remote) client socket info</span>
<span style="color: #445588; font-weight: bold;">int</span> client_sock;
<span style="font-weight: bold;">while</span> ((client_sock <span style="font-weight: bold;">=</span> accept(my_socket, (<span style="font-weight: bold;">struct</span> sockaddr <span style="font-weight: bold;">*</span>)<span style="font-weight: bold;">&</span>client, <span style="font-weight: bold;">&</span>socksize)) <span style="font-weight: bold;">!=</span> <span style="font-weight: bold;">-</span><span style="color: #009999;">1</span>) {
printf(<span style="color: #bb8844;">"%s: conexion aceptada\n"</span>, argv[<span style="color: #009999;">0</span>]);
<span style="font-weight: bold;">if</span> (pthread_create(<span style="font-weight: bold;">&</span>thread_id, <span style="color: #999999;">NULL</span>, <span style="font-weight: bold;">&</span>connHandler, (<span style="color: #445588; font-weight: bold;">void</span><span style="font-weight: bold;">*</span>)<span style="font-weight: bold;">&</span>client_sock) <span style="font-weight: bold;">==</span> <span style="font-weight: bold;">-</span><span style="color: #009999;">1</span>) {
<span style="color: #999988; font-style: italic;">// error pthread_create()</span>
printf(<span style="color: #bb8844;">"%s: pthread_create failed (%s)\n"</span>, argv[<span style="color: #009999;">0</span>], strerror(errno));
<span style="font-weight: bold;">return</span> EXIT_FAILURE;
}
}
<span style="color: #999988; font-style: italic;">// error accept()</span>
printf(<span style="color: #bb8844;">"%s: accept failed (%s)\n"</span>, argv[<span style="color: #009999;">0</span>], strerror(errno));
<span style="font-weight: bold;">return</span> EXIT_FAILURE;
}
<span style="color: #999988; font-style: italic;">// thread function para gestión conexiones</span>
<span style="color: #445588; font-weight: bold;">void</span> <span style="font-weight: bold;">*</span><span style="color: #990000; font-weight: bold;">connHandler</span>(<span style="color: #445588; font-weight: bold;">void</span> <span style="font-weight: bold;">*</span>conn_sock)
{
<span style="color: #999988; font-style: italic;">// extrae el client socket del argumento</span>
<span style="color: #445588; font-weight: bold;">int</span> client_sock <span style="font-weight: bold;">=</span> <span style="font-weight: bold;">*</span>(<span style="color: #445588; font-weight: bold;">int</span><span style="font-weight: bold;">*</span>)conn_sock;
<span style="color: #999988; font-style: italic;">// loop di recepción mensajes del client</span>
<span style="color: #445588; font-weight: bold;">int</span> read_size;
<span style="color: #445588; font-weight: bold;">char</span> client_msg[MYBUFSIZE];
<span style="font-weight: bold;">while</span> ((read_size <span style="font-weight: bold;">=</span> recv(client_sock, client_msg, MYBUFSIZE, <span style="color: #009999;">0</span>)) <span style="font-weight: bold;">></span> <span style="color: #009999;">0</span> ) {
<span style="color: #999988; font-style: italic;">// send mensaje de retorno al client</span>
printf(<span style="color: #bb8844;">"%s: recibido mensaje del sock %d: %s\n"</span>, __func__, client_sock, client_msg);
<span style="color: #445588; font-weight: bold;">char</span> server_msg[MYBUFSIZE];
sprintf(server_msg, <span style="color: #bb8844;">"me has escrito: %s"</span>, client_msg);
send(client_sock, server_msg, strlen(server_msg), <span style="color: #009999;">0</span>);
<span style="color: #999988; font-style: italic;">// clear buffer</span>
memset(client_msg, <span style="color: #009999;">0</span>, MYBUFSIZE);
}
<span style="color: #999988; font-style: italic;">// loop terminado: test motivo</span>
<span style="font-weight: bold;">if</span> (read_size <span style="font-weight: bold;">==</span> <span style="font-weight: bold;">-</span><span style="color: #009999;">1</span>) {
<span style="color: #999988; font-style: italic;">// error recv()</span>
printf(<span style="color: #bb8844;">"%s: recv failed\n"</span>, __func__);
}
<span style="font-weight: bold;">else</span> {
<span style="color: #999988; font-style: italic;">// read_size == 0: el client se ha desconectado</span>
printf(<span style="color: #bb8844;">"%s: client disconnected\n"</span>, __func__);
}
<span style="font-weight: bold;">return</span> <span style="color: #999999;">NULL</span>;
}</pre>
<br />
<span class="" id="result_box" lang="es"><span class="">Bueno, no
vamos a contar otra vez cómo funciona un <b>Socket Server</b> (<i>ya hecho en el antiguo
post, releerlo, please</i>), pero vamos a centrarnos en las diferencias
entre el codigo <i>single-thread</i> y el <i>multithread</i>: seguramente habeis notado que son
prácticamente idénticos hasta la fase de </span></span><span class="" id="result_box" lang="es"><span class=""><a href="http://man7.org/linux/man-pages/man2/listen.2.html" target="_blank"><b>listen()</b></a></span><span class="">, e,
incluso después, las diferencias son mínimas: la fase de </span></span><span class="" id="result_box" lang="es"><span class=""><a href="http://man7.org/linux/man-pages/man2/accept.2.html" target="_blank"><b>accept()</b></a>
está ahora en un loop y se crea un nuevo </span></span><span class="" id="result_box" lang="es"><span class=""><b>thread</b> para cada conexión
aceptada (de un <b>Client</b> remoto).</span> ¿Y qué hace el </span><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es"><span class=""><b>thread</b></span></span>? <span class="">Ejecuta
la función <b>connHandler()</b> que contiene, por cierto, el loop de</span></span><span class="" id="result_box" lang="es"><span class=""> <a href="http://man7.org/linux/man-pages/man2/recv.2.html" target="_blank"><b>recv()</b></a>
que en el código antiguo se ejecutava inmediatamente después de la fase de <b>accept()</b>.</span> <span class="">tambien el siguiente test del motivo de salida (antes de tiempo) del loop está
contenido en <b>connHandler()</b>, y enseña la señal de error correcta (</span></span><span class="" id="result_box" lang="es"><span class=""><i>recv() error</i> o <i>client disconnected</i>, dependiendo del código devuelto por la <b>recv()</b>).</span></span> <br />
<br />
<span class="" id="result_box" lang="es"><span class="">¿Qué añadir?</span> <span class="">Simple y super-funcional: ¡un </span></span><span class="" id="result_box" lang="es"><span class=""><b>Socket Server multithread</b> con cuatro líneas de código!</span> Por
supuesto, la sintaxis de creación de los </span><span class="" id="result_box" lang="es"><b>thread</b> y la ejecución de la
<i>start_routine</i> del mismo</span><span class="" id="result_box" lang="es"> son idénticas a las descritas <a href="https://artcprogramming-es.blogspot.com.es/2017/08/thread-ringers-como-usar-los-thread-en.html" target="_blank"><b>aquí</b></a>. Para
probar el nuestro <b>Socket Server</b>, también es necesario compilar un <b>Socket
Client</b> (<i>por supuesto el que se describe en mi antiguo post <a href="https://artcprogramming-es.blogspot.com.es/2015/10/el-client-oscuro-la-leyenda-renace-como.html" target="_blank"><b>El Client oscuro: la leyenda renace</b></a></i>) y ejecutar, por ejemplo, una instancia del <b>Socket
Server </b>y dos instancias del <b>Socket Client</b> (en tres terminales diferentes de la misma máquina, o en tres máquinas diferentes). Ejecutando en mi máquina (<i>Linux, por supuesto</i>) en tres terminales, el resultado es el siguiente:</span><br />
<span class="" id="result_box" lang="es"> </span> <br />
<i><b>En la terminal 1:</b></i><br />
<pre style="line-height: 125%; margin: 0;">aldo@ao-linux-nb:~/blogtest$ ./sockserver-mt 9999
./sockserver-mt: espera conexiones entrantes...
./sockserver-mt: conexión aceptada
./sockserver-mt: conexión aceptada
connHandler: recibido mensaje del sock 4: pippo
connHandler: recibido mensaje del sock 5: pluto
connHandler: client disconnected
connHandler: client disconnected</pre>
<br />
<i><b>En la terminal 2:</b></i><br />
<pre style="line-height: 125%; margin: 0;">aldo@ao-linux-nb:~/blogtest$ ./sockclient 127.0.0.1 9999
Escribe un mensaje para el Server remoto: pippo
./sockclient: Server reply: me has escrito<span style="color: #808030;"></span>: pippo
Escribe un mensaje para el Server remoto: ^C
aldo@ao-linux-nb:~/blogtest$</pre>
<br />
<i><b>En la terminal 3:</b></i><br />
<pre style="line-height: 125%; margin: 0;">aldo@ao-linux-nb:~/blogtest$ ./sockclient 127.0.0.1 9999
Escribe un mensaje para el Server remoto: pluto
./sockclient: Server reply: me has escrito<span style="color: #808030;"></span>: pluto
Escribe un mensaje para el Server remoto: ^C
aldo@ao-linux-nb:~/blogtest$
</pre>
<br />
<span class="" id="result_box" lang="es"><span class="">notar
que cuando uno de los <b>Client</b> sale (con un CTRL-C, por ejemplo) el </span></span><span class="" id="result_box" lang="es"><span class=""><b>Server</b> se da cuenta y enseña, como era de esperar, </span></span><span class="" id="result_box" lang="es"><span class=""><i>client disconnected</i>... perfecto.</span><br /><br />Ok, hemos acabado con los </span><span class="" id="result_box" lang="es"><b>thread</b>. Ahora intentaré pensar en algún nuevo tema interesante para el próximo post. <i>Como siempre os recomiendo de no aguantar la respiración en la espera...</i></span><br />
<span class="" id="result_box" lang="es"><br /></span>
¡Hasta el próximo post!
Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-25721758497585186602017-09-16T23:46:00.000+02:002017-09-17T00:14:46.200+02:00Thread Ringers cómo usar los thread en C - pt.2<span class="" id="result_box" lang="es"><span class="">¿Donde lo dejamos?</span> Ah,
sí: en la primera parte de <a href="https://es.wikipedia.org/wiki/Dead_Ringers" target="_blank"><b>Dead Ringers</b></a> (oops... <a href="https://artcprogramming-es.blogspot.com.es/2017/08/thread-ringers-como-usar-los-thread-en.html" target="_blank"><b>Thread Ringers</b></a>)
hemos introducido el argumento </span><span class="" id="result_box" lang="es"><a href="https://es.wikipedia.org/wiki/Hilo_de_ejecuci%C3%B3n" target="_blank"><b>thread</b></a> hablando de la base, es decir, los </span><span class="" id="result_box" lang="es"><a href="https://en.wikipedia.org/wiki/POSIX_Threads" target="_blank"><b>POSIX Threads</b></a>. <span class="">Ahora, como prometido, vamos a
tratar de escribir el mismo ejemplo de el último post utilizando una
interfaz alternativa, o sea los </span></span><a href="http://en.cppreference.com/w/c/thread" target="_blank"><b>C11 Threads</b></a>. <br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcK1PJFmNjWs_gRj-cS382sVstTDc4IBa8kHCYDm9oyPcYHktaRdmUU302iv-Fegz2C22mn_9K4WgUMmRRV2fwff26Nqhyphenhyphen5AiGhDYP4m4GbDVmmTbf5jWZyxxef6BTSpQ-2Rhi8pxiYewL/s1600/Inseparabili.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="720" data-original-width="1280" height="225" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcK1PJFmNjWs_gRj-cS382sVstTDc4IBa8kHCYDm9oyPcYHktaRdmUU302iv-Fegz2C22mn_9K4WgUMmRRV2fwff26Nqhyphenhyphen5AiGhDYP4m4GbDVmmTbf5jWZyxxef6BTSpQ-2Rhi8pxiYewL/s400/Inseparabili.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">...te lo explicaré: yo soy un POSIX thread y tu un C11 thread...</td></tr>
</tbody></table>
<span class="" id="result_box" lang="es"><span class="">Una premisa que
parte del<a href="https://es.wikipedia.org/wiki/La_Fuerza#Lado_Oscuro:_ideolog.C3.ADa_Sith" target="_blank"><b> lado oscuro de la fuerza</b></a> (<i>bueno, el C++</i>): el </span></span><span class="" id="result_box" lang="es"><span class="">committee ISO del <b>C++</b>
decidió introducir en la versión <b>C++11</b>, los <b>thread</b> dentro del lenguaje.</span> <span class="">Por
lo tanto, no más uso directo de los </span></span><span class="" id="result_box" lang="es"><span class=""><b>POSIX Threads</b> a través de (por
ejemplo) la librería <i>libpthread</i>, sino el uso directo de constructos del
lenguaje mismo.</span> El resultado final ha sido (en mi opinión)
brillante, y los </span><span class="" id="result_box" lang="es"><b>C++11 Threads</b> son una de las pocas cosas que uso frecuentemente del <b>C++11</b> (y ya sabéis lo que pienso del mal camino tomado por el <b>C++</b> por culpa del </span><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es"><span class="">committee ISO</span></span>, y si no ir a leer <a href="https://artcprogramming-es.blogspot.com.es/2017/06/el-bueno-el-feo-y-el-vla-como-usar-los.html" target="_blank"><b>ese post</b></a>). El </span><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es"><span class="">committee ISO del </span></span></span><b>C</b> no ha podido quedarse atrás, por lo que han pensado de hacer lo
mismo con el </span><span class="" id="result_box" lang="es"><a href="https://en.wikipedia.org/wiki/C11_%28C_standard_revision%29" target="_blank"><b>C11</b></a>, por lo que los </span><span class="" id="result_box" lang="es"><b>thread</b> son ahora directamente parte del <b>C</b>... o no? Os adelanto una consideración: aprecio el </span><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es"><span class="">committee ISO del</span></span></span> <b>C</b> muchísimo más que el </span><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es"><span class="">committee ISO</span></span></span> del <b>C++</b> (<i>esto era evidente...</i>), pero en este caso me parece que
algo no ha funcionado bien: a seguir veremos por qué .</span> <br />
<br />
<span class="" id="result_box" lang="es"><span class="">¿Cómo se han realizado los nuevos <b>C11 Threads</b>?</span> <span class="">Vale, han tomado todas las funciones y variables que componen los </span></span><span class="" id="result_box" lang="es"><span class=""><b>POSIX Threads</b> y le han cambiado el nombre (y tengo que admitir que los nuevos son más
simples);</span> <span class="">además, en algunos casos (pocos, afortunadamente), han cambiado los tipos de los códigos de retorno y de los argumentos de las funciones.</span> <i>Punto</i>. ¿Brillante? <span class="">No exactamente, diría, y nada que ver con la brillante solución utilizada en <b>C++11</b>.</span> <span class="">¿Razones para usar esta nueva versión?</span> <i>Cero</i>, diría, <i>y todavía no he expuesto el problema principal...</i><br /><br />De todos modos, he reescrito el ejemplo del último post usando los <b>C11 Threads</b>. ¡Vamos con el código!</span><br />
<br />
<pre style="line-height: 125%; margin: 0;"><span style="color: #999999; font-weight: bold;">#include <stdio.h></span>
<span style="color: #999999; font-weight: bold;">#include <threads.h></span>
<span style="color: #999999; font-weight: bold;">#include <string.h></span>
<span style="color: #999999; font-weight: bold;">#include <unistd.h></span>
<span style="color: #999988; font-style: italic;">// </span><span style="color: #999988; font-style: italic;">creo un nuevo tipo parar pasar datos a los thread</span>
<span style="font-weight: bold;">typedef</span> <span style="font-weight: bold;">struct</span> _tdata {
<span style="color: #445588; font-weight: bold;">int</span> index; <span style="color: #999988; font-style: italic;">// thread index</span>
<span style="color: #445588; font-weight: bold;">int</span> <span style="font-weight: bold;">*</span>comdata; <span style="color: #999988; font-style: italic;">// </span><span style="color: #999988; font-style: italic;">dato común </span><span style="color: #999988; font-style: italic;">a los thread</span>
<span style="color: #445588; font-weight: bold;">mtx_t</span> <span style="font-weight: bold;">*</span>lock; <span style="color: #999988; font-style: italic;">// mutex </span><span style="color: #999988; font-style: italic;">común </span><span style="color: #999988; font-style: italic;">a los thread</span>
} tdata;
<span style="color: #999988; font-style: italic;">// prototipos locales</span>
<span style="color: #445588; font-weight: bold;">int</span> <span style="color: #990000; font-weight: bold;">tMyThread</span>(<span style="color: #445588; font-weight: bold;">void</span> <span style="font-weight: bold;">*</span>arg);
<span style="color: #999988; font-style: italic;">// función main()</span>
<span style="color: #445588; font-weight: bold;">int</span> <span style="color: #990000; font-weight: bold;">main</span>(<span style="color: #445588; font-weight: bold;">int</span> argc, <span style="color: #445588; font-weight: bold;">char</span><span style="font-weight: bold;">*</span> argv[])
{
<span style="color: #445588; font-weight: bold;">int</span> error;
<span style="color: #999988; font-style: italic;">// init mutex</span>
<span style="color: #445588; font-weight: bold;">mtx_t</span> lock;
<span style="font-weight: bold;">if</span> ((error <span style="font-weight: bold;">=</span> mtx_init(<span style="font-weight: bold;">&</span>lock, mtx_plain)) <span style="font-weight: bold;">!=</span> thrd_success) {
printf(<span style="color: #bb8844;">"%s:</span><span style="color: #bb8844;"><span style="color: #bb8844;"> no puedo crear el mutex</span> (error=%d)\n"</span>, argv[<span style="color: #009999;">0</span>], error);
<span style="font-weight: bold;">return</span> <span style="color: #009999;">1</span>;
}
<span style="color: #999988; font-style: italic;">// init threads</span>
<span style="color: #445588; font-weight: bold;">thrd_t</span> tid[<span style="color: #009999;">2</span>];
tdata data[<span style="color: #009999;">2</span>];
<span style="color: #445588; font-weight: bold;">int</span> comdata <span style="font-weight: bold;">=</span> <span style="color: #009999;">0</span>;
<span style="font-weight: bold;">for</span> (<span style="color: #445588; font-weight: bold;">int</span> i <span style="font-weight: bold;">=</span> <span style="color: #009999;">0</span>; i <span style="font-weight: bold;"><</span> <span style="color: #009999;">2</span>; i<span style="font-weight: bold;">++</span>) {
<span style="color: #999988; font-style: italic;">// </span><span style="color: #999988; font-style: italic;">set data del thread y crea el thread</span>
data[i].index <span style="font-weight: bold;">=</span> i;
data[i].comdata <span style="font-weight: bold;">=</span> <span style="font-weight: bold;">&</span>comdata;
data[i].lock <span style="font-weight: bold;">=</span> <span style="font-weight: bold;">&</span>lock;
<span style="font-weight: bold;">if</span> ((error <span style="font-weight: bold;">=</span> thrd_create(<span style="font-weight: bold;">&</span>tid[i], tMyThread, <span style="font-weight: bold;">&</span>data[i])) <span style="font-weight: bold;">!=</span> thrd_success)
printf(<span style="color: #bb8844;">"%s: </span><span style="color: #bb8844;"><span style="color: #bb8844;"><span style="color: #bb8844;">no puedo crear el</span></span> thread %d (error=%d)\n"</span>, argv[<span style="color: #009999;">0</span>], i, error);
}
<span style="color: #999988; font-style: italic;">// join threads </span><span style="color: #999988; font-style: italic;">y borra mutex</span>
thrd_join(tid[<span style="color: #009999;">0</span>], <span style="color: #999999;">NULL</span>);
thrd_join(tid[<span style="color: #009999;">1</span>], <span style="color: #999999;">NULL</span>);
mtx_destroy(<span style="font-weight: bold;">&</span>lock);
<span style="color: #999988; font-style: italic;">// exit</span>
printf(<span style="color: #bb8844;">"%s: thread </span><span style="color: #bb8844;"><span style="color: #bb8844;">acabados</span>: comdata=%d\n"</span>, argv[<span style="color: #009999;">0</span>], comdata);
<span style="font-weight: bold;">return</span> <span style="color: #009999;">0</span>;
}
<span style="color: #999988; font-style: italic;">// thread routine</span>
<span style="color: #445588; font-weight: bold;">int</span> <span style="color: #990000; font-weight: bold;">tMyThread</span>(<span style="color: #445588; font-weight: bold;">void</span> <span style="font-weight: bold;">*</span>arg)
{
<span style="color: #999988; font-style: italic;">// </span><span style="color: #999988; font-style: italic;">obtengo los datos del thread con un cast (tdata*) de (void*) arg</span>
tdata <span style="font-weight: bold;">*</span>data <span style="font-weight: bold;">=</span> (tdata <span style="font-weight: bold;">*</span>)arg;
<span style="color: #999988; font-style: italic;">// thread loop</span>
printf(<span style="color: #bb8844;">"thread %d </span><span style="color: #bb8844;"><span style="color: #bb8844;">comenzado</span>\n"</span>, data<span style="font-weight: bold;">-></span>index);
<span style="color: #445588; font-weight: bold;">int</span> i <span style="font-weight: bold;">=</span> <span style="color: #009999;">0</span>;
<span style="font-weight: bold;">for</span> (;;) {
<span style="color: #999988; font-style: italic;">// lock mutex</span>
mtx_lock(data<span style="font-weight: bold;">-></span>lock);
<span style="color: #999988; font-style: italic;">// incrementa comdata</span>
(<span style="font-weight: bold;">*</span>data<span style="font-weight: bold;">-></span>comdata)<span style="font-weight: bold;">++</span>;
<span style="color: #999988; font-style: italic;">// unlock mutex</span>
mtx_unlock(data<span style="font-weight: bold;">-></span>lock);
<span style="color: #999988; font-style: italic;">// test counter </span><span style="color: #999988; font-style: italic;"><span style="color: #999988; font-style: italic;">parar eventual salida del</span> loop</span>
<span style="font-weight: bold;">if</span> (<span style="font-weight: bold;">++</span>i <span style="font-weight: bold;">>=</span> <span style="color: #009999;">100</span>) {
<span style="color: #999988; font-style: italic;">// </span><span style="color: #999988; font-style: italic;"><span style="color: #999988; font-style: italic;">sale del </span>loop</span>
<span style="font-weight: bold;">break</span>;
}
<span style="color: #999988; font-style: italic;">// thread sleep (10 ms)</span>
usleep(<span style="color: #009999;">10000</span>);
}
<span style="color: #999988; font-style: italic;">// el thread sale</span>
printf(<span style="color: #bb8844;">"thread %d </span><span style="color: #bb8844;"><span style="color: #bb8844;">acabado</span>\n"</span>, data<span style="font-weight: bold;">-></span>index);
<span style="font-weight: bold;">return</span> <span style="color: #009999;">0</span>;
}</pre>
<br />
<span class="" id="result_box" lang="es"><span class="">Como se puede
ver el código es prácticamente lo mismo, me he limitado a utilizar
las nuevas funciones en lugar de las viejas (por ejemplo, </span></span><span class="" id="result_box" lang="es"><span class=""><a href="http://en.cppreference.com/w/c/thread/thrd_create" target="_blank"><b>thrd_create()</b></a> en lugar de </span></span><span class="" id="result_box" lang="es"><span class=""><a href="http://man7.org/linux/man-pages/man3/pthread_create.3.html" target="_blank"><b>pthread_create()</b></a>), he utilizado los nuevos tipos (por
ejemplo, <b>mtx_t</b> en lugar de <b>pthread_mutex_t</b>) y he ligeramente</span> <span class="">modificado
el test de los valores de retorno: pocas diferencias, tengo que decir, y,
en algunos casos, a peor: por ejemplo, </span></span><span class="" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span class="">ha desaparecido </span></span>el parámetro <i>attr</i> de <b>pthread_create()</b>, que (por simplicidad) en el último ejemplo había dejado a
NULL, pero que</span> <span class="">a veces puede ser útil (leer el manual de <b>pthread_create()</b> para darse cuenta).</span> Sin
embargo, se podría decir (sin ser demasiados exigentes) que la nueva
interfaz no nos ofrece ninguna ventaja sustancial, pero ni siquiera un
deterioro decisivo, por lo que también se podría utiliza (<i>de gustibus</i>).</span> <br />
<br />
<span class="" id="result_box" lang="es"><span class="">Pero hay un
problema: parece que los <b>C11 Threads</b> no son considerados una prioridad para
los que escriben los compiladores y las <i>libc</i>, por lo que actualmente es difícil compilar/ejecutar un programa como el que he enseñado.</span> Incluso
nuestro amado <b>GCC</b> (que suele ser el primero en dar soporte a las últimas
novedades) no soporta los nuevos <b>thread</b> (en realidad debido a la falta de
integración en la <i>glibc</i>). Por lo tanto, si realmente deseáis
usarlos a toda costa, tendréis que esperar a que algún compilador/librería proporcione el soporte completo o bien, por ejemplo, utilizar la librería </span><span class="" id="result_box" lang="es"><a href="https://github.com/jtsiomb/c11threads" target="_blank"><b>c11threads</b></a>, que no es más que un </span><span class="" id="result_box" lang="es"><i>wrapper</i> que <i>simula</i> los <b>C11
Threads</b> utilizando los </span><span class="" id="result_box" lang="es"><b>POSIX Threads</b>.</span> <br />
<br />
<span class="" id="result_box" lang="es"><span class="">Yo, al final,
he compilado el ejemplo usando lo que (creo) es la solución más interesante
actualmente disponible: he instalado en mi sistema la </span></span><span class="" id="result_box" lang="es"><span class=""><a href="https://en.wikipedia.org/wiki/Musl" target="_blank"><b>musl libc</b></a> que es
una <i>libc</i> alternativa a la <i>glibc</i>, y tiene un <i>wrapper</i> para <b>GCC</b> (<i>musl</i></span><i>-gcc</i>): <b>musl</b> proporciona (en <b>Linux</b>) el soporte completo a <b>C11</b>, <b>thread</b> incluidos. Una vez compilado, el programa se comporta correctamente, como se puede ver a continuación:</span> <br />
<br />
<pre style="line-height: 125%; margin: 0;">aldo@ao-linux-nb:~/blogtest$ musl-gcc c11thread.c -o c11thread
aldo@ao-linux-nb:~/blogtest$ ./c11thread
thread 0 comenzado
thread 1 comenzado
thread 1 acabado
thread 0 acabado
./c11thread: thread acabados: comdata=200</pre>
<br />
<span class="" id="result_box" lang="es"><span class="">¿Pero vale la pena hacer este cambio?</span> <span class="">No,
por mi parte seguiré utilizando los </span></span><span class="" id="result_box" lang="es"><span class=""><b>POSIX Threads</b>, que
utilizo desde hace años y siguen siendo la referencia de excelencia.</span> <span class="">Y
una última consideración: independientemente de lo que estamos utilizando (</span></span><span class="" id="result_box" lang="es"><span class=""><b>C11</b>/<b>C++11 threads</b>) es muy probable que, por debajo, haya los </span></span><span class="" id="result_box" lang="es"><span class=""><b>POSIX Threads</b> (esto es
cierto en muchas implementaciones).</span> Y si cuando compiláis tenéis que añadir
el flag <i>-pthread</i> entonces la duda se convierte en una certeza, ya
que con este flag vais a usar <i>libpthread</i> o sea la librería <b>POSIX Threads</b>. Sorpresa, sorpresa...</span> <br />
<br />
¡Hasta el próximo post!Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-12977450671494762032017-08-25T19:18:00.000+02:002017-08-27T12:55:54.368+02:00Thread Ringers cómo usar los thread en C - pt.1Los <a href="https://es.wikipedia.org/wiki/Hilo_de_ejecuci%C3%B3n" target="_blank"><b>thread</b></a> son un poco como los gemelos de la <a href="https://es.wikipedia.org/wiki/Dead_Ringers" target="_blank"><b>obra maestra</b></a> de <a href="https://es.wikipedia.org/wiki/David_Cronenberg" target="_blank"><b>David Cronenberg</b></a>: tienen el mismo origen, parecen iguales pero son diferentes.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEjeW3JYOhb3ldDc7e1pHUOFMdWSS4pTB9EfDm0IrdeLdOgpxjeyZgkES1PfyuxNV1fjgZk1tGT5Pe8zTfC0EKol8aSsHZcY0HHZYaxv9JcoyeqJ7CPebuTVHXOlzTod2LzuF7PHcUT39c/s1600/inseparabili.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="633" data-original-width="1143" height="221" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEjeW3JYOhb3ldDc7e1pHUOFMdWSS4pTB9EfDm0IrdeLdOgpxjeyZgkES1PfyuxNV1fjgZk1tGT5Pe8zTfC0EKol8aSsHZcY0HHZYaxv9JcoyeqJ7CPebuTVHXOlzTod2LzuF7PHcUT39c/s400/inseparabili.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">...te lo explicaré: yo soy el thread A y tu eres el B...</td></tr>
</tbody></table>
En este post (<span class="short_text" id="result_box" lang="es"><span class="">que es el primero de una serie corta</span></span>) veremos un ejemplo muy simple de cómo usar los <b>thread</b> en <b>C</b>: obviamente el tema es vasto y se puede hacer mas complicado a voluntad, pero nuestro ejemplo ya contiene los conceptos básicos para entender cómo funciona todo, o sea: <i>la creación, la sincronización y la destrucción de los thread</i>. Obviamente empezaremos usando la versión de base (casi) universal, es decir, usaremos los <a href="https://en.wikipedia.org/wiki/POSIX_Threads" target="_blank"><b>POSIX Threads</b></a>. Y ahora vamos al grano, ¡vamos con el código! <br />
<br />
<pre style="line-height: 125%; margin: 0;"><span style="color: #999999; font-weight: bold;">#include <stdio.h></span>
<span style="color: #999999; font-weight: bold;">#include <pthread.h></span>
<span style="color: #999999; font-weight: bold;">#include <string.h></span>
<span style="color: #999999; font-weight: bold;">#include <unistd.h></span>
<span style="color: #999988; font-style: italic;">// creo un nuevo tipo parar pasar datos a los thread</span>
<span style="font-weight: bold;">typedef</span> <span style="font-weight: bold;">struct</span> _tdata {
<span style="color: #445588; font-weight: bold;">int</span> index; <span style="color: #999988; font-style: italic;">// thread index</span>
<span style="color: #445588; font-weight: bold;">int</span> <span style="font-weight: bold;">*</span>comdata; <span style="color: #999988; font-style: italic;">// dato común </span><span style="color: #999988; font-style: italic;">a los thread</span>
<span style="color: #445588; font-weight: bold;">pthread_mutex_t</span> <span style="font-weight: bold;">*</span>lock; <span style="color: #999988; font-style: italic;">// mutex común </span><span style="color: #999988; font-style: italic;">a los thread</span>
} tdata;
<span style="color: #999988; font-style: italic;">// prototipos locales</span>
<span style="color: #445588; font-weight: bold;">void</span><span style="font-weight: bold;">*</span> <span style="color: #990000; font-weight: bold;">tMyThread</span>(<span style="color: #445588; font-weight: bold;">void</span> <span style="font-weight: bold;">*</span>arg);
<span style="color: #999988; font-style: italic;">// función main()</span>
<span style="color: #445588; font-weight: bold;">int</span> <span style="color: #990000; font-weight: bold;">main</span>(<span style="color: #445588; font-weight: bold;">int</span> argc, <span style="color: #445588; font-weight: bold;">char</span><span style="font-weight: bold;">*</span> argv[])
{
<span style="color: #445588; font-weight: bold;">int</span> error;
<span style="color: #999988; font-style: italic;">// init mutex</span>
<span style="color: #445588; font-weight: bold;">pthread_mutex_t</span> lock;
<span style="font-weight: bold;">if</span> ((error <span style="font-weight: bold;">=</span> pthread_mutex_init(<span style="font-weight: bold;">&</span>lock, <span style="color: #999999;">NULL</span>)) <span style="font-weight: bold;">!=</span> <span style="color: #009999;">0</span>) {
printf(<span style="color: #bb8844;">"%s: no puedo crear el mutex (%s)\n"</span>, argv[<span style="color: #009999;">0</span>], strerror(error));
<span style="font-weight: bold;">return</span> <span style="color: #009999;">1</span>;
}
<span style="color: #999988; font-style: italic;">// init threads</span>
<span style="color: #445588; font-weight: bold;">pthread_t</span> tid[<span style="color: #009999;">2</span>];
tdata data[<span style="color: #009999;">2</span>];
<span style="color: #445588; font-weight: bold;">int</span> comdata <span style="font-weight: bold;">=</span> <span style="color: #009999;">0</span>;
<span style="font-weight: bold;">for</span> (<span style="color: #445588; font-weight: bold;">int</span> i <span style="font-weight: bold;">=</span> <span style="color: #009999;">0</span>; i <span style="font-weight: bold;"><</span> <span style="color: #009999;">2</span>; i<span style="font-weight: bold;">++</span>) {
<span style="color: #999988; font-style: italic;">// set data del thread y crea el thread</span>
data[i].index <span style="font-weight: bold;">=</span> i;
data[i].comdata <span style="font-weight: bold;">=</span> <span style="font-weight: bold;">&</span>comdata;
data[i].lock <span style="font-weight: bold;">=</span> <span style="font-weight: bold;">&</span>lock;
<span style="font-weight: bold;">if</span> ((error <span style="font-weight: bold;">=</span> pthread_create(<span style="font-weight: bold;">&</span>tid[i], <span style="color: #999999;">NULL</span>, <span style="font-weight: bold;">&</span>tMyThread, (<span style="color: #445588; font-weight: bold;">void</span> <span style="font-weight: bold;">*</span>)<span style="font-weight: bold;">&</span>data[i])) <span style="font-weight: bold;">!=</span> <span style="color: #009999;">0</span>)
printf(<span style="color: #bb8844;">"%s: </span><span style="color: #bb8844;"><span style="color: #bb8844;">no puedo crear el</span> thread %d (%s)\n"</span>, argv[<span style="color: #009999;">0</span>], i, strerror(error));
}
<span style="color: #999988; font-style: italic;">// join threads y borra mutex</span>
pthread_join(tid[<span style="color: #009999;">0</span>], <span style="color: #999999;">NULL</span>);
pthread_join(tid[<span style="color: #009999;">1</span>], <span style="color: #999999;">NULL</span>);
pthread_mutex_destroy(<span style="font-weight: bold;">&</span>lock);
<span style="color: #999988; font-style: italic;">// exit</span>
printf(<span style="color: #bb8844;">"%s: thread acabados: comdata=%d\n"</span>, argv[<span style="color: #009999;">0</span>], comdata);
<span style="font-weight: bold;">return</span> <span style="color: #009999;">0</span>;
}
<span style="color: #999988; font-style: italic;">// thread routine</span>
<span style="color: #445588; font-weight: bold;">void</span><span style="font-weight: bold;">*</span> <span style="color: #990000; font-weight: bold;">tMyThread</span>(<span style="color: #445588; font-weight: bold;">void</span> <span style="font-weight: bold;">*</span>arg)
{
<span style="color: #999988; font-style: italic;">// obtengo los datos del thread con un cast (tdata*) de (void*) arg</span>
tdata <span style="font-weight: bold;">*</span>data <span style="font-weight: bold;">=</span> (tdata <span style="font-weight: bold;">*</span>)arg;
<span style="color: #999988; font-style: italic;">// thread loop</span>
printf(<span style="color: #bb8844;">"thread %d comenzado\n"</span>, data<span style="font-weight: bold;">-></span>index);
<span style="color: #445588; font-weight: bold;">int</span> i <span style="font-weight: bold;">=</span> <span style="color: #009999;">0</span>;
<span style="font-weight: bold;">for</span> (;;) {
<span style="color: #999988; font-style: italic;">// lock mutex</span>
pthread_mutex_lock(data<span style="font-weight: bold;">-></span>lock);
<span style="color: #999988; font-style: italic;">// incrementa comdata</span>
(<span style="font-weight: bold;">*</span>data<span style="font-weight: bold;">-></span>comdata)<span style="font-weight: bold;">++</span>;
<span style="color: #999988; font-style: italic;">// unlock mutex</span>
pthread_mutex_unlock(data<span style="font-weight: bold;">-></span>lock);
<span style="color: #999988; font-style: italic;">// test counter parar eventual salida del loop</span>
<span style="font-weight: bold;">if</span> (<span style="font-weight: bold;">++</span>i <span style="font-weight: bold;">>=</span> <span style="color: #009999;">100</span>) {
<span style="color: #999988; font-style: italic;">// sale del loop</span>
<span style="font-weight: bold;">break</span>;
}
<span style="color: #999988; font-style: italic;">// thread sleep (10 ms)</span>
usleep(<span style="color: #009999;">10000</span>);
}
<span style="color: #999988; font-style: italic;">// el thread sale</span>
printf(<span style="color: #bb8844;">"thread %d acabado\n"</span>, data<span style="font-weight: bold;">-></span>index);
<span style="font-weight: bold;">return</span> <span style="color: #999999;">NULL</span>;
}</pre>
<br />
Ok, como se nota es <b><a href="http://artcprogramming-es.blogspot.com.es/2012/08/no-comment.html" target="_blank">ampliamente comentado</a> </b> y así se <i>auto-explica</i>, por lo cual no voy a detenerme sobre las instrucciones y/o grupos de instrucciones (<i>¡leer los comentarios! ¡Están ahí para eso!</i>), pero voy a añadir, solamente, algunos detalles estructurales. Suponiendo que ya sabeis lo que son y para qué sirven los <b>thread</b> (si no leer algunas guías introductorias, hay algunas muy buenas en la red) el flujo de código es obvio: primero hay que crear un <a href="https://es.wikipedia.org/wiki/Cierre_de_exclusi%C3%B3n_mutua" target="_blank"><b>mutex</b></a> (con <a href="http://man7.org/linux/man-pages/man3/pthread_mutex_destroy.3p.html" target="_blank"><b>pthread_mutex_init()</b></a>) para sincronizar los <b>thread</b> que vamos a utilizar, entonces hay que inicializar los datos que hay que pasar a los <b>thread</b> y crear (con <a href="http://man7.org/linux/man-pages/man3/pthread_create.3.html" target="_blank"><b>pthread_create()</b></a>) los dos <b>thread</b> de nuestro ejemplo (init de datos y creación los he puesto en un loop de 2, pero también se hubiera podido escribir en dos pasos, obviamente). Finalmente el <b>main()</b> se pone a la espera (con <a href="http://man7.org/linux/man-pages/man3/pthread_join.3.html" target="_blank"><b>pthread_join()</b></a>) de la terminación de los <b>thread</b> y, cuando terminan, destruye el <b>mutex</b> (con <a href="http://man7.org/linux/man-pages/man3/pthread_mutex_destroy.3p.html" target="_blank"><b>pthread_mutex_destroy()</b></a>) y sale.<br />
<br />
Como se puede ver <b>pthread_create()</b> tiene cuatro parámetros, que son (en el orden): un pointer a un <i>thread descriptor</i> (que identifica de forma exclusiva el <b>thread</b> creado), un pointer a un <i>contenedor de atributos</i> del <b>thread</b> a crear, un function pointer a la <i>función que ejecutará </i>el <b>thread</b> y, finalmente, un pointer al <i>único argumento</i> que se puede pasar a la función anterior. Específicamente, en nuestro ejemplo (muy simple), he usado los atributos por defecto (usando NULL para el segundo parámetro), y he creado (con <a href="https://es.wikipedia.org/wiki/Typedef" target="_blank"><b>typedef</b></a>) un nuevo tipo ad-hoc para pasar múltiples parámetros a la función que ejecutará el <b>thread</b>, explotando el hecho de que el argumento de función por defecto es un <b>void*</b> que puede ser fácilmente transformado (con una operación de <a href="https://en.wikipedia.org/wiki/Type_conversion" target="_blank"><b>cast</b></a>) a cualquier tipo complejo (en nuestro caso el nuevo tipo <i>tdata</i>).<br />
<br />
En este ejemplo, los dos <b>thread</b> creados ejecuta la misma función, <b>tMyThread()</b> (pero también podrían realizar dos funciones completamente diferentes: en este caso, por supuesto, hubiera tenido que escribir una <b>tMyThread1()</b> y una <b>tMyThread2 ()</b>). El flujo de la función es muy simple: primero ejecuta un <b>cast</b> sobre el argumento <i>arg</i> para utilizar los datos del tipo <i>tdata</i>, luego entra en un clásico <i>thread-loop</i> infinito con salida forzada: en nuestro caso sale cuando el índice i alcanza los 100, pero en un caso real se podría forzar la salida sólo en caso de error, por ejemplo. Tener en cuenta que el <i>thread-loop</i> utiliza una <i>sleep</i> de 10 ms (usando <a href="http://man7.org/linux/man-pages/man3/usleep.3.html" target="_blank"><b>usleep()</b></a>): <i>¡intentad olvidar de poner la sleep en un thread-loop realmente infinito y ya veréis los saltos de alegría que hará la CPU del vuestro PC!</i><br />
<br />
Como se puede ver, el tipo <i>tdata</i> contiene un índice típico del <b>thread</b> (en nuestro caso es 0 o 1) y los pointer a los dos datos comunes (locales al <b>main()</b>) que son <i>comdata</i> y <i>lock</i>. Entonces ¿qué hace el <i>thread-loop</i>? Puesto que es un ejemplo simple, sólo incrementa el dato comune <i>comdata</i> inicializado en el <b>main()</b> y lo hace de forma síncronizada utilizando <b><a href="http://man7.org/linux/man-pages/man3/pthread_mutex_lock.3p.html" target="_blank">pthread_mutex_lock()</a> </b>y <a href="http://man7.org/linux/man-pages/man3/pthread_mutex_lock.3p.html" target="_blank"><b>pthread_mutex_unlock()</b></a> sobre el <b>mutex </b>común<b> </b><i>lock</i>: esto sirve para evitar que los dos <b>thread</b> accedan al mismo tiempo a <i>comdata</i>.<br />
<br />
Compilando con <b>GCC</b> en una máquina <b>Linux</b> (<i>por supuesto</i>) y ejecutando, el resultado es:<br />
<br />
<pre style="line-height: 125%; margin: 0;">aldo@ao-linux-nb:~/blogtest$ gcc thread.c -o thread -pthread
aldo@ao-linux-nb:~/blogtest$ ./thread
thread 0 comenzado
thread 1 comenzado
thread 1 acabado
thread 0 acabado
./thread: thread acabados: comdata=200</pre>
<br />
Que es el resultado esperado. En el próximo post hablaremos de una interfaz alternativa a los<b> POSIX Threads</b>. Y, como siempre, os recomiendo que no contengáis la respiración esperando... <br />
<br />
¡Hasta el próximo post!<br />
<br />
<span style="font-size: x-large;"><b>P.D.</b></span><br />
Como bien sabéis, este es un blog de programación con un alma cinéfila, así que os comento (con gran tristeza) que el mes pasado nos ha dejado un <b>gran maestro</b>. D.E.P., <a href="https://es.wikipedia.org/wiki/George_A._Romero" target="_blank"><b>George</b></a>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOhl18U5QBFtpofL73FptTDhbf5JV_PY8NYF7PEgB5lSMHUN9nGtaQsuBUWGi8s5gu1aLbp0nAOy-Hs0VTh_n_0zFtgmf_K583PlxaWshCWF2wttX1PWc2GKX5iAdevNuP65edm7HB4geP/s1600/romero.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="343" data-original-width="550" height="248" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOhl18U5QBFtpofL73FptTDhbf5JV_PY8NYF7PEgB5lSMHUN9nGtaQsuBUWGi8s5gu1aLbp0nAOy-Hs0VTh_n_0zFtgmf_K583PlxaWshCWF2wttX1PWc2GKX5iAdevNuP65edm7HB4geP/s400/romero.jpg" width="400" /></a></div>
Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-75165398214925465592017-07-08T19:03:00.000+02:002017-07-08T19:10:24.063+02:00El bueno, el feo y el VLA cómo usar los Variable Length Arrays en C - pt.3<span class="" id="result_box" lang="es">Aquí estamos, y, <a href="https://artcprogramming-es.blogspot.com.es/2017/06/el-bueno-el-feo-y-el-vla-como-usar-los.html" target="_blank"><b>como prometido</b></a>, este mes vamos a hablar de un pariente cercano de los <b><a href="https://en.wikipedia.org/wiki/Variable-length_array" target="_blank">VLAs</a></b><span class="">, o sea de </span><span class="">la función</span> </span><a href="http://man7.org/linux/man-pages/man3/alloca.3.html" target="_blank"><b>alloca()</b></a><span class="" id="result_box" lang="es">... ¿será <a href="https://es.wikipedia.org/wiki/Il_buono,_il_brutto,_il_cattivo" target="_blank"><b>un bueno</b></a><a href="https://es.wikipedia.org/wiki/Il_buono,_il_brutto,_il_cattivo" target="_blank"><b>, un feo o un malo</b></a>?</span> <br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqddvafx2hNYXrZcZhuRym7Gv2kN41rHblzMVZiGszX0mq8YEDigcQ0hE323rD_prup4cd0SHn7VR00fxR5rfFVZvHOIlfcdTA1eUvuJJZEVKIzQ3-5yisUwciFM7MdCEA-aP3xH8CTZ37/s1600/ilbrutto.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="683" data-original-width="1600" height="170" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqddvafx2hNYXrZcZhuRym7Gv2kN41rHblzMVZiGszX0mq8YEDigcQ0hE323rD_prup4cd0SHn7VR00fxR5rfFVZvHOIlfcdTA1eUvuJJZEVKIzQ3-5yisUwciFM7MdCEA-aP3xH8CTZ37/s400/ilbrutto.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><span class="short_text" id="result_box" lang="es"><span class="">¡hola</span><span class="">, soy el</span> spoiler<span class=""></span> <span class="">de este post</span></span>!</td></tr>
</tbody></table>
<span class="" id="result_box" lang="es"><span class="">Entonces</span><span class="">, he añadido</span> código en el programa de test para probar la </span><span class="" id="result_box" lang="es"><b>alloca()</b>. Y, para terminar a lo grande, he añadido código para probar la </span><a href="http://man7.org/linux/man-pages/man3/malloc.3.html" target="_blank"><b>malloc()</b></a><span class="" id="result_box" lang="es"> del <b>C</b><b>++</b>, o sea la </span><span class="" id="result_box" lang="es"><a href="https://en.wikipedia.org/wiki/New_and_delete_%28C%2B%2B%29" target="_blank"><b>new </b></a>(después del </span><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es">problemático test de</span> </span><span class="" id="result_box" lang="es"><a href="https://it.wikipedia.org/wiki/Vector_%28STL%29" target="_blank"><b>std::vector</b></a> del último post era correcto acabar el argumento con algo con mejore prestaciones, <i>para que no se diga que tengo algo en contra del C++...</i>). Así que vamos a utilizar programa C++ del post pasado (ya que era prácticamente idéntico a la versión <b>C</b>): os enseño otra vez el <b>main()</b> y las dos funciones de test añadidas (para reconstruir el programa completo es suficiente consultar los dos post anteriores y hacer un poco </span><span class="" id="result_box" lang="es"><i>cut-and-paste</i>). ¡Vamos con el código!</span> <br />
<pre style="background: #ffffff; color: black;"><span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdio.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">time.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdlib.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">vector</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">define</span><span style="color: #004a43;"> MYSIZE 1000000</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;"></span><span style="color: dimgrey;"></span><span style="color: dimgrey;"><span class="" id="result_box" lang="es"><span class="">variable dummy</span> <span class="">con el fin de</span> <span class="">evitar</span> el vaciado <span class="">total de</span> <span class="">las funciones</span> <span class="">usando</span></span> G++ -O2</span></span>
<span style="color: maroon; font-weight: bold;">int</span> avoid_optimization<span style="color: purple;">;</span>
<span style="color: dimgrey;">// prototipos locales</span>
<span style="color: maroon; font-weight: bold;">void</span> testVLA<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> size<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">void</span> testMallocVLA<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> size<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">void</span> testStackFLA<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> dum<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">void</span> testHeapFLA<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> dum<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">void</span> testAllocaVLA<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> size<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">void</span> testVectorVLA<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> size<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">void</span> testNewVLA<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> size<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">void</span> runTest<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> iterations<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">void</span> <span style="color: #808030;">(</span><span style="color: #808030;">*</span>funcptr<span style="color: #808030;">)</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span><span style="color: #808030;">)</span><span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">int</span> size<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>name<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// función main()</span>
<span style="color: maroon; font-weight: bold;">int</span> <span style="color: #400000;">main</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> argc<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">char</span><span style="color: #808030;">*</span> argv<span style="color: #808030;">[</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// test argumentos</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>argc <span style="color: #808030;">!</span><span style="color: #808030;">=</span> <span style="color: #008c00;">2</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// error args</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">: wrong arguments counts</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">usage: </span><span style="color: #007997;">%s</span><span style="color: #0000e6;"> vla iterations [e.g.: </span><span style="color: #007997;">%s</span><span style="color: #0000e6;"> 10000]</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_FAILURE<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;">extrae iteraciones</span>
<span style="color: maroon; font-weight: bold;">int</span> iterations <span style="color: #808030;">=</span> <span style="color: #603000;">atoi</span><span style="color: #808030;">(</span>argv<span style="color: #808030;">[</span><span style="color: #008c00;">1</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;">ejecuta test</span>
runTest<span style="color: #808030;">(</span>iterations<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>testVLA<span style="color: #808030;">,</span> MYSIZE<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">testVLA</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
runTest<span style="color: #808030;">(</span>iterations<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>testMallocVLA<span style="color: #808030;">,</span> MYSIZE<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">testMallocVLA</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
runTest<span style="color: #808030;">(</span>iterations<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>testStackFLA<span style="color: #808030;">,</span> <span style="color: #008c00;">0</span><span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">testStackFLA</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
runTest<span style="color: #808030;">(</span>iterations<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>testHeapFLA<span style="color: #808030;">,</span> <span style="color: #008c00;">0</span><span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">testHeapFLA</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
runTest<span style="color: #808030;">(</span>iterations<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>testAllocaVLA<span style="color: #808030;">,</span> MYSIZE<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">testAllocaVLA</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
runTest<span style="color: #808030;">(</span>iterations<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>testVectorVLA<span style="color: #808030;">,</span> MYSIZE<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">testVectorVLA</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
runTest<span style="color: #808030;">(</span>iterations<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>testNewVLA<span style="color: #808030;">,</span> MYSIZE<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">testNewVLA</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// sale</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_SUCCESS<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">función</span> testAllocaVLA()</span>
<span style="color: maroon; font-weight: bold;">void</span> testAllocaVLA<span style="color: #808030;">(</span>
<span style="color: maroon; font-weight: bold;">int</span> size<span style="color: #808030;">)</span> <span style="color: dimgrey;">// size para alloca()</span>
<span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">int</span> <span style="color: #808030;">*</span>allocavla <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span><span style="color: #808030;">*</span><span style="color: #808030;">)</span>alloca<span style="color: #808030;">(</span>size <span style="color: #808030;">*</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// loop de test</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> i <span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span> i <span style="color: #808030;"><</span> size<span style="color: purple;">;</span> i<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: #808030;">)</span>
allocavla<span style="color: #808030;">[</span>i<span style="color: #808030;">]</span> <span style="color: #808030;">=</span> i<span style="color: purple;">;</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">instrucción </span><span style="color: dimgrey;"><span class="" id="result_box" lang="es"><span class="">con el fin de</span> <span class="">evitar</span> el vaciado <span class="">total de</span> <span class="">las funciones</span> <span class="">usando</span></span> G++ -O2</span></span>
avoid_optimization <span style="color: #808030;">=</span> allocavla<span style="color: #808030;">[</span>size <span style="color: #808030;">/</span> <span style="color: #008c00;">2</span><span style="color: #808030;">]</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">función</span> testNewVLA()</span>
<span style="color: maroon; font-weight: bold;">void</span> testNewVLA<span style="color: #808030;">(</span>
<span style="color: maroon; font-weight: bold;">int</span> size<span style="color: #808030;">)</span> <span style="color: dimgrey;">// size para new</span>
<span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">int</span> <span style="color: #808030;">*</span>newvla <span style="color: #808030;">=</span> new <span style="color: maroon; font-weight: bold;">int</span><span style="color: #808030;">[</span>size<span style="color: #808030;">]</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// loop de test</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> i <span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span> i <span style="color: #808030;"><</span> size<span style="color: purple;">;</span> i<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: #808030;">)</span>
newvla<span style="color: #808030;">[</span>i<span style="color: #808030;">]</span> <span style="color: #808030;">=</span> i<span style="color: purple;">;</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">instrucción </span><span style="color: dimgrey;"><span class="" id="result_box" lang="es"><span class="">con el fin de</span> <span class="">evitar</span> el vaciado <span class="">total de</span> <span class="">las funciones</span> <span class="">usando</span></span> G++ -O2</span></span>
avoid_optimization <span style="color: #808030;">=</span> newvla<span style="color: #808030;">[</span>size <span style="color: #808030;">/</span> <span style="color: #008c00;">2</span><span style="color: #808030;">]</span><span style="color: purple;">;</span>
delete<span style="color: #808030;">[</span><span style="color: #808030;">]</span> newvla<span style="color: purple;">;</span>
<span style="color: purple;">}</span></pre>
<span class="" id="result_box" lang="es"><span class="">Como se puede ver</span>, las dos funciones añadidas están perfectamente alineadas estilísticamente con las demás que ya había propuesto y son, como siempre, </span><span class="" id="result_box" lang="es"><a href="https://artcprogramming-es.blogspot.com.es/2012/08/no-comment.html" target="_blank"><b>hiper-comentadas</b></a>, así que ni siquiera tengo que detenerme en largas explicaciones. ¿Y los resultados de las pruebas? ¡Vamos a verlos!</span><br />
<pre style="background: #ffffff; color: black;">aldo@ao<span style="color: #808030;">-</span>linux<span style="color: #808030;">-</span>nb<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ g<span style="color: #808030;">+</span><span style="color: #808030;">+</span> vlacpp<span style="color: #008c00;">.</span>cpp <span style="color: #808030;">-</span>o vlacpp
aldo@ao<span style="color: #808030;">-</span>linux<span style="color: #808030;">-</span>nb<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ <span style="color: #808030;">.</span><span style="color: #808030;">/</span>vlacpp <span style="color: #008c00;">2000</span>
testVLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">4.318492</span> segundos
testMallocVLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">3.676805</span> segundos
testStackFLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">4.339859</span> segundos
testHeapFLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">4.340040</span> segundos
testAllocaVLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">3.678644</span> segundos
testVectorVLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">10.934088</span> segundos
testNewVLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">3.679624</span> segundos
aldo@ao<span style="color: #808030;">-</span>linux<span style="color: #808030;">-</span>nb<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ g<span style="color: #808030;">+</span><span style="color: #808030;">+</span> <span style="color: #808030;">-</span>O2 vlacpp<span style="color: #008c00;">.</span>cpp <span style="color: #808030;">-</span>o vlacpp
aldo@ao<span style="color: #808030;">-</span>linux<span style="color: #808030;">-</span>nb<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ <span style="color: #808030;">.</span><span style="color: #808030;">/</span>vlacpp <span style="color: #008c00;">2000</span>
testVLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">0.746956</span> segundos
testMallocVLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">0.697261</span> segundos
testStackFLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">0.696310</span> segundos
testHeapFLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">0.700047</span> segundos
testAllocaVLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">0.691677</span> segundos
testVectorVLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">1.384563</span> segundos
testNewVLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;"></span><span style="color: #808030;">:</span> <span style="color: #008c00;">0.695037</span> segundos</pre>
<span class="" id="result_box" lang="es"><span class="">Entonces, ¿qué</span> se puede decir? Los resultados de los test de los post anteriores ya los hemos ampliamente comentados, por lo que ahora sólo podemos añadir que: </span><span class="" id="result_box" lang="es"><b>alloca()</b> es muy rápida, ya que es, en práctica, una <b>malloc()</b> en el </span><span class="" id="result_box" lang="es"><a href="https://en.wikipedia.org/wiki/Memory_management" target="_blank"><b>stack</b></a> (y utilizándola de manera más apropriada, podría/debería ser la más rápida del grupo). ¿Y la <b>new</b>? Bueno, se comporta (como se esperaba) muy bien, también porque, casi siempre, la <b>new</b> internamente utiliza la <b>malloc()</b>.</span><br />
<br />
<span class="" id="result_box" lang="es"><span class="">De acuerdo,</span> la </span><span class="" id="result_box" lang="es"><b>alloca() </b>es rápida, pero lo es (sólo un poco menos) también un <b><span class="">VLA</span></b>, y esto no le ha salvado <a href="https://artcprogramming-es.blogspot.com.es/2017/06/el-bueno-el-feo-y-el-vla-como-usar-los.html" target="_blank"><b>de</b></a><a href="https://artcprogramming-es.blogspot.com.es/2017/06/el-bueno-el-feo-y-el-vla-como-usar-los.html" target="_blank"><b> ser elegido como el malo de la </b></a><a href="https://artcprogramming-es.blogspot.com.es/2017/06/el-bueno-el-feo-y-el-vla-como-usar-los.html" target="_blank"><b>película</b></a>. Así que vamos a hacer de nuevo una lista de pros y contras, y a ver cuál es el lado más pesado. Veamos primero los pros:</span><br />
<ol>
<li><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es">la <b>alloca()</b> es muy rápida, ya que usa el </span></span><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es"><b>stack</b> en lugar del </span></span><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es"><b>heap</b>.</span></span></li>
<li><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es">la <b>alloca()</b> es fácil de usar, es una <b>malloc()</b> sin <b>free</b><b>()</b>. La variable alocada <span class="">tiene un </span></span></span><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es"><a href="https://es.wikipedia.org/wiki/%C3%81mbito_%28programaci%C3%B3n%29" target="_blank"><b>scope</b></a> a nivel de función, entonces sigue siendo válida hasta que la <span class="">función retorna al <i>caller</i></span>, exactamente como cualquier variable automática local (también un <b>VLA</b> funciona más o menos así, pero su <b>scope</b> es a nivel de bloque, <span class="">no de función</span>, y esto es, probablemente, un punto a favor de los <b>VLAs</b>).</span></span></li>
<li><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es"><span class="">para la</span> razón explicada en la sección 2, </span></span><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es">la <b>alloca()</b> no deja residuos <span class="">de memoria</span> en caso de errores graves en las actividades de una función (con<b> malloc() </b>+ <b>free() </b>no es tan fácil lograr esto). Y si estás acostumbrado a utilizar cositas como </span></span><a href="http://man7.org/linux/man-pages/man3/longjmp.3.html" target="_blank"><b>longjmp()</b></a><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es"> las ventajas en este sentido son grandes. </span></span></li>
<li><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es">debido a su aplicación interna (sin entrar en más detalles) </span></span><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es">la <b>alloca()</b> no provoca la fragmentación de memoria.</span> </span> </li>
</ol>
<ol></ol>
¡<span class="short_text" id="result_box" lang="es">Uh, qué bien! ¿Y los contras<span class="">?</span></span><br />
<ol>
<li><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la gestione degli errori è problematica, perché non c'è maniera di sapere se alloca() ha allocato bene o ha provocato un stack overflow (in questo caso provoca effetti simili a quelli di un errore per ricorsione infinita)... uh, questo">la gestión
de errores es problemática, porque no hay forma de saber si </span></span></span></span><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la gestione degli errori è problematica, perché non c'è maniera di sapere se alloca() ha allocato bene o ha provocato un stack overflow (in questo caso provoca effetti simili a quelli di un errore per ricorsione infinita)... uh, questo"><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la alloca() non è molto portatile, visto che non è una funzione standard e il suo funzionamento/presenza dipende molto dal compilatore in uso.
">la <b>alloca()</b></span></span></span></span> </span></span></span></span>ha alocado bien o ha causado un <a href="https://en.wikipedia.org/wiki/Stack_overflow" target="_blank"><b>stack overflow</b></a> (en este caso
provoca efectos similares a los de un error para recursión infinita)<span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la gestione degli errori è problematica, perché non c'è maniera di sapere se alloca() ha allocato bene o ha provocato un stack overflow (in questo caso provoca effetti simili a quelli di un errore per ricorsione infinita)... uh, questo">... uh, esto </span><span title="è esattamente lo stesso problema dei VLAs.
">es exactamente el mismo problema de los <b>VLAs</b>.</span></span></span></span></li>
<li><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="è esattamente lo stesso problema dei VLAs.
"></span><span title="la alloca() non è molto portatile, visto che non è una funzione standard e il suo funzionamento/presenza dipende molto dal compilatore in uso.
">la <b>alloca()</b> no es muy portable, ya que no es una función
estándar y su funcionamiento/presencia depende en gran medida del
compilador en uso.</span></span></span></span></li>
<li><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la alloca() non è molto portatile, visto che non è una funzione standard e il suo funzionamento/presenza dipende molto dal compilatore in uso.
"></span><span title="la alloca() è error prone (parte 1): bisogna usarla con attenzione, visto che induce, tipicamente, a errori come usare la variabile allocata quando oramai non è più valida (passarla con un return o inserirla dentro una struttura dati esterna alla funzione">la
</span></span></span></span><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la alloca() è error prone (parte 1): bisogna usarla con attenzione, visto che induce, tipicamente, a errori come usare la variabile allocata quando oramai non è più valida (passarla con un return o inserirla dentro una struttura dati esterna alla funzione"><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la alloca() non è molto portatile, visto che non è una funzione standard e il suo funzionamento/presenza dipende molto dal compilatore in uso.
"><b>alloca() </b></span></span></span></span>es </span></span></span></span><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la alloca() è error prone (parte 1): bisogna usarla con attenzione, visto che induce, tipicamente, a errori come usare la variabile allocata quando oramai non è più valida (passarla con un return o inserirla dentro una struttura dati esterna alla funzione"><i>error prone</i> (parte 1): hay que utilizarla con
cuidado, ya que induce, por lo general, a errores cómo utilizar
la variable asignada cuando ya no es válida (pasarla con un <b>return</b> o
insertarla en una estructura de datos externa a la función, </span><span title=", per esempio)... ma noi siamo ottimi programmatori e questo punto non ci spaventa, no?
">por ejemplo)... <i>pero nosotros somos buenos programadores y este punto no nos da miedo, ¿no?</i></span></span></span></span></li>
<li><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la alloca() è error prone (parte 2): ci sono problemi ancora più sottili da considerare nell'uso, ad esempio può risultare MOLTO pericoloso mettere una alloca() dentro un loop o in una funzione ricorsiva (povero stack!) o in"><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la alloca() è error prone (parte 1): bisogna usarla con attenzione, visto che induce, tipicamente, a errori come usare la variabile allocata quando oramai non è più valida (passarla con un return o inserirla dentro una struttura dati esterna alla funzione">la
</span></span></span></span><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la alloca() è error prone (parte 1): bisogna usarla con attenzione, visto che induce, tipicamente, a errori come usare la variabile allocata quando oramai non è più valida (passarla con un return o inserirla dentro una struttura dati esterna alla funzione"><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la alloca() non è molto portatile, visto che non è una funzione standard e il suo funzionamento/presenza dipende molto dal compilatore in uso.
"><b>alloca() </b></span></span></span></span>es </span></span></span></span><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la alloca() è error prone (parte 1): bisogna usarla con attenzione, visto che induce, tipicamente, a errori come usare la variabile allocata quando oramai non è più valida (passarla con un return o inserirla dentro una struttura dati esterna alla funzione"><i>error prone</i> (parte </span></span></span></span>2): hay problemas aún más
sutiles que considerar en el uso, por ejemplo puede ser <b>MUY</b> peligroso poner una </span></span></span></span><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la alloca() è error prone (parte 2): ci sono problemi ancora più sottili da considerare nell'uso, ad esempio può risultare MOLTO pericoloso mettere una alloca() dentro un loop o in una funzione ricorsiva (povero stack!) o in"><b>alloca()</b> dentro de un bucle o de una función recursiva (<i>¡pobre stack!</i>) </span><span title="una funzione inline (che usa lo stack in una maniera che si scontra un po' con la maniera di usare lo stack della alloca())... ma noi siamo ottimi programmatori e questo punto non ci spaventa, no?
">o en una función </span></span></span></span><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="una funzione inline (che usa lo stack in una maniera che si scontra un po' con la maniera di usare lo stack della alloca())... ma noi siamo ottimi programmatori e questo punto non ci spaventa, no?
"><b>inline</b> (que utiliza el <b>stack</b> de manera que choca un
poco con la forma de utilizar el </span></span></span></span><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="una funzione inline (che usa lo stack in una maniera che si scontra un po' con la maniera di usare lo stack della alloca())... ma noi siamo ottimi programmatori e questo punto non ci spaventa, no?
"><b>stack </b>de la</span></span></span></span><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="una funzione inline (che usa lo stack in una maniera che si scontra un po' con la maniera di usare lo stack della alloca())... ma noi siamo ottimi programmatori e questo punto non ci spaventa, no?
"> <b>alloca()</b>) ... </span></span></span></span><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title=", per esempio)... ma noi siamo ottimi programmatori e questo punto non ci spaventa, no?
"><i>pero nosotros somos buenos programadores y este punto no nos da miedo, ¿no?</i></span></span></span></span></li>
<li><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la alloca() è error prone (parte 3): la alloca() usa lo stack, che è normalmente limitato rispetto allo heap (specialmente negli ambienti embedded che sono molto frequentati dai programmatori C...)."><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la alloca() è error prone (parte 1): bisogna usarla con attenzione, visto che induce, tipicamente, a errori come usare la variabile allocata quando oramai non è più valida (passarla con un return o inserirla dentro una struttura dati esterna alla funzione">la
</span></span></span></span><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la alloca() è error prone (parte 1): bisogna usarla con attenzione, visto che induce, tipicamente, a errori come usare la variabile allocata quando oramai non è più valida (passarla con un return o inserirla dentro una struttura dati esterna alla funzione"><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la alloca() non è molto portatile, visto che non è una funzione standard e il suo funzionamento/presenza dipende molto dal compilatore in uso.
"><b>alloca() </b></span></span></span></span>es </span></span></span></span><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la alloca() è error prone (parte 1): bisogna usarla con attenzione, visto che induce, tipicamente, a errori come usare la variabile allocata quando oramai non è più valida (passarla con un return o inserirla dentro una struttura dati esterna alla funzione"><i>error prone</i> (parte </span></span></span></span> 3): </span></span></span></span><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la alloca() è error prone (parte 3): la alloca() usa lo stack, che è normalmente limitato rispetto allo heap (specialmente negli ambienti embedded che sono molto frequentati dai programmatori C...)."><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="una funzione inline (che usa lo stack in una maniera che si scontra un po' con la maniera di usare lo stack della alloca())... ma noi siamo ottimi programmatori e questo punto non ci spaventa, no?
"><b>alloca()</b></span></span></span></span> utiliza el <b>stack</b>,
que normalmente está limitado con respecto al <b>heap</b> (especialmente en
entornos </span></span></span></span><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la alloca() è error prone (parte 3): la alloca() usa lo stack, che è normalmente limitato rispetto allo heap (specialmente negli ambienti embedded che sono molto frequentati dai programmatori C...)."><a href="https://es.wikipedia.org/wiki/Sistema_embebido" target="_blank"><b>embedded</b></a> que son muy frecuentados por los programadores <b>C</b>...). </span><span title="Quindi esaurire lo stack e provocare uno stack overflow è facile (e difficile da controllare, vedi il punto 1)... ma noi siamo ottimi programmatori e questo punto non ci spaventa, no?">Así
agotar el <b>stack</b> y causar un </span></span></span></span><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="Quindi esaurire lo stack e provocare uno stack overflow è facile (e difficile da controllare, vedi il punto 1)... ma noi siamo ottimi programmatori e questo punto non ci spaventa, no?"><b>stack overflow</b> es fácil (y difícil
de controlar, véase el punto 1)... </span></span></span></span><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="Quindi esaurire lo stack e provocare uno stack overflow è facile (e difficile da controllare, vedi il punto 1)... ma noi siamo ottimi programmatori e questo punto non ci spaventa, no?"><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title=", per esempio)... ma noi siamo ottimi programmatori e questo punto non ci spaventa, no?
"><i>pero nosotros somos buenos programadores y este punto no nos da miedo, ¿no?</i></span></span></span></span></span></span> </span></span></li>
</ol>
<ol></ol>
<span class="" id="result_box" lang="es"><span class="">Vale</span>, ¿las conclusiones? Habría motivos para declarar la </span><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="la alloca() è error prone (parte 3): la alloca() usa lo stack, che è normalmente limitato rispetto allo heap (specialmente negli ambienti embedded che sono molto frequentati dai programmatori C...)."><span class="short_text" id="result_box" lang="es"><span class=""><span class="" id="result_box" lang="es"><span title="una funzione inline (che usa lo stack in una maniera che si scontra un po' con la maniera di usare lo stack della alloca())... ma noi siamo ottimi programmatori e questo punto non ci spaventa, no?
"><b>alloca() </b></span></span></span></span></span></span></span></span><span class="" id="result_box" lang="es">como otro <i>malo</i> <span class="">(la misma</span> suerte que el <b>VLA</b>), pero teniendo en cuenta las importantes ventajas <span class="">y, sobre todo</span>, <span class="">porque hoy estoy de buen humor</span>, le declararemos solo como <i>feo</i> (<i>¿</i><i><span class="">habéis visto el spoiler </span><span class="">en la imagen</span> arriba?</i>). De todos modos utilizar la </span><span class="" id="result_box" lang="es"><b>alloca()</b> con <b>mucho cuidado</b>, </span><i>¡hombre prevenido vale por dos!</i> <br />
<br />
¡Hasta el próximo post!
Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-17979470025817030162017-06-10T16:40:00.002+02:002018-01-14T00:27:49.683+01:00El bueno, el feo y el VLA cómo usar los Variable Length Arrays en C - pt.2Así que, ¿dónde estábamos? Ah sí, <a href="https://artcprogramming-es.blogspot.com.es/2017/05/el-bueno-el-feo-y-el-vla-como-usar-los.html" target="_blank"><b>en el último post</b></a> (<i>que acabáis de leer otra vez, ¿verdad?</i>) hemos aprobado (con dudas) los <a href="https://en.wikipedia.org/wiki/Variable-length_array" target="_blank"><b>VLAs</b></a>, que son fáciles de usar, útiles y con un rendimiento excelente, pero entonces... ¿por qué dije que eran aptos para el rol del <i>malo</i> en el legendario "<a href="https://es.wikipedia.org/wiki/Il_buono,_il_brutto,_il_cattivo" target="_blank"><b>El bueno, el feo y el malo</b></a>"? <br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjgugpmYAX1tmj63iA-KrL0Y0ux22B6OXPZLHEdy_Jth8nQrx8b7mmRd1N1sHOQL8WWNcGqc9HDEgwGbcR929KTo_V22mg7E7N9oGtxwS1hbxxiCzbQgMsMB7HcensUsnLj0lNtppSX4VK/s1600/the-good-the-bad-and-the-ugly.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="277" data-original-width="655" height="167" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjgugpmYAX1tmj63iA-KrL0Y0ux22B6OXPZLHEdy_Jth8nQrx8b7mmRd1N1sHOQL8WWNcGqc9HDEgwGbcR929KTo_V22mg7E7N9oGtxwS1hbxxiCzbQgMsMB7HcensUsnLj0lNtppSX4VK/s400/the-good-the-bad-and-the-ugly.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">no confiáis en el VLA, ¡os lo dice el bueno!</td></tr>
</tbody></table>
Así de fácil: además de los (considerables) <i>pros</i> también hay algunos (pesados) <i>contras</i>. Antes de seguir siempre hay que recordar que un <b>VLA</b> se asigna dinámicamente en el <a href="https://en.wikipedia.org/wiki/Memory_management" target="_blank"><b>stack</b></a>, como una variable automática con <a href="https://es.wikipedia.org/wiki/%C3%81mbito_(programaci%C3%B3n)" target="_blank"><b>scope</b></a> limitado al bloque de código en la que la alocacion se lleva a cabo: considerando esto los (principales) problemas posibles son:<br />
<ol>
<li>la gestión de errores es problemática, porque no hay forma de saber si
el <b>VLA</b> ha sido alocado bien o ha causado un <a href="https://en.wikipedia.org/wiki/Stack_overflow" target="_blank"><b>stack overflow</b></a> (en este caso
provoca efectos similares a los de un error para recursión infinita).</li>
<li>el tamaño del <b>VLA</b> se decide en <i>run-time</i>, por lo que el
compilador debe hacer juegos un poco extraños: dependiendo de la
aplicación, es posible que una parte (a veces importante) del <b>stack</b> de
una función se reserve para un <b>VLA</b>, limitando mucho la memoria local
disponible. O sea: el <b>stack overflow</b> està siempre alrededor de la
esquina.</li>
<li>la portabilidad del código se desmorona un poco: el código se vuelve muy
<i>compiler-dependent</i> y, sobre todo, ya que una buena parte de los
programadores <b>C</b> escriben también código para <a href="https://es.wikipedia.org/wiki/Sistema_embebido" target="_blank"><b>sistemas embedded</b></a> (donde el
<b> stack</b> está, a menudo, limitado) resulta complicado el <i>porting</i> de
funciones de aplicaciones <i>normales</i> a aplicaciones <i>embedded</i>.
Funciones que, tal vez, dejarían de funcionar por razones misteriosas
(<i>bueno, no tan misteriosas</i>).</li>
<li>Por último, pero no menos importante: tal vez por las razones
mencionadas anteriormente (u otras más) de <a href="https://es.wikipedia.org/wiki/C_(lenguaje_de_programaci%C3%B3n)#C11" target="_blank"><b>C11</b></a> hacia adelante los <b>VLAs</b>
son opcionales y están sujetas a una variable del compilador
<b>__STDC_NO_VLA__</b>: mala señal<i>.</i></li>
</ol>
Entonces, ¿qué? Mejor no usarlos o utilizarlos con las precauciones necesarias, porque las alternativas no faltan. <i>¡Malo encontrado!</i><br />
<br />
Y ahora tenemos que buscar a alguien que se adapte a los papeles de <i>bueno</i> y <i>feo</i>. Vale, para el <i>bueno</i> no hay problema, el candidato ideal es la nuestra querida amiga <a href="http://man7.org/linux/man-pages/man3/malloc.3.html" target="_blank"><b>malloc()</b></a>, que es siempre una garantía y ha salido muy bien de los test. Sobre <b>malloc()</b> es inútil gasta mas palabras, es un punto fijo del <b>C</b> y <a href="https://artcprogramming-es.blogspot.com.es/2012/10/erase-una-vez-la-malloc.html" target="_blank"><b>ya hemos hablado a fondo sobre ella aquí</b></a>.<br />
<br />
Y el <i>feo</i>? Bueno, para encontrar uno adecuado tendremos, por desgracia, entrar en el <a href="https://es.wikipedia.org/wiki/La_Fuerza#Lado_Oscuro:_ideolog.C3.ADa_Sith" target="_blank"><b>lado oscuro de la fuerza</b></a>, es decir, en el territorio <b>C++</b>...<br />
<i><br />(...Abro un paréntesis: nunca hablo de temas que no conozco, porque creo que es estúpido hacerlo. Por ejemplo: no entiendo nada de motos y, se lo aseguro, nadie ha tenido el honor de escucharme charlar sobre el mundial de <a href="https://es.wikipedia.org/wiki/MotoGP" target="_blank"><b>MotoGP</b></a>. Sigo una filosofía, que, por desgracia, no es seguida por muchas personas, o sea: "mejor callar que hablar sólo para dar aire a la boca". Precisamente gracias a esta coherencia creo que tengo las cualidades para hablar de C++: yo lo he usado en paralelo a mi amado C para casi treinta años (!), y, modestia aparte, creo que se usarlo muy bien. Así que puede hablar sobre el, para bien o para mal. Cierro el paréntesis...).</i><br />
<br />
Entonces, he retomado el ejemplo <b>C</b> del post anterior y (haciendo el mínimo sindical de modificaciones) le he transformado en codigo <b>C++</b>, con el fin, por lo tanto, de añadir un nuevo test que usa <a href="https://en.wikipedia.org/wiki/Sequence_container_(C%2B%2B)#Vector" target="_blank"><b>std::vector</b></a> (<i>esto es un objeto particularmente querido para los C++ lovers, que lo usan también para condimentar la ensalada</i>). Para evitar de repetir todo el código del ultimo post os paso sólo el <b>main()</b> y la nueva función de test (el resto es, prácticamente, idéntico). ¡Vamos con el código!<br />
<pre style="background: #ffffff; color: black;"><span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdio.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">time.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdlib.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">vector</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">define</span><span style="color: #004a43;"> MYSIZE 1000000</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"></span><span style="color: dimgrey;"><span class="" id="result_box" lang="es"><span class="">variable dummy</span> <span class="">con el fin de</span> <span class="">evitar</span> el vaciado <span class="">total de</span> <span class="">las funciones</span> <span class="">usando</span></span> G++ -O2</span>
<span style="color: maroon; font-weight: bold;">int</span> avoid_optimization<span style="color: purple;">;</span>
<span style="color: dimgrey;">// prototipos locales</span>
<span style="color: maroon; font-weight: bold;">void</span> testVLA<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> size<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">void</span> testMallocVLA<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> size<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">void</span> testStackFLA<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> dum<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">void</span> testHeapFLA<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> dum<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">void</span> testVectorVLA<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> size<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">void</span> runTest<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> iterations<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">void</span> <span style="color: #808030;">(</span><span style="color: #808030;">*</span>funcptr<span style="color: #808030;">)</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span><span style="color: #808030;">)</span><span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">int</span> size<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>name<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// función main()</span>
<span style="color: maroon; font-weight: bold;">int</span> <span style="color: #400000;">main</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> argc<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">char</span><span style="color: #808030;">*</span> argv<span style="color: #808030;">[</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// test argumentos</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>argc <span style="color: #808030;">!</span><span style="color: #808030;">=</span> <span style="color: #008c00;">2</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// error args</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">: wrong arguments counts</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">usage: </span><span style="color: #007997;">%s</span><span style="color: #0000e6;"> vla iterations [e.g.: </span><span style="color: #007997;">%s</span><span style="color: #0000e6;"> 10000]</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_FAILURE<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// extrae iteraciones</span>
<span style="color: maroon; font-weight: bold;">int</span> iterations <span style="color: #808030;">=</span> <span style="color: #603000;">atoi</span><span style="color: #808030;">(</span>argv<span style="color: #808030;">[</span><span style="color: #008c00;">1</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// ejecuta test</span>
runTest<span style="color: #808030;">(</span>iterations<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>testVLA<span style="color: #808030;">,</span> MYSIZE<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">testVLA</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
runTest<span style="color: #808030;">(</span>iterations<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>testMallocVLA<span style="color: #808030;">,</span> MYSIZE<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">testMallocVLA</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
runTest<span style="color: #808030;">(</span>iterations<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>testStackFLA<span style="color: #808030;">,</span> <span style="color: #008c00;">0</span><span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">testStackFLA</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
runTest<span style="color: #808030;">(</span>iterations<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>testHeapFLA<span style="color: #808030;">,</span> <span style="color: #008c00;">0</span><span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">testHeapFLA</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
runTest<span style="color: #808030;">(</span>iterations<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>testVectorVLA<span style="color: #808030;">,</span> MYSIZE<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">testVectorVLA</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// sale</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_SUCCESS<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// función testVectorVLA()</span>
<span style="color: maroon; font-weight: bold;">void</span> testVectorVLA<span style="color: #808030;">(</span>
<span style="color: maroon; font-weight: bold;">int</span> size<span style="color: #808030;">)</span> <span style="color: dimgrey;">// size para std::vector</span>
<span style="color: purple;">{</span>
std<span style="color: purple;">::</span>vector<span style="color: #808030;"><</span><span style="color: maroon; font-weight: bold;">int</span><span style="color: #808030;">></span> vectorvla<span style="color: #808030;">(</span>size<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// loop de test</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> i <span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span> i <span style="color: #808030;"><</span> size<span style="color: purple;">;</span> i<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: #808030;">)</span>
vectorvla<span style="color: #808030;">[</span>i<span style="color: #808030;">]</span> <span style="color: #808030;">=</span> i<span style="color: purple;">;</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;">instrucción </span><span style="color: dimgrey;"><span class="" id="result_box" lang="es"><span class="">con el fin de</span> <span class="">evitar</span> el vaciado <span class="">total de</span> <span class="">las funciones</span> <span class="">usando</span></span> G++ -O2</span>
avoid_optimization <span style="color: #808030;">=</span> vectorvla<span style="color: #808030;">[</span>size <span style="color: #808030;">/</span> <span style="color: #008c00;">2</span><span style="color: #808030;">]</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span></pre>
Compilando (con/sin optimizaciones) y ejecutando este código los resultados son los siguientes:<br />
<pre style="background: #ffffff; color: black;">aldo@ao<span style="color: #808030;">-</span>linux<span style="color: #808030;">-</span>nb<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ g<span style="color: #808030;">+</span><span style="color: #808030;">+</span> vlacpp<span style="color: #008c00;">.</span>cpp <span style="color: #808030;">-</span>o vlacpp
aldo@ao<span style="color: #808030;">-</span>linux<span style="color: #808030;">-</span>nb<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ <span style="color: #808030;">.</span><span style="color: #808030;">/</span>vlacpp <span style="color: #008c00;">2000</span>
testVLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;">:</span> <span style="color: #008c00;">4.274441</span> segundos
testMallocVLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;">:</span> <span style="color: #008c00;">3.641508</span> segundos
testStackFLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;">:</span> <span style="color: #008c00;">4.340430</span> segundos
testHeapFLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;">:</span> <span style="color: #008c00;">4.312986</span> segundos
testVectorVLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;">:</span> <span style="color: #008c00;">10.660610</span> segundos
aldo@ao<span style="color: #808030;">-</span>linux<span style="color: #808030;">-</span>nb<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ g<span style="color: #808030;">+</span><span style="color: #808030;">+</span> <span style="color: #808030;">-</span>O2 vlacpp<span style="color: #008c00;">.</span>cpp <span style="color: #808030;">-</span>o vlacpp
aldo@ao<span style="color: #808030;">-</span>linux<span style="color: #808030;">-</span>nb<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ <span style="color: #808030;">.</span><span style="color: #808030;">/</span>vlacpp <span style="color: #008c00;">2000</span>
testVLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;">:</span> <span style="color: #008c00;">0.768702</span> segundos
testMallocVLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;">:</span> <span style="color: #008c00;">0.694418</span> segundos
testStackFLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;">:</span> <span style="color: #008c00;">0.682241</span> segundos
testHeapFLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;">:</span> <span style="color: #008c00;">0.694299</span> segundos
testVectorVLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;">:</span> <span style="color: #008c00;">1.364321</span> segundos</pre>
¿Cómo se ha portado <b>std::vector</b>? Yo diría que las cifras hablan por sí solas... bien, vamos a ponerlo en una forma diplomática: digamos que tenemos dos noticias, una buena y una mala:<br />
<ul>
<li>la buena noticia es que el <b>C++</b> es tan eficiente como el <b>C</b> (y sobre esto no tenía ninguna duda), de hecho el nuestro programa <b>C</b> transformado en <b>C++</b> obtiene (en los primeros cuatro test) el mismo rendimiento (<i>¡<a href="https://artcprogramming-es.blogspot.com.es/2017/05/el-bueno-el-feo-y-el-vla-como-usar-los.html" target="_blank"><b>ir a controlar ahi</b></a>, si no me creéis, eh!</i>).</li>
<li>la mala noticia es que el <b>C++</b> es tan eficiente como el <b>C</b>, <i>pero sólo si lo usas como el <b>C</b>, entonces nada de <a href="https://es.wikipedia.org/wiki/Standard_Template_Library" target="_blank"><b>STL</b></a> y parafernalias varias.</i></li>
</ul>
<i>(...Abro otro paréntesis: por supuesto, la mala noticia anterior no se deriva sólo del simple test propuesto en este post: se deriva de años y años de observaciones y uso intensivo de los dos lenguajes, solo faltaría. Cierro el paréntesis...).</i><br />
<br />
Sin entrar mucho en detalles (tal vez un día voy a escribir un post específico sobre el tema) os presento mi opinión: el lenguaje<b> C++</b> es un gran lenguaje potente, eficiente y expresivo (<i>¡es pariente cercano del C!</i>), con el cual se puede escribir Software de alta calidad. Pero los mejores resultados (al menos en términos de rendimiento y fluidez del código) se obtienen mediante el uso por lo cual fue diseñado originalmente, es decir, como un <b>C a objetos</b>. El cambio que tuvo más tarde (<i>desde cuando cayó en manos de los committee ISO</i>) no me gusta y no me convence... pero, por suerte (y esto es importante), todavía se puede utilizar en su esencia, la que permite escribir a objetos utilizando un lenguaje (casi) igual al <b>C</b> (<i>y esto corresponde a la buena noticia aqui arriba. Obviamente, si rendimiento y fluidez del código no se consideran factores importantes, entonces todas estas consideraciones pierden de significado...</i>).<br />
<br />
Ah, un último punto para los que se han sorprendido por el código <b>C++ </b>(aquí arriba) que incluye un <b>VLA</b>: es una amable oferta de nuestro amado <a href="https://es.wikipedia.org/wiki/GNU_Compiler_Collection" target="_blank"><b>GCC</b></a> (en su encarnación <b>G++</b>). Por lo tanto es una extensión del lenguaje proporcionada por el compilador, ya que los <b>VLAs</b> no forman parte del estándar <b>C++</b> (incluso las últimas versiones <b>C++11</b> y <b>C++14</b>).<br />
<br />
En el próximo posta, para cerrar el círculo, vamos a hablar de un pariente cercano de los <b>VLAs</b>, o sea de la función <a href="http://man7.org/linux/man-pages/man3/alloca.3.html" target="_blank"><b>alloca()</b></a>. <i>¿Será otro bueno, otro feo, u otro malo?</i><br />
<br />
¡Hasta el próximo post!Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-40202436305161992242017-05-19T19:29:00.000+02:002018-08-11T14:18:06.096+02:00El bueno, el feo y el VLA cómo usar los Variable Length Arrays en C - pt.1La referencia cinematográficas de este mes es ideal: un <a href="https://en.wikipedia.org/wiki/Variable-length_array" target="_blank"><b>Variable Length Array</b></a> (<b>VLA</b> para los amigos) sería perfecto para interpretar el malo en la obra maestra "<a href="https://es.wikipedia.org/wiki/Il_buono,_il_brutto,_il_cattivo" target="_blank"><b>El bueno, el feo y el malo</b></a>" del legendario <a href="https://es.wikipedia.org/wiki/Sergio_Leone" target="_blank"><b>Sergio Leone</b></a>. Y al final del(proximo) post resultará claro por qué. <br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBHcM0rJgDydl5UE5ws7V4Q1FoFsWBWaQLSqUJgzIQ0zxL95kbRYwWtXyaYQH1nsxL4ZwMxyS-WFQ_4Bdo4zX3DNS7BdHdWG2Y9KfezTXqGkZMDnLVmmUacFmQ2p9gPee84CKlD4OMe3F9/s1600/leevancleef.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" height="225" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBHcM0rJgDydl5UE5ws7V4Q1FoFsWBWaQLSqUJgzIQ0zxL95kbRYwWtXyaYQH1nsxL4ZwMxyS-WFQ_4Bdo4zX3DNS7BdHdWG2Y9KfezTXqGkZMDnLVmmUacFmQ2p9gPee84CKlD4OMe3F9/s400/leevancleef.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">...hola soy un VLA: comienza a preocuparte...</td></tr>
</tbody></table>
Los <b>VLAs</b> son una cosa relativamente nueva del <b>C</b>: se introdujeron en <a href="https://es.wikipedia.org/wiki/C_(lenguaje_de_programaci%C3%B3n)#C99" target="_blank"><b>C99</b></a>,
y son, al parecer, el sueño hecho realidad del mundo <b>C</b>: "<i>¡Finalmente
hay array con dimensiones variable! ¡Ah, si los hubiera tenido antes
'99!</i>". Vale, la idea es simple: con un <b>VLA</b> tipo podéis escribir cositas
como estas:<br />
<pre style="background: #ffffff; color: black;"><span style="color: maroon; font-weight: bold;">void</span> myVla<span style="color: #808030;">(</span>
<span style="color: maroon; font-weight: bold;">int</span> size1<span style="color: #808030;">,</span>
<span style="color: maroon; font-weight: bold;">int</span> size2<span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// mi VLA de int</span>
<span style="color: maroon; font-weight: bold;">int</span> ivla<span style="color: #808030;">[</span>size1<span style="color: #808030;">]</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// hace algo con el VLA de int</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
<span style="color: dimgrey;">// mi VLA bidimensional de float</span>
<span style="color: maroon; font-weight: bold;">float</span> fvla<span style="color: #808030;">[</span>size1<span style="color: #808030;">]</span><span style="color: #808030;">[</span>size2<span style="color: #808030;">]</span><span style="color: purple;">:</span>
<span style="color: dimgrey;">// hace algo con el VLA bidimensional de float</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
<span style="color: purple;">}</span>
</pre>
¿Fantastico, no? Demasiado bueno para ser verdad... pero habrá algunas contraindicaciones? Definitivamente no en el rendimiento: justo por eso he escrito un poco de código para poner a prueba las prestaciones de los <b>VLAs</b> respeto a las alternativas directas: array dinámicos (con <a href="http://man7.org/linux/man-pages/man3/malloc.3.html" target="_blank"><b>malloc()</b></a>) y array estáticos (en <b><a href="https://en.wikipedia.org/wiki/Memory_management" target="_blank">heap y stack</a></b>). ¡vamos con el código!<br />
<pre style="background: #ffffff; color: black;"><span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdio.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">time.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdlib.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">define</span><span style="color: #004a43;"> MYSIZE 1000000</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span class="" id="result_box" lang="es"><span class="">variable dummy</span> <span class="">con el fin de</span> <span class="">evitar</span> el vaciado <span class="">total de</span> <span class="">las funciones</span> <span class="">usando</span></span> GCC -O2</span>
<span style="color: maroon; font-weight: bold;">int</span> avoid_optimization<span style="color: purple;">;</span>
<span style="color: dimgrey;">// prototipos locales</span>
<span style="color: maroon; font-weight: bold;">void</span> testVLA<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> size<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">void</span> testMallocVLA<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> size<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">void</span> testStackFLA<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> dum<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">void</span> testHeapFLA<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> dum<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">void</span> runTest<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> iterations<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">void</span> <span style="color: #808030;">(</span><span style="color: #808030;">*</span>funcptr<span style="color: #808030;">)</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span><span style="color: #808030;">)</span><span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">int</span> size<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>name<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// función main()</span>
<span style="color: maroon; font-weight: bold;">int</span> <span style="color: #400000;">main</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> argc<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">char</span><span style="color: #808030;">*</span> argv<span style="color: #808030;">[</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// test argumentos</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>argc <span style="color: #808030;">!</span><span style="color: #808030;">=</span> <span style="color: #008c00;">2</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// error args</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">: wrong arguments counts</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">usage: </span><span style="color: #007997;">%s</span><span style="color: #0000e6;"> vla iterations [e.g.: </span><span style="color: #007997;">%s</span><span style="color: #0000e6;"> 10000]</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_FAILURE<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// extrae iteraciones</span>
<span style="color: maroon; font-weight: bold;">int</span> iterations <span style="color: #808030;">=</span> <span style="color: #603000;">atoi</span><span style="color: #808030;">(</span>argv<span style="color: #808030;">[</span><span style="color: #008c00;">1</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// ejecuta test</span>
runTest<span style="color: #808030;">(</span>iterations<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>testVLA<span style="color: #808030;">,</span> MYSIZE<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">testVLA</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
runTest<span style="color: #808030;">(</span>iterations<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>testMallocVLA<span style="color: #808030;">,</span> MYSIZE<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">testMallocVLA</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
runTest<span style="color: #808030;">(</span>iterations<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>testStackFLA<span style="color: #808030;">,</span> <span style="color: #008c00;">0</span><span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">testStackFLA</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
runTest<span style="color: #808030;">(</span>iterations<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>testHeapFLA<span style="color: #808030;">,</span> <span style="color: #008c00;">0</span><span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">testHeapFLA</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// sale</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_SUCCESS<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">función</span> runTest()</span>
<span style="color: maroon; font-weight: bold;">void</span> runTest<span style="color: #808030;">(</span>
<span style="color: maroon; font-weight: bold;">int</span> iterations<span style="color: #808030;">,</span> <span style="color: dimgrey;">// iteraciones del test</span>
<span style="color: maroon; font-weight: bold;">void</span> <span style="color: #808030;">(</span><span style="color: #808030;">*</span>funcptr<span style="color: #808030;">)</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span><span style="color: #808030;">)</span><span style="color: #808030;">,</span> <span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">función</span> de test</span>
<span style="color: maroon; font-weight: bold;">int</span> size<span style="color: #808030;">,</span> <span style="color: dimgrey;">// size del array</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>name<span style="color: #808030;">)</span> <span style="color: dimgrey;">// nombre </span><span style="color: dimgrey;"><span style="color: dimgrey;">función</span> de test</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// lee start time</span>
<span style="color: #603000;">clock_t</span> t_start <span style="color: #808030;">=</span> <span style="color: #603000;">clock</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// ejecuta iteraciones de test</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> i <span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span> i <span style="color: #808030;"><</span> iterations<span style="color: purple;">;</span> i<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: #808030;">)</span>
<span style="color: #808030;">(</span><span style="color: #808030;">*</span>funcptr<span style="color: #808030;">)</span><span style="color: #808030;">(</span>size<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// lee end time y enseña el resultado</span>
<span style="color: #603000;">clock_t</span> t_end <span style="color: #808030;">=</span> <span style="color: #603000;">clock</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">double</span> t_passed <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">double</span><span style="color: #808030;">)</span><span style="color: #808030;">(</span>t_end <span style="color: #808030;">-</span> t_start<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">/</span> CLOCKS_PER_SEC<span style="color: purple;">;</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #007997;">%-13s</span><span style="color: #0000e6;"> - Tiempo transcurrido: </span><span style="color: #007997;">%f</span><span style="color: #0000e6;"> segundos</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> name<span style="color: #808030;">,</span> t_passed<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">función</span> testVLA()</span>
<span style="color: maroon; font-weight: bold;">void</span> testVLA<span style="color: #808030;">(</span>
<span style="color: maroon; font-weight: bold;">int</span> size<span style="color: #808030;">)</span> <span style="color: dimgrey;">// size para VLA</span>
<span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">int</span> vla<span style="color: #808030;">[</span>size<span style="color: #808030;">]</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// loop de test</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> i <span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span> i <span style="color: #808030;"><</span> size<span style="color: purple;">;</span> i<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: #808030;">)</span>
vla<span style="color: #808030;">[</span>i<span style="color: #808030;">]</span> <span style="color: #808030;">=</span> i<span style="color: purple;">;</span>
<span style="color: dimgrey;">// instrucción </span><span style="color: dimgrey;"><span class="" id="result_box" lang="es"><span class="">con el fin de</span> <span class="">evitar</span> el vaciado <span class="">total de</span> <span class="">las funciones</span> <span class="">usando</span></span> GCC -O2</span>
avoid_optimization <span style="color: #808030;">=</span> vla<span style="color: #808030;">[</span>size <span style="color: #808030;">/</span> <span style="color: #008c00;">2</span><span style="color: #808030;">]</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">función</span> testMallocVLA()</span>
<span style="color: maroon; font-weight: bold;">void</span> testMallocVLA<span style="color: #808030;">(</span>
<span style="color: maroon; font-weight: bold;">int</span> size<span style="color: #808030;">)</span> <span style="color: dimgrey;">// size para malloc()</span>
<span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">int</span> <span style="color: #808030;">*</span>mallocvla <span style="color: #808030;">=</span> <span style="color: #603000;">malloc</span><span style="color: #808030;">(</span>size <span style="color: #808030;">*</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// loop de test</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> i <span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span> i <span style="color: #808030;"><</span> size<span style="color: purple;">;</span> i<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: #808030;">)</span>
mallocvla<span style="color: #808030;">[</span>i<span style="color: #808030;">]</span> <span style="color: #808030;">=</span> i<span style="color: purple;">;</span>
<span style="color: dimgrey;">// instrucción </span><span style="color: dimgrey;"><span class="" id="result_box" lang="es"><span class="">con el fin de</span> <span class="">evitar</span> el vaciado <span class="">total de</span> <span class="">las funciones</span> <span class="">usando</span></span> GCC -O2</span>
avoid_optimization <span style="color: #808030;">=</span> mallocvla<span style="color: #808030;">[</span>size <span style="color: #808030;">/</span> <span style="color: #008c00;">2</span><span style="color: #808030;">]</span><span style="color: purple;">;</span>
<span style="color: #603000;">free</span><span style="color: #808030;">(</span>mallocvla<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span></pre>
<pre style="background: #ffffff; color: black;"><span style="color: purple;"> </span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">función</span> testStackFLA()</span>
<span style="color: maroon; font-weight: bold;">void</span> testStackFLA<span style="color: #808030;">(</span>
<span style="color: maroon; font-weight: bold;">int</span> dum<span style="color: #808030;">)</span> <span style="color: dimgrey;">// parámetro dummy</span>
<span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">int</span> stackfla<span style="color: #808030;">[</span>MYSIZE<span style="color: #808030;">]</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// loop de test</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> i <span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span> i <span style="color: #808030;"><</span> MYSIZE<span style="color: purple;">;</span> i<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: #808030;">)</span>
stackfla<span style="color: #808030;">[</span>i<span style="color: #808030;">]</span> <span style="color: #808030;">=</span> i<span style="color: purple;">;</span>
<span style="color: dimgrey;">// instrucción </span><span style="color: dimgrey;"><span class="" id="result_box" lang="es"><span class="">con el fin de</span> <span class="">evitar</span> el vaciado <span class="">total de</span> <span class="">las funciones</span> <span class="">usando</span></span> GCC -O2</span>
avoid_optimization <span style="color: #808030;">=</span> stackfla<span style="color: #808030;">[</span>size <span style="color: #808030;">/</span> <span style="color: #008c00;">2</span><span style="color: #808030;">]</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">función</span> testHeapFLA()</span>
<span style="color: maroon; font-weight: bold;">int</span> heapfla<span style="color: #808030;">[</span>MYSIZE<span style="color: #808030;">]</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">void</span> testHeapFLA<span style="color: #808030;">(</span>
<span style="color: maroon; font-weight: bold;">int</span> dum<span style="color: #808030;">)</span> <span style="color: dimgrey;">// parámetro dummy</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// loop de test</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> i <span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span> i <span style="color: #808030;"><</span> MYSIZE<span style="color: purple;">;</span> i<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: #808030;">)</span>
heapfla<span style="color: #808030;">[</span>i<span style="color: #808030;">]</span> <span style="color: #808030;">=</span> i<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
</pre>
Aquí, como siempre, <i>corto-y-pego</i> lo que siempre escribo después de mostrar el código <i>(equipo que gana no se cambia...</i>): Ok, como se nota es <b><a href="http://artcprogramming-es.blogspot.com.es/2012/08/no-comment.html" target="_blank">ampliamente comentado</a> </b> y así se <i>auto-explica</i>, por lo cual no voy a detenerme sobre las instrucciones y/o grupos de instrucciones (<i>¡leer los comentarios! ¡Están ahí para eso!</i>), pero voy a añadir, solamente, algunos detalles estructurales.<br />
<br />
Así que: dado que es un test comparativo he escrito una función <b>runTest()</b> que llama n-iteraciones de la función a probar y cuenta el tiempo utilizado. el <b>main()</b> llama simplemente cuatro veces <b>runTest()</b>, una para cada función. Las cuatro funciones de test que he escrito prueban (como indicado por los nombres, por supuesto): un<b> C99-VLA</b>, un tradicional <b>malloc-VLA</b>, un <b>Fixed-LA</b> alocado en el stack, y un <b>Fixed-LA</b> alocado en el heap. Para cada test se utiliza un (gran) array-size de <b>1000000</b>, y el número de iteraciones se decide arrancando la aplicación (esto es muy útil, como veremos más adelante). <br />
<br />
Hay que notar que <b>runTest()</b> utiliza un <a href="https://en.wikipedia.org/wiki/Function_pointer" target="_blank"><b>function pointer</b></a> para arranar el test (hemos visto algo parecido hablando <a href="https://artcprogramming-es.blogspot.com.es/2012/12/la-maldicion-de-la-callback-de-jade.html" target="_blank"><b>aquí de las callback</b></a>): he utilizado la versión extendida de la declaración (<i>void (*funcptr)(int) + llamada de la función con el operador &</i>) pero os recuerdo que, por ejemplo, <b>GCC</b> también digiere fácilmente la declaración simplificada (<i>void funcptr(int) + llamada sin el operador &</i>). La versión extendida es, obviamente, más portátil. Y ya que estamos en el tema de los compiladores: aunque los <b>VLAs</b> (y los loop <i>for (int...)</i>) que utilizo el código de este mes se permiten sólo de C99 en adelante no es necesario (si se utiliza GCC) especificar el flag <i>-std=c99</i> en compilación: las versiones recientes de GCC incluyen por defecto (al menos) también el C99 (además de las extensiones GNU C): si realmente queréis estar seguros de que lo que habéis escrito cumpla con un standard en particular tenéis que utilizar otros flag: por ejemplo, si queréis escribir utilizando sólo el C89, tenéis que añadir en la línea de compilación: <i>-std=c89 -pedantic</i>. Si estáis utilizando un GCC un poco viejo la compilación del ejemplo os dará warning y/o errores, y habrá que volver a compilar forzando la compatibilidad con el C99.<br />
<br />
Los resultados son los siguientes:<br />
<pre style="background: #ffffff; color: black;">aldo@ao<span style="color: #808030;">-</span>linux<span style="color: #808030;">-</span>nb<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ gcc vla<span style="color: #008c00;">.</span>c <span style="color: #808030;">-</span>o vla
aldo@ao<span style="color: #808030;">-</span>linux<span style="color: #808030;">-</span>nb<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ <span style="color: #808030;">.</span><span style="color: #808030;">/</span>vla <span style="color: #008c00;">1000</span>
testVLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;">:</span> <span style="color: #008c00;">4.263985</span> segundos
testMallocVLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;">:</span> <span style="color: #008c00;">3.641929</span> segundos
testStackFLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;">:</span> <span style="color: #008c00;">4.292963</span> segundos
testHeapFLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;">:</span> <span style="color: #008c00;">4.285660</span> segundos
aldo@ao<span style="color: #808030;">-</span>linux<span style="color: #808030;">-</span>nb<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ gcc <span style="color: #808030;">-</span>O2 vla<span style="color: #008c00;">.</span>c <span style="color: #808030;">-</span>o vla
aldo@ao<span style="color: #808030;">-</span>linux<span style="color: #808030;">-</span>nb<span style="color: #808030;">:</span><span style="color: #808030;">~</span><span style="color: #808030;">/</span>blogtest$ <span style="color: #808030;">.</span><span style="color: #808030;">/</span>vla <span style="color: #008c00;">10000</span>
testVLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;">:</span> <span style="color: #008c00;">0.767087</span> segundos
testMallocVLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;">:</span> <span style="color: #008c00;">0.690925</span> segundos
testStackFLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;">:</span> <span style="color: #008c00;">0.678178</span> segundos
testHeapFLA <span style="color: #808030;">-</span> Tiempo transcurrido<span style="color: #808030;">:</span> <span style="color: #008c00;">0.687785</span> segundos</pre>
Como se puede ver he ejecutado dos test con/sin optimización (flag GCC <i>-O2</i>) y, obviamente, ha sido muy útil el parámetro n-iteraciones de la aplicación, que me ha permitido encontrar un valor adapto para obtener resultados significativos y, al mismo tiempo, para evitar tiempos de ejecución bíblicos con la versión sin optimizaciones. ¿Cómo podemos comentar? Pues bien, ¡el <b>VLA</b> se porta muy bien con/sin optimizaciones! Y consigue, prácticamente, los mismos resultados de su competidor directo, el <b>malloc-VLA</b>, ¡y es más fácil de usar!<br />
<br />
Así que, volviendo al tema principal: ¡<b>VLA</b> aprobado!<br />
<br />
<div style="text-align: center;">
<span style="font-size: x-large;">PERO...</span></div>
<br />
bueno, el porque del <b>VLA</b> malo os lo explicaré en el próximo post, y que sepáis que no todo lo que brilla es oro... y solo por hacer un pequeño spoiler sobre las consideraciones finales: <i>¡yo nunca utilizo los <b>VLAs</b> en el código que escribo! </i><br />
<br />
¡Hasta el próximo post!
Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-27426781422212894032017-04-25T19:46:00.001+02:002017-04-25T20:18:55.956+02:00The FileCopy cómo escribir una función de File Copy en C - pt.2<h3 class="post-title entry-title" itemprop="name">
</h3>
Ok, re-empezamos de donde nos dejamos en el <a href="https://artcprogramming-es.blogspot.com.es/2017/03/the-filecopy-como-escribir-una-funcion.html" target="_blank"><b>último post</b></a> (<i>¿lo habeis leido verdad?</i>) Y, como prometido, en esta ocasión el tema será una versión con <i>buffered I/O </i>de la función <b>cpFile()</b>. Es deber, antes, proporcionar otra imagen de<b> <a href="https://es.wikipedia.org/wiki/The_Thing_(pel%C3%ADcula_de_1982)" target="_blank">The Thing</a></b>, si no, de lo contrario, podría parecer que esto es sólo un blog de programación, mientras que, como bien sabéis, es un <i>blog para programadores cinefilos</i>...<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuzbL2nZx2HUnvlFhuIobppyBwOIkjOdkK4bfp4j4ggmUukRdzbXjss8SKkDkrz0EHOJDQKACWtctj2YGyUdo6g9wo82ZQXeOYKSU_8fax4OUZqgSZZDNNq1Bg7CD1F62dAFHKhJambfbW/s1600/kurtrussel.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" height="238" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuzbL2nZx2HUnvlFhuIobppyBwOIkjOdkK4bfp4j4ggmUukRdzbXjss8SKkDkrz0EHOJDQKACWtctj2YGyUdo6g9wo82ZQXeOYKSU_8fax4OUZqgSZZDNNq1Bg7CD1F62dAFHKhJambfbW/s400/kurtrussel.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">...con un sombrero así se programa mejor...</td></tr>
</tbody></table>
Vale, repetimos: <i>I/O buferizado </i>(y por lo tanto, por ejemplo, <a href="http://man7.org/linux/man-pages/man3/fread.3.html" target="_blank"><b>fread(3)</b></a> en lugar de <a href="http://man7.org/linux/man-pages/man2/read.2.html" target="_blank"><b>read(2)</b></a>, porque el objetivo esta vez es la portabilidad ¿y que hay más portátil (en C) que usar el contenido de <i>stdio.h</i>? El código que veremos en un momento utiliza (casi) el mismo <b>main() </b>de la versión <i>unbuffered</i> y los únicos cambios son internos a la función <b>cpFile()</b>. De hecho, incluso la <b>cpFile()</b> es casi idéntica desde un punto de vista lógico, ya que las funciones <i>buffered</i> tienen una sintaxis de uso y un funcionamiento muy similar a las equivalentes versiones <i>unbuffered</i>. Y claro, si la versión <i>buffered</i> os sale muy diferente (visualmente y lógicamente) de la versión <i>unbuffered</i> es que hay algo que no funciona... bueno, sobre este punto voy a hacer un pequeña digresión al final del post. Por ahora: ¡vamos con el código! <br />
<pre style="background: #ffffff; color: black;"><span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdio.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdlib.h</span><span style="color: maroon;">></span>
<span style="color: dimgrey;">// prototipos locales</span>
<span style="color: maroon; font-weight: bold;">static</span> <span style="color: maroon; font-weight: bold;">int</span> cpFile<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span><span style="color: #808030;">*</span> src<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span><span style="color: #808030;">*</span> dest<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// función main()</span>
<span style="color: maroon; font-weight: bold;">int</span> <span style="color: #400000;">main</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> argc<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>argv<span style="color: #808030;">[</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// test argumentos</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>argc <span style="color: #808030;">!</span><span style="color: #808030;">=</span> <span style="color: #008c00;">3</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// error args</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">: wrong arguments counts</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">usage: </span><span style="color: #007997;">%s</span><span style="color: #0000e6;"> srcfile destfile [e.g.: </span><span style="color: #007997;">%s</span><span style="color: #0000e6;"> try.c try.save]</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_FAILURE<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// ejecuta copia</span>
<span style="color: maroon; font-weight: bold;">int</span> retval<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>retval <span style="color: #808030;">=</span> cpFile<span style="color: #808030;">(</span>argv<span style="color: #808030;">[</span><span style="color: #008c00;">2</span><span style="color: #808030;">]</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">1</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;"><</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// enseña error y sale con error</span>
<span style="color: #603000;">fprintf</span><span style="color: #808030;">(</span><span style="color: #603000;">stderr</span><span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">: error: </span><span style="color: #007997;">%d</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">,</span> retval<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">exit</span><span style="color: #808030;">(</span>EXIT_FAILURE<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// sale con Ok</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_SUCCESS<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// función cpFile()</span>
<span style="color: maroon; font-weight: bold;">static</span> <span style="color: maroon; font-weight: bold;">int</span> cpFile<span style="color: #808030;">(</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span><span style="color: #808030;"> *</span>dest<span style="color: #808030;">,</span> <span style="color: dimgrey;">// file destinación</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span><span style="color: #808030;"> *</span>src<span style="color: #808030;">)</span> <span style="color: dimgrey;">// file fuente</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// abre el file fuente</span>
<span style="color: #603000;">FILE</span> <span style="color: #808030;">*</span>fp_in<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>fp_in <span style="color: #808030;">=</span> <span style="color: #603000;">fopen</span><span style="color: #808030;">(</span>src<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">r</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #7d0045;">NULL</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// return con error</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// abre el file destinación</span>
<span style="color: #603000;">FILE</span> <span style="color: #808030;">*</span>fp_out<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>fp_out <span style="color: #808030;">=</span> <span style="color: #603000;">fopen</span><span style="color: #808030;">(</span>dest<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">w</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #7d0045;">NULL</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// cierra el file y return con error</span><span style="color: dimgrey;"></span>
<span style="color: #603000;">fclose</span><span style="color: #808030;">(</span>fp_in<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #808030;">-</span><span style="color: #008c00;">2</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// r/w loop para la copia usando buffered I/O</span>
<span style="color: #603000;">size_t</span> n_read<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">char</span> buffer<span style="color: #808030;">[</span>BUFSIZ<span style="color: #808030;">]</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">while</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>n_read <span style="color: #808030;">=</span> <span style="color: #603000;">fread</span><span style="color: #808030;">(</span>buffer<span style="color: #808030;">,</span> <span style="color: #008c00;">1</span><span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span>buffer<span style="color: #808030;">)</span><span style="color: #808030;">,</span> fp_in<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">></span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">!</span> <span style="color: #603000;">ferror</span><span style="color: #808030;">(</span>fp_in<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// write buffer</span>
<span style="color: #603000;">fwrite</span><span style="color: #808030;">(</span>buffer<span style="color: #808030;">,</span> <span style="color: #008c00;">1</span><span style="color: #808030;">,</span> n_read<span style="color: #808030;">,</span> fp_out<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #603000;">ferror</span><span style="color: #808030;">(</span>fp_out<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// cierra los file y return con error</span>
<span style="color: #603000;">fclose</span><span style="color: #808030;">(</span>fp_in<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">fclose</span><span style="color: #808030;">(</span>fp_out<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #808030;">-</span><span style="color: #008c00;">3</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">else</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// cierra los file y return con error</span>
<span style="color: #603000;">fclose</span><span style="color: #808030;">(</span>fp_in<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">fclose</span><span style="color: #808030;">(</span>fp_out<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #808030;">-</span><span style="color: #008c00;">4</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// cierra los file</span>
<span style="color: #603000;">fclose</span><span style="color: #808030;">(</span>fp_in<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">fclose</span><span style="color: #808030;">(</span>fp_out<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// return con</span><span style="color: dimgrey;"> Ok</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span></pre>
Ok, como se nota es <b><a href="http://artcprogramming-es.blogspot.com.es/2012/08/no-comment.html" target="_blank">ampliamente comentado</a> </b> y así se <i>auto-explica</i>, por lo cual no voy a detenerme sobre las instrucciones y/o grupos de instrucciones (<i>¡leer los comentarios! ¡Están ahí para eso!</i>), pero voy a añadir, solamente, algunos detalles estructurales. El <b>main()</b>, como se había anticipado, es prácticamente idéntico, mientras que en la <b>cpFile() </b>se repiten, exactamente, las operaciones de la version unbuffered, pero usando <b><a href="http://man7.org/linux/man-pages/man3/fopen.3.html" target="_blank">fopen(3)</a></b> en lugar de <b> <a href="http://man7.org/linux/man-pages/man2/open.2.html" target="_blank">open(2)</a></b>, <a href="http://man7.org/linux/man-pages/man3/fclose.3.html" target="_blank"><b>fclose(3)</b></a> en lugar de <a href="http://man7.org/linux/man-pages/man2/close.2.html" target="_blank"><b>close(2)</b></a>, etc. ¿Donde encontramos algunas (pequeñas) diferencias? Sólo en el test de (eventuales) errores de lectura/escritura, que en la versión <i>unbuffered</i> estaban implícitas en las operaciones de <i>read/write</i> (probando si el resultado era igual a -1), mientras que en este caso hay que usar una función a parte, <a href="http://man7.org/linux/man-pages/man3/clearerr.3.html" target="_blank"><b>ferror(3)</b></a>, y esto es debido a que:<br />
<pre style="background: #ffffff; color: black;"> On success<span style="color: #808030;">,</span> fread<span style="color: #808030;">(</span><span style="color: #808030;">)</span> and fwrite<span style="color: #808030;">(</span><span style="color: #808030;">)</span> return the number of items read or
written<span style="color: #808030;">.</span> This number equals the number of bytes transferred only when
size is <span style="color: #008c00;">1</span><span style="color: #808030;">.</span> If an error occurs<span style="color: #808030;">,</span> or the end of the file is reached<span style="color: #808030;">,</span> the
return value is a short item count <span style="color: #808030;">(</span>or zero<span style="color: #808030;">)</span><span style="color: #808030;">.</span>
fread<span style="color: #808030;">(</span><span style="color: #808030;">)</span> does not distinguish between end<span style="color: #808030;">-</span>of<span style="color: #808030;">-</span>file and error<span style="color: #808030;">,</span> and callers
must use feof<span style="color: #808030;">(</span><span style="color: #008c00;">3</span><span style="color: #808030;">)</span> and ferror<span style="color: #808030;">(</span><span style="color: #008c00;">3</span><span style="color: #808030;">)</span> to determine which occurred<span style="color: #808030;">.</span></pre>
Lo anterior es lo que trae la <i>man-page</i> de <b>fread(3)</b>/<b>fwrite(3)</b>, y creo que es una justificación suficiente de por qué he escrito el código asì (y, por lo mismo, no podemos usar <a href="http://man7.org/linux/man-pages/man3/strerror.3.html" target="_blank"><b>strerror(3)</b></a> en el <b>main()</b> para enseñar los errores). Así que, como se había anticipado, las versiones <i>buffered</i> y <i>unbuffered</i> deben ser casi sobreponibles, y creo que es exactamente el resultado conseguido.<br />
<br />
Y ahora la digresión prometida: me ha pasado de encontrar en la red (<i>incluso en apreciados blogs/webs de programación</i>) ejemplos de <i>buffered-copy</i> de file que utilizan unos loop de este tipo:<br />
<pre style="background: #ffffff; color: black;"><span style="color: maroon; font-weight: bold;">while</span> <span style="color: #808030;">(</span><span style="color: #808030;">!</span><span style="color: #603000;">feof</span><span style="color: #808030;">(</span>fp_in<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// lee y escribe buffer</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
<span style="color: purple;">}</span></pre>
vale, ¿cómo se puede comentar esto? Con una sola palabra:<br />
<br />
<div style="text-align: center;">
<b><span style="font-family: inherit;"><span style="font-size: x-large;">NO</span></span></b></div>
<br />
Si uno escribe el loop de esta manera significa que no ha leído la <i>man-page</i> de <b>fread(3)</b>/<b>fwrite(3)</b> o que la ha leido y no ha entendido el contenido. No hay necesidad de reinventar la rueda, repito: <b>fread(3)</b>/<b>fwrite(3)</b> funcionan casi de la misma manera de <b>read(2)</b>/<b>write(2)</b>, por lo que, si el ejemplo de <b>cpFile()</b> <i>unbuffered</i> del post anterior era bueno (<i>¡y lo era!</i>), entonces el ejemplo del post actual debería ser (casi) idéntico. El loop con un <i>while() </i>que testea <a href="http://man7.org/linux/man-pages/man3/clearerr.3.html" target="_blank"><b>feof(3)</b></a> es <span class="short_text" id="result_box" lang="es"><span class="">sintácticamente</span></span> correcto, pero no lo es lógicamente, ya que comienza testeando algo que todavía no es utilizable (uhmm, ¿una prueba predictiva?) y que, además, no hay necesidad de testear. Bah, no quiero extender demasiado este argumento y os remito a la excelente análisis <a href="http://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong" target="_blank"><b>contenida ahí</b></a> (en el siempre optimo stackoverflow.com).<br />
Obviamente espero no haber ofendido a nadie (con la digresión anterior): Recordar que <i>errare humanum est</i>... y, por supuesto, también en este blog habré escrito en el pasado algunas tonterías (espero no graves como aquella enseñada hace un momento). Os aseguro, sin embargo, que yo siempre soy muy cuidadoso de no proponer soluciones que no he tenido tiempo para escribir y probar de manera curada, o de lo contrario en lugar de un blog de <i>programación artística</i> este sería un blog de <i>programación a la esperamos que funciona</i>...<br />
<br />
¡Hasta el próximo post!Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-90203832560095421542017-03-14T20:32:00.001+01:002017-04-24T20:18:03.316+02:00The FileCopy cómo escribir una función de File Copy en C - pt.1Ok, este post no tiene nada que ver con <a href="https://es.wikipedia.org/wiki/The_Thing_(pel%C3%ADcula_de_1982)" target="_blank"><b>The Thing</b></a>, la inmortal obra maestra de <a href="https://es.wikipedia.org/wiki/John_Carpenter" target="_blank"><b>John Carpenter</b></a> (aparte de la pequeña similitud del nombre). De hecho, es sólo una excusa para celebrarlo, ya que lo he visto (<i>por milésima vez</i>) recientemente. Sin embargo, ya que estamos, hablaremos también un poco de C...<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiuB1N5gwwkISCLjzesb0xRu-q2rGlqFtRf1-Hkjhny5K7kDchYp9yTjJCuj1lUzE65Rzm4tVAUOScOTbebmQCuqnrUx2kt3YRbPb6SOifCSKgGc6aQSwYaar4iXo-e2eBDvcv3W_MlES5/s1600/thething.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" height="167" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiuB1N5gwwkISCLjzesb0xRu-q2rGlqFtRf1-Hkjhny5K7kDchYp9yTjJCuj1lUzE65Rzm4tVAUOScOTbebmQCuqnrUx2kt3YRbPb6SOifCSKgGc6aQSwYaar4iXo-e2eBDvcv3W_MlES5/s400/thething.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">inmortal obra maestra</td></tr>
</tbody></table>
Con este post veremos cómo escribir una función para realizar una copia de un file, ya que los sistemas <a href="https://es.wikipedia.org/wiki/POSIX" target="_blank"><b>POSIX</b></a> (como <b>Linux</b>) no proporcionan una función específica de librería para hacerlo. Hay, por supuesto, mil formas de escribirla, y esta vez he pensado en dos versiones. ¡Vamos con la primera!<br />
<pre style="background: #ffffff; color: black;"><span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdio.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">stdlib.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">unistd.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">string.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">sys/stat.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">fcntl.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">errno.h</span><span style="color: maroon;">></span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">sys/sendfile.h</span><span style="color: maroon;">></span>
<span style="color: dimgrey;">// prototipos locales</span>
<span style="color: maroon; font-weight: bold;">static</span> <span style="color: maroon; font-weight: bold;">int</span> cpFile<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span><span style="color: #808030;">*</span> src<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span><span style="color: #808030;">*</span> dest<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// función main()</span>
<span style="color: maroon; font-weight: bold;">int</span> <span style="color: #400000;">main</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> argc<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>argv<span style="color: #808030;">[</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// test argumentos</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>argc <span style="color: #808030;">!</span><span style="color: #808030;">=</span> <span style="color: #008c00;">3</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// error args</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">: wrong arguments counts</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #0000e6;">usage: </span><span style="color: #007997;">%s</span><span style="color: #0000e6;"> srcfile destfile [e.g.: </span><span style="color: #007997;">%s</span><span style="color: #0000e6;"> try.c try.save]</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_FAILURE<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// ejecuta copia</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>cpFile<span style="color: #808030;">(</span>argv<span style="color: #808030;">[</span><span style="color: #008c00;">2</span><span style="color: #808030;">]</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">1</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// enseña error y sale</span>
<span style="color: #603000;">fprintf</span><span style="color: #808030;">(</span><span style="color: #603000;">stderr</span><span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">: error: </span><span style="color: #007997;">%s</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">,</span> <span style="color: #603000;">strerror</span><span style="color: #808030;">(</span>errno<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">exit</span><span style="color: #808030;">(</span>EXIT_FAILURE<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// sale</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_SUCCESS<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// función cpFile()</span>
<span style="color: maroon; font-weight: bold;">static</span> <span style="color: maroon; font-weight: bold;">int</span> cpFile<span style="color: #808030;">(</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span><span style="color: #808030;"> *</span>dest<span style="color: #808030;">,</span> <span style="color: dimgrey;">// file destinación</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span><span style="color: #808030;"> *</span>src<span style="color: #808030;">)</span> <span style="color: dimgrey;">// file fuente</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// abre el file fuente</span>
<span style="color: maroon; font-weight: bold;">int</span> fd_in<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>fd_in <span style="color: #808030;">=</span> open<span style="color: #808030;">(</span>src<span style="color: #808030;">,</span> O_RDONLY<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// return con errore</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// abre el file destinación</span>
<span style="color: maroon; font-weight: bold;">int</span> fd_out<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>fd_out <span style="color: #808030;">=</span> open<span style="color: #808030;">(</span>dest<span style="color: #808030;">,</span> O_WRONLY <span style="color: #808030;">|</span> O_CREAT <span style="color: #808030;">|</span> O_TRUNC<span style="color: #808030;">,</span> <span style="color: #008c00;">00644</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// cierra el file y return con error</span>
close<span style="color: #808030;">(</span>fd_in<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// r/w loop para la copia usando unbuffered I/O</span>
<span style="color: #603000;">size_t</span> n_read<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">char</span> buffer<span style="color: #808030;">[</span>BUFSIZ<span style="color: #808030;">]</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">while</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>n_read <span style="color: #808030;">=</span> read<span style="color: #808030;">(</span>fd_in<span style="color: #808030;">,</span> buffer<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span>buffer<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">></span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// write buffer</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>write<span style="color: #808030;">(</span>fd_out<span style="color: #808030;">,</span> buffer<span style="color: #808030;">,</span> n_read<span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// cierra el file y return con error</span>
close<span style="color: #808030;">(</span>fd_in<span style="color: #808030;">)</span><span style="color: purple;">;</span>
close<span style="color: #808030;">(</span>fd_out<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// cierra los file</span>
close<span style="color: #808030;">(</span>fd_in<span style="color: #808030;">)</span><span style="color: purple;">;</span>
close<span style="color: #808030;">(</span>fd_out<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// sale con el ultimo resultado de read() (0 o -1)</span>
<span style="color: maroon; font-weight: bold;">return</span> n_read<span style="color: purple;">;</span>
<span style="color: purple;">}</span></pre>
Ok, como se nota es <b><a href="http://artcprogramming-es.blogspot.com.es/2012/08/no-comment.html" target="_blank">ampliamente comentado</a> </b> y así se <i>auto-explica</i>, por lo cual no voy a detenerme sobre las instrucciones y/o grupos de instrucciones (<i>¡leer los comentarios! ¡Están ahí para eso!</i>), pero voy a añadir, solamente, algunos detalles estructurales. El <b>main()</b>, en este caso, sólo sirve para probar la función de copia y el programa generado se comporta (a nivel básico) como la función <b>POSIX</b> <a href="https://it.wikipedia.org/wiki/Cp_%28Unix%29" target="_blank"><b>cp(1)</b></a>, que es precisamente lo que queremos emular usando la nuestra nueva función de librería.<br />
<br />
La función que hace el trabajo la he llamada <b>cpFile()</b> y es bastante simple, como se ve. Utiliza el <i>I/O sin búfer</i> (así, por ejemplo, <a href="http://man7.org/linux/man-pages/man2/read.2.html" target="_blank"><b>read(2)</b></a> en lugar de <a href="http://man7.org/linux/man-pages/man3/fread.3.html" target="_blank"><b>fread(3)</b></a>) y, aunque es muy compacta y eficiente, también trata de forma integral los errores y està escrita para ser una función de librería, por lo que no escribe nada en <a href="https://es.wikipedia.org/wiki/Entrada_est%C3%A1ndar" target="_blank"><b>stderr y stdout</b></a> sino simplemente realiza el trabajo y devulve un código de retorno (0 ó -1), que puede ser tratado por el llamante (en este caso el <b>main()</b>) para mostrar eventuales errores utilizando <a href="http://man7.org/linux/man-pages/man3/strerror.3.html" target="_blank"><b>strerror(3)</b></a> y <a href="http://man7.org/linux/man-pages/man3/errno.3.html" target="_blank"><b>errno</b></a>. Todo el trabajo se realiza en un loop que lee un buffer en el file fuente y lo escribe en el file de destino, hasta el final del file. El resto del código es la abertura/cierre de los file y manejo de los errores. Teniendo en cuenta que utilizamos el <i>unbuffered I/O</i> he dimensionado el búfer de lectura/escritura utilizando la <i>define </i><b>BUFSIZ</b> del sistema que deberia garantizar un tamaño óptimo para las operaciones de <i>I/O</i>. <br />
<br />
He dicho que iba a proponer dos versiones: sin modificar la función <b>main() </b>(que está bien para ambos casos) la versión alternativa es la siguiente:<br />
<pre style="background: #ffffff; color: black;"><span style="color: dimgrey;">// función cpFile()</span>
<span style="color: maroon; font-weight: bold;">static</span> <span style="color: maroon; font-weight: bold;">int</span> cpFile<span style="color: #808030;">(</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span><span style="color: #808030;">*</span> dest<span style="color: #808030;">,</span> <span style="color: dimgrey;">// file destinación</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">char</span><span style="color: #808030;">*</span> src<span style="color: #808030;">)</span> <span style="color: dimgrey;">// file fuente</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// abre el file fuente</span>
<span style="color: maroon; font-weight: bold;">int</span> fd_in<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>fd_in <span style="color: #808030;">=</span> open<span style="color: #808030;">(</span>src<span style="color: #808030;">,</span> O_RDONLY<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// return con errore</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// abre el file destinación</span>
<span style="color: maroon; font-weight: bold;">int</span> fd_out<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>fd_out <span style="color: #808030;">=</span> open<span style="color: #808030;">(</span>dest<span style="color: #808030;">,</span> O_WRONLY <span style="color: #808030;">|</span> O_CREAT <span style="color: #808030;">|</span> O_TRUNC<span style="color: #808030;">,</span> <span style="color: #008c00;">00644</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// cierra el file y return con error</span>
close<span style="color: #808030;">(</span>fd_in<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// copia en kernel-space usando la función sendfile()</span>
off_t bytesCopied <span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">struct</span> stat fileinfo <span style="color: #808030;">=</span> <span style="color: purple;">{</span><span style="color: #008c00;">0</span><span style="color: purple;">}</span><span style="color: purple;">;</span>
fstat<span style="color: #808030;">(</span>fd_in<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>fileinfo<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">int</span> result <span style="color: #808030;">=</span> sendfile<span style="color: #808030;">(</span>fd_out<span style="color: #808030;">,</span> fd_in<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>bytesCopied<span style="color: #808030;">,</span> fileinfo<span style="color: #808030;">.</span>st_size<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// cierra los file</span>
close<span style="color: #808030;">(</span>fd_in<span style="color: #808030;">)</span><span style="color: purple;">;</span>
close<span style="color: #808030;">(</span>fd_out<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// sale con el resultado de sendfile()</span>
<span style="color: maroon; font-weight: bold;">return</span> result<span style="color: purple;">;</span>
<span style="color: purple;">}</span></pre>
Como se puede ver, casi se puede sobreponer a la anterior, pero es diferente, precisamente, en la parte que realiza el trabajo de copia: en lugar del loop se utiliza la función <a href="http://man7.org/linux/man-pages/man2/sendfile.2.html" target="_blank"><b>sendfile(2)</b></a>, lo que nos permite realizar una copia directa y super-eficiente a nivel de <b>kernel-space</b> (mientras que la primera versión trabajaba en <a href="https://en.wikipedia.org/wiki/User_space" target="_blank"><b>user-space</b></a>).<br />
<br />
Sin entrar en los detalles profundos que todo esto conlleva (<b>kernel-space</b> y <b>user-space</b> de los sistemas de la familia <b>UNIX</b>), me limitaré a señalar que esta segunda versión es mejor que la primera, pero es menos portátil, ya que la <b>sendfile(2)</b> tiene un comportamiento diferente en función del sistema (por ejemplo, sobre <b>Linux</b> se puede utilizar sólo con dal <i>Kernel 2.6.33 hacia adelante</i>, mientras que en <b>macOS</b>, por el contrario, <i>sólo funciona hasta la versión 10.8</i>). Y ya que estamos especificamos mejor: también la primera versión no es totalmente portátil, ya que en algunos sistemas (<i>como el que se inicia con W y que prefiero no nombrar ni siquiera</i>) la<i> system call</i> <b>read(2)</b> no está disponible.<br />
<br />
Ok, entonces ya se puede adivinar que el tema del próximo post será una versión con <i>buffered I/O</i> de la función <b>cpFile()</b>, o sea una versión intrínsecamente portátil, ya que utilizará el <i>I/O standard</i> del C (lo que está contenido en <i>stdio.h</i>, para entendernos).<br />
<br />
Y, por favor, no contengáis la respiración esperando...<br />
<br />
¡Hasta el próximo post!<br />
<div class="date-posts">
<div class="post-outer">
<div class="post hentry" itemprop="blogPost" itemscope="itemscope" itemtype="http://schema.org/BlogPosting">
<a href="https://www.blogger.com/null" name="3633285275821002739"></a></div>
</div>
</div>
Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-53004839941408020712017-02-18T23:55:00.000+01:002017-02-19T00:09:58.554+01:00Toma el makefile y corre cómo escribir un makefile universal<i>Este es un post rápido. </i>Y no es exactamente un post sobre el C. El consejo es tomar la información, huir y mantenerla celosamente para el futuro, ya que podría ser muy útil un dia. Y que no os capturen, de lo contrario pueden acabar como <a href="https://es.wikipedia.org/wiki/Take_the_Money_and_Run" target="_blank"><b>Virgil Starkwell</b></a>.<i><br /></i><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgAg0NUEA5ig0xMph9Yc0YKoXYr7larvOtDiHhchtBkuFyWwCGh5XjIhCZBckHaeVISuCi_Fl65VwwGKoHKtAdVtIP7N8KsI7Vqw52oa3e5cxDhIpnKPyRBXfRVtUNnu35x4VBVTbxrJW-/s1600/Prendi_i_soldi_e_scappa_-_Allen.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" height="225" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgAg0NUEA5ig0xMph9Yc0YKoXYr7larvOtDiHhchtBkuFyWwCGh5XjIhCZBckHaeVISuCi_Fl65VwwGKoHKtAdVtIP7N8KsI7Vqw52oa3e5cxDhIpnKPyRBXfRVtUNnu35x4VBVTbxrJW-/s400/Prendi_i_soldi_e_scappa_-_Allen.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">cara de "pero he solo rubato un makefile..."</td></tr>
</tbody></table>
Así, supongamos que tenemos que hacer un proyecto (que llamaremos, por ejemplo, <i>pluto</i>) y, por diversas razones, no queremos (somos de la vieja escuela) o no podemos (no hay uno adapto) utilizar un <a href="https://es.wikipedia.org/wiki/Entorno_de_desarrollo_integrado" target="_blank"><b>IDE</b></a>. Organizamos nuestros file de una manera clásica, en tres directory: <i>pluto</i>, <i>lib</i> y <i>include</i>. Obviamente escribiremos el código en <b>C</b> y colocaremos los file de una manera lógica (obviamente el file con el<i> main() </i>irá en la directory <i>pluto</i>). Los file son muchos, y cada vez que volvemos a compilar no quieremos reescribir el comando manualmente, y queremos volver a compilar sólo lo que lo necesita (<i>sólo los fuentes modificados</i>) satisfaciendo de forma automática las dependencias de los <i>header</i> (<i>re-compilar sólo aquellos fuentes que dependen de un header modificado</i>)... <b>¡Entonces nos necesita un <a href="https://en.wikipedia.org/wiki/Makefile" target="_blank">makefile</a>!</b> Ok, vosotros ya sabéis lo que es un <i>makefile</i>, pero... ¿ya sabéis escribir uno muy simple y, al mismo tiempo, muy funcional y, sobre todo, genérico y universal? Si la respuesta es <b>NO</b> esto es el vuestro post (y si la respuesta es <b>SI</b>, entonces <i>¡Hasta el próximo post!</i>).<br />
<br />
Acabamos con las chácharas: si estáis leyendo esta línea habéis
respondido <b>NO</b> a la pregunta anterior, ¡entonces vamos con el ejemplo! <br />
<pre style="background: #ffffff; color: black;"><span style="color: dimgrey;"># variables</span>
<span style="color: #797997;">CC </span><span style="color: #808030;">=</span><span style="color: #007997;"> gcc</span>
<span style="color: #797997;">SRCS </span><span style="color: #808030;">=</span><span style="color: #007997;"> </span><span style="color: purple;">$(</span><span style="color: #bb7977; font-weight: bold;">wildcard</span><span style="color: #007997;"> </span><span style="color: maroon; font-weight: bold;">*</span><span style="color: #808030;">.</span><span style="color: #007997;">c</span><span style="color: purple;">)</span>
<span style="color: #797997;">SRCS_LIB </span><span style="color: #808030;">=</span><span style="color: #007997;"> </span><span style="color: purple;">$(</span><span style="color: #bb7977; font-weight: bold;">wildcard</span><span style="color: #007997;"> </span><span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">/</span><span style="color: #007997;">lib</span><span style="color: #808030;">/</span><span style="color: maroon; font-weight: bold;">*</span><span style="color: #808030;">.</span><span style="color: #007997;">c</span><span style="color: purple;">)</span>
<span style="color: #797997;">OBJS </span><span style="color: #808030;">=</span><span style="color: #007997;"> </span><span style="color: purple;">$(</span><span style="color: #797997;">SRCS</span><span style="color: maroon; font-weight: bold;">:</span><span style="color: #808030;">.</span><span style="color: #007997;">c</span><span style="color: maroon; font-weight: bold;">=</span><span style="color: #808030;">.</span><span style="color: #007997;">o</span><span style="color: purple;">)</span>
<span style="color: #797997;">OBJS_LIB </span><span style="color: #808030;">=</span><span style="color: #007997;"> </span><span style="color: purple;">$(</span><span style="color: #797997;">SRCS_LIB</span><span style="color: maroon; font-weight: bold;">:</span><span style="color: #808030;">.</span><span style="color: #007997;">c</span><span style="color: maroon; font-weight: bold;">=</span><span style="color: #808030;">.</span><span style="color: #007997;">o</span><span style="color: purple;">)</span>
<span style="color: #797997;">DEPS </span><span style="color: #808030;">=</span><span style="color: #007997;"> </span><span style="color: purple;">$(</span><span style="color: #797997;">SRCS</span><span style="color: maroon; font-weight: bold;">:</span><span style="color: #808030;">.</span><span style="color: #007997;">c</span><span style="color: maroon; font-weight: bold;">=</span><span style="color: #808030;">.</span><span style="color: #007997;">d</span><span style="color: purple;">)</span>
<span style="color: #797997;">DEPS_LIB </span><span style="color: #808030;">=</span><span style="color: #007997;"> </span><span style="color: purple;">$(</span><span style="color: #797997;">SRCS_LIB</span><span style="color: maroon; font-weight: bold;">:</span><span style="color: #808030;">.</span><span style="color: #007997;">c</span><span style="color: maroon; font-weight: bold;">=</span><span style="color: #808030;">.</span><span style="color: #007997;">d</span><span style="color: purple;">)</span>
<span style="color: dimgrey;"># creación del target file ejecutable</span>
<span style="color: #e34adc;">pluto</span><span style="color: maroon; font-weight: bold;">:</span><span style="color: #0000e6;"> </span><span style="color: purple;">$(</span><span style="color: #797997;">OBJS</span><span style="color: purple;">)</span><span style="color: #0000e6;"> </span><span style="color: purple;">$(</span><span style="color: #797997;">OBJS_LIB</span><span style="color: purple;">)</span>
<span style="color: purple;">$(</span><span style="color: #797997;">CC</span><span style="color: purple;">)</span> <span style="color: #797997;">$^</span> -o <span style="color: #797997;">$@</span> -lcurl
<span style="color: dimgrey;"># creación de los object files</span>
<span style="color: #004a43;">%</span><span style="color: #808030;">.</span><span style="color: #e34adc;">o</span><span style="color: maroon; font-weight: bold;">:</span><span style="color: #0000e6;"> </span><span style="color: #004a43;">%</span><span style="color: #808030;">.</span><span style="color: #0000e6;">c</span>
<span style="color: purple;"> $(</span><span style="color: #797997;">CC</span><span style="color: purple;">)</span> -MMD -MP -I<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">/</span>include -c <span style="color: #797997;">$<</span> -o <span style="color: #797997;">$@</span> -g -Wall -std=c11 -D SIMULATION
<span style="color: dimgrey;"># directivas phony</span>
<span style="background: #a8a800; color: black;">.PHONY</span><span style="color: maroon; font-weight: bold;">:</span><span style="color: #0000e6;"> clean</span>
<span style="color: dimgrey;"># limpieza proyecto ($(RM) es de default "rm -f")</span>
<span style="color: #e34adc;">clean</span><span style="color: maroon; font-weight: bold;">:</span>
<span style="color: purple;"> $(</span><span style="color: #797997;">RM</span><span style="color: purple;">)</span> <span style="color: purple;">$(</span><span style="color: #797997;">OBJS</span><span style="color: purple;">)</span> <span style="color: purple;">$(</span><span style="color: #797997;">OBJS_LIB</span><span style="color: purple;">)</span> <span style="color: purple;">$(</span><span style="color: #797997;">DEPS</span><span style="color: purple;">)</span> <span style="color: purple;">$(</span><span style="color: #797997;">DEPS_LIB</span><span style="color: purple;">)</span>
<span style="color: dimgrey;"># creación dependencias</span>
<span style="color: #004a43;"> -include</span><span style="color: #0000e6;"> </span><span style="color: purple;">$(</span><span style="color: #797997;">DEPS</span><span style="color: purple;">)</span><span style="color: #0000e6;"> </span><span style="color: purple;">$(</span><span style="color: #797997;">DEPS_LIB</span><span style="color: purple;">)</span>
</pre>
Como se ve el <i>makefile</i> enseñado es verdaderamente simple. Pero también es muy completo: hace todo lo necesario, incluyendo la generación de los files de dependencia de los <i>header</i>, y podemos utilizarlo para cualquier proyecto, sin importar el número de file (las directory <i>lib</i> e <i>include</i> pueden estar vacías o contener cientos de file). Podemos agregar y eliminar <i>fuentes</i> y <i>header</i> y re-compilar sin cambiar una sola línea del <i>makefile</i>, porque el se adapta automáticamente a lo que encuentra en las tres directory del proyecto: ¿Qué queremos más? <br />
<br />
Algunos pequeños detalles sobre los bloques (comentados) que componen el <i>makefile</i>:<br />
<br />
<i># variables</i><br />
Aquí se ponen las variables que se utilizan en el resto del <i>makefile</i>. En particular, la variable <b>CC</b> indica que compilador utilizar: en nuestro caso es <b><a href="https://es.wikipedia.org/wiki/GNU_Compiler_Collection" target="_blank">gcc</a></b>, pero podría ser, por ejemplo, g++ (para C++). Obviamente, en este caso las fuentes serían <i>.cpp</i> o <i>.cc</i>, por lo que hay que acordarse de modificar las otras variables que hacen referencia a los <i>.c</i>. <br />
<br />
<i># creacion del target file ejecutable</i><br />
Aquí se pone el comando para <i>linkar</i> los file objeto creados y producir el file ejecutable final. Si utilizamos laguna librería externa la referencia se añade aquí (en el ejemplo se <i>linka</i> la <i>libcurl</i> usando <i>-lcurl</i>).<i> </i><br />
<br />
<i># creacion de los object files</i><br />
Aquí se pone el comando para compilar cada fuente y crear el file objeto correspondiente, activando todas las opciones del compilador que necesitamos. Si utilizamos alguna <i>#ifdef </i>especial (<a href="https://artcprogramming-es.blogspot.com.es/2017/01/por-un-punado-de-ifdef-como-usar-el.html" target="_blank"><b>como las que hemos visto alli</b></a>) la activación hay que ponerla aquí (en el ejemplo se activa una <i>define SIMULATION </i>utilizada en las fuentes).<i> </i><br />
<br />
<i># directivas phony</i><br />
Aquí se ponen las directivas <i>phony</i> (es un poco largo de explicar: <a href="https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html" target="_blank"><b>mirar en el link, que está muy claro</b></a>).<i> </i><br />
<br />
<i># limpieza proyecto ($(RM) es de default "rm -f")</i><br />
Aquí se pone el comando de borrado de los objetos para forzar, eventualmente, una siguiente completa re-compilación.<i> </i><br />
<br />
<i># creacion dependencias</i><br />
<span class="" id="result_box" lang="es"><span class="">Aquí</span> se pone el comando para generar los file de dependencia que nos permiten volver a compilar sólo lo que necesita cuando se modifica un <i>header</i> file.<span class=""> </span></span><br />
<br />
<span class="" id="result_box" lang="es"><span class="">El <i>makefile</i></span> enseñado es un ejemplo real, listo para su uso<span class="">.</span> <span class="">Obviamente las</span> directivas </span><i>-lcurl</i> y <i>-D SIMULATION</i> <span class="" id="result_box" lang="es"> </span><span class="" id="result_box" lang="es">se han añadido <span class="">como ejemplo</span> <span class="">para mostrar</span> cómo extender <span class="">las funcionalidades del</span> <i><span class="">makefile</span></i>: <span class="">si no las necesitamos</span> <span class="">las podemos</span> eliminar sin problemas (y añadiremos las que <span class="">necesitamos </span><span class="">utilizando</span> la misma sintaxis).</span> <br />
<br />
<span class="" id="result_box" lang="es"><span class="">¿Qué pensáis</span>? El objetivo no era explicar lo que es un <i>m</i><i>akefile</i> y cómo se escribe (<i>uh, hay una enorme documentación en la red </i><i>sobre el tema</i>). Tampoco era para explicar los secretos de la sintaxis (que permite </span><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es">también</span> soluciones complejas). El objetivo era proporcionar un <i>makefile</i> básico y completo al mismo tiempo, un <i>makefile</i> universal para (casi) cualquier proyecto. Yo diría que el objetivo se ha logrado... luego, si <span class="">tenemos que hacer</span> proyectos complejos y portátiles, con auto-instaladores, etc. tal vez nos vamos a encontrar más cómodos usando un <b>IDE</b> de buena calidad o usando herramientas manuales como <a href="https://es.wikipedia.org/wiki/GNU_build_system" target="_blank"><b>Autotools</b></a> o </span><a href="https://es.wikipedia.org/wiki/CMake" target="_blank"><b><span class="" id="result_box" lang="es"><span class="">CMake</span></span></b></a><span class="" id="result_box" lang="es">... pero os aseguro que el método rápido y de la vieja escuela que he descrito es utilizable siempre y sin limitaciones. <i>Que alivio</i>...</span> <br />
<br />
¡Hasta el próximo post!
Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-86189209504415334732017-01-14T19:22:00.002+01:002017-02-07T23:15:59.075+01:00Por un puñado de ifdef cómo usar el preprocesador en C<span class="" id="result_box" lang="es"><span class="">Después de</span> <span class="">la juerga</span> <span class="">de Año Nuevo</span> es mejor empezar con un <i>post ligero</i>. Hablaremos, <span class="">entonces del</span> <a href="https://es.wikipedia.org/wiki/Preprocesador" target="_blank"><b><span class="">preprocesador</span></b></a>... bueno, en realidad si hablasimos del <i>preprocesador</i> en <span class="">profundidad no</span> <span class="">sería un</span> post muy ligero, por lo que nos limitaremos a un caso <span class="">simple, es decir</span> <span class="">un uso interesante</span> de la directiva <i>#</i><i>ifdef</i>. Y os sugiero de seguir los consejos de <a href="https://es.wikipedia.org/wiki/Por_un_pu%C3%B1ado_de_d%C3%B3lares" target="_blank"><b>Joe (<span class="">el </span>extranjero)</b></a><span class="">, ya que es</span> uno que se enfada fácilmente...</span><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNOPhNPLzppdA1bYZUNtu3jkhorcMim82vWhDsAuWLCiINwieak-A8RrAzCl1vw8JWrqvvu3BXq3MkAmK4jw5ZC89yJveAOYvEuRYKS1h3VOW9UgKqlvqccj-zETLu3PwLbNSoLdBcNkJZ/s1600/Clint-Eastwood.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNOPhNPLzppdA1bYZUNtu3jkhorcMim82vWhDsAuWLCiINwieak-A8RrAzCl1vw8JWrqvvu3BXq3MkAmK4jw5ZC89yJveAOYvEuRYKS1h3VOW9UgKqlvqccj-zETLu3PwLbNSoLdBcNkJZ/s400/Clint-Eastwood.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">...y q<span class="" id="result_box" lang="es"><span class="">uién</span> <span class="">no utiliza el</span> <span class="">#ifdef</span> <span class="">tendrá que</span> <span class="">tratar conmigo</span></span>...</td></tr>
</tbody></table>
<span class="" id="result_box" lang="es"><span title="Allora: riprendiamo un vecchio pezzo di codice mostrato qui (subito a rileggerlo!), quello del Socket Server.">Por lo tanto: retomamos <a href="https://artcprogramming-es.blogspot.com.es/2015_09_01_archive.html" target="_blank"><b>un viejo pedazo de código que se muestra aquí</b></a> (<i>en seguida a releerlo!</i>), el del <i>Socket Server</i>. </span><span title="Dando per scontato che abbiate ben chiaro come funziona, lo modificheremo seguendo un possibile caso reale, che sarebbe il seguente: supponiamo di dover compilare il nostro codice per due diversi ambienti operativi, ad esempio per un Linux Desktop/Server recente, e per un Linux">Asumiendo
que tengáis muy claro cómo funciona, lo vamos a modificar siguiend un posible caso real, que sería el siguiente: supongamos que tenemos que
compilar el nuestro código para dos entornos operativos diferentes, por
ejemplo para un</span></span><span class="" id="result_box" lang="es"><span title="Dando per scontato che abbiate ben chiaro come funziona, lo modificheremo seguendo un possibile caso reale, che sarebbe il seguente: supponiamo di dover compilare il nostro codice per due diversi ambienti operativi, ad esempio per un Linux Desktop/Server recente, e per un Linux"><i> Linux Desktop/Server</i> reciente, y para </span></span><span class="" id="result_box" lang="es"><span title="Dando per scontato che abbiate ben chiaro come funziona, lo modificheremo seguendo un possibile caso reale, che sarebbe il seguente: supponiamo di dover compilare il nostro codice per due diversi ambienti operativi, ad esempio per un Linux Desktop/Server recente, e per un Linux"><a href="https://es.wikipedia.org/wiki/Linux_embebido" target="_blank"><b>Linux Embedded</b></a> </span><span title="Embedded un po' datato (con compilatore, Kernel e glibc di qualche annetto fa)."> un poco anticuado (con compilador, <i>kernel</i> y <i>glibc</i> de hace unos años). </span><span title="Abbiamo deciso per motivi vari che il nuovo codice deve creare un socket non bloccante nella fase di accept, quindi, ad esempio, si potrebbe sostituire la chiamata ad accept() con una ad accept4() che dispone di un argomento flags che si può impostare">Hemos decidido por diversas razones que el nuevo código tiene que crear un <i>socket</i> no bloqueante en la fase de <i>accept</i>, así que por ejemplo, se podría reemplazar
la llamada a </span></span><span class="" id="result_box" lang="es"><span title="Abbiamo deciso per motivi vari che il nuovo codice deve creare un socket non bloccante nella fase di accept, quindi, ad esempio, si potrebbe sostituire la chiamata ad accept() con una ad accept4() che dispone di un argomento flags che si può impostare"><b><a href="http://man7.org/linux/man-pages/man2/accept.2.html" target="_blank"><b>accept()</b></a> </b>con una a <i>accept4()</i> que tiene un argumento <i>
flags</i> que se puede establecer </span><span title="a SOCK_NONBLOCK che è proprio quello che ci serve.">a <i>SOCK_NONBLOCK </i>que es justo lo que necesitamos. </span><span title="Sfortunatamente il nostro sistema embedded (datato, come detto) usa un kernel più vecchio del 2.6.28 e una glibc più vecchia della 2.10 (che sono le due condizioni minime per poter usare la accept4()).">Por
desgracia, nuestro <i>sistema embedded</i> (anticuado, como se ha
mencionado) utiliza un <i>kernel</i> más antiguo del 2.6.28 y una <i>glibc</i> anterior a la
2,10 (que son las dos condiciones mínimas para poder utilizar la <i>accept4()</i>). </span><span title="Che fare?">¿Qué hacer? </span><span title="Facciamo due versioni del codice?">Hacemos dos versiones del código? </span><span title="NO!"><i><b>¡NO!</b></i> </span><span title="Perché così ci toccherebbe (in futuro) fare anche doppia manutenzione, che è una situazione da evitare.">¿Por qué así tendremos que hacer (en el futuro) doble mantenimiento, que es una situación a evitar. </span><span title="Manterremo, invece, una sola versione con degli opportuni ifdef per gestire la compilazione nei due ambienti operativi.">Vamos
a mantener, sin embargo, sólo una versión con las <b>#ifdef</b> apropiadas para
gestionar la compilación en los dos entornos operativos.</span></span> <br />
<br />
<span class="" id="result_box" lang="es"><span class="">Entonces, el</span> fragmento de código (<a href="https://artcprogramming-es.blogspot.com.es/2015_09_01_archive.html" target="_blank"><i><b>extracto de ese post </b></i></a><a href="https://artcprogramming-es.blogspot.com.es/2015_09_01_archive.html" target="_blank"><i><b>allí</b></i></a>) que hay que cambiar <span class="">es el siguiente:</span></span> <span style="color: #808030;"></span><br />
<pre style="background: #ffffff; color: black;"><span style="color: dimgrey;">... </span></pre>
<pre style="background: #ffffff; color: black;"><span style="color: dimgrey;">// acepta conexiones de un client entrante</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">: espera connexiones entrantes...</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
socklen_t socksize <span style="color: #808030;">=</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">struct</span> sockaddr_in<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">struct</span> sockaddr_in client<span style="color: purple;">;</span> <span style="color: dimgrey;">// (remote) client socket info</span>
<span style="color: maroon; font-weight: bold;">int</span> client_sock<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>client_sock <span style="color: #808030;">=</span> accept<span style="color: #808030;">(</span>my_socket<span style="color: #808030;">,</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">struct</span> <span style="color: #400000;">sockaddr</span> <span style="color: #808030;">*</span><span style="color: #808030;">)</span><span style="color: #808030;">&</span>client<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>socksize<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;"><</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// error accept()</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">: accept failed (</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">)</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">,</span> <span style="color: #603000;">strerror</span><span style="color: #808030;">(</span>errno<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_FAILURE<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span></pre>
<span class="" id="result_box" lang="es"><span class="">Y</span> <span class="">la nueva versión con</span> la compilación condicional a través de <i>#ifdef</i> será la siguiente:</span> <br />
<pre style="background: #ffffff; color: black;"><span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">acepta conexiones de un client entrante</span> (en non blocking mode: my_socket es SOCK_NONBLOCK)</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">: espera connexiones entrantes...</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
socklen_t socksize <span style="color: #808030;">=</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">struct</span> sockaddr_in<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">struct</span> sockaddr_in client<span style="color: purple;">;</span> <span style="color: dimgrey;">// (remote) client socket info</span>
<span style="color: maroon; font-weight: bold;">int</span> client_sock<span style="color: purple;">;</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">ifdef</span><span style="color: #004a43;"> OLD_LINUX</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>client_sock <span style="color: #808030;">=</span> accept<span style="color: #808030;">(</span>my_socket<span style="color: #808030;">,</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">struct</span> <span style="color: #400000;">sockaddr</span> <span style="color: #808030;">*</span><span style="color: #808030;">)</span><span style="color: #808030;">&</span>client<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>socksize<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;"><</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">else</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>client_sock <span style="color: #808030;">=</span> accept4<span style="color: #808030;">(</span>my_socket<span style="color: #808030;">,</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">struct</span> <span style="color: #400000;">sockaddr</span> <span style="color: #808030;">*</span><span style="color: #808030;">)</span><span style="color: #808030;">&</span>client<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>socksize<span style="color: #808030;">,</span> SOCK_NONBLOCK<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;"><</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">endif</span>
<span style="color: dimgrey;">// error accept()</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">: accept failed (</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">)</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">,</span> <span style="color: #603000;">strerror</span><span style="color: #808030;">(</span>errno<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_FAILURE<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">ifdef</span><span style="color: #004a43;"> OLD_LINUX</span>
<span style="color: maroon; font-weight: bold;">else</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// accept ejecutada: set socket a non-blocking</span>
<span style="color: maroon; font-weight: bold;">int</span> flags<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>flags <span style="color: #808030;">=</span> fcntl<span style="color: #808030;">(</span>client_sock<span style="color: #808030;">,</span> F_GETFL<span style="color: #808030;">,</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">></span><span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>fcntl<span style="color: #808030;">(</span>client_sock<span style="color: #808030;">,</span> F_SETFL<span style="color: #808030;">,</span> flags <span style="color: #808030;">|</span> O_NONBLOCK<span style="color: #808030;">)</span> <span style="color: #808030;"><</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// error accept()</span>
<span style="color: #603000;">printf</span><span style="color: #808030;">(</span><span style="color: maroon;">"</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">: fcntl failed (</span><span style="color: #007997;">%s</span><span style="color: #0000e6;">)</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> argv<span style="color: #808030;">[</span><span style="color: #008c00;">0</span><span style="color: #808030;">]</span><span style="color: #808030;">,</span> <span style="color: #603000;">strerror</span><span style="color: #808030;">(</span>errno<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">return</span> EXIT_FAILURE<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: purple;">}</span>
<span style="color: purple;">}</span>
<span style="color: #004a43;">#</span><span style="color: #004a43;">endif</span>
<span style="color: #808030;">.</span><span style="color: #808030;">.</span><span style="color: #808030;">.</span>
</pre>
Ok, como se nota es <b><a href="http://artcprogramming-es.blogspot.com.es/2012/08/no-comment.html" target="_blank">ampliamente comentado</a> </b> y así se <i>auto-explica</i>, por lo cual no voy a detenerme sobre las instrucciones y/o grupos de instrucciones (<i>¡leer los comentarios! ¡Están ahí para eso!</i>), pero voy a añadir, solamente, algunos detalles estructurales.<br />
<br />
<span class="" id="result_box" lang="es"><span class="">En</span> las partes incluidas en el <i>#ifdef OLD_LINUX</i> hay la versión del código que <i><b>NO</b></i> utiliza la <i>accept4()</i>, y que es, obviamente, un poco más complicada que la otra, porque hay que utilizar </span><a href="http://man7.org/linux/man-pages/man2/fcntl.2.html" target="_blank"><b>fcntl()</b></a><span class="" id="result_box" lang="es"> para transformar en no-bloqueante el <i>socket</i> creado, mientras que la <i>accept4</i><i>()</i> lo crea directamente usando, como se ha mencionado, el flag <i>SOCK_NONBLOCK</i>. Sin embargo, como se puede ver, el código resultante es bastante claro y legible y, después de todo, no se ve tan mal (¡el estilo primero!).</span> <br />
<br />
<span class="" id="result_box" lang="es"><span class="">Alguien podría decir</span>: la versión bajo<i> #ifdef</i> también trabaja con un Linux reciente<span class="">, así que</span> ¿por qué no dejar solo esa y quitar las <i>#</i><i>ifdef</i>? <b>¡NO! ¡NO! y otra vez ¡NO!</b> No hay que escribir código </span><span class="" id="result_box" lang="es"><i>old-style</i> para que sea retro-compatible: siempre hay que tratar de escribir de una manera moderna, y si es necesario (como en el ejemplo) hay que poner </span><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es">el <span class="">material antiguo</span></span> bajo <i>#ifdef</i>. Y cuando llegue el momento (cuando, por ejemplo, ya no vamos a usar un entorno operativo dual) limpiaremos y dejaremos sólo un bonito código <span class="">moderno.</span></span><br />
<br />
<span class="" id="result_box" lang="es"><span class="">Obviamente</span> la compilación condicional la efectuaremos mediante la introducción (<span class="">o no</span> introducción<span class="">)</span> <span class="">de una instrucción <i>-D</i></span><i> <span class="">OLD_LINUX</span></i> en la línea del nuestro <i>makefile</i> que genera lo files objeto<span class="">, o directamente</span> <span class="">en la línea</span> de comandos si no se utiliza un </span><span class="" id="result_box" lang="es"><span class="" id="result_box" lang="es"><i>makefile</i></span>. Bueno, por fin hemos escribito un código único <span class="">para</span> dos entornos operativos diferentes: <i>¡misión cumplida!</i></span> <br />
<br />
<span class="" id="result_box" lang="es"><span class="">Sólo una última</span> </span><span class="short_text" id="result_box" lang="es"><span class="">aclaración</span></span><span class="" id="result_box" lang="es"> sobre el nuestro </span><span class="" id="result_box" lang="es"><i>socket server</i> modificado (y que no tiene nada <span class="">que ver con las <i>#</i></span><i><span class="">ifdef</span></i>): si el </span><span class="" id="result_box" lang="es"><i>non-blocking socket</i> nos necesita para ejecutar unas <i>recv()</i> <span class="">no bloqueantes</span> <span class="">en la siguiente</span> fase a la de <i>accept</i>, es mucho más fácil modificar adecuadamente <span class="">el loop de recepción</span> y pasar el flag <i>MSG_DONTWAIT</i> a la <i>recv()</i> en el <span class="">argumento </span></span><span class="" id="result_box" lang="es"><span class=""><i>flags</i> (el</span> cuarto). Por lo que la <i>recv()</i> no bloquea en ambos entornos operativos, y <span class="">todo ello sin</span> el uso de <i>#ifdef</i>. <i>Pero esa es otra historia...</i></span> <br />
<br />
¡Hasta el próximo post!
Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-14197639826195179892016-12-23T16:13:00.002+01:002016-12-23T16:13:26.657+01:00¡Felices Fiestas!<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIAbCAv5u8t7OML4o4hp7Dw444YP0pPCgVzff6Dvyv0JrXflKEuWYCAZ1BJqzaDlAepUeYd1t-GrqwXVhxDyNDSQLG1HPt7G5W31-XaWC2nNMXUtZSkxSGDrxqEbOVlO2RDykO4WzjXPc0/s1600/BuoneFeste.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="266" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIAbCAv5u8t7OML4o4hp7Dw444YP0pPCgVzff6Dvyv0JrXflKEuWYCAZ1BJqzaDlAepUeYd1t-GrqwXVhxDyNDSQLG1HPt7G5W31-XaWC2nNMXUtZSkxSGDrxqEbOVlO2RDykO4WzjXPc0/s400/BuoneFeste.jpg" width="400" /></a></div>
<br />Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0tag:blogger.com,1999:blog-1964474160379161728.post-4813899744101429512016-12-08T16:56:00.002+01:002016-12-08T17:07:09.069+01:00El último Apache III - La venganza cómo escribir un módulo Apache en C - pt.3Entonces, hacemos el punto: en los últimos tres post hemos hablado de módulos <a href="https://es.wikipedia.org/wiki/Lighttpd" target="_blank"><b>lighttpd</b></a> (<a href="https://artcprogramming-es.blogspot.com.es/2016/09/el-gran-lighttpd-como-escribir-un.html" target="_blank"><b>aquí</b></a>, <a href="https://artcprogramming-es.blogspot.com.es/2016/10/el-gran-lighttpd-como-escribir-un.html" target="_blank"><b>aquí</b></a> y también <a href="https://artcprogramming-es.blogspot.com.es/2016/11/el-gran-lighttpd-como-escribir-un.html" target="_blank"><b>aquí</b></a>). En el primero de los tres he mencionado que el tema no era nuevo: antiguamente había escrito dos post (<a href="https://artcprogramming-es.blogspot.com.es/2012/11/el-ultimo-apache.html" target="_blank"><b>aquí</b></a> y <a href="https://artcprogramming-es.blogspot.com.es/2012/11/el-ultimo-apache-ii-el-regreso.html" target="_blank"><b>aquí</b></a>) que hablaban de módulos para Apache, que es un pariente cercano de <b>lighttpd</b> (familia <b>Web Servers</b>). Aquí, para cerrar el circulo os propongo una tercera entrega de <a href="https://es.wikipedia.org/wiki/The_Last_of_the_Mohicans_(pel%C3%ADcula_de_1992)" target="_blank"><b>"El último Apache" (¿o Mohicano?)</b></a>, con una (espero) interesante extensión de lo que se había escrito<b>.</b> <br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_mGAMlR2IbTeZPhZMVgEt2TSKnFpGm2LhA9eBDaAuXLSX2WFJswo-FEniw3etanxIeumd6DlxBRxj9MciFrdt0TGTmvuVFmK3-xyNH_Z97alDBY4gzW8-SX1hVyBQGAXL_pKSz3GQrb_q/s1600/V-Day-3-1024x434.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" height="168" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_mGAMlR2IbTeZPhZMVgEt2TSKnFpGm2LhA9eBDaAuXLSX2WFJswo-FEniw3etanxIeumd6DlxBRxj9MciFrdt0TGTmvuVFmK3-xyNH_Z97alDBY4gzW8-SX1hVyBQGAXL_pKSz3GQrb_q/s400/V-Day-3-1024x434.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">...en realidad soy un Mohicano, no un Apache...</td></tr>
</tbody></table>
Pues bien, en "<a href="https://artcprogramming-es.blogspot.com.es/2012/11/el-ultimo-apache-ii-el-regreso.html" target="_blank"><b>El último Apache II - El regreso</b></a>" (<i>que acabáis de releer, supongo...</i>) escribimos un agradable módulo elemental para <b>Apache</b>. Hacia poco (escribía solo una presentación en browser), pero era un buen comienzo para entrar en el mundo de los módulos para <b>Web Servers</b>. Vale, ahora vamos a reanudar ese módulo y les vamos a añadir una función que extrae los datos <b>POST</b> incluidos en una eventual petición <a href="https://it.wikipedia.org/wiki/Hypertext_Transfer_Protocol" target="_blank"><b>HTTP</b></a> de tipo <b>POST</b> que llega al módulo. ¿Por qué he elegido añadir esa funcionalidad? Bueno, obviamente porque es bastante normal que un módulo cuente con varios tipos de peticiones (<b>GET</b>, <b>POST</b>, etc.), y luego, en particular, recuerdo que la primera vez que he añadido esta funcionalidad en un módulo <b>Apache</b> que escribí, me di cuenta de que no estaban disponibles muchas indicaciones sobre este tema (y esto a pesar de la enorme cantidad de documentación de <b>Apache</b> disponible respeto a la de <b>lighttpd</b>). <br />
<br />
Pues bien, sin molestarse en repetir todo el código y la forma de generarlo (hay de todo en "<b>El último Apache II - El regreso</b>"), sólo reescribiremos la función <b>myapmodHandler()</b> y añadiremos una nueva función <b>getPost()</b>. ¡Vamos con el código! <br />
<pre style="background: #ffffff; color: black;"><span style="color: dimgrey;">// handler del modulo</span>
<span style="color: maroon; font-weight: bold;">static</span> <span style="color: maroon; font-weight: bold;">int</span> myapmodHandler<span style="color: #808030;">(</span>
request_rec <span style="color: #808030;">*</span>reqrec<span style="color: #808030;">)</span> <span style="color: dimgrey;">// request data</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// test handler</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">!</span> reqrec<span style="color: #808030;">-</span><span style="color: #808030;">></span>handler <span style="color: #808030;">|</span><span style="color: #808030;">|</span> <span style="color: #603000;">strcmp</span><span style="color: #808030;">(</span>reqrec<span style="color: #808030;">-</span><span style="color: #808030;">></span>handler<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">myapmod</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> DECLINED<span style="color: purple;">;</span>
<span style="color: dimgrey;">// set del apropiado content type</span>
ap_set_content_type<span style="color: #808030;">(</span>reqrec<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">text/html</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// test método http</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>reqrec<span style="color: #808030;">-</span><span style="color: #808030;">></span>method_number <span style="color: #808030;">=</span><span style="color: #808030;">=</span> M_GET<span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// petición GET: escribe solo "Hello, World!"</span>
ap_rprintf<span style="color: #808030;">(</span>reqrec<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">GET request: Hello, world!</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">else</span> <span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>reqrec<span style="color: #808030;">-</span><span style="color: #808030;">></span>method_number <span style="color: #808030;">=</span><span style="color: #808030;">=</span> M_POST<span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// </span><span style="color: dimgrey;"><span style="color: dimgrey;">petición</span> POST: lee POST data</span>
<span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span>data<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>readPost<span style="color: #808030;">(</span>reqrec<span style="color: #808030;">,</span> <span style="color: #808030;">&</span>data<span style="color: #808030;">)</span> <span style="color: #808030;">!</span><span style="color: #808030;">=</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// escribe "Hello, World!" y enseña POST data</span>
ap_rprintf<span style="color: #808030;">(</span>reqrec<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">POST request: Hello, world! postdata: </span><span style="color: #007997;">%s</span><span style="color: maroon;">"</span><span style="color: #808030;">,</span> data<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">else</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// POST data no disponible: escribe solo "Hello, World!"</span>
ap_rprintf<span style="color: #808030;">(</span>reqrec<span style="color: #808030;">,</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">POST request: Hello, world!</span><span style="color: maroon;">"</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">else</span>
<span style="color: maroon; font-weight: bold;">return</span> HTTP_METHOD_NOT_ALLOWED<span style="color: purple;">;</span>
<span style="color: dimgrey;">// sale con OK</span>
<span style="color: maroon; font-weight: bold;">return</span> OK<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// función para leer POST data</span>
<span style="color: maroon; font-weight: bold;">static</span> <span style="color: maroon; font-weight: bold;">int</span> readPost<span style="color: #808030;">(</span>
request_rec <span style="color: #808030;">*</span>reqrec<span style="color: #808030;">,</span> <span style="color: dimgrey;">// request data</span>
<span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span><span style="color: #808030;">*</span>data<span style="color: #808030;">)</span> <span style="color: dimgrey;">// buffer de destinazion paraer POST data</span>
<span style="color: purple;">{</span>
<span style="color: dimgrey;">// setup del client para permitir que Apache lea el request body</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>ap_setup_client_block<span style="color: #808030;">(</span>reqrec<span style="color: #808030;">,</span> REQUEST_CHUNKED_ERROR<span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> OK<span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// determina si el client ha enviado datos</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>ap_should_client_block<span style="color: #808030;">(</span>reqrec<span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: dimgrey;">// lee los datos de POST</span>
<span style="color: maroon; font-weight: bold;">char</span> argsbuffer<span style="color: #808030;">[</span>HUGE_STRING_LEN<span style="color: #808030;">]</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">int</span> rsize<span style="color: #808030;">,</span> len_read<span style="color: #808030;">,</span> rpos<span style="color: #808030;">=</span><span style="color: #008c00;">0</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">long</span> length <span style="color: #808030;">=</span> reqrec<span style="color: #808030;">-</span><span style="color: #808030;">></span>remaining<span style="color: purple;">;</span>
<span style="color: #808030;">*</span>data <span style="color: #808030;">=</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">char</span><span style="color: #808030;">*</span><span style="color: #808030;">)</span>apr_pcalloc<span style="color: #808030;">(</span>reqrec<span style="color: #808030;">-</span><span style="color: #808030;">></span>pool<span style="color: #808030;">,</span> length <span style="color: #808030;">+</span><span style="color: #008c00;">1</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: dimgrey;">// loop de inserción datos en el buffer de destinación</span>
<span style="color: maroon; font-weight: bold;">while</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>len_read <span style="color: #808030;">=</span> ap_get_client_block<span style="color: #808030;">(</span>reqrec<span style="color: #808030;">,</span> argsbuffer<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">sizeof</span><span style="color: #808030;">(</span>argsbuffer<span style="color: #808030;">)</span><span style="color: #808030;">)</span><span style="color: #808030;">)</span> <span style="color: #808030;">></span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span><span style="color: #808030;">(</span>rpos <span style="color: #808030;">+</span> len_read<span style="color: #808030;">)</span> <span style="color: #808030;">></span> length<span style="color: #808030;">)</span>
rsize <span style="color: #808030;">=</span> length <span style="color: #808030;">-</span> rpos<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">else</span>
rsize <span style="color: #808030;">=</span> len_read<span style="color: purple;">;</span>
<span style="color: dimgrey;">// copia un bloque de datos</span>
<span style="color: #603000;">memcpy</span><span style="color: #808030;">(</span><span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">char</span> <span style="color: #808030;">*</span><span style="color: #808030;">)</span><span style="color: #808030;">*</span>data <span style="color: #808030;">+</span> rpos<span style="color: #808030;">,</span> argsbuffer<span style="color: #808030;">,</span> rsize<span style="color: #808030;">)</span><span style="color: purple;">;</span>
rpos <span style="color: #808030;">+</span><span style="color: #808030;">=</span> rsize<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// POST data leido: return OK</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: purple;">}</span>
<span style="color: dimgrey;">// sale con NOK</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #808030;">-</span><span style="color: #008c00;">1</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span></pre>
Ok, como se nota es <b><a href="http://artcprogramming-es.blogspot.com.es/2012/08/no-comment.html" target="_blank">ampliamente comentado</a> </b> y así se <i>auto-explica</i>, por lo cual no voy a detenerme sobre las instrucciónes y/o grupos de instrucciones (<i>¡leer los comentarios! ¡Están ahí para eso!</i>), pero voy a añadir, solamente, algunos detalles estructurales.<br />
<br />
la función original <b>myapmodHandler()</b> se ha convertido en una especie de función <b><a href="https://es.wikipedia.org/wiki/Hola_mundo" target="_blank">helloworld</a></b> (bueno, en práctica lo era incluso antes), que distingue los tipos de petición y, en el caso <b>POST</b>, llama a la función<b> readPost()</b> y escribe como respuesta "<i>Hello, world!</i>" mas los datos. En el caso de peticiones <b>GET</b> o <b>POST</b> sin datos se limita a escribir "<i>Hola, mundo!</i>", y para otros tipos de peticiones sale con error. <br />
<br />
La función <b>readPost()</b> es simple, pero no es inmediata: en su tiempo la derivé de el único ejemplo interesante y bien hecho que encontré, y que estaba en el (óptimo) libro "<a href="http://shop.oreilly.com/product/9781565925670.do" target="_blank"><b>Writing Apache Modules with Perl and C</b></a>", que os recomiendo. Yo diría que en la última versión de <b>Apache</b>, la 2.4, se han introducido (<a href="http://httpd.apache.org/docs/2.4/developer/" target="_blank"><b>y descritos aquí</b></a>) otros métodos de extracción de datos de <b>POST</b> (<i>¡os deseo buena lectura!</i>), de todas maneras yo me la arreglé así, y el módulo funcionaba (y funciona todavía) muy bien. <br />
<br />
¡Ah, el test, se me olvidaba! Testear con peticiones <b>POST</b> no es simple cómo hacerlo con las <b>GET</b>, adonde es suficiente (como dicho en el post anterior) escribir la <a href="https://es.wikipedia.org/wiki/Identificador_de_recursos_uniforme" target="_blank"><b>URI</b></a> del modulo en la barra de direcciones de Firefox o Chrome y esperar la respuesta. Para probar el nuestro nuevo módulo es mejor usar un buen plugin (como <a href="https://addons.mozilla.org/it/firefox/addon/poster/" target="_blank"><b>Poster</b></a>, por ejemplo) que nos facilitará mucho la tarea. <br />
<br />
Vale, de momento, creo que podemos parar por un tiempo el tema módulos de <b>Apache</b>/<b>lighttpd</b>. Juro que en el próximo post voy a hablar de otra cosa, no me gustaría que pensaran que el <b>C</b> se utiliza sólo para <b>Web Servers</b>...<br />
<br />
¡Hasta el próximo post!Aldo Abatehttp://www.blogger.com/profile/15287664302661337866noreply@blogger.com0