Toda a verdade sobre o RTOS. Artigo 13. Estruturas de dados da tarefa e chamadas de API não suportadas



Neste terceiro e último artigo de tarefas, examinarei as estruturas de dados do Nucleus SE e descreverei as chamadas da API do RTOS que não são implementadas no Nucleus SE, além de outros problemas de compatibilidade.

Artigos anteriores da série:
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.

Estruturas de dados


As tarefas usam várias estruturas de dados (na RAM e na ROM), que, como outros objetos do Nucleus SE, são um conjunto de tabelas cujo tamanho corresponde ao número de tarefas e parâmetros selecionados.

Eu recomendo que o código do aplicativo acesse essas estruturas de dados usando funções da API, e não diretamente. Isso evita efeitos colaterais indesejados, incompatibilidade com versões futuras do Nucleus SE e também simplifica a portabilidade do aplicativo para o Nucleus RTOS. Para uma melhor compreensão da operação do código de chamada de serviço e do processo de depuração, é fornecida abaixo uma descrição detalhada das estruturas de dados.

Estruturas de dados do kernel hospedadas na RAM


Essas estruturas de dados incluem:

NUSE_Task_Context [] [] - uma matriz bidimensional do tipo ADDR , possui uma linha para cada tarefa. O número de colunas depende da arquitetura do controlador e é determinado pelo símbolo NUSE_REGISTERS , definido em nuse_types.h . Essa matriz é usada pelo planejador para salvar o contexto de cada tarefa e foi descrita em detalhes na seção "Salvando o contexto" do artigo 10. Não criado se o agendador RTC for usado.
NUSE_Task_Signal_Flags [] - uma matriz do tipo U8 , criada se os sinais estiverem ativados, e contém 8 sinalizadores de sinal para cada tarefa. Os sinais serão discutidos em um dos seguintes artigos.
NUSE_Task_Timeout_Counter [] é uma matriz do tipo U16 , consiste em subtrair contadores para cada tarefa e é criada se a chamada para a API NUSE_Task_Sleep () estiver ativada.
NUSE_Task_Status [] - uma matriz do tipo U8, contém o status de cada tarefa - NUSE_READY ou status de suspensão. Criado apenas se a suspensão da tarefa estiver ativada.
NUSE_Task_Blocking_Return [] - uma matriz do tipo U8, criada se o bloqueio de chamadas da API estiver ativado. Ele contém um código de retorno que será usado após o bloqueio de chamadas da API. Geralmente, contém NUSE_SUCCESS ou código indicando que o objeto foi redefinido (por exemplo, NUSE_MAILBOX_WAS_RESET ).
NUSE_Task_Schedule_Count [] - uma matriz do tipo U16 , contém um contador para cada tarefa e é criada apenas se a contagem do planejador tiver sido ativada.

NUSE_Task_Context [] [] é inicializado principalmente por zeros, exceto para entradas correspondentes ao registro de status (registro de status, SR), contador de programa (contador de programa, PC) e ponteiro de pilha (ponteiro de pilha, SP), aos quais são atribuídos valores iniciais (consulte "Dados na ROM "abaixo), e todas as outras estruturas de dados NUSE_Init_Task () recebem zeros ao iniciar o Nucleus SE. Um dos artigos a seguir conterá uma lista completa dos procedimentos de inicialização do Nucleus SE com sua descrição.

A seguir, são definidas as estruturas de dados contidas no arquivo nuse_init.c.



Dados do usuário da RAM


O usuário deve definir uma pilha para cada tarefa (se o planejador RTC não for usado). Essas devem ser matrizes ADDR , geralmente definidas em nuse_config.c . Endereços e tamanhos de pilha devem ser colocados nas entradas de tarefa NUSE_Task_Stack_Base [] e NUSE_Task_Stack_Size [], respectivamente (consulte Dados na ROM).

Dados ROM


Uma ROM armazena de uma a quatro estruturas de dados relacionadas às tarefas. A quantidade exata depende dos parâmetros selecionados:

NUSE_Task_Start_Address [] é uma matriz do tipo ADDR que possui uma entrada para cada tarefa, que é um ponteiro para o ponto de entrada de código da tarefa.
NUSE_Task_Stack_Base [] é uma matriz do tipo ADDR que possui uma entrada para cada tarefa, que é um ponteiro para o endereço base da pilha da tarefa. Essa matriz é criada se qualquer agendador diferente do RTC for usado.
NUSE_Task_Stack_Size [] é uma matriz do tipo U16 que possui uma entrada para cada tarefa, que mostra o tamanho da pilha da tarefa (em palavras). Essa matriz é criada se qualquer agendador diferente do RTC for usado.
NUSE_Task_Initial_State [] é uma matriz do tipo U8 , com uma entrada para cada tarefa, que mostra o estado inicial da tarefa. Pode ser NUSE_READY ou NUSE_PURE_SUSPEND . Essa matriz é criada se o suporte ao estado inicial da tarefa estiver selecionado.

Essas estruturas de dados são declaradas e inicializadas (estaticamente) em nuse_config.c :



A quantidade de memória para armazenar dados da tarefa (pegada de dados da tarefa)


Como todos os objetos principais do Nucleus SE, a quantidade de memória necessária para armazenar dados é previsível.

Tamanho da ROM (em bytes) necessário para todas as tarefas do aplicativo:
NUSE_TASK_NUMBER * sizeof (ADDR)

Além disso, se qualquer agendador diferente de RTC estiver selecionado:
NUSE_TASK_NUMBER * (tamanho (ADDR) +2)

Além disso, se o suporte ao estado inicial da tarefa estiver selecionado:
NUSE_TASK_NUMBER

Para armazenar dados na RAM, a quantidade de memória (em bytes) é determinada pelos parâmetros selecionados e pode ter um valor zero se nenhum dos parâmetros for selecionado.
Se um agendador diferente de RTC for selecionado:
NUSE_TASK_NUMBER * NUSE REGISTERS * sizeof (ADDR)

Além disso, se o suporte ao sinal estiver selecionado:
NUSE_TASK_NUMBER

Além disso, se a chamada para a API NUSE_Task_Sleep () estiver ativada:
NUSE_TASK_NUMBER * 2

Além disso, se a suspensão da tarefa estiver ativada:
NUSE_TASK_NUMBER

Além disso, se o bloqueio de chamadas da API estiver ativado:
NUSE_TASK_NUMBER

Além disso, se o contador do planejador estiver ativado:
NUSE_TASK_NUMBER * 2

Chamadas de API não implementadas no Nucleus SE


Listados abaixo estão sete chamadas de API disponíveis no Nucleus RTOS que não são implementadas no Nucleus SE.

Criar tarefa


Essa chamada de API cria uma tarefa do aplicativo. O Nucleus SE não precisa desse recurso porque as tarefas são criadas estaticamente.

Protótipo de chamada:

STATUS NU_Create_Task (tarefa NU_TASK *, nome CHAR *, VOID (* task_entry) (UNSIGNED, VOID *), args UNSIGNED, VOID * argv, endereço_ Stack_ID, número_da_ pilha, tamanho_da_ pilha, prioridade da opção ,_slice de tempo UNSIGNED, prioridade da opção

Parâmetros:

task - um ponteiro para um bloco de controle de tarefas do usuário, pode ser usado como um identificador / link ("identificador") de uma tarefa em outras chamadas de API;
nome - ponteiros para o nome da tarefa, uma cadeia de 7 caracteres com um zero final;
task_entry - indica a função de entrada para a tarefa;
argc - elemento de dados NÃO ASSINADO que pode ser usado para passar informações iniciais para a tarefa;
argv - um ponteiro que pode ser usado para transmitir informações para a tarefa;
stack_address - define o setor inicial da memória para a pilha de tarefas;
stack_size - indica o número de bytes na pilha;
prioridade - indica o valor da prioridade da tarefa: de 0 a 255, onde números mais baixos correspondem à prioridade mais alta;
time_slice - indica o número máximo de intervalos de tempo que podem decorrer durante esta tarefa. Um valor de "0" desativa o tempo de corte para esta tarefa;
preempt - indica se a tarefa é suplantada ou não. Pode ter valores NU_PREEMPT e NU_NO_PREEMPT ;
auto_start - mostra o estado inicial da tarefa. NU_START significa que a tarefa está pronta para execução e NU_NO_START significa que a tarefa está suspensa.

Valor de retorno:

NU_SUCCESS - indica uma conclusão bem sucedida do serviço;
NU_INVALID_TASK - indica que o ponteiro para a unidade de controle de tarefas é NULL ;
NU_INVALID_ENTRY - indica que o ponteiro para a função de entrada da tarefa é NULL ;
NU_INVALID_MEMORY - indica que o setor de memória atribuído pelo parâmetro stack_address é zero ( NULL );
NU_INVALID_SIZE - indica que o tamanho da pilha especificado é insuficiente;
NU_INVALID_PREEMPT - indica que o parâmetro preempt está definido incorretamente;
NU_INVALID_START - indica que o parâmetro auto_start está definido incorretamente.

Excluir tarefa


Essa chamada de API exclui uma tarefa de aplicativo criada anteriormente que deve ser concluída ou finalizada . Essa chamada também não é necessária para o Nucleus SE, pois as tarefas são criadas estaticamente e não podem ser excluídas.

Protótipo de chamada:

STATUS NU_Delete_Task (tarefa NU_TASK *);

Parâmetros:

task - ponteiro para o bloco de controle de tarefas

Valor de retorno:

NU_SUCCESS - indica uma conclusão bem sucedida do serviço;
NU_INVALID_TASK - indica que o ponteiro para a tarefa está definido incorretamente;
NU_INVALID_DELETE - indica que a tarefa não está no estado Concluído ou Terminado.

Obter indicadores de tarefas


Essa chamada de API compõe uma lista seqüencial de ponteiros para todas as tarefas no sistema. Não é necessário no Nucleus SE, pois as tarefas são identificadas usando um índice simples, não um ponteiro.

Protótipo de chamada:

UNSIGNED NU_Task_Pointers (NU_TASK ** pointer_list, UNSIGNED maximum_pointers);

Parâmetros:

pointer_list - ponteiro para uma matriz de ponteiros NU_TASK . Essa matriz será preenchida com ponteiros para as tarefas instaladas no sistema;
maximum_pointers - o número máximo de ponteiros que podem ser colocados na matriz.

Valor de retorno:

O número de ponteiros NU_TASK colocados na matriz.

Alterar prioridade da tarefa


Essa chamada de API atribui à tarefa uma nova prioridade. No Nucleus SE, isso não é obrigatório, pois as prioridades das tarefas são constantes.

Protótipo de chamada:

OPTION NU_Change_Priority (tarefa NU_TASK *, OPTION new_priority);

Parâmetros:

tarefa - um ponteiro para um bloco de controle de tarefas;
new_priority - define a prioridade de 0 a 255.

Valor de retorno:
O valor da prioridade da tarefa anterior.

Alterar algoritmo de preempção de tarefa


Essa chamada de API altera a ordem de exclusão da tarefa. O Nucleus SE não precisa dele porque usa um algoritmo de agendamento mais simples.

Protótipo de chamada:
OPTION NU_Change_Preemption (opção antecipada de OPTION);

Parâmetros:
preempt - novo algoritmo preemptivo , aceita NU_PREEMPT ou NU_NO_PREEMPT

Valor de retorno:
O algoritmo anterior para excluir uma tarefa.

Alterar a fatia do tempo da tarefa


Essa chamada de API altera o intervalo de tempo de uma tarefa específica. O Nucleus SE não precisa, pois as fatias de tempo da tarefa são corrigidas.

Protótipo de chamada:
UNSIGNED NU_Change_Time_Slice (tarefa NU_TASK *, UNSIGNED time_slice);

Parâmetros:
tarefa - um ponteiro para um bloco de controle de tarefas;
time_slice - o número máximo de intervalos de tempo que podem decorrer durante esta tarefa; um valor zero desse campo desativa a quantização de tempo para esta tarefa.

Valor de retorno:
O valor anterior do quantum de tempo da tarefa.

Finalizar tarefa


Essa chamada de API conclui uma tarefa específica. O Núcleo SE não precisa disso porque o estado Terminado não é suportado.

Protótipo de chamada:
ESTADO NU_Terminate_Task (NU_TASK * tarefa);

Parâmetros:
task - um ponteiro para um bloco de controle de tarefas.

Valor de retorno:
NU_SUCCESS - indica uma conclusão bem sucedida do serviço;
NU_INVALID_TASK - indica que o ponteiro da tarefa está incorreto.

Compatível com núcleo RTOS


Ao desenvolver o Nucleus SE, um dos principais objetivos era garantir um alto nível de compatibilidade de código com o Nucleus RTOS. As tarefas não são exceção e, do ponto de vista do usuário, são implementadas da mesma maneira que no Nucleus RTOS. Em algumas áreas incompatíveis, cheguei à conclusão de que tal incompatibilidade seria aceitável, uma vez que o código final é mais fácil de entender e pode usar a memória com mais eficiência. No entanto, além dessas incompatibilidades, o restante das chamadas da API do Nucleus RTOS pode ser usado quase diretamente como o Nucleus SE chama. Um dos artigos a seguir fornecerá mais detalhes sobre a transição do Nucleus RTOS para o Nucleus SE

Identificadores de objeto


No Nucleus RTOS, todos os objetos são descritos por uma estrutura de dados (unidades de controle) que são de um tipo específico. Um ponteiro para esta unidade de controle serve como um identificador para a tarefa. No Nucleus SE, decidi que era 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 tipos de objetos. O identificador de um objeto específico é o índice nessas tabelas. Então, eu defini NUSE_TASK como o equivalente a U8 . Uma variável desse tipo (não um ponteiro) serve como um identificador para tarefas. Essa é uma pequena incompatibilidade que é fácil descobrir se o código é portado para ou a partir do Nucleus RTOS. Os identificadores de objetos geralmente são armazenados e transmitidos inalterados.

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

Estados da tarefa


No Nucleus RTOS, as tarefas podem estar em um dos vários estados: Execução , Pronto , Suspenso (o que leva à incerteza: a tarefa está em espera ou bloqueada por uma chamada de API), Encerrada ou Concluída.

O Nucleus SE também suporta os estados Executando e Pronto . Todas as três opções suspensas são suportadas opcionalmente. Não há suporte para Terminado e Concluído. Nenhuma chamada de API para concluir tarefas. Uma função de tarefa externa nunca deve retornar um valor explícita ou implicitamente (isso resultará em um estado Finalizado no Nucleus RTOS).

Chamadas de API não realizadas


O núcleo RTOS suporta 16 chamadas do escritório para trabalhar com tarefas. Destes, 7 não estão implementados no Nucleus SE. Sua descrição e o motivo de sua exclusão estão descritos acima.

No próximo artigo, começaremos a analisar o gerenciamento de memória RTOS.

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/pt425353/


All Articles