
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 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 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 #define NUSE_SEMAPHORE_OBTAIN FALSE #define NUSE_SEMAPHORE_RELEASE FALSE #define NUSE_SEMAPHORE_RESET FALSE #define NUSE_SEMAPHORE_INFORMATION FALSE #define NUSE_SEMAPHORE_COUNT FALSE
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 RTOSProtó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 SEEssa 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 SEA 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 RTOSProtó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 SEEssa 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 SEO 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; 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.