Programación concurrente en Linux

# Parte 1. Toma de contacto con Linux y sus herramientas

Lo primero que necesitamos para afrontar este proyecto es conseguir una plataforma donde desarrollar nuestro trabajo. Quienes financian el proyecto nos imponen que tanto el software soporte como el desarrollado sea software libre. Lejos de ser un problema, esta condición tiene muchas ventajas. Sabemos que Linux puede instalarse en diferentes tipos de hardware, o como una máquina virtual en nuestro PC. También podremos optar por otros sistemas de la familia UNIX, siempre que cuidemos la compatibilidad.

A la espera de que el consorcio del proyecto defina de forma más concreta las especificaciones, en esta primera fase vamos a ir familiarizándonos con Linux. Una vez que tengamos Linux satisfactoriamente instalado, organizaremos nuestro entorno de trabajo y exploraremos las herramientas que Linux nos ofrece. Finalmente, estudiaremos el tema de la compatibilidad de las herramientas que vamos a usar para aplicarlo a las soluciones que adoptemos.

## Actividad 1.1. ¿Por qué Linux?

### Dedicación estimada: 60 minutos

Suponiendo que ya te has puesto manos a la obra en la instalación de la plataforma, te sugiero que aproveches los tiempos de espera de la descarga y la instalación para reflexionar sobre la idoneidad de Linux para un sistema empotrado como este. Es necesario situar Linux tanto en el ámbito de lo que ofrece el mercado de sistemas operativos como en el contexto histórico que ha marcado la evolución de los sistemas operativos y de Linux en particular.

Linux no es un sistema diseñado modernamente, sino que deriva de un sistema operativo, UNIX, con medio siglo de historia. En todo este tiempo los ámbitos de aplicación de los computadores y las necesidades de las aplicaciones han variado sustancialmente, como lo ha hecho la tecnología. ¿Por qué entonces Linux es un sistema de referencia en el mundo de los sistemas operativos y de los sistemas empotrados?

### Qué hay que hacer

Lee este documento sobre la evolución de los sistemas operativos.

### Resultados

Evalúa lo que has aprendido con este test de autoevaluación. Repasa los conceptos en función de los resultados obtenidos.

## Actividad 1.2. Creación de un entorno de trabajo en Linux

### Dedicación estimada: 2 horas y media

Si ya tienes una instalación Linux para desarrollar el curso, podrás organizar en tu cuenta un entorno de trabajo adecuado para desarrollar el proyecto.

Ya conoces que en un sistema operativo la información se organiza en una estructura arborescente. Los nodos del árbol se denominan directorios, que en la interfaz gráfica se representan como carpetas, mientras que las hojas del árbol son los ficheros. En realidad en Linux los directorios se consideran un tipo especial de ficheros, caracterizados porque contienen los nombres de los ficheros del directorio. Más adelante estudiaremos un poco más este tema. De momento, para organizar nuestro entorno de trabajo nos bastará con conocer la estructura que deseamos tener y las herramientas para crearla. Ya sabes cómo construir una estructura de carpetas mediante la interfaz gráfica, pero se trata de aprender a hacerlo desde el shell o terminal, lo que te permitirá automatizar la tarea, además de proporcionarte una primera toma de contacto con el modo de trabajo en un sistema Linux.

### Qué hay que hacer

1. Primero debes localizar la herramienta terminal en la interfaz gráfica de tu sistema Linux, habitualmente representada por este icono:

   Cuando lo abras te aparecerá una ventana del shell. Se trata de una representación de los antiguos terminales, no gráficos, en los que se podían escribir 24x80 caracteres. Ese era todo el espacio de trabajo del programador. Ahora, tú puedes darle la dimensión que quieras y abrir varios. En esa ventana se está ejecutando un programa que en tu distribución será casi con toda seguridad el `bash`, uno de los muchos shells que existen. El shell te mostrará un prompt o invitación a introducir una orden. Si nunca has trabajado con un shell, no te vendrá mal echar un vistazo a esta guía básica para echar a andar.

   Nota

   El término orden se usa a lo largo del curso como traducción del término original command, que históricamente se ha venido traduciendo, poco afortunadamente, como comando.

2. Ahora ya puedes crear el entorno de trabajo sobre tu cuenta Linux. El entorno de trabajo no es más que una estructura de directorios. Al directorio raíz tu entorno le vamos a llamar con el nombre de la asignatura (PCL) y, de momento, contendrá dos subdirectorios, uno para los proyectos que desarrollaremos y otro para “varios”. Este es el esquema de directorios que crearás para empezar. Abre un terminal de Linux y, con ayuda de la guía básica, crea el entorno de trabajo con los subdirectorios especificados. A lo largo del curso irás creando subdirectorios adicionales para las actividades a medida que lo vayamos necesitando. Estamos utilizando el shell en lugar de la interfaz gráfica para poder automatizar esta tarea. Por eso es importante que guardes en un fichero la secuencia de órdenes que te ha permitido crear el entorno. Aunque hay formas más elegantes de hacerlo que ya irás conociendo, de momento puedes utilizar el copiar-y-pegar las órdenes sobre un fichero. A ese fichero, que estará en tu directorio de login, llámale `crea_mi_entorno`.

   Nota

   Aunque el terminal te proporciona herramientas de edición, puedes editar más cómodamente los ficheros en la interfaz gráfica usando un editor de texto plano como `gedit`.

3. Una vez creado el entorno de trabajo, ejecuta sobre tu directorio de login:

       ls –R

   para comprobar que el entorno de trabajo tiene la estructura correcta.

4. Es el momento de crear una herramienta para automatizar la tarea de crear el entorno de trabajo. Una ventaja de una herramienta así es que permitiría, por ejemplo, que todos los desarrolladores del proyecto tuvieran su entorno creado la primera vez que se conectasen. Primero, borra todo el directorio PCL (pero no el fichero `crea_mi_entorno`). Este fichero debe contener la secuencia de órdenes que te ha permitido crear el entorno. Revísalo línea a línea para asegurarte de que está todo correcto. Ahora hay que hacer que las órdenes contenidas en `crea_mi_entorno` se puedan ejecutar en el shell. Para conseguirlo, nos conformaremos de momento con utilizar una “receta de cocina”, cuyo significado abordaremos en una actividad posterior. Ejecuta la orden:

       chmod +x crea_mi_entorno

   Básicamente, esta orden convierte a `crea_mi_entorno` en un fichero ejecutable. Esto no tiene nada que ver con lo que hace un compilador. Más tarde veremos cuál es la diferencia entre `crea_mi_entorno` y un programa C que hiciera lo mismo. Ahora ya puedes hacer:

       ./crea_mi_entorno

   Nota

   Si pones simplemente el nombre relativo del fichero probablemente no se va a ejecutar por un cuestión de política de seguridad que no viene al caso ahora.

   Comprueba que se ha creado tu entorno de trabajo. Si algo ha fallado, haz las correcciones oportunas hasta que funcione. `crea_mi_entorno` es tu la primera herramienta desarrollada en el curso. Muévela al directorio `PCL/Proyectos/P01/A01.2` y comprueba que ya no está en el directorio de login.

5. `crea_mi_entorno` es solo el primero de los muchos programas que desarrollarás a lo largo del curso. En Linux es costumbre que los programas ejecutables estén en un directorio `/bin` (compruébalo por ejemplo con `ls /bin`). Nosotros queremos agrupar también todos los ejecutables que desarrollemos en las actividades dentro de un directorio `PCL/bin`. Te resultará sencillo crear el nuevo directorio `bin` en tu entorno, y de paso añadir la orden en `crea_mi_entorno`. Una vez creado, nos gustaría “ver” `crea_mi_entorno` en `PCL/bin`. Tenemos varias opciones para ello: (a) podemos mover `crea_mi_entorno` a `PCL/bin` con la orden `mv`, y (b) lo podemos copiar con la orden `cp`. La opción (a) provoca que `crea_mi_entorno` desaparezca del directorio original, `PCL/Proyectos/P01/A01.2`, lo que tiene el inconveniente de que este directorio deja de ser útil como un lugar de desarrollo, que es para lo que lo habíamos creado. La opción (b) preserva `crea_mi_entorno` en su sitio original, pero, ahora tenemos dos copias de `crea_mi_entorno`, y si modificamos una de ellas deberíamos actualizar la otra para que sean consistentes. ¿Hay una alternativa mejor? Explora el resultado de ejecutar:

       ln ./PCL/Proyectos/P01/A01.2/crea_mi_entorno ./PCL/bin

> ¿Cuál es el resultado? ¿Cuántas copias de `crea_mi_entorno` tenemos? Realiza las pruebas necesarias para responder a esta pregunta. Para asentar las ideas, te será útil estudiar algunos conceptos adicionales sobre la organización de los ficheros en Linux.

6. Ya tienes tu directorio para ejecutables `bin` al estilo Linux. Pero hay una diferencia en cuanto al uso. Mientras que puedes ejecutar cualquier orden Linux, como `cat` o `man` sin más que especificar el nombre relativo de la orden, para ejecutar `crea_mi_entorno` debes especificar el nombre absoluto o alguna de sus simplificaciones sintácticas, como `./crea_mi_entorno`. ¿Qué tienen las órdenes de Linux que no tenga la tuya? Quizás hayas oído la expresión “poner en el path” que utilizan algunos manuales de instalación. Prueba a ejecutar:

       printenv PATH

   Verá que como salida has obtenido una serie de nombres absolutos de directorios (muchos de ellos `bin`) separados por `:`. `PATH` es lo que se denomina una variable de entorno del shell. No es la única variable de entorno. Si ejecutas:

       printenv

   obtendrás una lista de variables de entorno con sus valores. Podrás reconocer el significado de algunas de ellas. La variable `PATH` establece dónde ha de buscar el shell para ejecutar un programa cuando le das el nombre absoluto. Como tu directorio actual y tu `bin` no están en la variable `PATH`, el shell no busca allí un ejecutable, por eso tienes que especificar el nombre absoluto. Las variables de entorno se pueden modificar, algunas implícitamente, como `PWD` cuando ejecutamos `cd`, otras explícitamente:

       PATH=$PATH:bin

   Ejecutando esta orden en tu directorio `PCL` tendrás tu directorio de ejecutables “metido en el path” y puedes ejecutar `crea_mi_entorno` y todos los programas que pongas en `bin` escribiendo su nombre relativo.

   Nota

   Sin embargo, esta solución no será permanente, ya que `PATH` es una variable del shell. El shell es un programa más, y sus variables duran tanto como el propio shell. Fijar el `PATH` de manera permanente conlleva otras acciones complementarias, aunque esto escapa al ámbito del curso. No te será difícil dar con ello si decides dedicarle un poco de tiempo.

### Resultados

La actividad se evalúa como correcta si has conseguido establecer el entorno de trabajo como se especifica y ejecutar `bin/crea_mi_entorno` dándole el nombre relativo, `crea_mi_entorno`.

## Actividad 1.3. ¿Están seguros mis ficheros?

### Dedicación estimada: 45 minutos

Linux es un sistema multiusuario, lo que significa que otros usuarios pueden compartir el sistema de ficheros contigo. Según has aprendido, podrías navegar por el sistema de ficheros y llegar al directorio de otro usuario. ¿Cómo evita Linux que atentes contra la confidencialidad y la integridad de ese usuario? Fíjate que el otro usuario podría ser el administrador, root. Desde otro punto de vista, el otro usuario y tú podríais estar interesados en compartir algunos ficheros, y Linux debería permitirlo. De hecho la filosofía original de UNIX, un sistema operativo pensado para desarrolladores de aplicaciones, es que los usuarios se organizan en grupos que comparten información.

### Qué hay que hacer

1. Si ejecutas `ls -l` obtienes cierta información acerca de los atributos o propiedades de tus ficheros. Fíjate en el primer campo de información de cada línea escrita por el `ls -l`, una secuencia de 10 caracteres. El primer carácter identifica el tipo de fichero: `-` significa que es un fichero ordinario y `d` que es un directorio. Hay más tipos de ficheros, pero no vamos a entrar en detalle ahora. Los nueve caracteres restantes de ese primer campo, en tres grupos de tres, identifican los derechos de acceso al fichero. Fíjate ahora en la tercera y cuarta columnas de la salida del `ls -l`, que identifican respectivamente al usuario propietario del fichero y al grupo de usuarios propietario (acabas de descubrir a qué grupo de usuarios perteneces). Volvamos a los nueve caracteres de los derechos de acceso, que representan otros tantos bits de información. Si lo divides en tres bloques de tres bits, cada bloque de tres bits identifica, de izquierda a derecha, el permiso (o su ausencia) para la lectura (`r`), modificación/borrado (`w`) y ejecución (`x`) del fichero por respectivamente: el usuario propietario, el grupo del usuarios propietario, y el resto de usuarios del sistema. Los permisos no se interpretan igual sobre ficheros y directorios, especialmente el de ejecución. Se trata de que seas capaz de interpretar esta información para (a) `crea_mi_entorno`, (b) el directorio `Proyectos` y (c) un fichero de texto (créalo si no tienes todavía). En cualquier libro de UNIX/Linux, o en la Web (por ejemplo aquí) encontrarás información de ayuda.
2. Ahora ya estás en condiciones de entender el significado de la orden `chmod +x crea_mi_entorno`, y también el de `chmod -x crea_mi_entorno`, si lo pruebas. Consultando el `man`, trata de personalizar los derechos de acceso de `crea_mi_entorno` para que: tú puedas leerlo, modificarlo y ejecutarlo; tu grupo pueda leerlo y ejecutarlo, y el resto no pueda hacer nada.
3. Verifica con `ls -l crea_mi_entorno` que los derechos de acceso han quedado así: `rwxr-x---`. Corrígelo si es necesario.

### Resultados

La actividad se evalúa como correcta si consigues que los derechos de acceso queden establecidos según se especifica.

## Actividad 1.4. Desarrollando aplicaciones para Linux

### Dedicación estimada: 3 horas

Linux puede considerarse como una enorme caja de herramientas para construir aplicaciones. O, mejor habría que decir, varias cajas de herramientas. Con los años, las cajas de herramientas han ido aumentando en tamaño y complejidad. Debemos aprender a identificar qué caja usar en cada ocasión y a encontrar en ella las herramientas adecuadas.

En esta actividad y las siguientes vamos a aprender a localizar las herramientas de Linux en el manual y a reconocer los diferentes enfoques de implementación que nos ofrece. A falta de más concreción sobre lo que se nos pedirá para el proyecto, elegiremos como ejemplo de implementación un reloj (seguro que nos será de utilidad). Paradójicamente, a pesar de que la medida del tiempo parece algo genuinamente síncrono, los sistemas operativos tratan los eventos del reloj como eventos asíncronos. Esto es así porque el reloj que utiliza el sistema para medir el tiempo no guarda relación alguna con el reloj que marca el ritmo de ejecución de las instrucciones en el procesador (afortunadamente, porque en ese caso el ritmo de paso del tiempo dependería de la velocidad del procesador...). De esta forma un evento de tiempo, por ejemplo el que marca el paso del último segundo, puede producirse en cualquier momento de la ejecución de un programa. En este sentido, no es muy diferente del evento producido por un sensor o por un dispositivo de entrada como un teclado. De hecho, ya conocerás que todos estos eventos se tratan de la misma forma, como interrupciones. Se pueden programar aplicaciones concurrentes programando el tratamiento de las rutinas de atención convenientemente. Así se hacía antiguamente y algunos programadores aún lo hacen hoy en día. Sin embargo, si tenemos un sistema operativo de por medio, esto no es ni conveniente ni necesario, ya que el sistema operativo nos proporciona herramientas mucho más manejables y seguras.

¿Cómo encontrar esas herramientas? Linux proporciona una herramienta, que bien podría considerarse “la madre de todas las herramientas”, que nos abre la puerta de esta búsqueda. Se trata del manual, o `man`, que ya hemos utilizado. Originalmente el `man` se pensó como una herramienta off-line (en realidad no había otra posibilidad), que evitaba el uso del manual en papel. Hoy en día el `man` puede consultarse en la Web con un navegador, aunque hay que cuidar de que la versión consultada sea compatible con nuestra instalación. Como las herramientas que usaremos son las más comunes, esto en general no va a ser un problema.

Vamos ya con nuestro reloj. Ya que podríamos construir diferentes relojes, primero definiremos cómo queremos que se comporte. No será una definición demasiado precisa (dejaremos para más adelante el tema de cómo definir especificaciones funcionales precisas). Queremos que nuestro reloj escriba repetidamente en el terminal una cuenta con un periodo que le especificaremos como parámetro. Es decir, tecleando:

    ./reloj 1

escribirá una cuenta de segundos:

    1
    2
    3
    ...

mientras que:

    ./reloj 60

escribirá una cuenta de minutos.

### Qué hay que hacer

1. Antes de empezar es conveniente que tengas claros algunos conceptos sobre el tratamiento de eventos en un sistema operativo, en particular de tiempo. No vamos a usar directamente el mecanismo de interrupciones (Linux no te va a dejar), pero debemos reconocer los mecanismos subyacentes de las herramientas que vamos a usar. Puedes repasar los conceptos, o simplemente revisar la terminología que usaremos, en esta presentación sobre eventos e interrupciones.

2. Para buscar las herramientas que te permitirán desarrollar el reloj, primero tendrás que conocer un poco mejor el `man`, cómo está organizado y qué podemos encontrar en cada una de sus secciones (las cajas). Para empezar, desde un terminal Linux, prueba:

       man man

   La forma de usar la orden `man` es la habitual en las herramientas de terminal Linux, con la que probablemente no estés familiarizado. Alternativamente, puedes consultar un man en la Web. Es comprensible que saques muy poco en claro en un primer vistazo. Lo cierto es que `man man` no solo cuenta cómo consultar las herramientas, sino también cómo instalar documentación de nuevas herramientas en el `man`, de ahí su complejidad. Pero empezar a manejarlo es sencillo. Por ejemplo, si haces:

       man printf

   Te aparece la página que describe la orden `printf` en la Sección 1. ¿Cómo obtener la página de la función de biblioteca C `printf`? Investiga cómo acceder a una página en una sección concreta y encuentra la de `printf` en la Sección 3.

3. ¿Qué tipo de herramientas podemos esperar encontrar en el `man` para implementar el reloj? ¿En qué secciones? Necesitamos conocer un poco mejor la estructura del `man`. Consulta esta breve descripción de las secciones más importantes, donde encontrarás enlaces a información muy útil. Cuando tengas una idea aproximada de lo que contiene cada sección, pasa al siguiente punto.

4. Ahora que conoces algo del `man`, el resto de la actividad la dedicaremos a construir una primera versión de nuestro reloj. La versión utilizará herramientas de la Sección 1, es decir, órdenes del shell. Esto mismo ya lo hiciste en la actividad anterior para construir `crea_mi_entorno`, solo que ahora debes hacer el esfuerzo de encontrar las órdenes adecuadas. Fíjate en lo que hace el reloj: (1) esperar un tiempo determinado; (2) escribir la cuenta de tiempo, y (3) volver al punto 1. Además, el tiempo de espera hay que pasárselo como parámetro. Para simplificar, iremos paso a paso. Primero construirás una orden que espere un segundo (por lo tanto no necesita parámetro), escriba `1` y acabe. Dejaremos el parámetro y el bucle para después. ¿Qué herramienta de la Sección 1 te permite esperar un tiempo? ¿Cuál permite escribir un mensaje? Comienza a explorar el `man`. Como sugerencia, resulta de ayuda para la exploración el apartado “SEE ALSO” de las páginas del `man`. Por supuesto, puedes usar el buscador para encontrar ideas en Internet.

5. Una vez que conoces las herramientas básicas de la Sección 1, podrás montar este reloj supersencilo que espera un segundo, escribe `1` y acaba. Prepara un fichero `reloj` con las órdenes y hazlo ejecutable con `chmod`.

6. Para completar el reloj (el parámetro y el bucle) necesitas un poquito de programación del shell. Hay que decir que vamos a encontrar pocas cosas menos estandarizadas que los lenguajes de programación del shell (los llamados lenguajes de shell scripting). Para empezar, no hay uno, sino muchos shells para Linux (`bash`, `csh`, `ksh`, por citar algunos). Además, la sintaxis del lenguaje resulta bastante poco atractiva cuando se utilizan estructuras de control. El shell scripting no es un objetivo de este curso, así que en esta actividad solo pretendo mostrarlo como alternativa para la construcción de programas. Puedes consultar la página del `bash`, donde se describe como pasar el parámetro y cómo programar el bucle, entre otras muchas cosas. Pero entenderé si tomas un atajo y encuentras un ejemplo en la Web donde inspirarte. Como resultado, tendrás un primer reloj que se comporta según la especificación.

   Nota

   Si has programado un bucle infinito, puedes pararlo pulsando simultáneamente `[ctrl] C`.

### Resultados

La actividad se evalúa como correcta si el script que has construido funciona de acuerdo a la especificación.

## Actividad 1.5. Un reloj en C para Linux

### Dedicación estimada: 4 horas

La Sección 1 del `man` nos permite conocer la funcionalidad y la forma de uso de un gran número de órdenes y utilidades que se incluyen en la distribución del sistema operativo. Estas órdenes en general son programas, en su mayoría escritos en C, que se han ido desarrollando durante la larga historia de UNIX/Linux. De igual forma, nosotros podemos utilizar el lenguaje C para programar órdenes como las de Linux o nuevas utilidades, como es el caso del reloj.

En la actividad anterior describimos el funcionamiento del reloj de manera informal. Cuando se construye profesionalmente una aplicación es necesario proporcionar un documento con la especificación funcional detallada de la aplicación, que permita preparar los casos de prueba para verificar su funcionamiento. Este documento viene a ser el “contrato” al que como desarrolladores debemos atenernos. Quizás te hayas dado cuenta de que una página del `man`, más allá de servir como manual, proporciona también una especificación funcional. Nosotros también seguiremos este formalismo y contamos con esta especificación en formato man del reloj.

Es importante señalar que la especificación de una herramienta o aplicación no depende de como se implemente. Podríamos haber construido en la actividad anterior un reloj como el que se especifica ahora. No lo hicimos por razones prácticas, ya que las herramientas de la Sección 1 del `man` son menos adecuadas que las que proporciona la Sección 3, que son las que usaremos en la presente actividad.

### Qué hay que hacer

1. En primer lugar vamos a analizar en detalle la especificación funcional de nuestro reloj. Lee el documento con atención fijándote en qué tipo de información (acerca de la aplicación) incluye la especificación y qué tipo de información no incluye. Ya que la especificación del reloj es demasiado simple, consulta otras páginas de la Sección 1 del `man` para tener una visión más general.

2. Responde a este test para elaborar tu propia definición de qué has entendido por una especificación funcional. Si tienes dudas, consulta más ejemplos en la Sección 1 del `man`.

3. Utilizaremos la especificación funcional para diseñar el programa. Pero antes vamos a fijarnos un aspecto muy importante en el desarrollo de software: la verificación. Antes de construir el programa, o en paralelo si se trabaja en equipo, es necesario preparar los casos de prueba para la verificación. Se trata de un conjunto de tests que verificarán las diferentes situaciones a las que debe responder el programa. Por supuesto, esta es una ardua tarea si la especificación del programa es medianamente compleja, pero para nuestro ejemplo bastará con desarrollar unos pocos casos de prueba. Hay que insistir en que los casos de prueba se elaboran a partir de la especificación, no del código. Por eso lo ideal es que esta tarea sea independiente de la construcción del programa. Te proporciono esta plantilla, para establecer los casos de prueba. Una vez rellenada la plantilla, el documento servirá después para la verificación del reloj. Por ser la primera vez, te proporcionaré yo los casos de prueba para que te sirvan de ejemplo más adelante :-)

4. ¿Qué herramientas tenemos para construir el reloj en lenguaje C? De nuevo, utiliza el `man`. Encontrarás herramientas tanto en la Sección 2 como en la 3. Más adelante entenderemos por qué, de momento, vamos a preferir las funciones de biblioteca estándar de la Sección 3. Te recuerdo que necesitarás funciones para: (1) esperar un tiempo determinado; (2) implementar un cronómetro de alta resolución, y (3) escribir. Son herramientas muy habituales, así que no dudo que las encontrarás aunque necesites ayuda en Internet.

5. Como supongo que posees cierto manejo con el lenguaje C, no deberías tener problemas para programar el reloj de acuerdo a la especificación. Para compilar, utiliza el compilador de C de GNU:

       gcc -o reloj reloj.c

   Una vez que esté operativo, verifica su funcionamiento con los casos de prueba y haz los ajustes oportunos.

   Nota

   Lo ideal es que la verificación la haga una tercera persona, pero probablemente esto no está dentro de tus posibilidades, salvo que tengas la suerte de estar siguiendo el curso con otra gente.

   La especificación, por simplicidad, dejaba deliberadamente ambiguo el comportamiento del reloj cuando no se pasan los parámetros correctamente, como habrás observado. Puedes dedicar más o menos esfuerzo en tratar estas situaciones, como, por ejemplo, asignar un comportamiento razonable cuando introduces un valor negativo, pero recuerda que el contrato (la especificación funcional) no te obliga a ello. `reloj -1` podría hacer saltar por los aires tu PC y no incumpliría la especificación…

6. Aparte del código, que dejarás en sus lugares correspondientes del entorno de trabajo, tienes que elaborar un Documento de verificación. El mismo documento con los casos de prueba servirá como plantilla del documento de verificación, cambiando el título a “Documento de verificación”. Para cada caso de prueba, indica si el programa pasa la prueba satisfactoriamente o si hay algún problema.

   Nota

   Estamos intentando seguir una metodología de verificación de programas con un cierto nivel de formalismo, aunque pueda resultar exagerado para cosas tan sencillas como este reloj. Sin embargo, conviene acostumbrarse a ello para aplicarlo en la vida profesional.

7. Probablemente te hayas planteado alguna cuestión con respecto a los tiempos que escribe el programa. Comprueba en el `man` de las funciones usadas que Linux no te está engañando y que tú no puedes hacer mucho más al respecto. Linux no es un sistema de tiempo real.

### Resultados

La actividad se evalúa como correcta si el programa funciona de acuerdo a la especificación.

## Actividad 1.6. Historia de un programa

### Dedicación estimada: 2 horas

En actividades anteriores has implementado tus primeras herramientas y aplicaciones sencillas utilizando dos enfoques diferentes. Según avance el proyecto, la complejidad y el tamaño de los desarrollos irá en aumento. Por una parte, habrá que decidir cuándo desarrollar en un lenguaje de programación compilado, como C, y cuándo usar el lenguaje del shell. Por otra, el tamaño de los programas llevará a la necesidad de dividir los programas en módulos, que habrá que gestionar.

### Qué hay que hacer

1. La herramienta `crea_mi_entorno` pudiste construirla con bastante comodidad como un conjunto de órdenes para el shell. Si tuvieras que construirla en C, te llevaría bastante más tiempo y modificarla sería engorroso. Parece que el shell scripting es bastante adecuado para construir una herramienta como `crea_mi_entorno`. Sin embargo, no está tan claro qué enfoque usar para construir un reloj. La versión de la actividad 1.4 no tiene problemas para un programador acostumbrado al shell scripting, pero no es posible implementar directamente el cronómetro de la versión especificada en la actividad 1.5. Aunque siempre queda la opción de implementar en C una orden con la funcionalidad de `gettimeofday()` para usarla en un reloj shell… Es el momento de insistir en que el shell es un programa más, una especie de intermediario para ejecutar programas. Extrae tus propias conclusiones al respecto.
2. En definitiva, todo lo que Linux acaba ejecutando son programas en código ejecutable. Y un programa es mucho más que lo que parece. Consulta el tamaño de tu `reloj.c` y el del ejecutable. ¿Por qué el ejecutable es tan grande? Quizás conozcas la respuesta, pero en cualquier caso trataremos este tema en el siguiente punto.
3. El programa reloj que has construido en C es muy sencillo y ocupa unas cuántas líneas de código. Proyectos más complejos, como el que desarrollarás a lo largo del curso, requieren dividir los programas en módulos. Usar las herramientas del shell para componer programas es una excelente idea, como veremos, pero a veces no nos libraremos de un programa de gran tamaño. Además, como habrás deducido del punto anterior, un programa ejecutable incluye código añadido de bibliotecas de E/S, etc. Desde su desarrollo posiblemente en módulos, hasta que está listo en memoria junto a otros programas para ejecutarse, un programa tiene una historia compleja. Lee esta presentación, que te ayudará a comprender cómo el compilador y el sistema operativo gestionan un programa para que pueda ejecutarse adecuadamente compartiendo la memoria con otros programas.

### Resultados

Evalúa lo que has aprendido con este test de autoevaluación. Repasa los conceptos en función de los resultados obtenidos.

## Actividad 1.7. ¿Es compatible nuestro reloj con otros sistemas?

### Dedicación estimada: 2 horas y media

Más adelante deberemos acordar con los otros miembros del consorcio del proyecto los detalles de la compatibilidad del código C a desarrollar, para evitar sorpresas desagradables cuando finalmente se integre todo. Para la negociación debemos armarnos con argumentos técnicos.

Has programado un reloj en C que funciona en Linux. Tanto si optaste por instalar una partición de Linux en arranque dual con la de Windows en tu PC, como si utilizas una máquina virtual, te será fácil plantearte la siguiente cuestión: ¿funcionará el reloj en Windows? A priori, vamos a considerar tres posibles respuestas a la pregunta:

1. El programa ejecutable `reloj` compilado para Linux se puede ejecutar en Windows sin mayor problema, ya que que ambos sistemas operativos utilizan idéntico hardware.
2. El programa ejecutable `reloj` compilado para Linux no se puede ejecutar en Windows directamente, pero podemos recompilar `reloj.c` con un compilador de C para Windows y ejecutarlo sin mayor problema.
3. El programa `reloj.c` debe ser modificado y recompilado con un compilador de C para Windows si queremos que funcione en Windows.

### Qué hay que hacer

1. Antes de hacer ninguna prueba, piensa detenidamente en cuál de las tres afirmaciones de arriba debería ser la cierta. Si trabajas en grupo, discútelo con tus compañeros. Trata de argumentarlo por escrito. Quizás tengas el tema bastante claro y no necesites hacer pruebas. Si no es tu caso, experimenta las hipótesis planteadas y extrae conclusiones.
2. Consulta ahora esta versión para Windows de un reloj (ligeramente distinto al que has desarrollado). Este código, como todos los que te suministro en el curso, puedes copiarlo de la página web y pegarlo en un fichero de texto para compilarlo (pero no uses un editor con formato). Pruébalo sobre Linux y, si tienes la oportunidad, sobre Windows (donde debería funcionar).
3. Analiza ahora esta versión compatible del reloj. Como habrás comprobado, he utilizado compilación condicional para que el compilador, que sabe sobre qué tipo de plataforma está instalado, pueda generar el código adecuado. Puedes compilarla y probarla tanto sobre Linux como sobre Windows para comprobar que funciona en ambas plataformas. Hemos conseguido una versión compatible del reloj, pero siempre has necesitado compilarla para cada plataforma, aunque todas usen el mismo hardware.
4. Habrás observado en ejemplo anterior que el problema de incompatibilidad parece residir en la función `sleep()`. El resto del código es idéntico para ambas versiones. ¿Necesitaremos compilar un programa que solo tenga la función `printf()`? Si te apetece, puedes hacer la prueba con el archiconocido Hello World. Verás que sigue siendo necesario compilar...
5. En esta última versión del reloj tienes una implementación del reloj con llamadas al sistema Linux. Pruébala sobre Linux. Busca en el `man` las nuevas funciones que usa esta versión y trata de interpretar los resultados en base a ello. Puedes imaginar que no vas a poder compilarla para Windows, pero inténtalo si quieres. Es el momento de extraer algunas conclusiones sobre la compatibildad de los programas.
6. ¿Cuál es la razón de la incompatibilidad binaria de las llamadas al sistema? Las llamadas al sistema siguen siendo funciones de biblioteca (agrupadas en la Sección 2 del `man`), pero ese es solo un aspecto sintáctico. En última instancia, usemos `sleep()` o usemos `alarm()` y `pause()`, el código compilado utiliza los servicios propios del sistema operativo, que echará mano de los mecanismos hardware disponibles para ejecutar satisfactoriamente el programa. Y esto cada sistema operativo lo hace a su modo. Lee esta presentación sobre el modelo de funcionamiento de un sistema operativo. Se trata de un modelo general para comprender cómo los sistemas operativos ejecutan las llamadas al sistema. Dentro de este modelo, como acabamos de comentar, cada sistema operativo implementa su propio conjunto de llamadas al sistema mediante los mecanismos que sus diseñadores estimaron más convenientes.

### Resultados

Evalúa lo que has aprendido con este test de autoevaluación. Repasa los conceptos en función de los resultados obtenidos.