350 likes | 447 Views
Controladores de Dispositivos em Linux. Edgar Szilagyi edgar@cecm.usp.br. IME-USP. Sumário. Módulos em Linux Escrevendo um controlador de dispositivo tipo caractere Dispositivos de bloco e rede Arquitetura do hardware Interrupções Portas de E/S DMA ( Direct Memory Access )
E N D
Controladores de Dispositivos em Linux Edgar Szilagyi edgar@cecm.usp.br IME-USP
Sumário • Módulos em Linux • Escrevendo um controlador de dispositivo tipo caractere • Dispositivos de bloco e rede • Arquitetura do hardware • Interrupções • Portas de E/S • DMA (Direct Memory Access) • Barramentos ISA e PCI
Módulos no Linux • O Linux é um S.O. modular • Por exemplo, o suporte a determinado sistema de arquivos ou a certo dispositivo de hardware é fornecido através de módulos independentes • Em geral, tais módulos podem ser implantados dinamicamente, isto é, com o S.O. rodando, sem a necessidade de reinicialização
Escrevendo um módulo simples #define MODULE #include <linux/module.h> int init_module(void) { printk("<1>Hello, world!\n"); return 0; } void cleanup_module(void) { printk("<1>Goodbye cruel world!\n"); }
Por que printk e não printf ? • Os módulos, quando implantados, tornam-se parte do núcleo do S.O. • Não é possível utilizar nenhuma biblioteca • printk é implementada pelo S.O. Assemelha-se à printf, mas não oferece suporte a números de ponto flutuante. • Os indicadores <n> fornecem a prioridade da mensagem. • Quanto menor o número, maior é a prioridade • Não funciona em consoles gráficos!
Módulos x Programas • Os módulos rodam no espaço de endereçamento do núcleo do S.O. em modo supervisor. Isso exige alguns cuidados expeciais: • Concorrência • Segurança • Controle de versão • Tratamento de erros, alocação e desalocação de recursos judiciosa • O cuidado com ponteiros perdidos deve ser redobrado. Pode-se travar completamente o sistema!
Compilando, implantando e removendo o módulo root# gcc -c -O2 hello.c root# insmod ./hello.o Hello, world! root# rmmod hello Goodbye cruel world! root# • Diretiva de otimização • Necessária algumas vezes, e não é recomendado usar um valor maior • Implantação e remoção dinâmicas • Pode-se testar várias vezes o módulo sem a necessidade de reinicialização
A tabela de símbolos do Linux • O comando ksyms lista essa tabela. É uma associação de símbolos com endereços de memória. • insmod funciona como o programa de ligação de um compilador convencional. • Permite que uma parte acesse funções ou variáveis declarados na outra parte. • Use static para os símbolos que não devem ser exportados. • É permitido que um módulo acesse os símbolos exportados por outro módulo.
O papel dos controladores de dispositivos • Fornecer uma camada de abstração do dispositivo • Prover um mecanismo de acesso • A política de acesso deve ser deixada sob responsabilidade do S.O.
Tipos de dispositivos • Caractere • Lembra uma cadeia de caractere • Bloco • Basicamente são os dispositivos de armazenamento de massa (discos) • Rede • São aqueles que trocam dados com outras máquinas
Dispositivos de caractere • É o tipo de dispositivo mais simples que existe. Está associado aqueles que oferecem uma cadeia de entrada ou saída de dados. • Implementam as operações de abertura, fechamento, leitura e escrita. Em alguns casos, também implementam outras funções, como a indexação (seek). • Assim como vários outros dispositivos em Linux, são acessíveis através de uma entrada no sistema de arquivos /dev.
Números Maior e Menor • Os arquivos especiais em /dev estão associados a dois números que aparecem na listagem com ls -l • O número maior, isto é, o que aparece em primeiro, identifica qual módulo está associado a esse arquivo • O número menor é passado como parâmetro diretamente para o módulo. É útil quando temos diversos dispositivos sendo controlados pelo mesmo módulo.
Criando um arquivo especial root# mknod /dev/teste c <N.Maior> <N.Menor> root# chmode 644 /dev/teste • Utiliza-se a opção c, seguida dos números maior e menor. • Os ajustes de permissão de acesso são feitos em seguida.
Um módulo mais sofisticado int register_chrdev(unsigned int major, const char *name, struct file_operations *fops); • Na inicialização do módulo, registra-se o mesmo como um driver de caractere, informando o número maior, um nome e uma estrutura file_operations. • insmod registrará o módulo de acordo com esse número maior • o nome é o mesmo que aparece em /proc/devices
A estrutura file_operations • As operações realizadas no arquivo especial criado em /dev são encaminhadas pelo S.O. para o módulo correspondente. • A estrutura file_operations é um “vetor” contendo referências para as funções de operações de arquivos implementadas pelo módulo e que se tornaram acessíveis pelo S.O. • O número menor torna-se disponível para que o módulo possa interagir com o dispositivo correto.
Resumindo: • Um módulo possui um número maior • Um arquivo especial possui um número maior • insmod associa ao arquivo especial o módulo correspondente. Fornece ao S.O. as ref. para as funções que implementam as operações de arquivo como abrir, fechar, ler ou escrever. • Ao acessar o arquivo especial, o S.O. chama a função correspondente à operação, fornecendo entre outros argumentos, o número menor.
Dispositivos de bloco • Basicamente são os dispositivos de armazenamento de massa, (discos) • São bem mais complicados que os dispositivos de caractere. • O número maior é distindo dos dispositivos de caractere • Pode coexistir um dispositivo de caractere e outro de bloco no mesmo número maior.
Registrando o dispositivo de bloco • Principal diferença: • block_device_operations x file_operations #include <linux/fs.h> int register_blkdev(unsigned int major, const char *name, struct block_device_operations *bdops); int unregister_blkdev(unsigned int major, const char *name);
A estrutura block_device_operations • A função responsável pela escrita e leitura no dispositivo não permanece nessa estrutura por questões de eficiência. • Essa função porém é muito importante. É requisitada pelo S.O. somente quando as informações devem ser transferidas do ou para o disco. • Existem funções especiais correspondentes à montagem e desmontagem do dispositivo
Dispositivos de rede • Não possuem uma entrada em /dev. São melhor representados como um canal de comunicação. • A maior diferença: Os dispositivos de bloco respondem à requisições provenientes apenas do S.O. local, enquanto que os dispositivos de rede respondem às requisições que chegam pelo canal de comunicação. O dispositivo passa a ser um elemento ativo.
Dispositivos de rede • Possuem funções relacionadas à tarefas administrativas como configuração de parâmetros de comunicação e endereço. • O driver é responsável pela “camada física” de transmissão e não pelo protocolo. Sua interação com o S.O. ocorre em nível de pacotes completos. • Isso permite a independência entre os protocolos de hardware como Ethernet e token ring e protocolos de comunicação como IP ou IPX.
Controle do tempo • O Linux possui um relógio interno de 100Hz • jiffies, declarado em<linux/sched.h> • Para uma precisão maior • do_gettimeofday() em <linux/time.h> • Em <asm/msr.h> (machine-specific registers) existem contadores com resolução próxima ao clock do sistema. • Em <linux/delay.h> existem 2 funções busy-waiting para temporizações da ordem de microsegundos e milisegundos.
Arquitetura de hardware • CPU • Memória • Portas de E/S • Interrupções • DMA - Direct Memory Access • Barramento ISA • Barramento PCI
Controle de interrupções • As interrupções são necessárias quando algum dispositivo precisa solicitar a atenção do S.O. • O registro e liberação da interrupção é feito por duas funções • request_irq • free_irq
Registro e liberação do tratador de interrupções <linux/sched.h> int request_irq(unsigned int irq void (*handler)(int, void *, struct pt_regs *) unsigned long flags, const char *dev_name, void *dev_id); void free_irq(unsigned int irq, void *dev_id);
Tratadores de interrupções • Devido ao número limitado de interrupções do sistema, é aconselhável registrar um tratador de interrupções na operação de abertura do arquivo, ao invés de fazê-lo na instalação do módulo. • Devem ser rápidos e não podem entrar na fila de espera para execução, podendo causar impasses. • Compartilhamento de interrupções. • A cada interrupção gerada, o S.O. invoca cada um dos tratadores individualmente.
Reservando portas de E/S #include <linux/ioport.h> int check_region(unsigned long start, unsigned long len); struct resource *request_region( unsigned long start, unsigned long len, char *name); void release_region(unsigned long start, unsigned long len);
Acessando as portas de E/S #include <asm/io.h>) #Entrada/Saída de 8 bits unsigned inb(unsigned port); void outb(unsigned char byte, unsigned port); #Entrada/Saída de 16 bits unsigned inw(unsigned port); void outw(unsigned short word, unsigned port); #Entrada/Saída de 32 bits unsigned inl(unsigned port); void outl(unsigned longword, unsigned port);
Alguns cuidados com E/S • Operações de E/S são muito mais lentas que a velocidade dos processadores atuais. • Se operações sucessivas acontecerem, corre-se o risco de se perder dados. • Existem funções que evitam o problema. São elas: inb_p, outb_p, inw_p, etc. • A escrita de dados em arquivos é controlada pelo S.O. • Para garantir que o dado foi enviado ao hardware, use a função fflush().
Memória do dispositivo: mmap • Operação que mapeia uma região de memória virtual no espaço do usuário para um dispositivo físico. • Muito comum em interfaces gráficas cat /proc/731/maps 08048000-08327000 r-xp 00000000 08:01 55505 /usr/X11R6/bin/XF86_SVGA 08327000-08369000 rw-p 002de000 08:01 55505 /usr/X11R6/bin/XF86_SVGA 40015000-40019000 rw-s fe2fc000 08:01 10778 /dev/mem 40131000-40141000 rw-s 000a0000 08:01 10778 /dev/mem 40141000-40941000 rw-s f4000000 08:01 10778 /dev/mem
DMA - Direct Memory Access • Maneira eficiente de transferir dados para ou da memória RAM • O buffer de transferência deve ser alocado com cuidado, pois a região de memória será acessada pelo dispositivo. • Em dispositivos ISA o problema é ainda maior. • Em PCI, existe uma interface melhor, conhecida como Bus Mastering • Deve-se tomar o cuidado de mapear os endereços virtuais para físicos.
O barramento ISA • Obsoleto, mas ainda existem muitos dispositivos nesse padrão. • Portas de E/S limitadas a 1024 endereços • Não permite endereçamento geográfico • Memória de E/S limitada entre 640Kb e 1Mb, e entre 15Mb e 16Mb • Necessita de configuração manual • Tecnologia Plug and Play minimiza esse problema
O barramento PCI • Independente de plataforma • Muito mais rápido que o ISA • Possui endereçamento geográfico • Permite que os recursos, isto é, mapeamentos de memórias e linhas de interrupção sejam configurados na inicialização do sistema de forma automática. • A interface do Linux para dispositivos PCI facilita bastante o desenvolvimento de drivers para esses dispositivos.
Para saber mais • Rubini, A. & Corbet, J.; Linux Device Drivers, 2nd Edition, 2001 O´Reilly • Livro disponível em formato eletrônico em www.xml.com/ldd/chapter/book/ • Código fonte do Linux