Como utilizar os semáforos para compartilhar recursos no mbed OS

Threads no MBED OS Semáforos no mbed OS Recurso compartilhado
Este post faz parte da série mbed OS. Leia também os outros posts da série:

Olá caro leitor. Neste artigo vamos seguir falando sobre o uso de semáforos no mbed OS. No artigo anterior demos uma introdução sobre o que são semáforos e alertamos para seu uso mais amplo, diferentemente de uma Thread que possui um objetivo bem definido. No texto de hoje vamos abordar um outro uso muito comum para os semáforos do mbed OS, como controladores de acesso em recurso compartilhado.

 

Recurso compartilhado? O que seria isso?

 

O recurso compartilhado se trata de um conceito simples de explicar. Sabe aquela tela de cristal líquido que a placa shield da LPC4337 da NXP que estamos utilizando possui? Certo, agora imagine que a sua aplicação realize a leitura de uma gama de sensores, e que essa tela funcione como um console de comandos (similar ao terminal do Linux / Windows) imprimindo dados dos sensores lidos ou da execução de uma determinada tarefa. Nesse cenário vamos ter basicamente duas ou mais tarefas que precisam acessar um recurso em comum, nesse caso o driver para impressão de mensagens nessa tela. Como a placa possui apenas um driver e uma tela, podemos dizer que o driver se passa pelo recurso e as várias tarefas o acessam de forma compartilhada.

 

Faz parte dos objetivos de um recurso compartilhado, que esse seja acessado por aplicações diferentes e que seu uso não modifique (pelo menos de modo a prejudicar) a atuação desse mesmo recurso quando foi utilizado pelas tarefas anteriores. Por exemplo, voltemos à nossa tela, sabemos que só existe uma única na placa, porém temos vários framebuffers, onde cada aplicação escreve no seu, e ele só vai ser escrito na tela quando ela não estiver sendo utilizada, ou seja, nunca um framebuffer de uma Thread, por exemplo (por maior que sua prioridade seja), irá interromper a escrita de um framebuffer que encontra-se em curso, pois isso acarretaria corrupção dos dados impressos na tela, violando o principal objetivo de ter um recurso compartilhado. Vejamos a figura abaixo que ilustra esse comportamento:

 

Figura 1: Mutex em recurso compartilhado

 

Se na teoria isso parece funcionar bem, na prática existem mais variáveis, pois o recurso compartilhado nem sempre (ou na maioria das vezes) vem implementado no driver de acesso ao recurso que precisa ser compartilhado. Para isso mecanismo de controle de acesso precisam ser criados.

 

 

Mutex, o semáforo especialista em gerenciar acesso a recurso compartilhado

 

Parece título de filme, mas chegamos ao real objetivo deste artigo. Sabemos, genericamente, qual o aspecto de um semáforo, sabemos ainda que podemos utilizar e liberar um semáforo e que o seu uso está condicionado à disponibilidade de valor para decremento no seu contador interno. O Mutex (conhecido por semáforo de exclusão mútua) cai em um caso especial de semáforo, seu contador apenas consegue incrementar uma única vez, e caso outra aplicação precise utilizar, ela será colocada em uma lista de espera ordenada pela prioridade de todas as outras Threads que precisam do mesmo semáforo.

 

O leitor deve estar se perguntando no que diferencia o Mutex do semáforo de sincronismo do artigo passado. De forma bem objetiva, nada, são iguais se não fosse por um detalhe sutil, para ilustrar considere o seguinte cenário: 

  • Thread A de alta prioridade;
  • Thread B com a prioridade mais baixa do sistema;
  • Recurso R a ser compartilhado entre A e B;
  • Semáforo S a ser utilizado por A e B inicialmente liberado;
  • Mutex M a ser utilizado por A e B inicialmente liberado.

 

Agora vamos considerar o seguinte caso. Thread A, que possui alta prioridade, acessa e utiliza o recurso R, libera e encerra sua execução. Neste momento B tenta acessar R, e consegue pois A está suspenso, então ele inicia suas operações em R, porém, dado um espaço de tempo, Thread A fica novamente pronta, porém Thread B ainda não liberou o recurso R, mas A possui maior prioridade, e suspende B, acessa o recurso R que estava em uso por B. Nesse momento o comportamento do sistema será imprevisível pois A não sabe em que ponto R foi colocado por B, assim o conteúdo a ser processado por R de ambas as Threads vai ser corrompido.

 

Como resolver? Vamos à primeira abordagem, suponha agora o mesmo comportamento, porém A, antes de acessar R, decrementa S, com isso qualquer outra tarefa terá que decrementar S e será colocado em uma lista de espera até que R processe todo os dados que A enviar a ele. Seguindo nosso caso de uso, após terminar suas tarefas, A fecha o recurso R e libera o semáforo S e encerra a execução, então B inicia sua execução. S possui valor para contagem, então B pega S e acessa R e inicia sua execução. Neste momento A fica pronta, suspende B, porém agora ela tem que pegar S para acessar R, como S está sendo utilizado, A será suspenso, B volta a rodar e A (que possui maior prioridade) somente será liberado depois que B terminar o uso do recurso R, liberando o semáforo S. Se em um primeiro momento isso não representa um problema, se a prioridade de B for baixa a ponto de ser suspensa por outras tarefas do sistema, A que possui prioridade elevada e precisa rodar começará a sofrer atrasos, perdendo eventos e até mesmo disparando mecanismos de proteção como watchdogs. Esse problema chama-se inversão de prioridade.

 

Com o Mutex M, nessa mesma abordagem, quando A tentar pegar o recurso que está com B, A será suspenso, isso não vai mudar. Porém para que B libere o recurso mais rapidamente sua prioridade vai ser modificada, sendo elevada temporariamente para que nenhuma outra tarefa do sistema a bloqueie e que o recurso R fique disponível o mais rápido possível para A. Existem diferentes políticas de como a prioridade pode ser modificada que estão além do escopo deste artigo, porém podemos resumir que o Mutex possui o mecanismo de sincronização já conhecido de um semáforo comum com o adicional de poder atuar sobre a prioridade da tarefa que o utiliza, de forma que o recurso protegido seja liberado mais rapidamente.

 

 

Mutex no mbed OS, como funciona?

 

De forma muito similar ao semáforo de contagem do artigo anterior, utilizar o Mutex no mbed OS não requer nenhum conhecimento especial, sendo fácil instanciar um semáforo desses para controlar o acesso ao recurso compartilhado do seu hardware alvo. Para demonstrar, prepare o ambiente do mbed OS conforme explicado nos os artigos anteriores. Se o leitor desejar pode utilizar o projeto do artigo anterior, pois modificaremos apenas o arquivo main.cpp, copie e cole o código abaixo: 

 

 

Digite CTRL+D para gerar e fazer o download do arquivo .bin, conecte a placa LPC4337 no seu computador e arraste o arquivo .bin para a unidade que possuir nomes como DAPLINK ou MBED, aguarde o arquivo ser copiado e a placa ser re-enumerada na USB. Pressione o botão reset no canto superior esquerdo, você deverá ter algo parecido com o que temos no vídeo:

 

 

Falando do código, apesar do Mutex construtivamente ser mais complexo que um semáforo de contagem, a demonstração acabou sendo mais simples, primeiramente vamos instanciar um Mutex: 

 

A variável console_buffer, vai ser nosso recurso compartilhado, logo sua declaração como global foi proposital: 

 

A função log_thread() será acessada constantemente pelas Threads criadas para imprimir seu status, dentro dela a variável console_buffer vai ser alterada. Percebam que nesse momento as funções message_lock.lock() e message_lock.unlock() vão sendo chamadas para que a escrita em console_buffer seja feita de modo seguro, ou seja, apenas uma Thread por vez vai modificar essa variável: 

 

E finalmente temos as duas Threads a serem utilizadas. Nesse exemplo não vamos utilizar a Thread np_task(), apenas as duas Threads de prioridade maior vão ser executadas alternadamente chamando a funcao log_thread para imprimir seu estado de execução: 

 

 

Conclusão

 

Com isso apresentamos mais um uso principal do semáforo, seu aspecto como agente de controle de acesso a recursos compartilhados, apresentamos também a forma de instanciar e como utilizar o Mutex dentro do mbed OS bem como o projeto de referência para demonstrar o seu uso. Agora possuímos bons fundamentos para construir aplicações reais com o uso do mbed OS. Assim, nos próximos artigos vamos utilizar os conhecimentos até aqui obtidos e fazer um experimento com cara de mundo real nessa placa. Espero que os textos estejam de agrado ao leitor. Bons projetos!

 

 

Links úteis

 

Acesse o aqui o repositorio no mbed contendo o projeto completo.

Outros artigos da série

<< Como utilizar Semáforos no mbed OS - Sincronização
Este post faz da série mbed OS. Leia também os outros posts da série:
NEWSLETTER

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Obrigado! Sua inscrição foi um sucesso.

Ops, algo deu errado. Por favor tente novamente.

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

Felipe Neves
Engenheiro de sistemas embarcados apaixonado pelo que faz, já trabalhou em diversos setores de tecnologia nos últimos 14 anos com destaque para Defesa, Automação, Agricultura, Wearables, Robótica e mais recentemente Semicondutores. Possui sangue maker, tendo paixão por construir e compatilhar suas próprias coisas e explorar novos sabores dentro do mundo "embedded". Atualmente trabalha como Engenheiro de Software Senior na Espressif, sim aquela do ESP32 e do ESP8266. Tem interesse em tópicos que envolvam Robótica, Controle de Movimento, DSP e Sistemas de Tempo Real.

Deixe um comentário

avatar
 
  Notificações  
Notificar