Toda a verdade sobre o RTOS. Artigo 22. Caixas de correio: serviços auxiliares e estruturas de dados



Este artigo continua a revisão de caixas de correio iniciadas no artigo anterior da série "A verdade sobre o RTOS".

Artigos anteriores da série:

Artigo 21. Caixas de correio: Introdução e serviços básicos
Artigo 20. Semáforos: Serviços Auxiliares e Estruturas de Dados
Artigo 19. Semáforos: introdução e serviços básicos
Artigo # 18 Grupos de Sinalizadores de Eventos: Serviços Auxiliares e Estruturas de Dados
Artigo 17. Grupos de Sinalizadores de Eventos: Introdução e Serviços Básicos
Artigo 16. Signals
Artigo 15. Partições de memória: serviços e estruturas de dados
Artigo 14. Seções de memória: introdução e serviços básicos
Artigo 13. Estruturas de dados da tarefa e chamadas de API não suportadas
Artigo 12. Serviços para trabalhar com tarefas
Artigo 11. Tarefas: configuração e introdução à API
Artigo 10. Agendador: recursos avançados e preservação de contexto
Artigo 9. Agendador: implementação
Artigo 8. Núcleo SE: Projeto Interno e Implantação
Artigo # 7 Núcleo SE: Introdução
Artigo 6. Outros serviços RTOS
Artigo 5. Interação e sincronização de tarefas
Artigo 4. Tarefas, alternância de contexto e interrupções
Artigo # 3 Tarefas e planejamento
Artigo 2. RTOS: estrutura e modo em tempo real
Artigo 1. RTOS: introdução.


Serviços Auxiliares de Caixa de Correio


O Nucleus RTOS possui quatro chamadas de API que fornecem funções auxiliares relacionadas a caixas de correio: descarregar uma caixa de correio, recuperar informações sobre uma caixa de correio, recuperar o número de caixas de correio em um aplicativo e recuperar ponteiros para todas as caixas de correio em um aplicativo. Os três primeiros desses recursos são implementados no Nucleus SE.

Redefinição de caixa de correio


Essa chamada de serviço da API redefine a caixa de correio para seu estado inicial não utilizado. A mensagem armazenada na caixa postal será perdida. Todas as tarefas suspensas na caixa de correio serão retomadas com o código de retorno NUSE_MAILBOX_WAS_RESET .

Ligue para redefinir uma caixa de correio no Nucleus RTOS
Protótipo de chamada de serviço:
STATUS NU_Reset_Mailbox (NU_MAILBOX * caixa de correio);

Parâmetros:
caixa de correio - um ponteiro para a unidade de controle da caixa de correio.

Valor de retorno:
NU_SUCCESS - a chamada foi concluída com sucesso;
NU_INVALID_MAILBOX - ponteiro de caixa de correio inválido.

Ligue para redefinir uma caixa de correio no Nucleus SE
Esta chamada de serviço da API suporta a funcionalidade principal da API Nucleus RTOS.

Protótipo de chamada de serviço:
STATUS NUSE_Mailbox_Reset (caixa de correio NUSE_MAILBOX);

Parâmetros:
caixa de correio - índice (ID) da caixa de correio despejada.

Valor de retorno:
NUSE_SUCCESS - a chamada foi concluída com sucesso;
NUSE_INVALID_MAILBOX - índice de caixa de correio inválido.

Implementar redefinição de caixa de correio no Nucleus SE
A variante do código de função NUSE_Mailbox_Reset (após verificar os parâmetros) é selecionada usando a compilação condicional, dependendo se o suporte para tarefas de bloqueio (pausa) está ativado ou não. Vamos considerar essas duas opções.

Se o bloqueio não estiver ativado, o código para esta função da API é bastante simples. A caixa de correio é marcada como não utilizada, definindo o parâmetro NUSE_Mailbox_Status [] como FALSE .

Se o bloqueio estiver ativado, o código se tornará mais complexo:

while (NUSE_Mailbox_Blocking_Count[mailbox] != 0) { U8 index; /* check whether any tasks are blocked */ /* on this 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_MAILBOX_WAS_RESET; NUSE_Task_Status[index] = NUSE_READY; break; } } NUSE_Mailbox_Blocking_Count[mailbox]--; } #if NUSE_SCHEDULER_TYPE == NUSE_PRIORITY_SCHEDULER NUSE_Reschedule(NUSE_NO_TASK); #endif 

A caixa de correio é redefinida para o estado "Vazio".

Cada tarefa suspensa na caixa de correio recebe o status "Pronto" com o código de retorno NUSE_MAILBOX_WAS_RESET . Após a conclusão desse processo, se o agendador de prioridade for usado, a chamada de serviço NUSE_Reschedule () será feita , pois uma ou mais tarefas com prioridade mais alta poderão ficar prontas e aguardar a permissão da execução.

Recuperando informações da caixa de correio


Esta chamada de serviço fornece um conjunto de informações da caixa de correio. A implementação dessa chamada no Nucleus SE difere do Nucleus RTOS, pois retorna menos informações, pois a nomeação de objetos e a ordem de pausa não são suportadas e a pausa de tarefas pode ser desativada.

Chamada para informações da caixa de correio do núcleo RTOS
Essa chamada de API suporta a funcionalidade principal da API do Nucleus RTOS.

Protótipo de chamada de serviço:
STATUS NU_Mailbox_Information (NU_MAILBOX * caixa de correio, CHAR * nome, OPTION * suspend_type, DATA_ELEMENT * message_present, UNSIGNED * tasks_waiting, NU_TASK ** first_task);

Parâmetros:

caixa postal - ponteiro para a unidade de controle da caixa postal;
nome - um ponteiro de 8 caracteres para o nome da caixa de correio. O byte nulo final também está incluído nesta área;
suspend_type - ponteiro para a variável na qual o tipo de suspensão da tarefa está armazenado. Pode levar os valores NU_FIFO e NU_PRIORITY ;
message_present - um ponteiro para uma variável que assumirá o valor NU_TRUE ou NU_FALSE , dependendo de a caixa de correio estar cheia ou não;
task_waiting - um ponteiro para uma variável que terá o número de tarefas suspensas nesta caixa de correio;
first_task - um ponteiro para um ponteiro de tarefa que levará um ponteiro para a primeira tarefa pausada.

Valor de retorno:

NU_SUCCESS - a chamada foi concluída com sucesso;
NU_INVALID_MAILBOX - ponteiro de caixa de correio inválido.

Ligue para obter informações da caixa de correio do Núcleo SE
Esta chamada de serviço da API suporta a funcionalidade principal da API Nucleus RTOS.

Protótipo de chamada de serviço:

STATUS NUSE_Mailbox_Information (caixa de correio NUSE_MAILBOX, U8 * message_present, U8 * task_waiting, NUSE_TASK * first_task);

Parâmetros:

caixa de correio - índice da caixa de correio sobre a qual as informações são solicitadas;
message_present - um ponteiro para uma variável que assume o valor TRUE ou FALSE , dependendo de a caixa de correio estar cheia ou não;
tasks_waiting - um ponteiro para uma variável que terá o número de tarefas suspensas nesta caixa de correio (nada será retornado se a suspensão de tarefas estiver desativada);
first_task - um ponteiro para uma variável do tipo NUSE_TASK , que aceita o índice da primeira tarefa suspensa (nada será retornado se a suspensão da tarefa estiver desativada).

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 - um ou mais parâmetros do ponteiro estão incorretos.

Implementando informações da caixa de correio no Nucleus SE

A implementação desta chamada de API é bastante simples:

 *message_present = NUSE_Mailbox_Status[mailbox]; #if NUSE_BLOCKING_ENABLE *tasks_waiting = NUSE_Mailbox_Blocking_Count[mailbox]; if (NUSE_Mailbox_Blocking_Count[mailbox] != 0) { U8 index; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_MAILBOX_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == mailbox)) { *first_task = index; break; } } } else { *first_task = 0; } #else *tasks_waiting = 0; *first_task = 0; #endif return NUSE_SUCCESS; 

A função retorna o status da caixa de correio. Em seguida, se as chamadas de serviço para bloquear tarefas estiverem ativadas, o número de tarefas suspensas e o índice da primeira serão retornados (caso contrário, esses parâmetros serão definidos como 0).

Obtendo o número de caixas de correio


Essa chamada de utilitário retorna o número de caixas de correio no aplicativo. Enquanto no Nucleus RTOS, o número deles pode mudar com o tempo e o valor de retorno mostrará o número atual de caixas de correio, no Nucleus SE, o número de caixas de correio é definido no estágio de criação e não pode ser alterado.

Chamando um contador de caixa de correio no núcleo RTOS
Essa chamada de API suporta a funcionalidade principal da API do Nucleus RTOS.

Protótipo de chamada de serviço:
UNSIGNED NU_Established_Mailboxes (VOID);

Parâmetros:
Estão ausentes.

Valor de retorno:
O número de caixas de correio criadas no aplicativo.

Chamando um contador de caixas de correio no Nucleus SE
Essa chamada de API suporta a funcionalidade principal da API do Nucleus RTOS.

Protótipo de chamada de serviço:
U8 NUSE_Mailbox_Count (nulo);

Parâmetros:
Estão ausentes.

Valor de retorno:
O número de caixas de correio configuradas no aplicativo.

Implementando um contador de caixas de correio no Nucleus SE
A implementação desta chamada de API é extremamente simples: o valor da diretiva #define NUSE_MAILBOX_NUMBER é retornado .

Estruturas de dados


As caixas de correio usam duas ou três matrizes de estruturas de dados (todas localizadas na RAM), que, como outros objetos do Nucleus SE, são um conjunto de tabelas cujo tamanho depende do número de caixas de correio configuradas e de seus parâmetros.

Eu recomendo fortemente que o código do aplicativo não use acesso direto a essas estruturas de dados, mas faça referência a elas através das funções de API fornecidas. Isso evita incompatibilidade com versões futuras do Nucleus SE e efeitos colaterais indesejados, além de simplificar a portabilidade do aplicativo para o Nucleus RTOS. A seguir, é apresentada uma visão geral detalhada das estruturas de dados para uma melhor compreensão de como o código de chamada de serviço funciona e para depuração.

Dados RAM


Esses dados têm a seguinte estrutura:

NUSE_Mailbox_Data [] - uma matriz do tipo ADDR , que possui uma entrada para cada caixa de correio configurada, armazena dados da caixa de correio.
NUSE_Mailbox_Status [] é uma matriz U8 que possui uma entrada para cada caixa de correio configurada e monitora o uso de caixas de correio. Um valor diferente de zero ( TRUE ) indica que a caixa de correio está cheia.
NUSE_Mailbox_Blocking_Count [] - uma matriz do tipo U8 , que contém um contador de tarefas bloqueadas para cada caixa de correio. Essa matriz é criada apenas se a funcionalidade de bloqueio de chamada da API estiver ativada.

Essas estruturas de dados são inicializadas com zeros na função NUSE_Init_Mailbox () quando o Nucleus SE é iniciado. Isso é lógico, pois cada caixa de correio é criada vazia (não utilizada).

A seguir, estão as definições dessas estruturas de dados do arquivo nuse_init.c .

 RAM ADDR NUSE_Mailbox_Data[NUSE_MAILBOX_NUMBER]; RAM U8 NUSE_Mailbox_Status[NUSE_MAILBOX_NUMBER]; #if NUSE_BLOCKING_ENABLE RAM U8 NUSE_Mailbox_Blocking_Count[NUSE_MAILBOX_NUMBER]; #endif 

Dados ROM


Para a implementação de caixas de correio, os dados na ROM não são usados.

A quantidade de memória para caixas de correio


Como em todos os objetos de núcleo do Nucleus SE, a quantidade de memória necessária para caixas de correio é conhecida com antecedência.

A quantidade de memória para dados na ROM de todas as caixas de correio no aplicativo é 0.

A quantidade de dados na RAM para todas as caixas de correio no aplicativo (em bytes) com chamadas de API ativadas para bloquear tarefas pode ser calculada da seguinte maneira:
NUSE_MAILBOX_NUMBER * (tamanho (ADDR) +2)

Caso contrário:
NUSE_MAILBOX_NUMBER * (tamanho (ADDR) +1)

Chamadas de API não realizadas


As quatro chamadas de utilidade que podem ser encontradas no Nucleus RTOS não são implementadas no Nucleus SE.

Criar caixa de correio


Essa chamada de serviço da API cria uma caixa de correio. O Nucleus SE não precisa, pois as caixas de correio são criadas estaticamente.

Protótipo de chamada de serviço:
STATUS NU_Create_Mailbox (NU_MAILBOX * caixa de correio, nome CHAR *, UNSIGNED OPTION suspend_type);

Parâmetros:

caixa postal - um ponteiro para a unidade de controle da caixa postal fornecida pelo usuário; Usado para gerenciar caixas de correio em outras chamadas de API
nome - ponteiro para o nome de 7 caracteres da caixa de correio com zero byte final;
suspend_type - indica o princípio de suspender uma tarefa em uma caixa de correio. Pode levar os valores NU_FIFO e NU_PRIORITY , que significam o princípio de FIFO (primeiro a entrar , primeiro a sair) ou o princípio de prioridade da suspensão de tarefas, respectivamente.

Valor de retorno:

NU_SUCCESS - a chamada foi concluída com sucesso;
NU_INVALID_MAILBOX - ponteiro nulo para a unidade de controle de caixa de correio ( NULL ) ou o ponteiro já está em uso;
NU_INVALID_SUSPEND - parâmetro suspend_type inválido.

Excluir uma caixa de correio


Essa chamada de serviço da API exclui uma caixa de correio criada anteriormente. O Núcleo SE não precisa disso, porque as caixas de correio são criadas estaticamente e não podem ser excluídas.

Protótipo de chamada de serviço:

STATUS NU_Delete_Mailbox (NU_MAILBOX * caixa de correio);

Parâmetros:

caixa de correio - um ponteiro para a unidade de controle da caixa de correio.

Valor de retorno:

NU_SUCCESS - a chamada foi concluída com sucesso;
NU_INVALID_MAILBOX - ponteiro de caixa de correio inválido.

Ponteiros de caixa de correio


Essa chamada de API compõe uma lista seqüencial de ponteiros para todas as caixas de correio no sistema. O Nucleus SE não precisa, pois as caixas de correio são identificadas por um índice simples, não por um ponteiro.

Protótipo de chamada de serviço:

UNSIGNED NU_Mailbox_Pointers (NU_MAILBOX ** pointer_list, UNSIGNED maximum_pointers);

Parâmetros:

pointer_list - ponteiro para uma matriz de ponteiros NU_MAILBOX ; essa matriz será preenchida com ponteiros para caixas de correio criadas no sistema;
maximum_pointers - o número máximo de ponteiros na matriz.

Valor de retorno:

O número de ponteiros NU_MAILBOX na matriz.

Grave uma mensagem na caixa de correio para entrega a todos os destinatários


Essa chamada de serviço envia uma mensagem para todas as tarefas que aguardam mensagens de uma caixa de correio específica. No Nucleus SE, essa chamada de utilitário não é implementada, pois adicionaria complexidade desnecessária.

Protótipo de chamada de serviço:

STATUS NU_Broadcast_To_Mailbox (NU_MAILBOX * caixa de correio, mensagem VOID *, suspensão NÃO ASSINADA);

Parâmetros:

caixa postal - ponteiro para a unidade de controle da caixa postal;
message - ponteiro para a mensagem transmitida;
suspender - indica se a tarefa de chamada deve ser suspensa se a caixa de correio já contiver uma mensagem; pode ser NU_NO_SUSPEND , NU_SUSPEND ou valor de 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 já contém uma mensagem;
NU_TIMEOUT - após o tempo limite expirar, a caixa de correio ainda está cheia;
NU_MAILBOX_DELETED - A caixa de correio foi excluída enquanto a tarefa estava suspensa.
NU_MAILBOX_RESET - a caixa de correio foi redefinida enquanto a tarefa estava suspensa.

Compatível com núcleo RTOS


Como com todos os outros objetos do Nucleus SE, meu objetivo era maximizar a compatibilidade do código do aplicativo com o Nucleus RTOS. As caixas de correio não são exceção e, do ponto de vista do usuário, são implementadas da mesma maneira que no Nucleus RTOS. Há também uma certa incompatibilidade, que eu considerei aceitável, pois, como resultado, o código se tornará mais compreensível e mais eficiente em termos da quantidade de memória necessária. Caso contrário, as chamadas da API do Nucleus RTOS podem ser portadas diretamente para o Nucleus SE.

Identificadores de objeto


No Nucleus RTOS, todos os objetos são descritos por estruturas de dados (unidades de controle) de um tipo específico. Um ponteiro para esta unidade de controle serve como identificador para a caixa de correio. Decidi que no Nucleus SE é necessária uma abordagem diferente para o uso eficiente da memória: todos os objetos do kernel são descritos por um conjunto de tabelas na RAM e / ou ROM. O tamanho dessas tabelas é determinado pelo número de objetos configurados de cada tipo. O identificador de um objeto específico é o índice nesta tabela. Por isso, defini NUSE_MAILBOX como o equivalente a U8 , uma variável (não um ponteiro) desse tipo serve como identificador da caixa de correio. Essa pequena incompatibilidade é fácil de lidar se o código for portado do Nucleus SE para o Nucleus RTOS e vice-versa. Normalmente, nenhuma operação é executada nos identificadores de objeto além de mover e armazenar.

O núcleo RTOS também suporta a nomeação de caixas de correio. Esses nomes são usados ​​apenas para depuração. Excluí-os do Nucleus SE para economizar memória.

Tamanho e tipo da mensagem


No Nucleus RTOS, uma mensagem da caixa de correio consiste em quatro palavras de 32 bits. No Núcleo SE, decidi reduzir esse valor para uma variável do tipo ADDR . Essa alteração resulta em economia significativa de memória e tempo de execução de tarefas reduzido. Também sugere que o uso usual de uma caixa de correio é encaminhar informações de uma tarefa para outra. Essa incompatibilidade não causará grandes problemas ao transportar aplicativos para o Nucleus RTOS. O Núcleo SE pode ser modificado se for necessário um formato de mensagem diferente.

Chamadas de API não realizadas


O núcleo RTOS suporta nove chamadas de escritório de caixa de correio. Destes, quatro não estão implementados no Nucleus SE. Os detalhes desses desafios, bem como as razões pelas quais eles foram excluídos do Nucleus SE, estão descritos acima.

O próximo artigo analisará as filas.

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.

Source: https://habr.com/ru/post/pt431118/


All Articles