Sistema operativo en tiempo real

Ajustar Compartir Imprimir Citar

Un sistema operativo en tiempo real (RTOS) es un sistema operativo (SO) para aplicaciones en tiempo real que procesa datos y eventos que tienen limitaciones de tiempo definidas de manera crítica. Un RTOS es distinto de un sistema operativo de tiempo compartido, como Unix, que administra el uso compartido de los recursos del sistema con un programador, búferes de datos o priorización de tareas fijas en un entorno multitarea o multiprogramación. Los requisitos de tiempo de procesamiento deben comprenderse y consolidarse por completo, en lugar de limitarse a un mínimo. Todo el procesamiento debe ocurrir dentro de las restricciones definidas. Los sistemas operativos en tiempo real están basados en eventos y son preventivos, lo que significa que el sistema operativo es capaz de monitorear la prioridad relevante de las tareas que compiten y realizar cambios en la prioridad de la tarea. Los sistemas basados en eventos cambian entre tareas según sus prioridades, mientras que los sistemas de tiempo compartido cambian la tarea según las interrupciones del reloj.

Características

Una característica clave de un RTOS es el nivel de su coherencia con respecto a la cantidad de tiempo que lleva aceptar y completar la tarea de una aplicación; la variabilidad es 'jitter'. Un 'duro' El sistema operativo en tiempo real (RTOS duro) tiene menos inestabilidad que un sistema operativo 'soft' Sistema operativo en tiempo real (soft RTOS). Una respuesta tardía es una respuesta incorrecta en un RTOS estricto, mientras que una respuesta tardía es aceptable en un RTOS flexible. El principal objetivo de diseño no es un alto rendimiento, sino más bien una garantía de una categoría de rendimiento blando o duro. Un RTOS que normalmente o generalmente puede cumplir con una fecha límite es un sistema operativo en tiempo real suave, pero si puede cumplir con una fecha límite de manera determinista, es un sistema operativo en tiempo real duro.

Un RTOS tiene un algoritmo avanzado para la programación. La flexibilidad del programador permite una orquestación más amplia del sistema informático de las prioridades del proceso, pero un sistema operativo en tiempo real se dedica con mayor frecuencia a un conjunto limitado de aplicaciones. Los factores clave en un sistema operativo en tiempo real son una latencia de interrupción mínima y una latencia de conmutación de subprocesos mínima; un sistema operativo en tiempo real se valora más por la rapidez o la previsibilidad con la que puede responder que por la cantidad de trabajo que puede realizar en un período de tiempo determinado.

Consulte la comparación de sistemas operativos en tiempo real para obtener una lista completa. Además, consulte la lista de sistemas operativos para todos los tipos de sistemas operativos.

Filosofías de diseño

Un RTOS es un sistema operativo en el que el tiempo necesario para procesar un estímulo de entrada es menor que el tiempo transcurrido hasta el siguiente estímulo de entrada del mismo tipo.

Los diseños más comunes son:

Los diseños de tiempo compartido cambian tareas con más frecuencia de lo estrictamente necesario, pero brindan una multitarea más fluida, dando la ilusión de que un proceso o usuario tiene el uso exclusivo de una máquina.

Los primeros diseños de CPU requerían muchos ciclos para cambiar tareas durante los cuales la CPU no podía hacer nada más útil. Debido a que el cambio tomaba tanto tiempo, los primeros sistemas operativos intentaron minimizar el desperdicio de tiempo de la CPU al evitar el cambio de tareas innecesarias.

Programación

En diseños típicos, una tarea tiene tres estados:

  1. Correr (ejecutar en la CPU);
  2. Listo (listo para ser ejecutado);
  3. Bloqueado (esperando un evento, I/O por ejemplo).

La mayoría de las tareas están bloqueadas o listas la mayor parte del tiempo porque, por lo general, solo se puede ejecutar una tarea a la vez por CPU. La cantidad de elementos en la cola de espera puede variar mucho, según la cantidad de tareas que el sistema necesita realizar y el tipo de programador que utiliza el sistema. En sistemas no preventivos más simples pero aún multitarea, una tarea tiene que ceder su tiempo en la CPU a otras tareas, lo que puede causar que la cola lista tenga una mayor cantidad de tareas generales en estado listo para ejecutarse (agotamiento de recursos).

Por lo general, la estructura de datos de la lista lista en el programador está diseñada para minimizar el peor de los casos de tiempo invertido en la sección crítica del programador, durante el cual se inhibe la preferencia y, en algunos casos, todos las interrupciones están deshabilitadas, pero la elección de la estructura de datos también depende del número máximo de tareas que pueden estar en la lista lista.

Si nunca hay más de unas pocas tareas en la lista de tareas listas, entonces una lista doblemente enlazada de tareas listas probablemente sea óptima. Si la lista de tareas preparadas generalmente contiene solo unas pocas tareas, pero ocasionalmente contiene más, entonces la lista debe ordenarse por prioridad. De esa manera, encontrar la tarea de mayor prioridad para ejecutar no requiere iterar a través de toda la lista. Insertar una tarea requiere recorrer la lista de tareas listas hasta llegar al final de la lista o a una tarea de menor prioridad que la tarea que se está insertando.

Se debe tener cuidado de no inhibir la preferencia durante esta búsqueda. Las secciones críticas más largas deben dividirse en partes pequeñas. Si ocurre una interrupción que prepara una tarea de alta prioridad durante la inserción de una tarea de baja prioridad, esa tarea de alta prioridad se puede insertar y ejecutar inmediatamente antes de que se inserte la tarea de baja prioridad.

El tiempo de respuesta crítico, a veces denominado tiempo de retorno, es el tiempo que se tarda en poner en cola una nueva tarea lista y restaurar el estado de ejecución de la tarea de mayor prioridad. En un RTOS bien diseñado, la preparación de una nueva tarea requerirá de 3 a 20 instrucciones por cada entrada de cola lista, y la restauración de la tarea lista de mayor prioridad requerirá de 5 a 30 instrucciones.

En sistemas más avanzados, las tareas en tiempo real comparten recursos informáticos con muchas tareas que no son en tiempo real, y la lista de listas puede ser arbitrariamente larga. En tales sistemas, una lista preparada del programador implementada como una lista enlazada sería inadecuada.

Algoritmos

Algunos algoritmos de programación RTOS comúnmente utilizados son:

Comunicación entre tareas y uso compartido de recursos

Un sistema operativo multitarea como Unix es pobre en tareas en tiempo real. El planificador otorga la prioridad más alta a los trabajos con la demanda más baja en la computadora, por lo que no hay forma de garantizar que un trabajo de tiempo crítico tendrá acceso a suficientes recursos. Los sistemas multitarea deben gestionar el intercambio de datos y recursos de hardware entre múltiples tareas. Por lo general, no es seguro que dos tareas accedan a los mismos datos específicos o recursos de hardware simultáneamente. Hay tres enfoques comunes para resolver este problema:

Enmascaramiento/deshabilitación temporal de interrupciones

Los sistemas operativos de uso general generalmente no permiten que los programas de usuario enmascaren (desactiven) las interrupciones, porque el programa de usuario podría controlar la CPU todo el tiempo que desee. Algunas CPU modernas no permiten que el código de modo de usuario deshabilite las interrupciones, ya que dicho control se considera un recurso clave del sistema operativo. Muchos sistemas integrados y RTOS, sin embargo, permiten que la aplicación se ejecute en modo kernel para una mayor eficiencia de llamadas al sistema y también para permitir que la aplicación tenga un mayor control del entorno operativo sin necesidad de la intervención del sistema operativo.

En los sistemas de un solo procesador, una aplicación que se ejecuta en modo kernel y enmascara las interrupciones es el método de sobrecarga más bajo para evitar el acceso simultáneo a un recurso compartido. Si bien las interrupciones están enmascaradas y la tarea actual no realiza una llamada al sistema operativo de bloqueo, la tarea actual tiene un uso exclusivo de la CPU ya que ninguna otra tarea o interrupción puede tomar el control, por lo que la sección crítica está protegida. Cuando la tarea sale de su sección crítica, debe desenmascarar las interrupciones; las interrupciones pendientes, si las hay, se ejecutarán. El enmascaramiento temporal de interrupciones solo debe realizarse cuando la ruta más larga a través de la sección crítica es más corta que la latencia de interrupción máxima deseada. Por lo general, este método de protección se usa solo cuando la sección crítica es solo unas pocas instrucciones y no contiene bucles. Este método es ideal para proteger registros de mapas de bits de hardware cuando los bits están controlados por diferentes tareas.

Mutexes

Cuando el recurso compartido debe reservarse sin bloquear todas las demás tareas (como esperar a que se escriba la memoria Flash), es mejor usar mecanismos también disponibles en los sistemas operativos de uso general, como un mutex y un sistema supervisado por el sistema operativo. mensajería entre procesos. Dichos mecanismos implican llamadas al sistema y, por lo general, invocan el código del despachador del sistema operativo al salir, por lo que normalmente requieren cientos de instrucciones de la CPU para ejecutarse, mientras que las interrupciones de enmascaramiento pueden requerir tan solo una instrucción en algunos procesadores.

Un mutex (no recursivo) está bloqueado o desbloqueado. Cuando una tarea ha bloqueado la exclusión mutua, todas las demás tareas deben esperar a que su propietario desbloquee la exclusión mutua: el subproceso original. Una tarea puede establecer un tiempo de espera en su espera de un mutex. Existen varios problemas bien conocidos con los diseños basados en exclusión mutua, como la inversión de prioridad y los interbloqueos.

En la inversión de prioridad, una tarea de alta prioridad espera porque una tarea de baja prioridad tiene un mutex, pero la tarea de menor prioridad no recibe tiempo de CPU para terminar su trabajo. Una solución típica es hacer que la tarea que posee un mutex 'inherit' la prioridad de la tarea de espera más alta. Pero este enfoque simple se vuelve más complejo cuando hay varios niveles de espera: la tarea A espera un mutex bloqueado por la tarea B, que espera un mutex bloqueado por la tarea C. El manejo de múltiples niveles de herencia hace que otro código se ejecute en un contexto de alta prioridad y, por lo tanto, puede provocar la inanición de subprocesos de prioridad media.

En un interbloqueo, dos o más tareas bloquean la exclusión mutua sin tiempos de espera y luego esperan para siempre la exclusión mutua de la otra tarea, creando una dependencia cíclica. El escenario de interbloqueo más simple ocurre cuando dos tareas bloquean alternativamente dos mutex, pero en el orden opuesto. El interbloqueo se evita mediante un diseño cuidadoso.

Transmisión de mensajes

El otro enfoque para compartir recursos es que las tareas envíen mensajes en un esquema organizado de paso de mensajes. En este paradigma, el recurso es administrado directamente por una sola tarea. Cuando otra tarea quiere interrogar o manipular el recurso, envía un mensaje a la tarea de gestión. Aunque su comportamiento en tiempo real es menos nítido que los sistemas de semáforos, los sistemas simples basados en mensajes evitan la mayoría de los peligros de punto muerto del protocolo y, en general, se comportan mejor que los sistemas de semáforos. Sin embargo, son posibles problemas como los de los semáforos. La inversión de prioridad puede ocurrir cuando una tarea está trabajando en un mensaje de baja prioridad e ignora un mensaje de mayor prioridad (o un mensaje que se origina indirectamente en una tarea de alta prioridad) en su cola de mensajes entrantes. Los interbloqueos de protocolo pueden ocurrir cuando dos o más tareas esperan una a la otra para enviar mensajes de respuesta.

Controladores de interrupciones y programador

Dado que un controlador de interrupciones bloquea la ejecución de la tarea de mayor prioridad y dado que los sistemas operativos en tiempo real están diseñados para mantener la latencia de subprocesos al mínimo, los controladores de interrupciones generalmente se mantienen lo más breves posible. El manejador de interrupciones difiere toda interacción con el hardware si es posible; por lo general, todo lo que se necesita es reconocer o deshabilitar la interrupción (para que no vuelva a ocurrir cuando regrese el controlador de interrupciones) y notificar a una tarea que se debe realizar un trabajo. Esto se puede hacer desbloqueando una tarea del conductor liberando un semáforo, configurando una bandera o enviando un mensaje. Un programador a menudo brinda la capacidad de desbloquear una tarea del contexto del controlador de interrupciones.

Un sistema operativo mantiene catálogos de objetos que administra, como subprocesos, mutexes, memoria, etc. Las actualizaciones de este catálogo deben ser estrictamente controladas. Por esta razón, puede ser problemático cuando un controlador de interrupciones llama a una función del sistema operativo mientras la aplicación también está haciéndolo. La función del sistema operativo llamada desde un controlador de interrupciones podría encontrar que la base de datos de objetos se encuentra en un estado inconsistente debido a la actualización de la aplicación. Hay dos enfoques principales para hacer frente a este problema: la arquitectura unificada y la arquitectura segmentada. Los RTOS que implementan la arquitectura unificada resuelven el problema simplemente deshabilitando las interrupciones mientras se actualiza el catálogo interno. La desventaja de esto es que aumenta la latencia de las interrupciones, lo que puede provocar la pérdida de interrupciones. La arquitectura segmentada no realiza llamadas directas al sistema operativo, sino que delega el trabajo relacionado con el sistema operativo a un controlador independiente. Este controlador se ejecuta con una prioridad más alta que cualquier subproceso, pero menor que los controladores de interrupción. La ventaja de esta arquitectura es que agrega muy pocos ciclos para interrumpir la latencia. Como resultado, los sistemas operativos que implementan la arquitectura segmentada son más predecibles y pueden lidiar con tasas de interrupción más altas en comparación con la arquitectura unificada.

Del mismo modo, el modo de gestión del sistema en hardware compatible con x86 puede tardar mucho tiempo antes de devolver el control al sistema operativo.

Asignación de memoria

La asignación de memoria es más crítica en un sistema operativo en tiempo real que en otros sistemas operativos.

Primero, por motivos de estabilidad no puede haber fugas de memoria (memoria que se asigna pero no se libera después de su uso). El dispositivo debería funcionar indefinidamente, sin necesidad de reiniciar. Por esta razón, la asignación de memoria dinámica está mal vista. Siempre que sea posible, toda la asignación de memoria requerida se especifica estáticamente en tiempo de compilación.

Otra razón para evitar la asignación de memoria dinámica es la fragmentación de la memoria. Con la asignación y liberación frecuentes de pequeños fragmentos de memoria, puede ocurrir una situación en la que la memoria disponible se divide en varias secciones y el RTOS es incapaz de asignar un bloque de memoria continuo lo suficientemente grande, aunque hay suficiente memoria libre. En segundo lugar, la velocidad de asignación es importante. Un esquema de asignación de memoria estándar escanea una lista vinculada de longitud indeterminada para encontrar un bloque de memoria libre adecuado, lo cual es inaceptable en un RTOS ya que la asignación de memoria tiene que ocurrir dentro de un período de tiempo determinado.

Debido a que los discos mecánicos tienen tiempos de respuesta mucho más largos e impredecibles, el intercambio a archivos de disco no se usa por las mismas razones que la asignación de RAM discutida anteriormente.

El algoritmo simple de bloques de tamaño fijo funciona bastante bien para sistemas integrados simples debido a su baja sobrecarga.