Toda a verdade sobre o RTOS. Artigo 19. Semáforos: introdução e serviços básicos



Os semáforos foram mencionados em um dos artigos anteriores (nº 5). Sua principal tarefa é controlar o acesso aos recursos.

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

Usando semáforos


No Nucleus SE, os semáforos são definidos na fase de montagem. Um aplicativo pode ter até 16 semáforos. Se os semáforos não forem especificados, o código de chamadas de serviço e as estruturas de dados não serão incluídas no aplicativo.

Um semáforo é um contador do tipo U8 , cujo acesso é controlado de maneira que várias tarefas possam ser usadas. Uma tarefa pode diminuir o valor do contador de semáforo (captura) ou aumentá-lo (liberação). Tentar capturar um semáforo com um valor nulo pode levar a um erro ou a uma suspensão da tarefa, dependendo dos parâmetros de chamada da API selecionados e da configuração do Nucleus SE.

Configurando semáforos


Número de semáforos


Como na maioria dos objetos do Nucleus SE, a configuração dos semáforos é determinada pelas diretivas #define em nuse_config.h . O parâmetro principal é NUSE_SEMAPHORE_NUMBER , que determina o número de semáforos no aplicativo. Por padrão, o parâmetro é definido como 0 (os semáforos não são usados ​​no aplicativo) e pode assumir valores até 16. Um valor incorreto levará a um erro de compilação, que será gerado pela verificação de nuse_config_check.h (esse arquivo está incluído no nuse_config.c , o que significa que ele compila juntamente com este módulo), como resultado, a diretiva #error será acionada .

A escolha de um valor diferente de zero serve como o principal ativador de semáforos. Este parâmetro é usado ao definir estruturas de dados e seu tamanho depende de seu valor (para obter mais detalhes, consulte mais adiante neste 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 em nuse_config.h . Para semáforos, incluem:

NUSE_SEMAPHORE_OBTAIN
NUSE_SEMAPHORE_RELEASE
NUSE_SEMAPHORE_RESET
NUSE_SEMAPHORE_INFORMATION
NUSE_SEMAPHORE_COUNT


Por padrão, eles são definidos como FALSE , desabilitando cada chamada de serviço e bloqueando a inclusão do código que os implementa. Para configurar semáforos, é necessário selecionar as chamadas de API necessárias e definir as diretivas correspondentes como TRUE .

A seguir, um trecho do arquivo nuse_config.h padrão.

 #define NUSE_SEMAPHORE_NUMBER 0 /* Number of semaphores in the system - 0-16 */ #define NUSE_SEMAPHORE_OBTAIN FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_RELEASE FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_RESET FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_INFORMATION FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_COUNT FALSE /* Service call enabler */ 

Uma função de API ativada, se não houver semáforos no aplicativo, levará a um erro de compilação (exceto NUSE_Semaphore_Count () , que sempre está ativado). 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 semáforo de utilidade


O núcleo RTOS suporta oito chamadas de serviço que fornecem a seguinte funcionalidade:

  • Captura de semáforo. O núcleo SE é implementado na função NUSE_Semaphore_Obtain () .
  • Solte o semáforo. No Nucleus SE, ele é implementado na função NUSE_Semaphore_Release () .
  • Retornando o semáforo a um estado não utilizado com o lançamento de todas as tarefas pausadas (reinicialização). O núcleo SE é implementado em NUSE_Semaphore_Reset () .
  • Fornecendo informações sobre um semáforo específico. O núcleo SE é implementado em NUSE_Semaphore_Information () .
  • Retorna o número de semáforos configurados no aplicativo. Núcleo SE implementado em NUSE_Semaphore_Count () .
  • Adicionando um novo semáforo ao aplicativo. O núcleo SE não está implementado.
  • Removendo o semáforo do aplicativo. O núcleo SE não está implementado.
  • Retornando ponteiros para todos os semáforos. O núcleo SE não está implementado.

A implementação de cada chamada de serviço é descrita em detalhes abaixo.

Chamadas de utilidade para capturar e liberar semáforos


As operações fundamentais que podem ser executadas nos semáforos são captura e liberação (diminuição e aumento de valor). O Nucleus RTOS e o Nucleus SE fornecem duas chamadas de API básicas para essas operações.

Captura de semáforo


A chamada do utilitário Nucleus RTOS para capturar um semáforo é muito flexível e permite pausar tarefas implicitamente ou com um tempo limite específico se a operação não puder ser executada no momento, por exemplo, se você tentar capturar um semáforo com um valor zero. O Nucleus SE oferece os mesmos recursos, apenas a pausa da tarefa é opcional e o tempo limite não é implementado.

Desafio para capturar semáforo no Nucleus RTOS
Protótipo de chamada de serviço:
STATUS NU_Obtain_Semaphore (NU_SEMAPHORE * semáforo, suspensão não assinada);

Parâmetros:

semáforo - ponteiro para o bloco de controle de semáforo fornecido pelo usuário;
suspender - o parâmetro de suspensão da tarefa, pode assumir os valores NU_NO_SUSPEND ou NU_SUSPEND , bem como o valor do tempo limite.

Valor de retorno:

NU_SUCCESS - a chamada foi concluída com sucesso;
NU_UNAVAILABLE - o semáforo tinha um valor nulo;
NU_INVALID_SEMAPHORE - ponteiro inválido para um semáforo;
NU_INVALID_SUSPEND - tenta pausar de um thread não relacionado à tarefa;
NU_SEMAPHORE_WAS_RESET - o semáforo foi redefinido enquanto a tarefa estava suspensa.

Desafio para capturar 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_Obtain (semáforo NUSE_SEMAPHORE, suspensão U8);

Parâmetros:

semáforo - índice (ID) do semáforo usado;
suspender - o parâmetro de suspensão da tarefa, pode ser NUSE_NO_SUSPEND ou NUSE_SUSPEND .

Valor de retorno:

NUSE_SUCCESS - a chamada foi concluída com sucesso;
NUSE_UNAVAILABLE - o semáforo tinha um valor nulo;
NUSE_INVALID_SEMAPHORE - índice de semáforo inválido;
NUSE_INVALID_SUSPEND - uma tentativa de pausar de um thread não relacionado à tarefa ou quando a funcionalidade de bloqueio da API está desativada;
NUSE_SEMAPHORE_WAS_RESET - o semáforo foi redefinido enquanto a tarefa estava suspensa;

Implementando captura de semáforo no Nucleus SE
A versão do código de função NUSE_Semaphore_Obtain () (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. Considere as duas opções.

Se o bloqueio não estiver ativado, a lógica desta chamada de API é bastante simples:

 if (NUSE_Semaphore_Counter[semaphore] != 0) /* semaphore available */ { NUSE_Semaphore_Counter[semaphore]--; return_value = NUSE_SUCCESS; } else /* semaphore unavailable */ { return_value = NUSE_UNAVAILABLE; } 

O valor do contador de semáforo é verificado e, se não for igual a zero, diminui.

Se o bloqueio de tarefas estiver ativado, a lógica se tornará mais complexa:

 do { if (NUSE_Semaphore_Counter[semaphore] != 0) /* semaphore available */ { NUSE_Semaphore_Counter[semaphore]--; return_value = NUSE_SUCCESS; suspend = NUSE_NO_SUSPEND; } else /* semaphore unavailable */ { if (suspend == NUSE_NO_SUSPEND) { return_value = NUSE_UNAVAILABLE; } else { /* block task */ NUSE_Semaphore_Blocking_Count[semaphore]++; NUSE_Suspend_Task(NUSE_Task_Active, semaphore << 4) | NUSE_SEMAPHORE_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 o semáforo tiver um valor diferente de zero, ele diminuirá. A variável de suspensão está definida como NUSE_NO_SUSPEND e a chamada da API termina e retorna NUSE_SUCESS .

Se o semáforo for nulo e a variável de suspensão estiver definida como NUSE_NO_SUSPEND , a chamada da API retornará NUSE_UNAVAILABLE . Se a suspensão foi definida como NUSE_SUSPEND , a tarefa será interrompida. Depois que a chamada é concluída (por exemplo, quando a tarefa é retomada), se o valor de retorno for NUSE_SUCCESS (que indica que a tarefa foi retomada depois que o semáforo foi liberado e não após a redefinição), o ciclo começa do início.

Liberação de semáforo


A chamada de utilitário para a API Nucleus RTOS para liberar o semáforo é bastante simples: o valor do contador de semáforo aumenta e uma mensagem de sucesso é retornada. O Nucleus SE oferece os mesmos recursos, mas com verificação adicional de estouro.

Desafio para liberar semáforos no Nucleus RTOS
Protótipo de chamada de serviço:

ESTADO NU_Release_Semaphore (NU_SEMAPHORE * semáforo);

Parâmetros:

semáforo - um ponteiro para um bloco de controle de semáforo fornecido pelo usuário.

Valor de retorno:

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

Desafio para liberar 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:

ESTADO NUSE_Semaphore_Release (semáforo NUSE_SEMAPHORE);

Parâmetros:

semáforo - O índice (ID) do semáforo liberado.

Valor de retorno:

NUSE_SUCCESS - a chamada foi concluída com sucesso;
NUSE_INVALID_SEMAPHORE - índice de semáforo inválido;
NUSE_UNAVAILABLE - o semáforo tem um valor de 255 e não pode ser aumentado.

Implementando a liberação de semáforo no Nucleus SE
O código da função NUSE_Semaphore_Release () (após verificar os parâmetros) é comum, independentemente de o bloqueio da tarefa estar ativado ou não. O valor do contador de semáforo é verificado e, se for menor que 255, aumenta.

O código adicional é selecionado usando a compilação condicional se o suporte para chamadas de bloqueio de API (suspensão de tarefas) estiver ativado:

 NUSE_CS_Enter(); if (NUSE_Semaphore_Counter[semaphore] < 255) { NUSE_Semaphore_Counter[semaphore]++; return_value = NUSE_SUCCESS; #if NUSE_BLOCKING_ENABLE if (NUSE_Semaphore_Blocking_Count[semaphore] != 0) { U8 index; /* check whether a task is blocked */ /* on this semaphore */ NUSE_Semaphore_Blocking_Count[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_SUCCESS; NUSE_Wake_Task(index); break; } } } #endif } else { return_value = NUSE_UNAVAILABLE; } NUSE_CS_Exit(); return return_value; 

Se alguma tarefa for suspensa nesse semáforo, a primeira será retomada.

O artigo a seguir descreve chamadas de API adicionais associadas a semáforos e suas estruturas de dados.

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


All Articles