Introdução aos Sistemas Operacionais de Tempo Real (RTOS)
Um RTOS (Real-Time Operating System) é um sistema operacional projetado para garantir que tarefas sejam executadas dentro de prazos definidos. Ele é amplamente utilizado em sistemas embarcados, automação industrial, dispositivos médicos e aplicações críticas como controle de robôs e veículos autônomos.
Entre os RTOS mais utilizados estão:
- FreeRTOS – leve, popular em microcontroladores
- VxWorks – usado em aplicações aeroespaciais
- RTEMS – código aberto, usado em missões da NASA
- Zephyr – voltado para IoT, da Linux Foundation
void vTarefaExemplo(void *pvParameters) {
for(;;) {
printf("Executando tarefa!\n");
vTaskDelay(pdMS_TO_TICKS(1000)); // espera 1 segundo
}
}
Ao contrário de sistemas tradicionais como GNU/Linux, um RTOS prioriza previsibilidade e baixa latência em vez de throughput. O foco é garantir que cada tarefa crítica seja concluída dentro do seu deadline.
Características de um Sistema em Tempo Real
Um sistema operacional de tempo real (RTOS) se diferencia por sua capacidade de responder a eventos externos dentro de prazos específicos. A previsibilidade é mais importante do que o desempenho bruto.
🎯 Características principais
- Determinismo: resposta previsível aos estímulos.
- Baixa latência: tempo mínimo entre a solicitação e a execução da tarefa.
- Preempção: tarefas podem ser interrompidas por tarefas mais prioritárias.
- Prioridades: cada tarefa possui uma prioridade relativa definida pelo desenvolvedor.
- Uso mínimo de recursos: ideal para dispositivos embarcados com pouca memória e CPU limitada.
- Clock e temporização precisa: uso intensivo de timers e delays precisos.
// Delay em um RTOS (preciso)
vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms com precisão
// Delay em um sistema convencional (pode variar)
sleep(0.5); // não garantido em sistemas multitarefa
💡 Exemplo prático: sensor com deadline
Imagine um sistema que lê a temperatura de um motor a cada 100 ms. Se o RTOS atrasar essa leitura, pode causar superaquecimento ou falhas no controle. O sistema precisa garantir que:
- A tarefa do sensor execute exatamente a cada 100 ms
- Outras tarefas não interfiram nessa execução
Esse controle refinado só é possível com o uso de um sistema operacional de tempo real. A previsibilidade é o elemento chave que torna o RTOS essencial para aplicações críticas.
Tipos de RTOS: Hard vs Soft Real-Time
Nem todos os sistemas de tempo real possuem os mesmos requisitos de precisão. Os RTOS podem ser classificados em duas grandes categorias, com base na severidade de seus prazos (deadlines):
🟥 Hard Real-Time (Tempo Real Rígido)
- O cumprimento do deadline é absolutamente obrigatório.
- Qualquer atraso pode resultar em falha catastrófica ou danos físicos.
- Exemplos: controle de airbag, sistemas cirúrgicos robóticos, controle de usinas nucleares.
void vTarefaAirbag(void *pvParameters) {
for (;;) {
if (sensorColisaoDetectado()) {
acionarAirbag();
}
vTaskDelay(pdMS_TO_TICKS(10)); // leitura a cada 10 ms
}
}
🟡 Soft Real-Time (Tempo Real Flexível)
- O deadline deve ser cumprido, mas atrasos ocasionais são toleráveis.
- O desempenho pode ser afetado, mas não compromete a segurança ou funcionalidade crítica.
- Exemplos: streaming de vídeo, navegação GPS, jogos online com latência aceitável.
void vTarefaDisplayGPS(void *pvParameters) {
for (;;) {
atualizarTelaComLocalizacao();
vTaskDelay(pdMS_TO_TICKS(500)); // atraso pode ser tolerado
}
}
📌 Observação importante:
Em um Hard RTOS, o sistema é projetado para garantir que tarefas críticas nunca percam o prazo, mesmo que isso exija sacrificar tarefas menos prioritárias. Já um Soft RTOS busca melhor desempenho geral com menor prioridade aos prazos rígidos.
Escalonamento em Sistemas em Tempo Real
O escalonamento é o mecanismo que determina qual tarefa será executada a cada instante. Em RTOS, o escalonador é projetado para garantir que tarefas críticas sejam executadas dentro de seus prazos.
📌 Conceitos-chave
- Prioridade fixa: cada tarefa tem uma prioridade definida em tempo de compilação.
- Preempção: tarefas de menor prioridade podem ser interrompidas por tarefas mais importantes.
- Deadline: tempo máximo para que a tarefa seja concluída.
- Periodismo: tarefas que se repetem em intervalos regulares.
📋 Algoritmos comuns
1. Rate Monotonic Scheduling (RMS)
- Prioridades baseadas na frequência de execução.
- Tarefa com menor período (mais frequente) recebe maior prioridade.
- É estático e amplamente usado em RTOS.
// Tarefa com período de 100ms = prioridade mais alta
xTaskCreate(sensorAtualiza, "Sensor", 1000, NULL, 3, NULL);
xTaskCreate(enviaDados, "Transmissão", 1000, NULL, 2, NULL);
xTaskCreate(logMemoria, "Log", 1000, NULL, 1, NULL);
2. Earliest Deadline First (EDF)
- Tarefas são reordenadas dinamicamente com base no deadline mais próximo.
- Mais eficiente que RMS em uso de CPU, mas mais complexo de implementar.
Tempo 0ms: Tarefa A (deadline 100ms) ativa
Tempo 10ms: Tarefa B (deadline 50ms) chega → preempção!
Resultado: B executa antes de A pois seu prazo é menor
📌 Escalonamento no FreeRTOS
O FreeRTOS utiliza um escalonador baseado em prioridade fixa, com suporte a preempção:
vTaskStartScheduler(); // Inicia o escalonamento
// Tarefas são selecionadas com base em prioridade numérica (maior = mais prioridade)
A escolha do algoritmo de escalonamento depende do tipo de aplicação. Sistemas críticos normalmente usam RMS por sua previsibilidade. Já sistemas mais flexíveis podem usar EDF para melhor aproveitamento da CPU.
Ilustração Comparativa: RMS vs EDF
O gráfico abaixo representa três tarefas (A, B e C) com diferentes períodos e deadlines.
Tarefas, Prioridades e Preempção
Em um RTOS, o sistema é composto por várias tarefas que competem por tempo de CPU. Cada tarefa pode ser atribuída a uma prioridade e, com preempção habilitada, o RTOS pode interromper uma tarefa em execução para iniciar outra mais urgente.
📌 Tarefa (Task)
- Uma função com um loop infinito:
for(;;)
- Criada com
xTaskCreate()
- Gerenciada pelo escalonador do RTOS
void vTarefaExemplo(void *pvParameters) {
for (;;) {
printf("Executando tarefa\n");
vTaskDelay(pdMS_TO_TICKS(1000)); // espera 1 segundo
}
}
xTaskCreate(vTarefaExemplo, "Exemplo", 1000, NULL, 1, NULL);
📈 Prioridades
- Quanto maior o número, maior a prioridade
- O RTOS sempre executa a tarefa de maior prioridade pronta para rodar
- Prioridades são atribuídas no momento da criação da tarefa
xTaskCreate(tarefaAlta, "Alta", 1000, NULL, 3, NULL);
xTaskCreate(tarefaMedia, "Media", 1000, NULL, 2, NULL);
xTaskCreate(tarefaBaixa, "Baixa", 1000, NULL, 1, NULL);
⚡ Preempção
- Se uma tarefa de prioridade mais alta ficar pronta, ela interrompe imediatamente a tarefa em execução
- Essa interrupção é chamada de preempção
- O FreeRTOS deve estar configurado com
configUSE_PREEMPTION 1
Tempo 0 ms → Tarefa Baixa executa
Tempo 50 ms → Tarefa Alta torna-se pronta
Resultado: Tarefa Alta preempte imediatamente a Tarefa Baixa
🧠 Boa prática
Evite usar a mesma prioridade para todas as tarefas. Isso pode gerar comportamento imprevisível ou injusto. Além disso, use vTaskDelay()
ou vTaskDelayUntil()
para liberar a CPU e evitar ocupação contínua.
Sincronização e Comunicação entre Tarefas
Em um sistema de tempo real com múltiplas tarefas, é essencial coordenar o acesso a recursos compartilhados e permitir a troca de informações entre elas. O RTOS fornece primitivas específicas para isso, como semaphores, mutexes e queues.
🔒 Semáforos
Semáforo binário serve para sinalização entre tarefas (ex: uma tarefa sinaliza que um dado está pronto).
SemaphoreHandle_t xSinal;
// Criação
xSinal = xSemaphoreCreateBinary();
// Tarefa A (produtora)
xSemaphoreGive(xSinal); // envia o sinal
// Tarefa B (consumidora)
xSemaphoreTake(xSinal, portMAX_DELAY); // espera o sinal
🔁 Mutex (Mutual Exclusion)
Usado para proteger acesso a recursos compartilhados (ex: variável global, periférico). Permite apenas uma tarefa por vez.
SemaphoreHandle_t xMutex;
xMutex = xSemaphoreCreateMutex();
xSemaphoreTake(xMutex, portMAX_DELAY);
// Seção crítica protegida
xSemaphoreGive(xMutex);
📬 Filas (Queues)
Permitem comunicação entre tarefas enviando dados (inteiros, structs, etc.) com segurança e controle de sincronização.
QueueHandle_t xFila;
// Criar fila para 10 inteiros
xFila = xQueueCreate(10, sizeof(int));
// Tarefa A
int dado = 42;
xQueueSend(xFila, &dado, portMAX_DELAY);
// Tarefa B
int recebido;
xQueueReceive(xFila, &recebido, portMAX_DELAY);
⚠️ Problemas comuns
- Inversão de prioridade: tarefa de baixa prioridade bloqueia um recurso usado por uma de alta prioridade.
- Deadlocks: tarefas ficam esperando indefinidamente por recursos umas das outras.
- Starvation: tarefa de baixa prioridade nunca é executada devido à preempção contínua.
O uso correto dessas ferramentas garante um sistema confiável e eficiente. O FreeRTOS também oferece versões com herança de prioridade para prevenir inversões.
Exemplos Práticos com FreeRTOS
A seguir, veja exemplos reais do uso do FreeRTOS em sistemas embarcados. Os códigos estão em linguagem C, e funcionam em plataformas como ESP32, STM32 ou simuladores de RTOS.
📊 Exemplo 1: Leitura de sensor periódica
Tarefa que lê um sensor a cada 500 ms e envia pela UART.
void vSensorTask(void *pvParameters) {
for (;;) {
int valor = lerSensor(); // função fictícia
printf("Sensor: %d\n", valor);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
xTaskCreate(vSensorTask, "Sensor", 1000, NULL, 1, NULL);
🔀 Exemplo 2: Produtor e consumidor com fila
Uma tarefa gera dados e outra os processa usando queue.
QueueHandle_t xFila;
void vProdutor(void *pvParameters) {
int dado = 0;
for (;;) {
xQueueSend(xFila, &dado, portMAX_DELAY);
dado++;
vTaskDelay(pdMS_TO_TICKS(200));
}
}
void vConsumidor(void *pvParameters) {
int valor;
for (;;) {
xQueueReceive(xFila, &valor, portMAX_DELAY);
printf("Recebido: %d\n", valor);
}
}
// Criação
xFila = xQueueCreate(5, sizeof(int));
xTaskCreate(vProdutor, "Produtor", 1000, NULL, 2, NULL);
xTaskCreate(vConsumidor, "Consumidor", 1000, NULL, 1, NULL);
🔐 Exemplo 3: Controle de acesso com mutex
Protegendo o uso do display entre duas tarefas.
SemaphoreHandle_t xDisplayMutex;
void vTarefa1(void *pvParameters) {
for (;;) {
xSemaphoreTake(xDisplayMutex, portMAX_DELAY);
printf("Tarefa 1 exibindo\n");
xSemaphoreGive(xDisplayMutex);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
void vTarefa2(void *pvParameters) {
for (;;) {
xSemaphoreTake(xDisplayMutex, portMAX_DELAY);
printf("Tarefa 2 exibindo\n");
xSemaphoreGive(xDisplayMutex);
vTaskDelay(pdMS_TO_TICKS(700));
}
}
xDisplayMutex = xSemaphoreCreateMutex();
xTaskCreate(vTarefa1, "T1", 1000, NULL, 2, NULL);
xTaskCreate(vTarefa2, "T2", 1000, NULL, 2, NULL);
Esses exemplos demonstram o uso típico de tarefas concorrentes, comunicação com filas e exclusão mútua em sistemas embarcados com RTOS.
RTOS vs Sistemas Operacionais Convencionais
Embora ambos gerenciem tarefas, recursos e periféricos, um RTOS é otimizado para previsibilidade e precisão temporal, enquanto um Sistema Operacional Convencional (como Linux ou Windows) foca em desempenho geral, multitarefa e experiência do usuário.
📊 Comparativo lado a lado
Critério | RTOS | SO Convencional |
---|---|---|
Foco | Tempo real, precisão, controle | Desempenho geral, usabilidade |
Tempo de resposta | Determinístico e previsível | Indeterminado, pode variar |
Uso de recursos | Mínimo, ideal para microcontroladores | Elevado, requer processadores potentes |
Gerenciamento de tarefas | Com prioridade fixa ou EDF | Multitarefa com foco em justiça e interatividade |
Interface | Geralmente sem interface gráfica | GUI, suporte multimídia, drivers complexos |
Exemplos | FreeRTOS, VxWorks, Zephyr | GNU/Linux, Windows, macOS |
💡 Quando usar cada um?
- RTOS: aplicações críticas e embarcadas, onde atrasos não são aceitáveis (ex: robótica, indústria, automotivo).
- SO convencional: quando há foco em produtividade, multitarefa com interface gráfica, e os prazos não são tão críticos.
Em muitos projetos modernos, ambos coexistem: um microcontrolador com RTOS lida com tarefas em tempo real e se comunica com um sistema maior baseado em Linux.
Quiz Interativo
Teste seus conhecimentos sobre GNU/Linux:
FreeRTOS: Visão Geral e Primeiros Passos
O FreeRTOS é um dos sistemas operacionais de tempo real mais utilizados no mundo. Ele é leve, modular, de código aberto e altamente portátil, funcionando em microcontroladores de 8, 16 e 32 bits. É mantido pela AWS (Amazon Web Services) e tem uma comunidade ativa de desenvolvedores.
✅ Características principais
- Kernel leve (menos de 10 KB)
- Suporte a tarefas com prioridades e preempção
- Semáforos, mutexes, filas, timers e notificações
- Portável para dezenas de arquiteturas (ARM, RISC-V, AVR, etc.)
- Licença MIT — livre para uso comercial
⬇️ Onde baixar e documentação oficial
- Site oficial: freertos.org
- Repositório GitHub: github.com/FreeRTOS
- Tutoriais: RTOS Book e exemplos na própria pasta "FreeRTOS/Demo"
⚙️ Como instalar e começar
Via PlatformIO (ESP32/STM32/Arduino):
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
build_flags = -D FREERTOS
; FreeRTOS já está incluído no core ESP32/STM32
Manual (via CMSIS ou port próprio):
- Clone o repositório
- Inclua os diretórios
FreeRTOS/Source
eportable
no seu projeto - Configure
FreeRTOSConfig.h
- Chame
vTaskStartScheduler()
nomain()
🧪 Teste prático: tarefa com e sem RTOS
Este exemplo compara um código com delay bloqueante tradicional com outro usando vTaskDelay() para mostrar o comportamento multitarefa do RTOS.
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
delay(1000); // tudo bloqueia aqui
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
}
void vPiscaLED(void *pvParameters) {
for (;;) {
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void vSerialTask(void *pvParameters) {
for (;;) {
Serial.println("Sistema rodando...");
vTaskDelay(pdMS_TO_TICKS(500));
}
}
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
xTaskCreate(vPiscaLED, "LED", 1000, NULL, 1, NULL);
xTaskCreate(vSerialTask, "Serial", 1000, NULL, 1, NULL);
vTaskStartScheduler();
}
Nesse teste, vemos que mesmo com o LED piscando, a mensagem "Sistema rodando..." aparece no meio do intervalo — algo que seria impossível com delay()
. Esse comportamento é um dos grandes diferenciais do uso de um RTOS.
Zephyr RTOS: Visão Geral e Primeiros Passos
O Zephyr RTOS é um sistema operacional de tempo real moderno, mantido pela Linux Foundation, focado em aplicações embarcadas e IoT. Ele não é um sistema que você "instala" em seu computador como Linux ou Windows, mas sim um framework embarcado que você compila junto com sua aplicação e grava em microcontroladores.
✅ Características principais
- Kernel leve com suporte a multitarefa preemptiva
- Arquitetura modular baseada em Kconfig + CMake
- Suporte a ARM, RISC-V, x86, ARC, Xtensa e outros
- Integração com Bluetooth, Wi-Fi, USB, TCP/IP, sensores e mais
- Licença Apache 2.0 — livre para uso comercial
⬇️ Onde baixar e documentação oficial
- Site oficial: zephyrproject.org
- Documentação: docs.zephyrproject.org
- Repositório GitHub: github.com/zephyrproject-rtos/zephyr
⚙️ Como instalar o Zephyr no Linux
O Zephyr é instalado como um conjunto de bibliotecas e ferramentas para serem usadas com placas embarcadas ou em simulação.
1. Instale os pré-requisitos:
sudo apt update
sudo apt install git cmake ninja-build gperf python3-pip \
python3-setuptools python3-wheel xz-utils file make gcc g++ \
device-tree-compiler dfu-util ccache
2. Instale o West (gerenciador de projetos do Zephyr):
pip3 install west
3. Baixe o Zephyr e inicialize:
mkdir zephyrproject && cd zephyrproject
west init
west update
west zephyr-export
4. Instale as dependências Python:
pip3 install -r zephyr/scripts/requirements.txt
5. (Opcional) Baixe o Zephyr SDK completo:
Disponível em: Zephyr SDK Releases
💻 Executando um exemplo sem hardware (QEMU)
Se você não tiver uma placa física, pode simular com qemu_x86
:
# Compilar o exemplo Hello World
west build -b qemu_x86 zephyr/samples/hello_world
# Executar no simulador
west run
Você verá no terminal:
Hello World! zephyr on qemu_x86
🚀 Primeiro exemplo prático: piscar LED
#include
#include
#include
#define LED0_NODE DT_ALIAS(led0)
#define LED0 DT_GPIO_LABEL(LED0_NODE, gpios)
#define PIN DT_GPIO_PIN(LED0_NODE, gpios)
#define FLAGS DT_GPIO_FLAGS(LED0_NODE, gpios)
void main(void) {
const struct device *dev = device_get_binding(LED0);
gpio_pin_configure(dev, PIN, GPIO_OUTPUT_ACTIVE | FLAGS);
while (1) {
gpio_pin_toggle(dev, PIN);
k_msleep(1000);
}
}
🔧 Compilar e gravar (caso tenha uma placa)
west build -b nucleo_f401re zephyr/samples/basic/blinky
west flash
🧪 Multitarefa com Zephyr: criando threads
void tarefa1(void) {
while (1) {
printk("Tarefa 1 executando\n");
k_sleep(K_MSEC(500));
}
}
void tarefa2(void) {
while (1) {
printk("Tarefa 2 executando\n");
k_sleep(K_MSEC(1000));
}
}
K_THREAD_DEFINE(t1_id, 512, tarefa1, NULL, NULL, NULL, 1, 0, 0);
K_THREAD_DEFINE(t2_id, 512, tarefa2, NULL, NULL, NULL, 2, 0, 0);
O escalonador do Zephyr intercalará essas tarefas com base em suas prioridades e tempo de espera, demonstrando multitarefa real com precisão temporal.