Toda a verdade sobre o RTOS. Artigo 20. Semáforos: Serviços Auxiliares e Estruturas de Dados



Este artigo continua a revisão de semáforos.

Serviços de semáforo auxiliar


O Nucleus RTOS possui quatro chamadas de API que fornecem funcionalidades relacionadas ao semáforo: redefinir um semáforo, recuperar informações, recuperar o número de semáforos em um aplicativo e recuperar ponteiros para todos os semáforos em um aplicativo. Os três primeiros são implementados no Nucleus SE.

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

Redefinir semáforo


Essa chamada de API redefine o semáforo para seu estado inicial não utilizado. Essa função da API é incomum em comparação com as funções de outros objetos do kernel, porque, apesar de executar uma redefinição, ela não apenas define o contador para o valor inicial, mas um novo valor inicial do contador é passado na chamada. Qualquer tarefa que foi pausada no semáforo continua e retorna o código NUSE_SEMAPHORE_WAS_RESET no Nucleus SE e no Nucleus RTOS, NU_SEMAPHORE_RESET .

Chamada para redefinir o semáforo no Nucleus RTOS

Protótipo de chamada de serviço:

STATUS NU_Reset_Semaphore (NU_SEMAPHORE * semáforo, UNSIGNED initial_count);

Parâmetros:

semáforo - ponteiro para o bloco de controle de semáforo fornecido pelo usuário;
initial_count - o valor para o qual o semáforo será definido.

Valor de retorno:

NU_SUCCESS - a chamada foi concluída com sucesso;
NU_INVALID_SEMAPHORE - Ponteiro de semáforo inválido .

Chamada para redefinir um semáforo no Nucleus SE
Essa chamada de API suporta a funcionalidade principal da API do Nucleus RTOS.

Protótipo de chamada de serviço:

STATUS NUSE_Semaphore_Reset (NUSE_SEMAPHORE semáforo, U8 número_inicial);

Parâmetros:

semáforo - índice (ID) do semáforo despejado;
initial_count - o valor para o qual o semáforo será definido.

Valor de retorno:

NUSE_SUCCESS - a chamada foi concluída com sucesso;
NUSE_INVALID_SEMAPHORE - índice de semáforo inválido.

Implementando uma redefinição de semáforo no Nucleus SE

A principal tarefa da função da API NUSE_Semaphore_Reset () é definir o elemento NUSE_Semaphore_Counter [] correspondente para o valor especificado (após verificar os parâmetros).

Se o bloqueio de tarefas estiver ativado, o seguinte código será necessário para desbloquear tarefas:

while (NUSE_Semaphore_Blocking_Count[semaphore] != 0) { U8 index; /* check whether any tasks are blocked */ /* on this semaphore */ for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_SEMAPHORE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == semaphore)) { NUSE_Task_Blocking_Return[index] = NUSE_SEMAPHORE_WAS_RESET; NUSE_Task_Status[index] = NUSE_READY; break; } } NUSE_Semaphore_Blocking_Count[semaphore]--; } #if NUSE_SCHEDULER_TYPE == NUSE_PRIORITY_SCHEDULER NUSE_Reschedule(NUSE_NO_TASK); #endif 

Cada tarefa suspensa no semáforo é marcada como "concluída" e o código de suspensão da tarefa retorna NUSE_SEMAPHORE_WAS_RESET . Após a conclusão desse processo, se o agendador de prioridade for usado, a chamada inicializa NUSE_Reschedule () , pois uma ou mais tarefas com prioridade mais alta podem entrar em um estado pronto e aguardam a retomada.

Informações do semáforo


Essa chamada de utilitário retorna informações de semáforo. A implementação desta chamada no Nucleus SE difere do Nucleus RTOS, pois menos informações são retornadas, porque a nomeação de objetos e a ordem de pausa não são suportadas, e a suspensão da tarefa em si pode ser desativada.

Ligue para obter informações sobre semáforos no Nucleus RTOS

Protótipo de chamada de serviço:

STATUS NU_Semaphore_Information (NU_SEMAPHORE * semáforo, CHAR * nome, UNSIGNED * current_count, OPTION * suspend_type, UNSIGNED * tasks_waiting, NU_TASK ** first_task);

Parâmetros:

semáforo - um ponteiro para o bloco de controle do semáforo sobre o qual as informações são necessárias;
nome - ponteiro para o nome de 8 caracteres do semáforo, com zero byte final incluído nesta área;
current_count - um ponteiro para uma variável que aceita o valor atual do contador de semáforo;
suspend_type - um ponteiro para uma variável que aceitará o tipo de tarefa de suspensão, pode usar os valores NU_FIFO e NU_PRIORITY ;
task_waiting - um ponteiro para uma variável que leva o número de tarefas suspensas no semáforo;
first_task - um ponteiro para uma variável do tipo NU_TASK , que levará um ponteiro para a unidade de controle da primeira tarefa suspensa.

Valor de retorno:

NU_SUCCESS - a chamada foi concluída com sucesso;
NU_INVALID_SEMAPHORE - Ponteiro de semáforo inválido .

Ligue para obter informações sobre semáforos no Núcleo SE
Essa chamada de API suporta a funcionalidade principal da API do Nucleus RTOS.

Protótipo de chamada de serviço:

ESTADO NUSE_Semaphore_Information (semáforo NUSE_SEMAPHORE, U8 * contagem atual, U8 * contagem de tarefas em espera, NUSE_TASK * first_task);

Parâmetros:

semáforo - um índice de um semáforo sobre o qual é necessário fornecer informações;
current_count - um ponteiro para uma variável que aceita o valor atual do contador de semáforo;
tasks_waiting - um ponteiro para uma variável que terá o número de tarefas suspensas nesse semáforo (nada será retornado se o suporte à suspensão de tarefas estiver desativado);
first_task - um ponteiro para uma variável do tipo NUSE_TASK , que aceita o índice da primeira tarefa pausada (nada será retornado se o suporte para tarefas pausadas estiver desativado).

Valor de retorno:

NUSE_SUCCESS - a chamada foi concluída com sucesso;
NUSE_INVALID_SEMAPHORE - índice de semáforo inválido;
NUSE_INVALID_POINTER - um ou mais parâmetros do ponteiro estão incorretos.

Implementando informações de semáforo no Nucleus SE

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

 NUSE_CS_Enter(); *current_count = NUSE_Semaphore_Counter[semaphore]; #if NUSE_BLOCKING_ENABLE *tasks_waiting = NUSE_Semaphore_Blocking_Count[semaphore]; if (NUSE_Semaphore_Blocking_Count[semaphore] != 0) { U8 index; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_SEMAPHORE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == semaphore)) { *first_task = index; break; } } } else { *first_task = 0; } #else *tasks_waiting = 0; *first_task = 0; #endif NUSE_CS_Exit(); return NUSE_SUCCESS; 

A função retorna o status do semáforo. Em seguida, se a funcionalidade de bloqueio de chamada da API estiver ativada, o número de tarefas pendentes e o índice da primeira serão retornados (caso contrário, esses parâmetros serão definidos como 0).

Obtendo o número de semáforos


Essa chamada de utilitário retorna o número de semáforos no aplicativo. No Nucleus RTOS, esse valor muda com o tempo e o valor de retorno corresponde ao número atual de semáforos, e no Nucleus SE, o valor de retorno é definido no estágio de montagem e não muda mais.

Chamando um contador de semáforo no Nucleus RTOS

Protótipo de chamada de serviço:

UNSIGNED NU_Established_Semaphores (VOID);

Parâmetros:
Estão ausentes.

Valor de retorno:
O número de semáforos criados no aplicativo.

Ligar para um contador de semáforo 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_Semaphore_Count (nulo);

Parâmetros:
Estão ausentes.

Valor de retorno:
O número de semáforos configurados no aplicativo.

Implementando contadores de semáforo no Nucleus SE
A implementação desta chamada de API é bastante simples: o valor do símbolo #define NUSE_SEMAPHORE_NUMBER é retornado .

Estruturas de dados


Os semáforos usam duas ou três matrizes de estruturas de dados (em RAM e ROM), que, como todos os outros objetos do Nucleus SE, são um conjunto de tabelas cujo tamanho depende do número de semáforos no aplicativo e dos parâmetros selecionados.

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 evitará incompatibilidade com versões futuras do Nucleus SE e efeitos colaterais indesejados, além de simplificar a portabilidade do aplicativo para o Nucleus RTOS. Para uma melhor compreensão de como o código de chamada de serviço funciona e para depuração, é fornecida uma visão geral detalhada das estruturas de dados.

Dados RAM


Esses dados têm a seguinte estrutura:
NUSE_Semaphore_Counter [] - uma matriz do tipo U8 que possui uma entrada para cada semáforo configurado, armazena o valor do contador.
NUSE_Semaphore_Blocking_Count [] - uma matriz do tipo U8 , contém contadores de tarefas bloqueadas em cada semáforo. Essa matriz existe apenas se a funcionalidade de bloqueio de chamada da API estiver ativada.
NUSE_Semaphore_Counter [] é inicializado com o valor inicial (consulte "Dados na ROM" abaixo) e NUSE_Semaphore_Blocking_Count [] é redefinido usando NUSE_Init_Semaphore () quando o Nucleus SE é iniciado. Um dos seguintes artigos fornecerá uma descrição completa dos procedimentos de inicialização do Nucleus SE.

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

 RAM U8 NUSE_Semaphore_Counter[NUSE_SEMAPHORE_NUMBER]; #if NUSE_BLOCKING_ENABLE RAM U8 NUSE_Semaphore_Blocking_Count[NUSE_SEMAPHORE_NUMBER]; #endif 

Dados ROM


Estrutura de dados:
NUSE_Semaphore_Initial_Value [] - uma matriz do tipo U8 , com um registro para cada semáforo, esses são os valores iniciais dos semáforos.

Essa estrutura de dados é declarada e inicializada (estaticamente) em nuse_config.c :

 ROM U8 NUSE_Semaphore_Initial_Value[NUSE_SEMAPHORE_NUMBER] = { /* semaphore initial count values */ }; 

A quantidade de memória para semáforos


Como todos os objetos do kernel do Nucleus SE, a quantidade de dados necessária para os semáforos é previsível.

A quantidade de memória na ROM (em bytes) para todos os semáforos no aplicativo é NUSE_SEMAPHORE_NUMBER .

A quantidade de memória na RAM (em bytes) para todos os semáforos no aplicativo com chamadas ativadas para a API de bloqueio pode ser calculada da seguinte maneira:
NUSE_SEMAPHORE_NUMBER * 2

Caso contrário, é NUSE_SEMAPHORE_NUMBER .

Chamadas de API não realizadas


Três chamadas de API para semáforos presentes no Nucleus RTOS não são implementadas no Nucleus SE.

Criando semáforos


Essa chamada de API cria um semáforo. Não há necessidade do Nucleus SE porque os semáforos são criados estaticamente.

Protótipo de chamada de serviço:
STATUS NU_Create_Semaphore (NU_SEMAPHORE * semáforo, nome CHAR *, número inicial UNSIGNED, contagem_inicial, OPÇÃO suspend_type);

Parâmetros:

semáforo - um ponteiro para um bloco de controle de semáforo fornecido pelo usuário; é usado para controlar semáforos em outras chamadas de API;
name - um ponteiro para o nome do semáforo de 8 caracteres, com o byte nulo final;
initial_count - o valor inicial do semáforo;
suspend_type - Indica o princípio de pausar uma tarefa em um semáforo. Pode levar os valores NU_FIFO e NU_PRIORITY , correspondentes ao princípio de FIFO (primeiro a entrar , primeiro a sair) e a ordem de prioridade da suspensão de tarefas.

Valor de retorno:

NU_SUCCESS - a chamada foi concluída com sucesso;
NU_INVALID_SEMAPHORE - Diz que o ponteiro para o bloco de controle de semáforo é NULL ou já está em uso;
NU_INVALID_SUSPEND - parâmetro suspend_type inválido.

Remoção de semáforo


Essa chamada de API remove o semáforo criado anteriormente. O núcleo SE não é necessário porque os semáforos são criados estaticamente e não podem ser excluídos.

Protótipo de chamada de serviço:

ESTADO NU_Delete_Semaphore (NU_SEMAPHORE * semáforo);

Parâmetros:

semáforo - ponteiro para o bloco de controle de semáforo.

Valor de retorno:

NU_SUCCESS - a chamada foi concluída com sucesso;
NU_INVALID_SEMAPHORE - Ponteiro de semáforo inválido .

Ponteiros para semáforos


Essa chamada de API forma uma lista seqüencial de ponteiros para todos os semáforos no sistema. Ele não precisa do Nucleus SE, pois os semáforos são identificados por um índice simples, não por um ponteiro.

Protótipo de chamada de serviço:

UNSIGNED NU_Semaphore_Pointers (NU_SEMAPHORE ** pointer_list, UNSIGNED maximum_pointers);

Parâmetros:

pointer_list - um ponteiro para uma matriz de ponteiros NU_SEMAPHORE , essa matriz é preenchida com ponteiros para semáforos;
maximum_pointers - o número máximo de ponteiros na matriz.

Valor de retorno:
O número de ponteiros NU_SEMAPHORE na matriz.

Compatível com núcleo RTOS


Como com todos os outros objetos do Nucleus SE, o objetivo era maximizar a compatibilidade do código do aplicativo com o Nucleus RTOS. Os semáforos não são exceção e, do ponto de vista do usuário, são implementados da mesma maneira que no Nucleus RTOS. Há também uma certa incompatibilidade, que eu considerei aceitável, pois o código final 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 usadas quase diretamente como as chamadas do 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 um identificador para o semáforo. 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. Então, eu defini NUSE_SEMAPHORE como o equivalente a U8 , uma variável (não um ponteiro) desse tipo serve como identificador para o semáforo. 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 semáforos de nomeação. Esses nomes são usados ​​apenas para depuração. Excluí-os do Nucleus SE para economizar memória.

Tamanho do contador


No Nucleus RTOS, o contador de semáforo é do tipo não assinado , que geralmente é uma variável de 32 bits. O Nucleus SE possui um contador de 8 bits, mas isso pode ser facilmente alterado. Normalmente, o Nucleus RTOS não verifica se há excesso de semáforo. Chamar a API do Nucleus SE não permitirá que o contador tenha valores acima de 255.

Chamadas de API não realizadas


O Nucleus RTOS suporta oito chamadas de utilidade para trabalhar com semáforos. Destes, três não estão implementados no Nucleus SE. Os detalhes desses desafios, bem como a decisão de excluí-los do Nucleus SE, foram descritos acima.

O artigo a seguir examinará as caixas de correio.

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


All Articles