MCU & FPGA RTOS Filas, Flags, Mutexes, Notificações e Eventos no Zephyr RTOS

Filas, Flags, Mutexes, Notificações e Eventos no Zephyr RTOS

Table of Contents

Visão Geral dos Mecanismos de Sincronização e Comunicação

1.1 Por que o Zephyr oferece múltiplos mecanismos?

Diferente de um RTOS minimalista, o Zephyr OS foi projetado para atender desde microcontroladores simples até sistemas complexos com múltiplos cores, drivers, stacks de comunicação e middleware. Por isso, ele não impõe um único modelo de comunicação entre threads, mas oferece vários mecanismos, cada um otimizado para um tipo específico de problema.

Em termos práticos, isso significa que não existe “o melhor mecanismo”, mas sim o mecanismo correto para cada cenário. Usar k_mutex onde um k_queue seria adequado gera latência desnecessária. Usar k_poll quando um k_sem resolveria o problema aumenta complexidade sem ganho real.

De forma conceitual, os mecanismos do Zephyr podem ser classificados em três grandes grupos:

  • Comunicação de dados: quando informações precisam ser transferidas entre threads
  • Sincronização: quando o objetivo é coordenar execução
  • Exclusão mútua: quando recursos compartilhados precisam ser protegidos

Nas próximas seções, vamos analisar como Queues, Flags, Mutexes, Notificações e Eventos se encaixam nesses grupos.


1.2 Threads no Zephyr: o pano de fundo necessário

Antes de falar de filas e eventos, é importante lembrar que o Zephyr adota um modelo de threads preemptivas, com prioridades explícitas. Threads de prioridade menor podem ser interrompidas por threads de prioridade maior a qualquer momento.

Isso cria dois desafios fundamentais:

  1. Compartilhamento seguro de dados
  2. Coordenação determinística entre tarefas concorrentes

Sem mecanismos adequados, surgem problemas clássicos:

  • Race conditions
  • Deadlocks
  • Priority inversion
  • Leitura de dados inconsistentes

Os objetos do kernel (k_*) existem exatamente para resolver esses problemas.


1.3 Introdução às Queues no Zephyr

As Queues no Zephyr são usadas quando dados precisam ser transferidos entre contextos, tipicamente entre threads produtoras e consumidoras. Diferente de simples variáveis globais, as queues:

  • Garantem acesso thread-safe
  • Permitem bloqueio automático
  • São integradas ao escalonador do kernel

O Zephyr oferece múltiplos tipos de fila. A mais usada conceitualmente é a k_queue, que trabalha com ponteiros genéricos (void *), permitindo grande flexibilidade.


1.4 Estrutura básica de uma k_queue

Uma fila no Zephyr é declarada estaticamente ou dinamicamente. O caso mais comum em sistemas embarcados é a declaração estática, que evita alocação dinâmica em tempo de execução.

#include <zephyr/kernel.h>

K_QUEUE_DEFINE(minha_fila);

Essa macro cria:

  • A estrutura de controle da fila
  • A sincronização interna
  • A integração com o escalonador

Não há necessidade de inicialização manual.


1.5 Enviando dados para a fila (Producer)

Um produtor insere elementos na fila usando k_queue_append() ou k_queue_prepend().

Exemplo simples: uma thread que produz mensagens.

struct mensagem {
    uint32_t id;
    uint32_t valor;
};

void produtor_thread(void)
{
    static struct mensagem msg;

    while (1) {
        msg.id++;
        msg.valor = k_uptime_get_32();

        k_queue_append(&minha_fila, &msg);

        k_sleep(K_MSEC(500));
    }
}

⚠️ Ponto crítico:
A fila armazena ponteiros, não cópias.
Isso significa que:

  • O objeto precisa existir enquanto estiver na fila
  • Variáveis automáticas de pilha não devem ser usadas

Esse detalhe é uma das principais fontes de bugs para quem vem de FreeRTOS (onde queues normalmente copiam dados).


1.6 Recebendo dados da fila (Consumer)

O consumidor usa k_queue_get(), podendo:

  • Bloquear indefinidamente
  • Bloquear por tempo definido
  • Não bloquear
void consumidor_thread(void)
{
    struct mensagem *msg;

    while (1) {
        msg = k_queue_get(&minha_fila, K_FOREVER);

        printk("Recebido: id=%d valor=%d\n",
               msg->id, msg->valor);
    }
}

Aqui o kernel:

  • Coloca a thread em estado bloqueado
  • Acorda automaticamente quando um item chega
  • Garante que apenas um consumidor receba cada item

1.7 Quando usar k_queue (e quando não usar)

Use Queues quando:

  • Dados precisam ser transferidos entre threads
  • Existe um padrão produtor–consumidor
  • O volume de dados não é trivial
  • A ordem de processamento importa

Evite k_queue quando:

  • Apenas um flag é necessário
  • Não há dados associados ao evento
  • Latência mínima é crítica (flags são mais rápidas)

Nesses casos, Flags e Eventos são mais apropriados — tema da próxima seção.


1.8 Erros comuns no uso de Queues no Zephyr

Alguns erros aparecem com frequência em projetos reais:

  1. Inserir ponteiros para variáveis locais
  2. Reutilizar a mesma estrutura sem controle
  3. Usar fila quando um simples evento resolveria
  4. Esquecer que múltiplos consumidores competem pelo mesmo item

Esses erros não geram warnings de compilação, mas resultam em comportamento não determinístico, especialmente sob carga.


0 0 votos
Classificação do artigo
Inscrever-se
Notificar de
guest
0 Comentários
mais antigos
mais recentes Mais votado
Feedbacks embutidos
Ver todos os comentários

Related Post

0
Adoraria saber sua opinião, comente.x