Signal – O que é e como usar?

american football player catching ball 62860 174
Este post faz parte da série IPC(Inter process communication). Leia também os outros posts da série:

Introdução

Signal é uma notificação que avisa um determinado processo que um evento ocorreu. Signal é considerado uma interrupção por software, similar a interrupção via hardware, onde quando há um evento, o fluxo do programa é alterado, normalmente chamando uma função que foi registrada para ser invocada quando esse sinal acontecer. Signal pode ser considerado um IPC porém não transmite dados, e são assíncronos, mas quando um processo o recebe, interrompe o processamento atual para atender o evento, ou seja, assim que um evento é recebido, o processamento é imediato, como boa prática os handlers registrados para os sinais devem possui uma rotina muito pequena para o tratamento desse sinal, para que possa retornar rapidamente para o ponto onde foi interrompido. Existem mais de 31 sinais sendo que alguns deles podem ser gerados através do teclado como o SIGINT, os sinais existentes estão definidos em /usr/include/bits/signum.h para 32bits, /usr/include/x86_64-linux-gnu/bits/signum.h para 64 bits e /usr/include/arm-linux-gnueabihf/bits/signum.h para ARM.

Registrando uma Callback para um Signal

Para realizar um registro de uma callback faz-se o uso da system call signal que recebe dois argumentos o SIG[TIPO] que é o evento, e a callback que será executado quando houver o evento, onde a callback deve respeitar a assinatura do sighandler, que recebe um argumento do tipo int e não retorna nada

Para uma explicação mais detalhada dos tipos de Signals, pesquise no manual do Linux

Emitindo um Signal

Com Signal é possível emitir o evento para si mesmo através da system call

ou para um processo externo, nesse caso é necessário conhecer o pid do processo no qual se quer enviar

Aguardando um Signal

Para fazer com que o programa somente processe mediante a um evento, pode-se usar a system call

que permite que o programa entre em sleep até que um Signal seja recebido para assim acordar e processar.

Funcionamento da recepção de um Signal

Para exemplificar melhor é apresentado uma imagem animada que demonstra o fluxo de um programa em execução, e como é realizado o tratamento do evento:

Signal 2

Pode-se se notar no programa que, em nenhum momento é chamado o handler, mas mediante o registro prévio do callback, quando o evento é gerado, o callback é invocado, trata o sinal e retoma o fluxo normal após o tratamento do evento.

Implementação

Para demonstrar o uso desse IPC, iremos utilizar um esquema de notifição, onde o processo Notificador (button_process) vai notificar o processo Consumidor(led_process) que está em sleep aguardando a notificação para alterar seu estado, essa aplicação é composta por 3 executáveis:

  • launch_processes – é responsável por lançar os processos button_process e led_process através da combinação fork e exec
  • button_interface – é responsável por ler o GPIO em modo de leitura da Raspberry Pi e enviar um evento Signal
  • led_interface – é responsável por aguardar um Signal e mudar o estado do GPIO configurado como saída

launch_processes

No main criamos duas variáveis para armazenar o PID do button_process e do led_process, e um buffer que vai ser formatado para enviar argumentos para o processo button_process

Em seguida lançamos o processo led_process

Nesse ponto temos o pid do processo led_process, que será passado para o processo button_process como argumento para que seja possível notificá-lo

button_interface

Definimos a variável que vai receber o argumento recebido via exec

Verificamos se o argumento é válido

Inicializamos a interface de botão

Recuperamos o pid do processo led_process

Aguardamos o pressionamento do botão e por fim notificamos o processo led_process através do kill

led_interface

Definimos uma variável para controlar o estado interno do LED

Registramos o sinal que desejamos receber

Inicializamos a interface de LED

Aplicamos o estado inicial do LED, alteramos o estado interno e colocamos o processo em modo sleep, que aguarda um evento para aplicar o novo estado

Compilando, Executando e Matando os processos

Para compilar e testar o projeto é necessário instalar a biblioteca de hardware necessária para resolver as dependências de configuração de GPIO da Raspberry Pi.

Compilando

Para facilitar a execução do exemplo, o exemplo proposto foi criado baseado em uma interface, onde é possível selecionar se usará o hardware da Raspberry Pi 3, ou se a interação com o exemplo vai ser através de input feito por FIFO e o output visualizado através de LOG.

Clonando o projeto

Pra obter uma cópia do projeto execute os comandos a seguir:

Selecionando o modo

Para selecionar o modo devemos passar para o cmake uma variável de ambiente chamada de ARCH, e pode-se passar os seguintes valores, PC ou RASPBERRY, para o caso de PC o exemplo terá sua interface preenchida com os sources presentes na pasta src/platform/pc, que permite a interação com o exemplo através de FIFO e LOG, caso seja RASPBERRY usará os GPIO’s descritos no artigo.

Modo PC

Modo RASPBERRY

Executando

Para executar a aplicação execute o processo launch_processes para lançar os processos button_process e led_process que foram determinados de acordo com o modo selecionado.

Uma vez executado podemos verificar se os processos estão rodando através do comando

O output

Interagindo com o exemplo

Dependendo do modo de compilação selecionado a interação com o exemplo acontece de forma diferente

MODO PC

Para o modo PC, precisamos abrir um terminal e monitorar os LOG’s

Dessa forma o terminal irá apresentar somente os LOG’s referente ao exemplo.

Para simular o botão, o processo em modo PC cria uma FIFO para permitir enviar comandos para a aplicação, dessa forma todas as vezes que for enviado o número 0 irá logar no terminal onde foi configurado para o monitoramento, segue o exemplo

Output do LOG quando enviado o comando algumas vezes

MODO RASPBERRY

Para o modo RASPBERRY a cada vez que o botão for pressionado irá alternar o estado do LED.

Matando os processos

Para matar os processos criados execute o script kill_process.sh

Conclusão

Signal é um IPC bastante versátil, apesar de não trafegar dados, permite de forma rápida e simples a sincronização entre os processos, como no Shared File, que é usado o polling para verificar se o arquivo está em uso, podemos remover o polling e usar a notificação para que o processo leia quando e somente for atualizado, reduzindo assim processamento desnecessário.

Referência

Outros artigos da série

<< Shared FileMMAP >>
Veja + conteúdo

Engenheiro Elétrico de Formação mas é Engenheiro de Software de profissão, Pós Graduado em Sistemas Embarcados. Apaixonado por Idiomas, mas o idioma que mais lhe fascina é a Linguagem C.
Jogador de CTF, mas prefire Battlefield 1, exige menos da capacidade cognitiva :P. Atualmente atua como desenvolvedor de sistemas distribuídos no ramo aeronáutico. Quando está de bobeira fica desenhando personagens de Anime ou pedalando pela cidade de São Paulo.

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

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

Comentários:
Notificações
Notificar
guest
0 Comentários
Inline Feedbacks
View all comments
Talvez você goste:

Nenhum resultado encontrado.

Séries



Outros da Série

Menu