
As caixas de correio foram mencionadas em um dos artigos anteriores (nº 5). Eles são o segundo método de comunicação entre tarefas mais fácil de sinal para sinal suportado pelo Nucleus SE e fornecem uma maneira flexível e de baixo custo para transferir mensagens simples entre tarefas.
Artigos anteriores da série:
Artigo 20. Semáforos: Serviços Auxiliares e Estruturas de DadosArtigo 19. Semáforos: introdução e serviços básicosArtigo # 18 Grupos de Sinalizadores de Eventos: Serviços Auxiliares e Estruturas de DadosArtigo 17. Grupos de Sinalizadores de Eventos: Introdução e Serviços BásicosArtigo 16. SignalsArtigo 15. Partições de memória: serviços e estruturas de dadosArtigo 14. Seções de memória: introdução e serviços básicosArtigo 13. Estruturas de dados da tarefa e chamadas de API não suportadasArtigo 12. Serviços para trabalhar com tarefasArtigo 11. Tarefas: configuração e introdução à APIArtigo 10. Agendador: recursos avançados e preservação de contextoArtigo 9. Agendador: implementaçãoArtigo 8. Núcleo SE: Projeto Interno e ImplantaçãoArtigo # 7 Núcleo SE: IntroduçãoArtigo 6. Outros serviços RTOSArtigo 5. Interação e sincronização de tarefasArtigo 4. Tarefas, alternância de contexto e interrupçõesArtigo # 3 Tarefas e planejamentoArtigo 2. RTOS: estrutura e modo em tempo real
Artigo 1. RTOS: introdução.
Usando caixas de correio
No Nucleus SE, as caixas de correio são definidas durante a fase de criação. Um aplicativo pode ter até 16 caixas de correio. Se o aplicativo não tiver caixas de correio, o código de chamada de serviço e as estruturas de dados associadas às caixas de correio não serão incluídos no aplicativo.
Uma caixa de correio é apenas um local para armazenar dados, cujo tamanho é suficiente para armazenar uma variável do tipo
ADDR e o acesso seguro ao qual é controlado de tal maneira que várias tarefas podem ser usadas. Uma tarefa pode enviar dados para uma caixa de correio. Como resultado, a caixa de correio ficará cheia e nenhuma tarefa poderá enviar dados até que a tarefa execute a operação de leitura da caixa de correio ou até que ela esteja vazia. Tentar enviar dados para uma caixa de correio cheia ou tentar ler uma caixa de correio vazia resultará em erro ou suspensão de tarefas, dependendo das configurações de chamada da API selecionadas e da configuração do Nucleus SE.
Caixas de correio e filas
Em algumas implementações do sistema operacional, as caixas de correio não são implementadas, mas como alternativa, propõe-se o uso de uma fila. Isso parece lógico, pois essa fila fornecerá a mesma funcionalidade que uma caixa de correio. No entanto, a fila é uma estrutura de dados mais complexa e carrega muito mais dados auxiliares, código e possui um tempo de serviço mais longo.
No Nucleus SE, como no Nucleus RTOS, você pode selecionar qualquer um desses tipos de objetos.
Se seu aplicativo tiver várias filas e uma caixa de correio, faz sentido considerar a substituição da caixa de correio por uma fila. Isso aumentará um pouco a sobrecarga, mas eliminará todo o código da API associado às caixas de correio. Como alternativa, você pode configurar o aplicativo com os dois métodos e comparar a quantidade de dados e desempenho.
Filas serão discutidas em artigos futuros.
Configurar caixas de correio
Número de caixas de correio
Como na maioria dos objetos do Nucleus SE, a configuração da caixa de correio é definida principalmente pelas diretivas
#define no arquivo
nuse_config.h . O parâmetro principal é
NUSE_MAILBOX_NUMBER , que determina o número de caixas de correio no aplicativo. O valor padrão é zero (ou seja, não há caixas de correio) e pode levar valores até 16. Um valor incorreto levará a um erro durante a compilação, que será gerado pela verificação de
nuse_config_check.h (incluído no arquivo
nuse_config.c e compilado com ele ), que acionará a diretiva
#error .
A seleção de um valor diferente de zero é o principal ativador de caixas de correio. Este parâmetro é usado ao definir estruturas de dados e seu tamanho depende de seu valor (mais sobre isso no próximo artigo). Além disso, um valor diferente de zero ativa as configurações da API.
Ativar chamadas de API
Cada função da API (chamada de utilitário) no Nucleus SE é ativada pela diretiva
#define no arquivo
nuse_config.h . Para caixas de correio, essas diretivas são:
NUSE_MAILBOX_SEND NUSE_MAILBOX_RECEIVE NUSE_MAILBOX_RESET NUSE_MAILBOX_INFORMATION NUSE_MAILBOX_COUNT
Por padrão, eles são definidos como
FALSE , desativando todas as chamadas de serviço e bloqueando a inclusão do código que as implementa. Para configurar caixas de correio no aplicativo, você precisa selecionar as chamadas de API necessárias e defini-las como
TRUE .
A seguir, uma seção do código do arquivo
nuse_config.h .
/* Number of mailboxes in the system - 0-16 */ #define NUSE_MAILBOX_NUMBER 0 /* Service call enablers: */ #define NUSE_MAILBOX_SEND FALSE #define NUSE_MAILBOX_RECEIVE FALSE #define NUSE_MAILBOX_RESET FALSE #define NUSE_MAILBOX_INFORMATION FALSE #define NUSE_MAILBOX_COUNT FALSE
Se a função API da caixa de correio estiver ativada e não houver caixas de correio no aplicativo (exceto
NUSE_Mailbox_Count () , que sempre está ativado), ocorrerá um erro de compilação. Se o seu código usar uma chamada de API que não foi ativada, ocorrerá um erro de layout porque o código de implementação não foi incluído no aplicativo.
Chamadas de serviço de caixa de correio
O núcleo RTOS suporta nove chamadas de serviço associadas a caixas de correio e fornece a seguinte funcionalidade:
- Enviando mensagens para a caixa de correio. O núcleo SE é implementado na função NUSE_Mailbox_Send () .
- Lendo uma mensagem de uma caixa de correio. O Núcleo SE implementa a função NUSE_Mailbox_Receive () .
- Restaurando uma caixa de correio para um estado não utilizado com a liberação de todas as tarefas suspensas (redefinição). No Nucleus SE, implementado em NUSE_Mailbox_Reset () .
- Fornecendo informações sobre uma caixa de correio específica. O Núcleo SE é implementado em NUSE_Mailbox_Information () .
- Retorna o número de caixas de correio atualmente configuradas no aplicativo. No Nucleus SE, implementado em NUSE_Mailbox_Count () .
- Adicionando uma nova caixa de correio (criação). O núcleo SE não está implementado.
- Exclua uma caixa de correio. O núcleo SE não está implementado.
- Retorne os ponteiros para todas as caixas de correio no aplicativo. O núcleo SE não está implementado.
- Enviando uma mensagem para todas as tarefas suspensas na caixa de correio (transmissão). O núcleo SE não está implementado.
Considere em detalhes a implementação de cada chamada de serviço.
Chamadas de serviço de leitura e gravação de caixa de correio
As operações básicas que podem ser executadas nas caixas de correio são a gravação e a leitura de dados (envio e recebimento). O Nucleus RTOS e o Nucleus SE fornecem duas chamadas de API básicas para essas operações, que serão descritas abaixo.
Gravar na caixa postal
Chamar a API do Nucleus RTOS para gravar na caixa de correio é muito flexível, o que permite pausar a tarefa implicitamente ou com um tempo limite se a operação não puder ser concluída imediatamente (por exemplo, quando você tenta gravar em uma caixa de correio cheia). O Nucleus SE fornece uma chamada de serviço semelhante, apenas a pausa da tarefa é opcional e o tempo limite não é implementado.
O Nucleus RTOS também oferece uma chamada de serviço para transmissão de dados para uma caixa de correio; essa chamada não é suportada no Nucleus SE e será descrita na seção "Chamadas de API não realizadas" no próximo artigo.
Chamada para gravar em uma caixa de correio no Nucleus RTOSProtótipo de chamada de serviço:
STATUS NU_Send_To_Mailbox (NU_MAILBOX * caixa de correio, mensagem VOID *, suspensão não assinada);Parâmetros:
caixa de correio - ponteiro para a caixa de correio;
message - um ponteiro para a mensagem a ser enviada, consistindo em quatro elementos do tipo
não assinado ;
suspender - a especificação da suspensão da tarefa pode levar os valores
NU_NO_SUSPEND ,
NU_SUSPEND ou o valor do tempo limite.
Valor de retorno:
NU_SUCCESS - a chamada foi concluída com sucesso;
NU_INVALID_MAILBOX - ponteiro de caixa de correio inválido;
NU_INVALID_POINTER - ponteiro nulo para uma mensagem (
NULL );
NU_INVALID_SUSPEND - tente suspender de um thread não relacionado à tarefa;
NU_MAILBOX_FULL - a caixa de correio está cheia, mas o tipo de suspensão não está especificado;
NU_TIMEOUT - a caixa de correio ainda está cheia, mesmo após a suspensão pelo período especificado;
NU_MAILBOX_DELETED - a caixa de correio foi excluída enquanto a tarefa estava suspensa;
NU_MAILBOX_WAS_RESET - a caixa de correio foi redefinida enquanto a tarefa estava suspensa.
Ligue para escrever em uma caixa de correio no Nucleus SEEssa chamada de API suporta a funcionalidade principal da API do Nucleus RTOS.
Protótipo de chamada de serviço:
STATUS NUSE_Mailbox_Send (caixa de correio NUSE_MAILBOX, mensagem ADDR *, suspensão U8);Parâmetros:
caixa de correio - índice da
caixa de correio (ID);
message - um ponteiro para a mensagem a ser enviada, é uma variável do tipo
ADDR ;
suspender - a especificação da suspensão da tarefa pode assumir os valores
NUSE_NO_SUSPEND ou
NUSE_SUSPEND .
Valor de retorno:
NUSE_SUCCESS - a chamada foi concluída com sucesso;
NUSE_INVALID_MAILBOX - índice de caixa de correio inválido;
NUSE_INVALID_POINTER - ponteiro nulo para uma mensagem (
NULL );
NUSE_INVALID_SUSPEND - uma tentativa de suspender de um thread não relacionado ou quando a funcionalidade de bloqueio de chamada da API está desativada;
NUSE_MAILBOX_FULL - a caixa de correio está cheia, mas o tipo de suspensão não está especificado;
NUSE_MAILBOX_WAS_RESET - a caixa de correio foi redefinida enquanto a tarefa estava suspensa.
Implementando entradas de caixa de correio no Nucleus SEA versão do código de função da API
NUSE_Mailbox_Send () (após verificar os parâmetros) é selecionada usando a compilação condicional, dependendo se o suporte a chamadas da API está ativado para bloquear (suspender tarefas) ou não. Considere as duas opções.
Se o bloqueio de tarefas estiver desativado, a lógica desta chamada de API é bastante simples e o código não exige explicações:
if (NUSE_Mailbox_Status[mailbox]) /* mailbox full */ { return_value = NUSE_MAILBOX_FULL; } else /* mailbox empty */ { NUSE_Mailbox_Data[mailbox] = *message; NUSE_Mailbox_Status[mailbox] = TRUE; return_value = NUSE_SUCCESS; }
A mensagem é armazenada no elemento correspondente
NUSE_Mailbox_Data [] e a caixa de correio é marcada como usada.
Se o bloqueio de tarefas estiver ativado, o código se tornará mais complexo:
do { if (!NUSE_Mailbox_Status[mailbox]) /* mailbox empty */ { NUSE_Mailbox_Data[mailbox] = *message; NUSE_Mailbox_Status[mailbox] = TRUE; if (NUSE_Mailbox_Blocking_Count[mailbox] != 0) { U8 index; /* check whether a task is blocked */ /* on this mailbox */ NUSE_Mailbox_Blocking_Count[mailbox]--; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_MAILBOX_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == mailbox)) { NUSE_Task_Blocking_Return[index] = NUSE_SUCCESS; NUSE_Wake_Task(index); break; } } } return_value = NUSE_SUCCESS; suspend = NUSE_NO_SUSPEND; } else /* mailbox full */ { if (suspend == NUSE_NO_SUSPEND) { return_value = NUSE_MAILBOX_FULL; } else { /* block task */ NUSE_Mailbox_Blocking_Count[mailbox]++; NUSE_Suspend_Task(NUSE_Task_Active, (mailbox << 4) | NUSE_MAILBOX_SUSPEND); return_value = NUSE_Task_Blocking_Return[NUSE_Task_Active]; if (return_value != NUSE_SUCCESS) { suspend = NUSE_NO_SUSPEND; } } } } while (suspend == NUSE_SUSPEND);
Algumas explicações podem ser úteis.
O código é colocado em um
loop do ... while , que é executado enquanto o parâmetro
suspend é
NUSE_SUSPEND .
Se a caixa de correio estiver vazia, a mensagem será gravada e o status da caixa de correio mudará para indicar que está cheia. Verifica se há tarefas em pausa (que estão aguardando para serem lidas) nesta caixa de correio. Se houver tais tarefas, a primeira delas será retomada. A variável de suspensão está definida como
NUSE_NO_SUSPEND e a chamada da API termina e retorna
NUSE_SUCCESS .
Se a caixa de correio estiver cheia e a suspensão for
NUSE_NO_SUSPEND , a chamada da API retornará
NUSE_MAILBOX_FULL . Se a suspensão for
NUSE_SUSPEND , a tarefa será interrompida. Após o término da função (por exemplo, quando a tarefa é retomada), se o valor de retorno for
NUSE_SUCCESS (que indica que a tarefa foi retomada porque a mensagem foi lida e não que a caixa de correio foi redefinida), o ciclo começa do início.
Ler caixa de correio
A chamada do utilitário Nucleus RTOS API para ler uma caixa de correio é muito flexível e permite pausar tarefas implicitamente ou com um tempo limite se a operação não puder ser concluída imediatamente (por exemplo, ao ler em uma caixa de correio vazia). O Nucleus SE fornece um serviço semelhante, apenas a suspensão da tarefa é opcional e o tempo limite não é implementado.
Ligue para ler uma caixa de correio no Nucleus RTOSProtótipo de chamada de serviço:
STATUS NU_Receive_From_Mailbox (NU_MAILBOX * caixa de correio, mensagem VOID *, suspensão NÃO ASSINADA);Parâmetros:
caixa postal - um ponteiro para a unidade de controle da caixa postal fornecida pelo usuário;
message - um ponteiro para o armazenamento da mensagem recebida, que tem um tamanho igual a quatro variáveis do tipo
não assinado ;
suspender - a especificação de suspensão da tarefa, pode assumir os valores
NUSE_NO_SUSPEND ,
NUSE_SUSPEND ou o tempo limite.
Valor de retorno:
NU_SUCCESS - a chamada foi concluída com sucesso;
NU_INVALID_MAILBOX - ponteiro de caixa de correio inválido;
NU_INVALID_POINTER - ponteiro nulo para uma mensagem (
NULL );
NU_INVALID_SUSPEND - tente suspender de um fluxo incorreto (não associado à tarefa do fluxo);
NU_MAILBOX_EMPTY - a caixa de correio está vazia e o tipo de suspensão não foi especificado;
NU_TIMEOUT - a caixa de correio ainda está vazia, mesmo após a tarefa ser pausada pelo valor de tempo limite especificado;
NU_MAILBOX_DELETED - a caixa de correio foi excluída enquanto a tarefa estava suspensa;
NU_MAILBOX_WAS_RESET - a caixa de correio foi redefinida enquanto a tarefa estava suspensa.
Ligue para ler uma caixa de correio no Nucleus SEEssa chamada de API suporta a funcionalidade principal da API do Nucleus RTOS.
Protótipo de chamada de serviço:
STATUS NUSE_Mailbox_Receive (caixa de correio NUSE_MAILBOX, mensagem ADDR *, suspensão U8);Parâmetros:
caixa de correio - índice da
caixa de correio (ID);
mensagem - um ponteiro para o armazenamento da mensagem recebida, é uma variável do tipo
ADDR ;
suspender - a especificação da suspensão da tarefa pode assumir os valores
NUSE_NO_SUSPEND ou
NUSE_SUSPEND .
Valor de retorno:
NUSE_SUCCESS - a chamada foi concluída com sucesso;
NUSE_INVALID_MAILBOX - índice de caixa de correio inválido;
NUSE_INDALID_POINTER - ponteiro nulo para uma mensagem (
NULL );
NUSE_INVALID_SUSPEND - uma tentativa de suspender de um fluxo incorreto ou quando as chamadas de API são desativadas para bloquear tarefas;
NUSE_MAILBOX_EMPTY - a caixa de correio está vazia e o tipo de suspensão de tarefa não está especificado;
NUSE_MAILBOX_WAS_RESET - a caixa de correio foi redefinida enquanto a tarefa estava suspensa.
Implementando o Mailbox Reader no Nucleus SEA versão do código de função da API
NUSE_Mailbox_Receive () (após verificar os parâmetros) é selecionada usando a compilação condicional, dependendo se as chamadas da API estão ativadas para bloquear (suspender tarefas) ou não. Vamos considerar as duas opções.
Se o bloqueio de tarefas estiver desativado, a lógica desta chamada de API é bastante simples e o código não exige explicações:
if (!NUSE_Mailbox_Status[mailbox]) /* mailbox empty */ { return_value = NUSE_MAILBOX_EMPTY; } else { /* mailbox full */ *message = NUSE_Mailbox_Data[mailbox]; NUSE_Mailbox_Status[mailbox] = FALSE; return_value = NUSE_SUCCESS; }
A mensagem é lida no elemento
NUSE_Mailbox_Data [] correspondente e a caixa de correio é marcada como vazia.
Se o bloqueio de tarefas estiver ativado, o código se tornará mais complexo:
do { if (NUSE_Mailbox_Status[mailbox]) /* mailbox full */ { *message = NUSE_Mailbox_Data[mailbox]; NUSE_Mailbox_Status[mailbox] = FALSE; if (NUSE_Mailbox_Blocking_Count[mailbox] != 0) { U8 index; /* check whether a task is blocked */ /* on this mailbox */ NUSE_Mailbox_Blocking_Count[mailbox]--; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_MAILBOX_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == mailbox)) { NUSE_Task_Blocking_Return[index] = NUSE_SUCCESS; NUSE_Wake_Task(index); break; } } } return_value = NUSE_SUCCESS; suspend = NUSE_NO_SUSPEND; } else /* mailbox empty */ { if (suspend == NUSE_NO_SUSPEND) { return_value = NUSE_MAILBOX_EMPTY; } else { /* block task */ NUSE_Mailbox_Blocking_Count[mailbox]++; NUSE_Suspend_Task(NUSE_Task_Active, (mailbox << 4) | NUSE_MAILBOX_SUSPEND); return_value = NUSE_Task_Blocking_Return[NUSE_Task_Active]; if (return_value != NUSE_SUCCESS) { suspend = NUSE_NO_SUSPEND; } } } } while (suspend == NUSE_SUSPEND);
Alguns esclarecimentos podem ser úteis.
O código é colocado em um
loop do ... while , que é executado enquanto o parâmetro
suspend é
NUSE_SUSPEND .
Se a caixa de correio estiver cheia, a mensagem armazenada será retornada e o status da caixa de correio será alterado para indicar que está vazia. Verifica tarefas em pausa (que estão aguardando gravação) nesta caixa de correio. Se houver tais tarefas, a primeira delas será retomada. A variável de suspensão está definida como
NUSE_NO_SUSPEND e a chamada da API termina e retorna
NUSE_SUCCESS .
Se a caixa de correio estiver vazia e o parâmetro de
suspensão for
NUSE_NO_SUSPEND , a chamada da API retornará
NUSE_MAILBOX_EMPTY . Se a suspensão for
NUSE_SUSPEND , a tarefa será interrompida. Se, no final da chamada, o valor de retorno for
NUSE_SUCCESS , o que indica que a tarefa foi retomada porque a mensagem foi enviada (e não que a caixa de correio foi redefinida), o ciclo começa do início.
O artigo a seguir analisará chamadas de API adicionais associadas a caixas de correio, bem como as estruturas de dados correspondentes.
Sobre o autor: Colin Walls trabalha na indústria eletrônica há mais de trinta anos, dedicando a maior parte de seu tempo ao firmware. Ele agora é engenheiro de firmware na Mentor Embedded (uma divisão da Mentor Graphics). Colin Walls frequentemente fala em conferências e seminários, autor de vários artigos técnicos e dois livros sobre firmware. Vive no Reino Unido.
Blog profissional
de Colin , e-mail: colin_walls@mentor.com.