Hola, muy buenas. Bienvenidos a esta tutoría de sistemas en tiempo real. Soy Nieves Pedreira, tutora del Centro Asociado de Acoruña y hoy os voy a hablar un poco, hacer un pequeño resumen de la unidad 8 que se corresponde con el control de acceso a los recursos. Para ello, si disponéis del libro de la edición en inglés, se trata del capítulo 8 y si tenéis la edición en español, pues el capítulo 11. Aquí tenéis un pequeño resumen de los diferentes apartados que tiene el tema. Lo tenéis también en la organización del curso, así que no me voy a parar nada. Van a ser los siete puntos que vamos a tocar. Bueno, a la hora de hablar, de presentar el control de acceso a los recursos, es importante tener en cuenta que todas las tareas, como hemos visto ya en los capítulos anteriores, necesitan comunicarse y sincronizarse para poder utilizar los diferentes recursos, que pueden ser dispositivos externos, ficheros, buffers, algoritmos. ¿Qué puede pasar si no se realiza bien esta sincronización? Bueno, pues el fallo de una tarea puede producir que un recurso no esté disponible para otras. Tampoco se puede permitir que algunas tareas monopolicen los recursos y otras, por la forma en que se realicen, no se realicen. Esa gestión al acceso no puedan llegar nunca a ellos y tampoco se puede permitir que se produzcan interbloqueos cuando hay tareas que están ocupando unos recursos que necesitan otras tareas y esas primeras tareas no sueltan los recursos hasta que puedan acceder a los otros, que como también están ocupados por otros, pues al final no se puede avanzar. Si la comunicación que se produce entre cada tarea y los controladores de recursos fuesen acciones atómicas, bueno, simplificarían mucho estas complicaciones porque no se podrían interrumpir la asignación o liberación de cada recurso. ¿Cómo podemos hacer esto? Bueno, en general los lenguajes de tiempo real no soportan este tipo de acciones atómicas, pero sí que siempre se puede conseguir ese efecto utilizando las primitivas de comunicación y sincronización. ¿Cómo se puede hacer esto? Bueno, pues siempre va a hacer falta realizar una implementación de estas entidades de recursos. ¿Cómo se puede hacer esto? Esto se puede hacer a través de un agente de control. Estos agentes de control pueden ser pasivos, de manera que en ese caso se dice que los recursos son protegidos o sincronizados y también se pueden necesitar agentes activos para programar el nivel correcto de control y en este caso el controlador de recursos se llama servidor. Bueno, las características de la modularidad nos dicen que los recursos deben estar encapsulados y que solo se puede acceder a ellos a través de una interfaz de alto nivel. En ADA, por ejemplo, podría utilizarse un paquete que contenga una única tarea para el control. El uso del synchronized interface nos permite representar estos controladores de recursos tanto activos como pasivos. Si lo que tenemos es sincronización basada en monitor como un POSIX o Java RealTime, los recursos protegidos se encapsulan de manera natural en estos monitores. Sin embargo, la sincronización de recursos es muy fácil. La sincronización de tipo espera ocupada o semáforos no permite este encapsulamiento. A la hora de identificar la capacidad de los lenguajes de programación para expresar estas restricciones de sincronización requeridas, hablamos de la potencia expresiva o expressive power. También podemos tener en cuenta más conceptos relacionados con estos, como son la facilidad de uso de una primitiva de sincronización, es decir, la facilidad con la que se puede expresar cada una de las restricciones de sincronización y también la facilidad de combinar diferentes restricciones para conseguir esquemas más complejos. Varias aproximaciones o las dos aproximaciones que tenemos para esta sincronización son fundamentalmente la espera condicional, en la que se aceptan todas las solicitudes, pero las tareas cuya solicitud no se puedan atender se suspenden en una cola hasta que puedan ser atendidas. La otra aproximación, que es la evitación, sólo acepta las solicitudes que puedan ser atendidas en cada momento. Para poder expresar estas restricciones es necesario acceder a diferentes tipos de información. En función de diferentes parámetros, digamos, se tiene que poder expresar. Por ejemplo, es importante saber a qué tipo de servicio se solicita, para poder dar preferencia a determinadas operaciones. Por ejemplo, pues dar preferencia a operaciones de lectura antes que a las de escritura en bases de datos de tiempo real, porque son más rápidas. ¿Cómo se puede emular esto? Bueno, pues si lo que tenemos son monitores, estos utilizan siempre colas FIFO. Es decir, el primero que llega es el primero en ser atendido, de manera que esto no lo podemos implementar. El primero que llega va a ser atendido independientemente del tipo de servicio requerido. Sin embargo, en ADA, por ejemplo, sí que se pueden utilizar guardas que utilicen el atributo COUNT de las entries. Otra información, otro parámetro que hay que tener en cuenta para poder asignar las restricciones de los recursos es el orden en que llegan las solicitudes. Si se quieren atender, por lo que sea, hay una política de atender estas solicitudes en orden, pues los monitores, como vimos antes, ya lo hacen de manera automática porque gestionan las solicitudes en este orden de que el primero que llega es el primero en ser atendido. En ADA se podría hacer para cada tipo de solicitud. Todas las solicitudes de lectura o todas las de escritura por separado pueden ir en orden, pero no hay una cola general. No hay una cola general que una las diferentes tipos de solicitudes. Por lo tanto, independientemente del tipo, no se puede conseguir un orden FIFO que garantice que todas las solicitudes van a ser atendidas en el orden de llegada. También es importante poder atender a las solicitudes en función del estado del servidor o de los objetos que gestiona. Para algunas operaciones solo se pueden permitir cuando el servidor... Se encuentra en un cierto estado, ¿vale? Pues el servidor está disponible, el recurso está disponible, en fin. Si lo que tenemos es sincronización de evitación, las restricciones basadas en el estado sí que se pueden expresar como guardas y las que se basan en servidores con el posicionamiento de las sentencias ACCEPT, ¿vale? Los monitores también pueden utilizar algunas variables de condición para implementar estas restricciones. ¿Qué más cosas nos pueden afectar a la hora? Puede asignar un recurso o no los parámetros de la solicitud, ¿vale? Puede haber solicitudes de tareas que requieran varios recursos. Entonces, si no están disponibles todos los recursos que necesita la tarea, pues esa tarea es suspendida. En el momento en que se libera un recurso, se despierta a todas estas tareas suspendidas para ver si se pueden atender, ¿vale? Si ya están todos los recursos que necesita una tarea en concreto disponibles, esa tarea podría ser atendida. También es importante la prioridad del cliente, ¿no? De la propia tarea que hace la solicitud y tanto en ADA, Java tipo real y POSIX es posible gestionar las colas en función de esta prioridad de las tareas. También es importante la funcionalidad de reencolado que consiste en mover una tarea que estaba detrás de una guarda barrera detrás de otra guarda, ¿vale? Pues se acepta la tarea porque el recurso aparentemente está disponible o está disponible. Pero la tarea no puede ser atendida o da un error, ¿vale? Pues se pueda volver a poner en la cola porque realmente no ha cumplido su objetivo, ¿vale? ADA permite estos reencolados entre tareas y entradas de objetos protegidos. También es importante el hecho de poder saber qué tarea está solicitando el recurso, ¿vale? Esto nos permite saber, bueno, si sabemos qué tarea solicita el recurso, bueno, pues se puede actuar en función de la prioridad de esa tarea, ¿no? En los lenguajes que tienen nombramiento simétrico directo o indirecto basado en un intermediario de uno a uno, las tareas servidores o recursos protegidos conocen la identidad de la tarea cliente con la que están tratando. Sin embargo, si en lugar del nombramiento simétrico, tanto directo como indirecto, ya digo, tenemos el asimétrico, esto no sucede. Por lo tanto, no se puede priorizar unas tareas sobre otras, ¿vale? Solo las solicitudes. El hecho de conocer esto permite rechazar una petición para prevenir interbloqueo y también garantizar que los recursos son liberados solo por el proceso o por la tarea que los había solicitado, que nos había obtenido. Bueno, cuando... Varias tareas compiten para recursos o cooperan sobre los recursos, el modo normal de operación es primero hacer una petición del recurso, esperar a que esté disponible, luego utilizarlo y a continuación liberarlo. El acceso al recurso puede ser compartido o exclusivo. Compartido, pues varias tareas pueden acceder a él. Exclusivo, únicamente en cada momento una tarea puede acceder a ese recurso. Hay algunos recursos que se pueden usar. En cualquiera de los modos. Entonces, dependiendo de cómo lo haya solicitado la tarea que lo tiene en el momento actual, si lo ha solicitado de manera compartida, pues otra tarea u otras tareas que lo hayan solicitado en el mismo modo pueden acceder también. Si alguna de las... Si lo está utilizando una tarea en modo exclusivo, ninguna otra la puede utilizar aunque haya solicitado el modo compartido, ¿vale? O si lo está utilizando una tarea en modo compartido y la tarea que quiere entrar actualmente... Si lo requiere en modo exclusivo, tampoco podría entrar. Es importante que las tareas soliciten los recursos en el momento en que los necesitan, porque si no pueden ser bloqueadas a la espera de ese recurso y realmente podrían estar haciendo otras funcionalidades mientras, ¿vale? Y también es importante que liberen los recursos en cuanto terminen con ellos. ¿Qué pasa si estas... Condiciones no se producen? Bueno, pues que se puede dar un interbloqueo, ¿vale? Es decir, tenemos varias tareas esperando por recursos cruzados de manera que se puedan suspender indefinidamente. Pues una tarea quiere el recurso A y está ocupando el B. Y otra tarea quiere el B y está ocupando el A. Si no lo liberan hasta que pueden acceder al otro recurso, pues se produciría ahí un interbloqueo, ¿vale? Es posible también producirse bloqueo... Un bloqueo activo donde... Las tareas no pueden avanzar, no pueden acceder a sus recursos, pero siguen en ejecución. Por ejemplo, un bloqueo en bucles en los que no se está haciendo nada, ¿vale? También se puede producir un aplazamiento indefinido cuando varias tareas intentan acceder de manera exclusiva al mismo recurso en función de cómo esté programada la política de asignación, ¿vale? Porque podría producirse que alguna de las tareas nunca consiga el acceso a ese recurso. En realidad hay cuatro condiciones que se tienen que dar a la vez para que se produzca interbloqueo, ¿vale? Que son la exclusión mutua, es decir, sólo una tarea puede usar un recurso al mismo tiempo. Mantenimiento y espera, donde las tareas mantienen los recursos mientras esperan por otros. No desalojo, los recursos sólo pueden ser liberados voluntariamente por las tareas, el controlador no puede liberar ningún recurso. Y espera circular, donde cada proceso mantiene recursos. Recursos solicitados por el siguiente en cadenas circulares, ¿vale? ¿Cómo se puede solucionar el interbloqueo? Bueno, pues para solucionar el interbloqueo hay tres posibilidades, ¿no? Una de ellas es la prevención. La prevención, pues consiste en garantizar que alguna de esas cuatro condiciones nunca se dé, ¿vale? Pues que no se produzca, por ejemplo, el mantenimiento y espera. Es decir, donde las tareas se han acabado. Con un recurso lo liberen, aunque tenga que esperar por otro, ¿vale? O que no se produzca el no desalojo, donde el controlador sí que pueda liberar recursos, ¿vale? Si hay una de esas cuatro condiciones que nunca se produce, sabemos que no vamos a tener interbloqueo, ¿vale? Otra opción consiste en la evitación, es decir, controlar que sí que se pueden dar las condiciones de interbloqueo, pero en el momento en que se detecta que se podría dar... Esa condición, pues actuar para evitarlo. O la otra posible solución es la detección y recuperación. Es decir, sí se puede producir el interbloqueo, pero se detecta y se corrige sobre la marcha. Bueno, con esto creo que os he hecho un pequeño resumen de este tema 8 y como siempre os animo a que hagáis los ejercicios de autoevaluación correspondiente a esta unidad, ¿vale? Echaros... Podéis, en primer lugar, ver este pequeño... Recurso, luego ampliar los conocimientos con el capítulo del libro y después realizar la autoevaluación y ver... Bueno, sabéis que la podéis realizar hasta 10 veces, bueno, hasta que consigáis... Si tenéis dudas en algunas de las cuestiones, hasta que consigáis que funcione. Y, por supuesto, como siempre, tanto en el foro como a través del correo podéis contactar conmigo en el caso de que tengáis alguna duda. Pues nada, muchas gracias por vuestra atención y hasta la próxima tutoría. Un saludo fuerte. Chao.