Práctica 2: El problema de la sección crítica Programación de Sistemas Concurrentes y Distribuidos Grado de Ingeniería Informática Dpto. de Informática e Ingeniería de Sistemas, Escuela de Ingeniería y Arquitectura Universidad de Zaragoza 1. Objetivos Esta práctica tiene dos partes. La primera, que se puede considerar una prolongación de la práctica anterior, incide en los mismos objetivos de aquélla, que no vamos a repetir aquí. La segunda parte, centrada en el problema de la sección crítica, busca que el alumno: comprenda el problema de la sección crítica en el contexto de determinados sistemas concurrentes, y comprenda el algoritmo de Peterson y lo utilice para la implementación de soluciones al problema de la sección crítica 2. Un ejercicio de clase de problemas En clase de problemas hemos planteado una solución al ejercicio 6 del libro de M. Ben-Ari titulado Principles of Concurrent and Distributed Programming. El algoritmo propuesto para ordenar un vector de enteros era el siguiente: constant integer n = 10 integer array [1.. n] C :=... -- inicializado ; datos distintos integer array [1.. n] D Process P( i :1.. n) integer mynumber, count mynumber := C[ i] count := number of elements of C less than mynumber D[ count +1] := mynumber End Process El ejercicio pide lo siguiente: Escribir un programa que primero inicialice el vector C, después lance los 10 procesos involucrados y espere a que todos terminen, para mostrar por 1
Programación de Sistemas Concurrentes y Distribuidos, curso 15-16 2 la salida estándar el vector D. Nótese que esta versión no contiene esperas activas. El directorio con los fuentes para la entrega es ejercicio 2 1. Se trata de escribir una versión alternativa del ejercicio anterior de manera que es el proceso 1 el que se encarga de inicializar el vector C, ejecutar su parte de ordenación y mostrar D una vez todos cada proceso involucrado haya acabado su parte del trabajo. Por su parte, los demás procesos involucrados (aquéllos con un identificador superior a 1) esperaran a que el primero termine la inicialización de C y después realizan su trabajo de ordenación. En este caso está permitido el uso de esperas activas. Directorio con los fuentes para la entrega: ejercicio 2 2. 3. El algoritmo de Peterson El algoritmo de Peterson (1981) es una solución válida al problema de la sección crítica, permitiendo que dos o más procesos compartan un recurso sin conflictos. Este algoritmo es una solución alternativa al algoritmo de Decker presentado en las sesiones de aula. La figura 1 muestra el algoritmo de Peterson para dos procesos. El algoritmo tiene tres variables compartidas por los procesos P y Q. La variable en1 es utilizada por el proceso P para declarar su interés en ejecutar su sección crítica, asignándole valor true. Una vez ésta ha sido ejecutada, el proceso P asigna de nuevo valor false a la variable. La variable en2 es empleada del mismo modo por el Proceso Q. Por último, la variable ult almacena el identificador del último proceso que manifestó su interés en ejecutar su sección crítica. En esta versión del algoritmo, los procesos P y Q tienen identificadores 1 y 2, respectivamente. Figura 1: Algoritmo de Peterson Vamos a centrarnos en el proceso P (Q es simétrico). Este proceso itera un número infinito de veces la siguiente secuencia: protocolo de entrada a la sección crítica, sección crítica, protocolo de salida y, finalmente, sección no crítica. El protocolo de entrada consiste en: primero, declarar el interés de P en ejecutar su sección crítica (acciones atómicas a1 y a2) y, segundo, en esperar si el proceso Q también quiere ejecutar o está ejecutando su sección crítica y manifestó esta
Programación de Sistemas Concurrentes y Distribuidos, curso 15-16 3 intención con anterioridad a P (a3 y a4). Posteriormente, se ejecuta la sección crítica de P (a5) y, una vez ésta haya finalizado, el protocolo de salida simplemente consiste en declarar este evento (a6). Finalmente, se ejecuta la sección no crítica del proceso (a7). El objetivo final de esta parte de la práctica es implementar un sencillo programa que simule el funcionamiento de un sistema de procesado de piezas. Este sistema presenta un problema de sección crítica que deberá ser resuelto aplicando el algoritmo de Peterson. Descripción del sistema La Figura 2 muestra, de una manera esquemática, un sistema de producción compuesto por dos máquinas, denominadas M1 y M2. Cada máquina puede realizar un conjunto de operaciones. Una máquina no puede albergar en su interior más de una pieza. Por otro lado, dos procesos, llamados P1 y P2, utilizan estas máquinas para producir dos tipos diferentes de piezas. Más concretamente, el proceso P1 produce piezas de Tipo 1 y el proceso P2 piezas de Tipo2. Las piezas de Tipo1 son procesadas primero en la M1 (durante 0.05 unidades de tiempo) y a continuación en la M2 (durante 0.05 unidades de tiempo). Por su parte, las piezas de Tipo2 son procesadas primero en la M1 (durante 2.0 unidades de tiempo) y a continuación en la M2 (durante 2.0 unidades de tiempo). Es preciso tener en cuenta que una pieza que se encuentre en M1 no podrá pasar a M2 hasta que ésta se encuentre vacía. Figura 2: Representación esquemática de un sistema de producción. Se pide escribir un programa que simule el funcionamiento del sistema descrito. Durante su ejecución cada proceso productor deberá producir 7 piezas. El programa debe indicar, para cada pieza de cada tipo, el instante de tiempo en que entra en el sistema de producción y el instante de tiempo en que lo abandona. Por otro lado debe dar el tiempo medio que una pieza de cada tipo pasa dentro del sistema. Para llevar a cabo la simulación, debéis tener presente que: por razones de seguridad, no puede haber simultáneamente en el sistema dos piezas del mismo tipo las posibles sincronizaciones necesarias para el programa se han de implementar mediante exclusiones mutuas, usando el algoritmo de Peterson. el algoritmo de Peterson asume que las operaciones de lectura y escritura de un dato entero o booleano son atómicas. No obstante, este nivel de atomicidad no está garantizado cuando se trabaja con datos del tipo entero
Programación de Sistemas Concurrentes y Distribuidos, curso 15-16 4 o booleano en un programa de C++. Por este motivo, se recomienda utilizar las plantillas de tipos de datos atómicos ofrecidas por el lenguaje, así como las operaciones asociadas (véase la documentación disponible en http://en.cppreference.com/w/cpp/atomic/atomic). Una posible traza de la ejecución de una simulación puede ser la siguiente: -->T1 a M1 -->T1 a M2 ------->T2 a M1 -->T1 sale: 67622.181662000 -->T1 a M1 ----->T2 sale: 67626.682033000 -->T1 a M2 ------->T2 a M1 -->T1 sale: 67626.952391000... -->T1 a M1 ----->T2 sale: 67651.481190000 -->T1 a M2 ------->T2 a M1 -->T1 sale: 67651.740862000 ----->T2 sale: 67655.701026000 Procesadas 7 piezas de tipo1 en 30.113481000 unidades Tiempo medio en el sistema: 4.301925857 unidades Procesadas 7 piezas de tipo2 en 34.080672000 unidades Tiempo medio en el sistema: 4.868667428 unidades Directorio con los fuentes para la entrega: ejercicio 2 3. 4. Generalización del sistema de producción (Optativo) El algoritmo de Peterson que hemos manejado está diseñado para el caso en que dos procesos van a acceder a zonas en exclusión mutua, pero su generalización para el caso de tres o más procesos no es sencilla. En clase hemos visto una solución sencilla para el caso de más de dos procesos basada en instrucciones atómicas del tipo test-and-set, fetch-and-add,... En este ejercicio
Programación de Sistemas Concurrentes y Distribuidos, curso 15-16 5 se pide dar una solución al ejercicio anterior para el caso en que intervienen simultáneamente dos procesos de cada tipo. 5. Entrega de la práctica La práctica se realizará de forma individual. Cuando se finalice se debe entregar un fichero comprimido practica2 minip.tar (donde minip es el NIP del autor de los ejercicios) con el siguiente contenido: 1. Todos los ficheros con los fuentes solicitados 2. Un fichero de texto denominado autor.txt que contendrá el NIP, los apellidos y el nombre del autor de la práctica en las primeras líneas del fichero. Por ejemplo: NIP Apellidos Nombre - 345689 Rodríguez Quintela Sabela También deberá contener: una descripción de las principales dificultades encontradas para la realización de la práctica para cada uno de los ejercicios del enunciado el listado de los nombres de los ficheros fuente que conforman la solución solicitada así como la forma de compilarlos para obtener el ejecutable correspondiente. Para la entrega del fichero.tar se utilizará el comando someter en la máquina hendrix01.cps.unizar.es. Los alumnos pertenecientes a grupos de prácticas cuya primera sesión de prácticas se celebra el día 21 de octubre de 2016 deberán someter la práctica no más tarde del día 3 de noviembre de 2016 a las 23:59. Los alumnos pertenecientes a grupos de prácticas cuya primera sesión de prácticas se celebra el día 28 de octubre de 2016 deberán someter la práctica no más tarde del día 10 de noviembre de 2015 a las 23:59. 6. Procedimiento de corrección y recomendaciones Una vez realizadas las prácticas y entregadas, cada estudiante debe presentárselas al profesor en la siguiente sesión de prácticas. Al realizar la presentación el profesor le podrá formular cuestiones sobre las decisiones de diseño e implementación que ha realizado. La práctica debe entregarse en los términos indicados anteriormente, deber funcionar correctamente y no haber sido copiada. En particular, hay que asegurarse de que la práctica funciona correctamente en los ordenadores del laboratorio (vigilar aspectos como los permisos de ejecución, juego de caracteres utilizado en los ficheros, etc.). También es importante someter código limpio (donde se ha evitado introducir mensajes de depuración que no proporcionan
Programación de Sistemas Concurrentes y Distribuidos, curso 15-16 6 información al usuario). El tratamiento de errores debe ser adecuado, de forma que si se producen debería informarse al usuario del tipo de error producido. Además se considerarán otros aspectos importantes como calidad del diseño del programa, adecuada documentación de los fuentes, correcto formateado de los fuentes, etc. Para el adecuado formateado de los fuentes, es conveniente seguir unas pautas. Hay varias, y es posible que podáis configurar el entorno de desarrollo para cualquiera de ellas. Una posible, sencilla de seguir, es la Google C++ Style Guide, que se puede encontrar en https://google-styleguide.googlecode.com/svn/trunk/cppguide.html