Conceitos Básicos do Mecanismo poll
Em sistemas embarcados, frequentemente precisamos monitorar eventos em dispositivos de harwdare, como botões ou interfaces de comunicação. Uma abordagem comum é usar operações bloqueantes, mas isso pode levar a ineficiências ou travamentos. O mecanismo poll no Linux oferece uma solução baseada em eventos, permitindo que processos aguardem por ocorrências específicas com um tempo limite configurável.
Considere um cenário de leitura de um dispositivo de botão em um sistema Linux. Se um aplicativo abrir o arquivo de dispositivo /dev/botoes e chamar read(), ele ficará bloqueado até que um botão seja pressionado. No entanto, em muitos casos, é necessário evitar bloqueios indefinidos. O poll permite que o processo entre em suspensão e seja despertado por um evento, ou retorne um erro de tempo limite após um período especificado, otimizando o uso da CPU.
Uso do poll em Aplicações Linux
Para ilustrar a aplicação do poll, considere um exemplo que monitora eventos de botões. O código a seguir abre um dispositivo, configura uma estrutura pollfd e utiliza a função poll() para aguardar dados ou um tempo limite de 5 segundos:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
int main(int argc, char **argv)
{
int file_desc;
int resultado;
unsigned char valor_teclas;
struct pollfd monitor[1];
file_desc = open("/dev/botoes", O_RDONLY);
if (file_desc < 0) {
perror("Falha ao abrir /dev/botoes");
return EXIT_FAILURE;
}
monitor[0].fd = file_desc;
monitor[0].events = POLLIN;
while (1) {
resultado = poll(monitor, 1, 5000);
if (resultado == 0) {
printf("Tempo limite atingido!\n");
} else if (resultado > 0) {
read(file_desc, &valor_teclas, sizeof(valor_teclas));
printf("Valor das teclas: 0x%x\n", valor_teclas);
} else {
perror("Erro na chamada poll");
break;
}
}
close(file_desc);
return EXIT_SUCCESS;
}
Neste exemplo, a função poll() verifica se há dados disponíveis para leitura no dispositivo de botão. Se um evento ocorrer, o valor é lido e exibido; caso contrário, após 5 segundos, uma mensagem de tempo limite é impressa.
Detalhes da Função poll()
A assinatura da função poll() é:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
Parâmetros:
fds: Ponteiro para um array de estruturaspollfd, cada uma representando um arquivo a ser monitorado.nfds: Número de elementos no arrayfds.timeout: Tempo limite em milissegundos; um valor negativo significa espera indefinida.
A estrutura pollfd contém os campos:
fd: Descritor de arquivo.events: Máscara de eventos desejados (por exemplo,POLLINpara dados disponíveis para leitura).revents: Máscara de eventos retornados pelo kernel.
Eventos comuns incluem POLLIN (dados para leitura), POLLOUT (escrita sem bloqueio) e POLLERR (condição de erro). O valor de retorno indica o número de estruturas com eventos ocorridos, 0 para tempo limite ou -1 em caso de erro.
Implementação de poll em Drivers Linux
No lado do driver, a implementação do poll envolve registrar uma fila de espera e retornar máscaras de eventos. O exemplo abaixo mostra um driver de botão simplificado:
#include <linux/poll.h>
#include <linux/wait.h>
static DECLARE_WAIT_QUEUE_HEAD(fila_espera_botao);
static volatile int evento_ocorreu = 0;
static unsigned int driver_poll(struct file *arquivo, struct poll_table_struct *tabela)
{
unsigned int mascara = 0;
poll_wait(arquivo, &fila_espera_botao, tabela);
if (evento_ocorreu) {
mascara |= POLLIN | POLLRDNORM;
evento_ocorreu = 0; // Limpa o flag após evento
}
return mascara;
}
Neste código, a função driver_poll() é chamada pelo kernel quando um aplicativo invoca poll(). Ela utiliza poll_wait() para associar o processo à fila de espera fila_espera_botao. Quando uma interrupção de hardware ocorre (por exemplo, pressionamento de botão), o driver sinaliza o evento definindo evento_ocorreu e acordando os processos na fila de espera. Se o evento já tiver ocorrido, a máscara retorna com bits definidos, permitindo que o poll retorne imediatamente.
Mecanismo Interno do poll no Kernel Linux
Quando uma aplicação chama poll(), o kernel executa uma sequência de operações para gerenciar a espera por eventos. Resumidamente, o processo é colocado em suspensão através de uma fila de espera associada ao dispositivo. O kernel então verifica repetidamente se algum evento ocorreu, chamando a função poll do driver. Se um evento to detectado (máscara não-zero) ou se o tempo limite expirar, o processo é acordado e o poll retorna.
Essa abordagem baseada em eventos evita o desperdício de ciclos de CPU em loops de espera ativa, melhorando a eficiência do sistema. A integração com interrupções de hardware permite respostas rápidas a mudanças no estado do dispositivo.