
En artículos anteriores, examinamos el modelo multitarea y descubrimos que cada tarea es un programa casi independiente. Aunque las tareas en los sistemas integrados tienen un cierto grado de independencia, esto no significa que no se "conozcan" entre sí. Algunas tareas estarán realmente aisladas de otras, pero la interacción y sincronización entre ellas es un requisito común. Este mecanismo es una de las funciones clave del RTOS. El rango de funciones puede variar según el RTOS, por lo tanto, en este artículo consideraremos las opciones disponibles públicamente.
Artículos anteriores de la serie:
Artículo # 4. Tareas, cambio de contexto e interrupcionesArtículo # 3. Tareas y planificaciónArtículo # 2. RTOS: estructura y modo en tiempo real
Artículo # 1. RTOS: introducción.
Rango de funciones
Hay tres modelos de interacción y sincronización entre tareas:
- Los servicios están vinculados a tareas: RTOS proporciona tareas con atributos que proporcionan interacción entre ellos. Considere las señales como un ejemplo.
- Los objetos del núcleo son medios autónomos de comunicación o sincronización. Ejemplos: banderas de eventos, buzones, colas / canales, semáforos y mutexes.
- La mensajería es un esquema simplificado en el que el RTOS le permite crear objetos de mensaje y transferirlos de una tarea a otra o varias. Esto es fundamental para la arquitectura del núcleo y, por lo tanto, dicho sistema se denomina "mensajes RTOS".
Los mecanismos que son ideales para diferentes procesos variarán. Sus capacidades pueden superponerse, por lo que vale la pena considerar la escalabilidad de estos modelos. Por ejemplo, si una aplicación requiere varias colas, pero solo un buzón, puede implementar un buzón con una cola para un elemento. Este objeto no será completamente óptimo, pero el código completo del buzón no se incluirá en la aplicación y, por lo tanto, la escalabilidad reducirá la cantidad de memoria utilizada por el RTOS.
Variables comunes o áreas de memoria
Un enfoque simplificado para la interacción entre tareas es la presencia en el sistema de variables o áreas de memoria que están disponibles para todas las tareas. Este enfoque se puede aplicar a varios procesos, a pesar de su simplicidad. El acceso debe ser controlado. Si la variable es solo un byte, es probable que escribir en ella o leerla sea una operación atómica (es decir, continua), pero se debe tener cuidado si el procesador permite otras operaciones en bytes de memoria, ya que pueden ser interrumpibles y pueden ocurrir problema de sincronización Una forma de implementar el bloqueo / desbloqueo es deshabilitar las interrupciones por un corto tiempo.
Si usa un área de memoria, aún necesita un candado. Puede usar el primer byte como indicador de bloqueo, dado que la arquitectura de memoria proporciona acceso atómico a este byte. Una tarea carga datos en un área de memoria, establece un indicador y luego espera a que se restablezca. Otra tarea es esperar a que se establezca el indicador, leer datos y restablecer el indicador. Usar la desactivación de interrupción como bloqueo es menos sensato ya que mover todo el búfer de datos puede llevar algo de tiempo.
Este uso de memoria compartida es similar a la implementación de muchas comunicaciones entre procesadores en sistemas de múltiples núcleos. En algunos casos, el bloqueo y / o la interrupción del hardware están integrados en la interfaz interprocesadora de la memoria compartida.
Señales
Las señales son uno de los mecanismos más simples para la interacción entre tareas que ofrece RTOS tradicional. Contienen un conjunto de indicadores de bits (8, 16 o 32, según la aplicación específica), que está asociado con una tarea específica.
El indicador de señal (o varios indicadores) se puede establecer mediante cualquier tarea utilizando la operación lógica "O". Los indicadores solo pueden ser leídos por una tarea que contiene una señal. El proceso de lectura suele ser destructivo, es decir, los indicadores también se restablecen.
En algunos sistemas, las señales se implementan de una manera más compleja, de modo que una función especial asignada por el propietario de la tarea de la señal se ejecuta automáticamente cuando se activa cualquier señal de señal. Esto elimina la necesidad de que la tarea controle las banderas en sí. Esto es algo similar a un controlador de interrupciones.
Grupos de banderas de eventos
Los grupos de indicadores de eventos son similares a las señales en el sentido de que son una herramienta orientada a bits para la interacción entre tareas. Del mismo modo, pueden contener 8, 16 o 32 bits. A diferencia de las señales, son objetos centrales independientes y no "pertenecen" a ninguna tarea en particular. Cualquier tarea puede establecer y restablecer indicadores de evento utilizando las operaciones lógicas "OR" y "AND". Del mismo modo, cualquier tarea puede verificar indicadores de eventos utilizando las mismas operaciones. En muchos RTOS, puede realizar una llamada de API de bloqueo para una combinación de indicadores de evento. Es decir, la tarea puede suspenderse hasta que se establezca una combinación específica de indicadores de evento. La opción "consumir" también puede estar disponible al verificar las banderas de eventos, que restablece todas las banderas.
Semáforos
Los semáforos son objetos de kernel independientes utilizados para la contabilidad de recursos. Hay dos tipos de semáforos: binario (puede tener solo dos valores) y general (número ilimitado de valores). Algunos procesadores admiten instrucciones (atómicas) que facilitan la implementación rápida de semáforos binarios. Los semáforos binarios se pueden implementar como semáforos generales con un valor de 1.
Cualquier tarea puede intentar asignar un semáforo para obtener acceso al recurso. Si el valor actual del semáforo es mayor que 0 (el semáforo es libre), el valor del contador se reduce en 1, por lo tanto, la asignación será exitosa. En muchos sistemas operativos, se puede utilizar un mecanismo de bloqueo para asignar un semáforo. Esto significa que la tarea puede estar en estado de espera hasta que otra tarea libere el semáforo. Cualquier tarea puede liberar el semáforo, y luego el valor del semáforo aumentará.
Buzones
Los buzones son objetos independientes del núcleo que proporcionan los medios para enviar mensajes. El tamaño del mensaje depende de la implementación, pero generalmente es fijo. Los tamaños de mensaje típicos son de uno a cuatro elementos del tamaño de un puntero. Por lo general, se envía un puntero a datos más complejos a través del buzón. Algunos núcleos implementan buzones de tal manera que los datos simplemente se almacenan en una variable regular y el núcleo controla el acceso a ellos. Los buzones también se pueden llamar "intercambio", aunque este nombre ahora rara vez se ve.
Cualquier tarea puede enviar mensajes a un buzón, que luego se rellena. Si una tarea intenta enviar un mensaje a un buzón completo, recibirá una respuesta de error. En muchos RTOS, puede usar un mecanismo de bloqueo para enviar al buzón. Esto significa que la tarea se suspenderá hasta que se lea el mensaje en el buzón. Cualquier tarea puede leer mensajes del buzón, después de lo cual está vacío. Si una tarea intenta leer desde un buzón vacío, recibirá una respuesta de error. En muchos RTOS, puede usar una llamada de bloqueo para leer desde un buzón. Esto significa que la tarea se suspenderá hasta que aparezca un nuevo mensaje en el buzón.
Algunos RTOS admiten la función de "transmisión". Esto le permite enviar mensajes a todas las tareas que están actualmente en pausa mientras lee un buzón específico.
Algunos RTOS no admiten buzones en absoluto. En cambio, se recomienda que use una cola de un solo elemento. Esto es funcionalmente equivalente, pero conlleva una sobrecarga adicional para la memoria y el tiempo de ejecución.
Colas
Las colas son objetos de kernel independientes que proporcionan un mecanismo para enviar mensajes. Son un poco más flexibles y complejos que los buzones. El tamaño del mensaje depende de la implementación, pero generalmente es fijo y está orientado a la palabra / puntero.
Cualquier tarea puede enviar mensajes a la cola, y esto puede repetirse hasta que la cola esté llena, después de lo cual cualquier intento de envío dará como resultado un error. La longitud de la cola generalmente la determina el usuario al crear o configurar el sistema. En muchos RTOS, puede aplicar un mecanismo de bloqueo a la cola. Es decir, si la cola está llena, la tarea puede suspenderse hasta que otra tarea lea el mensaje en la cola. Cualquier tarea puede leer mensajes de la cola. Los mensajes se leen en el mismo orden en que se enviaron (Primero en entrar - Primero en salir, FIFO). Si una tarea intenta leer desde una cola vacía, recibirá una respuesta de error. En muchos RTOS, se puede usar un mecanismo de bloqueo para leer desde una cola vacía. Es decir, si la cola está vacía, la tarea puede suspenderse hasta que otra tarea envíe el mensaje a la cola.
Lo más probable es que haya un mecanismo en el RTOS para enviar un mensaje al frente de la cola, esto se llama interferencia. Algunos RTOS también admiten la función de transmisión. Esto le permite enviar mensajes a todas las tareas pausadas mientras lee la cola.
Además, el RTOS puede admitir el envío y la lectura de mensajes de longitud variable. Esto proporciona más flexibilidad, pero conlleva una sobrecarga adicional.
Muchos RTOS admiten otro tipo de objeto de núcleo, las "tuberías". En esencia, un canal es similar a una cola, pero procesa datos orientados a bytes.
La funcionalidad de las colas no es de interés, pero debe entenderse que tienen más sobrecarga de memoria y tiempo de ejecución que los buzones, principalmente porque es necesario guardar dos punteros: el comienzo y el final de la cola.
Mutexes
Los mutexes (semáforos mutuamente excluyentes) son objetos del núcleo independientes que se comportan de manera muy parecida a los semáforos binarios regulares. Son un poco más complicados que los semáforos e incluyen el concepto de propiedad temporal (un recurso para el cual se controla el acceso). Si una tarea asigna un mutex, solo la misma tarea puede liberarlo nuevamente: el mutex (y, por lo tanto, el recurso) pertenece temporalmente a la tarea.
Todos los RTOS no proporcionan mutexes, pero el semáforo binario regular es bastante fácil de adaptar. Debe escribir una función de obtención de mutex que asigne un semáforo y asigne un identificador de tarea. Luego, la función adicional "liberación de mutex" verifica el identificador de la tarea de llamada y libera el semáforo solo si coincide con el valor almacenado, de lo contrario devolverá un error.
Cuando trabajamos en nuestro propio sistema operativo OSRV MAX en tiempo real (artículos publicados anteriormente sobre él), nuestro equipo "encontró" el blog de Colin Walls, un experto en microelectrónica y firmware en Mentor Graphics. Los artículos parecían interesantes, los tradujeron por sí mismos, pero para no "escribir en la mesa" decidieron publicar. Espero que también te sean útiles. Si es así, entonces planeamos publicar todos los artículos traducidos en la serie.
Sobre el autor: Colin Walls ha trabajado en la industria electrónica durante más de treinta años, dedicando la mayor parte de su tiempo al firmware. Ahora es ingeniero de firmware en Mentor Embedded (una división de Mentor Graphics). Colin Walls a menudo habla en conferencias y seminarios, autor de numerosos artículos técnicos y dos libros sobre firmware. Vive en el Reino Unido. Blog profesional de Colin: blogs.mentor.com/colinwalls , correo electrónico: colin_walls@mentor.comLea los artículos
primero, segundo, tercero y cuarto publicados anteriormente.