<< >> Título

Descripción detallada


Funcionamiento del servidor

El proceso intérprete de órdenes deberá esperar a que algún proceso cliente le solicite sus servicios. Mientras esto no ocurra permanecerá bloqueado. Cuando se le demande un servicio, saldrá del estado de bloqueo y actuará. Básicamente el esquema de funcionamiento de este proceso es el siguiente:

Generar el proceso demonio.
Establecer mecanismos de comunicación.

for (;;)
{
	recibe_petición (&mi_peticion);

	if ( orden de finalizar ) break;

	Procesar línea de órdenes.
	Ejecutar orden.
	Esperar por la finalización de la orden.
	envia_respuesta (codigo_de_respuesta);
}

Eliminar los mecanismos de comunicación.
Terminar

Observe cómo se trata de un proceso que entra en un bucle infinito, del cual se sale cuando algún proceso cliente le ordene al servidor su finalización. Esta es una estructura típica para un proceso servidor, y aparece con frecuencia en los lenguajes de paso de mensajes o los sistemas dirigidos por eventos (por ejemplo, al programar en un entorno de ventanas). Consiste en un bucle sin fin en el que cada iteración consiste en recibir un mensaje y atenderlo, así de simple.

Seguidamente se detallan los aspectos más importantes de cada punto de la ejecución.

Generación de un proceso demonio

Consiste en la creación de un nuevo proceso que se deberá ejecutar en segundo plano (proceso demonio). Una vez creado el nuevo proceso, el proceso deberá finalizar. De esta forma, el servidor no acapara la terminal donde se ha lanzado.

Establecimiento de los mecanismos de comunicación

Consiste en la creación de los recursos que permitirán la comunicación entre el servidor y los clientes. Para esta práctica se podrán utilizar dos clases de herramientas de intercomunicación: colas de menajes, o bien usar conjuntamente semáforos y memoria compartida.

Espera por una nueva petición

El servidor habrá de bloquearse mientras no tenga ninguna solicitud de servicio. Así pues, se tendrá que diseñar un mecanismo de sincronización y comunicación que garantice que mientras no haya solicitud, el servidor permanezca en estado de bloqueo (sin consumo de CPU). Además, este mecanismo deberá garantizar la integridad de las peticiones; esto es, una petición que se está recogiendo no podrá ser destruida por la llegada de una nueva.

Comprobación del servicio solicitado

En este paso se comprueba si el servicio requerido se corresponde con una orden de finalización del servidor; o, por contra, se solicita la ejecución de un programa. Como se ha dicho, esto viene expresado por el valor del campo orden[0] de la petición. Si vale FINALIZA, el servidor sale del bucle, elimina los recursos IPC que haya creado y termina su ejecución.

Procesamiento de la orden

En este paso se produce el análisis de la cadena de caracteres que contiene la orden a ejecutar. Este tratamiento tiene la finalidad de obtener: a) el nombre del fichero ejecutable y b) cada uno de los argumentos del programa. Las funciones de rastreo de cadenas, como strchr, pueden ser útiles para trocear la línea de órdenes.

Cada uno de los datos extraídos se empleará para invocar posteriormente al programa solicitado, haciendo uso de alguna función exec... del UNIX.

Ejecución de la orden

El programa servidor creará un nuevo proceso hijo mediante la función fork. El proceso padre deberá esperar a que el hijo recién lanzado finalice su ejecución; la espera se hará mediante la función wait.

Por su parte, el proceso hijo deberá cargar y ejecutar el programa solicitado con los argumentos especificados en la línea de órdenes mediante el uso de alguna de las funciones exec.

La salida estándar de la orden habrá de redirigirse al archivo especificado en el campo canal de la petición recibida. Más adelante se explica cómo se realiza esto.

Algunas de las situaciones de error que pueden surgir, y que deberán ser notificadas al cliente en la respuesta, son:

1. No se pudo crear el proceso hijo con el fork (devolver ERR_HIJO)

2. No se pudo lanzar el programa con exec (devolver ERR_CARGA)

3. La orden dio un resultado erróneo (devolver ERR_EJEC)

Si no ocurre nada anómalo, se le entrega al cliente el valor ERR_OK

Espera por la finalización del servicio solicitado

Tal y como se ha indicado, esto lo realizará el servidor mediante el uso de la llamada al sistema wait. Lo único a reseñar es que no se deberá llegar a este punto si se produce un error en la invocación a la función fork.

Si el error se produce en la carga o en la ejecución del programa solicitado, entonces, se podrá detectar consultando el argumento utilizado en la llamada a la función wait.

Notificación del resultado al cliente

El servidor deberá comunicar al cliente que le solicitó el servicio si la orden demandada se ha realizado o no. Para ello se utilizará la función envia_respuesta(), que ustedes habrán de implementar.

Estructura de un proceso cliente

Los pasos básicos de ejecución de un cliente son:

Construir petición en mi_peticion

envia_peticion(&mi_peticion);

recibe_respuesta(&resultado);

Comprobar resultado de la ejecución.

Finalizar.

Seguidamente se describe lo que hace cada uno de estos pasos:

Construcción de la petición para el servidor

Las primeras instrucciones del servidor consisten en elaborar la petición que se enviará al servidor. Se tendrá que leer los parámetros argc y argv[] de la función main en el programa cliente, y convertirlos en un struct peticion.

Por otra parte, el cliente deberá rellenar el campo canal de la estructura peticion, con la ruta de la terminal o el archivo donde se desea visualizar el resultado. Para conocer la terminal actual, se invoca a la función función().

Solicitud de servicio y espera por la notificación de servicio realizado

Una vez que el cliente ha construido la petición, deberá solicitar el servicio al proceso servidor. Deberá hacer uso del mecanismo de sincronización y comunicación diseñado por ustedes, basado en algún IPC de UNIX. La implementación deberá garantizar que el cliente se mantenga bloqueado hasta que el servidor le notifique que se ha completado su petición, o bien hasta que el servidor finalice.

Observe que el servidor puede encontrarse ocupado con peticiones de otros clientes, y que no se deben interferir unas peticiones con otras.

Comprobación del resultado del servicio realizado

Una vez que se le ha notificado que el servicio ha sido realizado, el cliente deberá consultar el campo resultado de la estructura struct Respuesta, para determinar si la solicitud de tramitó correctamente o no.


<< >> Título