| Desenvolvendo Device Drivers no Linux |
|
|
|
| Escrito por Artur Baruchi | ||||||||||||||||
| Sex, 04 de Fevereiro de 2011 01:19 | ||||||||||||||||
|
Resumo: Uma das principais características do Linux é a facilidade de criação de novos Drivers de Dispositivos. Neste artigo, serão abordados alguns conceitos sobre sistemas operacionais e como utilizar as ferramentas e interfaces disponibilizadas pelo Linux para dar o primeiro passo na criação de novos Drivers.
Um Device Driver pode ser visto como o Software que permite que o Kernel do Linux se comunique com algum dispositivo (Hardware). Em se tratando de Linux, o interessante é que o código do Kernel é aberto, isto é, qualquer pessoa que se proponha a desenvolver um Device Driver poderá fazê-lo.
Uma característica importante de Device Drivers no Linux é a sua modularização, em outras palavras, um Device Driver pode ser anexado ao Kernel sem a necessidade de recompilar todo o Kernel ou mesmo reiniciar a máquina. Outro ponto interessante no desenvolvimento de Device Drivers é que não necessariamente um Device Driver é implementado para se comunicar com algum dispositivo de Hardware; existem módulos construídos somente para coletar informações do próprio Kernel. Um exemplo de módulo desse tipo é o que implementa o sistema de arquivos virtual /proc do Linux, que permite obter informações sobre a memória virtual, processador, escalonador, processos e outros mais, como se fossem arquivos.
A linguagem predominante no código fonte do Linux é a linguagem C. Apenas algumas partes bem específicas (por exemplo, a parte do código que realiza o bootstrap) são escritas em Assembly. É importante salientar que o código, apesar de possuir comentários em quase todos os lugares e principalmente nas bibliotecas, não é trivial. Lembre-se que qualquer processamento feito pelo Sistema Operacional é considerado sobrecarga, por isso o código é otimizado sempre que possível pelos desenvolvedores do Kernel. Neste artigo, serão apresentados alguns conceitos básicos para a implementação de Device Drivers e um passo-a-passo de como começar a desenvolver os módulos para o Kernel do Linux.
Kernel Space e User Space
Antes de começar a implementar o módulo, é importante ter em mente alguns conceitos básicos sobre Sistemas Operacionais. Em geral, um sistema operacional é dividido em duas regiões, o Kernel Space (Espaço do Kernel) e o User Space (Espaço do Usuário). Basicamente, essa diferença serve para identificar o que é código do Kernel (por exemplo, códigos do escalonador de processo, código de gerência de memória, etc.) e o que é código do Usuário (por exemplo, códigos de aplicações, etc.).
Esta separação é necessária, principalmente por questões de segurança. Caso esta separação não fosse muito bem definida, o sistema se tornaria extremamente vulnerável a ataques de códigos maliciosos, como vírus. Abaixo, são listadas algumas das principais diferenças entre códigos que são executados em Kernel Space e User Space:
Apesar destas restrições de acesso impostas a programas que estão executando em User Space, é necessário criar alguma forma de permitir que estes programas consigam acessar um dispositivo de Hardware (como disco ou interface de rede) com segurança. Para resolver este problema, os Sistemas Operacionais disponibilizam aos programadores as chamadas de sistemas (System Calls). Estas chamadas podem ser entendidas como uma interface dos programas em User Space com o Kernel. Quando um programa faz uma chamada de sistema, o Kernel irá parar a execução do programa que fez a chamada. Em seguida, as rotinas de tratamento necessárias para a chamada realizada (que estão em Kernel Space) são processadas. O ultimo passo é retornar a solicitação feita pelo programa e continuar com o seu processamento.
Ao implementar um Device Driver ou algum módulo para o Kernel, é importante lembrar que o código será executado em Kernel Space. Isto significa que qualquer erro de programação ou mesmo de lógica pode causar danos incalculáveis ao ambiente (desde simples congelamentos à perda completa do sistema). Realizar depurações em Device Drivers é um trabalho complexo e necessita que o programador tenha bons conhecimentos de Sistemas Operacionais e do Kernel (a depuração de Device Drivers está fora do escopo deste artigo).
Preparando o Ambiente
Para este artigo, usaremos como exemplo um ambiente Fedora Core 14 utilizando o Kernel 2.6.35. O uso de distribuições com algum aplicativo de gerenciamento de pacotes de software (como o apt-get de distribuições baseadas no Debian ou o yum no caso de distribuições baseadas no Red-Hat) facilita o trabalho de instalação dos pacotes.
Os primeiros pacotes a serem instalados são o kernel-headers e o kernel-devel. Estes pacotes possuem as bibliotecas e o código fonte do kernel, respectivamente, necessários para a compilação. Para a instalação dos pacotes no Fedora, execute os seguintes comandos como usuário root:
Em seguida, deve-se instalar os pacotes das ferramentas utilizadas para compilar o Kernel, os pacotes make e o gcc.
Uma vez que os pacotes estejam instalados, o próximo passo é a criação do arquivo Makefile e o código fonte do próprio Device Driver.
Implementação
Para implementar o Device Driver, deve-se iniciar o código fonte referenciando o cabeçalho
module.h. Neste artigo, será usado um módulo bem simples como exemplo, que chamaremos de helloworld.c. Na primeira linha do módulo, é adicionado o cabeçalho module.h:
Após a adição dos cabeçalhos, deve-se colocar a macro
MODULE_LICENSE. Esta macro é utilizada para informar ao Kernel se o módulo está sob a licença GPL ou se é um código proprietário. Esta macro reconhece, entre outras, as licenças: "GPL", "GPL v2" e "Dual MIT/GPL".
Os módulos devem conter uma função
init_module, que deve retornar um int e não recebe nenhum parâmetro. Esta função contém o código que será executado quando o módulo for inserido no Kernel. Além desta função, o módulo deve conter outra função, chamada cleanup_module, que é executada todas as vezes que o módulo é removido do Kernel. O módulo helloworld ficaria assim:
O código do módulo
helloworld.c está pronto. O próximo passo consiste em criar o arquivo Makefile usado pela ferramenta make para compilar o módulo. O arquivo deverá ter o conteúdo a seguir (observe que a indentação deve ser feita usando tabulação – tab – e não espaços):
Após criar o arquivo Makefile, o diretório deverá conter os dois arquivos criados até o momento, o
Makefile e o helloworld.c
Para compilar o módulo, execute o comando make passando como parâmetro a diretiva module. Observe que no arquivo Makefile temos a seguinte diretiva:
Essa linha será transformada em:
O comando para compilar o módulo será:
Após a compilação, serão gerados diversos arquivos:
Entre todos estes arquivos, o que nos interessa é o arquivo com o sufixo .ko (helloworld.ko). Este é o arquivo que vamos usar no comando insmod para anexar o nosso driver no Kernel. Desta forma, para adicionarmos o módulo, usamos o seguinte comando:
Para verificar se o modulo foi inserido com sucesso, use o comando lsmod para listar os módulos carregados:
Durante o desenvolvimento do módulo, utilizamos o comando
printk para imprimir algumas mensagens triviais. É importante notar que, ao programar em Kernel Space, não teremos acesso a algumas das funções mais convencionais, como printf e malloc.
O
printf recebe um parâmetro a mais, que define o tipo de mensagem que será enviada pelo Kernel; no caso do módulo construído aqui, será uma mensagem de informação (KERN_INFO) e de acordo com as configurações do syslog, mensagens de informação enviadas pelo Kernel são escritas no arquivo /var/log/messages do Linux (esta configuração pode ser alterada facilmente no arquivo /etc/syslog.conf). No caso de não haver configuração no syslog referente à mensagem enviada pelo Kernel, a mensagem será enviada para o terminal serial do sistema.
Para verificar as mensagens, abra o arquivo /var/log/messages do Linux e procure por uma linha parecida com a que se segue:
A remoção do módulo deve ser feita com o comando
rmmod como a seguir:
Ao remover o módulo, o Kernel imprimirá uma linha no arquivo /var/log/messages:
Caso haja necessidade de limpar os arquivos criados no momento da compilação do módulo, execute o comando
make passando como parâmetro a diretiva clean. Este parâmetro (e também o parâmetro module, usado no passo da compilação do módulo) pode ser modificado de acordo com a necessidade do usuário. Outras diretivas também podem ser criadas.
Conclusão
A criação de módulos do Kernel exige uma série passos e algumas ferramentas para que tudo funcione, entretanto não são passos complexos de se executar. A parte mais complexa do processo é a leitura e o entendimento do código fonte do próprio Kernel. A realização de depuração do Kernel também não é uma tarefa trivial de ser realizada, mas existem algumas ferramentas que auxiliam nesta tarefa.
Referencias
Ferramentas de Depuração para Linux:
Mais Informações sobre Device Drivers:
[1] Corbet, Jonathan, Alessandro Rubini, Greg Kroah-Hartman, and Alessandro Rubini. Linux Device Drivers. Beijing: O'Reilly, 2005.
[2] Love, Robert. Linux Kernel Development. Upper Saddle River, NJ: Addison-Wesley, 2010.
[3] Bovet, Daniel P., and Marco Cesati. Understanding the Linux Kernel. Beijing: O'Reilly, 2006.
[4] Cooperstein, Jerry. Writing Linux Device Drivers: a Guide with Exercises. [Beaverton, OR.]: J. Cooperstein, 2009.
[5] Salzman, Peter Jay., Michael Burian, and Ori Pomerantz. The Linux Kernel Module Programming Guide. [United States]: CreateSpace, 2009.
Editado by Mascotmobile |
||||||||||||||||
| Última atualização em Sex, 04 de Fevereiro de 2011 20:11 |


