Você está na página 1de 15

Perifricos e Interfaces had

Introduo aos mdulos do kernel Linux

5.1 Introduo Neste captulo ser apresentada uma introduo programao de mdulos do kernel Linux. Um mdulo do kernel, ou LKM de Linux Kernel Module, um segmento de cdigo que pode ser inserido ou removido a quente, i.e., sem necessidade de reinicializar o sistema, e que adiciona funcionalidade ao kernel. Os mdulos correm no chamado espao do kernel (kernel space), enquanto uma aplicao corre no espao de utilizador (user space), mesmo que o utilizador corrente seja o supervisor. No espao do kernel tudo permitido ao passo que no espao de utilizador existe proteco quanto ao acesso no autorizado a recursos como perifricos e memria. Esta funcionalidade possvel porque os processadores de hoje em dia implementam pelo menos dois nveis, ou modos de operao. A famlia x86 tem mesmo mais do que dois nveis de operao. Nos nveis inferiores algumas operaes no so permitidas. Assim em Linux, e de um modo geral em Unix, o kernel executa-se no nvel mais alto do CPU, tambm chamado modo supervisor (supervisor mode), enquanto as aplicaes so executadas no nvel mais baixo, o nvel de utilizador, onde o processador regula o acesso directo aos recursos de hardware. Cada modo de operao pode ter o seu prprio mapeamento de memria, isto o espao de endereamento diferente. Endereos virtuais iguais em modo de utilizador e modo supervisor podem ser mapeados em endereos fsicos diferentes. O sistema operativo transfere a execuo do espao de utilizador quando: 1. A aplicao efectua uma chamada ao sistema 2. Ocorre uma interrupo de hardware No primeiro caso o kernel opera em benefcio do processo que efectuou a chamada, e capaz de aceder a dados no espao de endereamento do processo. No segundo caso, o cdigo que trata as interrupes no est sincronizado com nenhum processo. Poder estar a responder a uma interrupo de um perifrico. Um tipo de mdulo o device driver, que permite ao kernel aceder a dispositivos de entradasada. Este tipo de mdulos sero abordados num captulo posterior. 5.2 Insero e remoo de mdulos no kernel Para se determinar quais os mdulos inseridos no kernel num dado instante pode ser dado no interpretador de comandos a instruo:
lsmod

Sendo apresentada uma lista com o seguinte formato:


Module 8250 serial_core usb_storage usbcore nls_iso8859_1 nls_cp850 http://w3.ualg.pt/~hdaniel/pin Size 33796 15208 83120 96224 3624 4424 Used by 0 [permanent] 1 8250 0 [permanent] 1 usb_storage,[permanent] 0 [permanent] 0 [permanent] PIN 2005-6 T05 - 1/15

Perifricos e Interfaces had

Na primeira coluna indicado o nome do modulo, que normalmente o nome do ficheiro sem extenso. Na segunda coluna indicado o seu tamanho em bytes, na terceira um contador de utilizao do mdulo. Tipicamente refere-se a dispositivos abertos ou sistemas de ficheiros montados. Um mdulo no pode ser removido enquanto o contador de utilizao no for zero. A ltima coluna indica que mdulos dependem do mdulo corrente. No exemplo anterior o mdulo usb_storage depende do mdulo usbcore, isto de algum modo usb_storage acede a alguma funcionalidade disponibilizada por usbcore. Para remover mdulos com dependncias, primeiro tero de ser removidos os mdulos que dele dependem. Se esta coluna indicar [permanet] o mdulo no pode ser removido. Se indicar (unused) o mdulo nunca foi usado, i.e, que nunca esteve numa situao em que no poderia ser removido. O comando lsmod apenas formata o contedo de /proc/modules. Tambm seria possvel identificar os mdulos presentes no kernel, com:
cat /proc/modules smbfs 54680 1 [permanent], Live 0xc889e000 8250 33796 0 [permanent], Live 0xc8894000 serial_core 15208 1 8250, Live 0xc884d000 usb_storage 83120 0 [permanent], Live 0xc887e000 usbcore 96224 1 usb_storage,[permanent], Live 0xc8865000 nls_iso8859_1 3624 0 - Live 0xc8845000 nls_cp850 4424 0 [permanent], Live 0xc8842000

Para inserir um modulo pode-se usar o comando insmod que como argumento espera o caminho do cdigo objecto do mdulo a inserir. como o hello.ko deve-se usar:
modprobe <caminho para cdigo objecto do mdulo>

A partir da verso 2.6 do kernel o cdigo objecto dos mdulos tem a extenso *.ko. Para remover usado o comando rmmod onde o argumeno o nome do mdulo como indicado por lsmod.
rmmod <nome do mdulo como listado por lsmod>

A insero de mdulos pode ser efectuada de um modo inteligente pelo comando insmod:
modprobe <nome do ficheiro com o cdigo objecto do mdulo sem extenso>

Neste caso o argumento apenas o nome do ficheiro com o cdigo objecto do mdulo sem extenso. O caminho completo do ficheiro com o cdigo objecto do mdulo encontra-se no ficheiro:
/lib/modules/<verso do kernel/modules.dep

Neste ficheiro tambm esto especificadas as dependncias de cada mdulo. Por exemplo se o mdulo: /lib/modules/2.6/a.ko depende dos mdulos b.ko e c.ko na mesma directoria e alm disso o mdulo c.ko depende tambm de b.ko, o ficheiro modules.dep poderia ser:
/lib/modules/2.6/a.ko: /lib/modules/2.6/c.ko /lib/modules/2.6/b.ko /lib/modules/2.6/b.ko: http://w3.ualg.pt/~hdaniel/pin PIN 2005-6 T05 - 2/15

Perifricos e Interfaces had /lib/modules/2.6/c.ko: /lib/modules/2.6/b.ko

Assim aps o caminho para o cdigo do mdulo, tem-se as lista de dependncias precedida por dois pontos (:). Deste modo, o comando modprobe antes de carregar o mdulo pedido carrega os mdulos na lista de dependncias, da direita para a esquerda. A remoo ser efectuada em sentido inverso. Este ficheiro pode ser automaticamente criado com o comando depmod. Este comando analisa todos os mdulos na directoria:
/lib/modules/<verso do kernel

procurando os smbolos exportados e os smbolos que um dado mdulo necessita. Os mdulos podem ser automaticamente carregados pelo kernel quando necessrio. Para isso existe o processo kerneld, que quando necessrio um mdulo que no est carregado executa modprobe para o carregar e indica ao kernel se a operao teve ou no sucesso. 5.3 Estrutura elementar dos mdulos O cdigo seguinte representa um mdulo muito simples, mas ainda com alguma funcionalidade:
/* hello.c */ #include <linux/module.h> #include <linux/kernel.h>

/* porque um mdulo */ /* para KERN_ALERT */

int init_module(void) { printk("<1>Hello world\n"); return 0; // Um valor no nulo indica que init_module falhou } // no tendo sido carregado.

void cleanup_module(void) { printk(KERN_ALERT "Goodbye world\n"); }

Em termos elementares o mdulo dividido em duas funes:


int init_module(void)

e
void cleanup_module(void)

que respectivamente so chamadas quando o mdulo inserido e quando removido. A primeira usada como ponto de entrada para inicializao do mdulo. Esta funo poder chamar ainda outras funes. Repare-se no entanto que numa aplicao tipca, quando a funo de entrada: main( ), termina, termina tambm a aplicao. No entanto num mdulo a funo de entrada, init_module ( ) serve apenas para registar o mdulo e indicar quais as funes que este disponibiliza. Aps a execuo desta funo o mdulo fica inactivo espera de pedidos.
http://w3.ualg.pt/~hdaniel/pin PIN 2005-6 T05 - 3/15

Perifricos e Interfaces had

Ao ser removido o mdulo a funo cleanup_module ( ) deve desfazer tudo o que foi feito por init_module ( ). Um uso possvel tambm para os mdulos substituir o cdigo de uma funo do kernel. Normalmente efectuado qualquer operao e depois chamada a funo original. Este desvio da chamada para cdigo do mdulo e depois para a funo original efectuado por init_module ( ). Assim, ao ser removido o mdulo deve repr o estado original do sistema de modo que este no fique instvel. Tal deve ser efectuado por cleanup_module ( ). A novas verses do kernel definem duas macros que permitem alterar o nome destas duas funes para outro definido pelo programador. Se o objectivo fosse alterar o nome das funes de entrada e sada respectivamente para hello_init ( ) e hello_exit ( ), deveriam ser colocadas no final do cdigo as macros definidas em <linux/init.h>: module_init (hello_init); module_exit (hello_exit); Como j foi referido este mdulo muito simples mas tem alguma funcionalidade, imprime uma mensagem quando inserido e outra quando removido, respectivamente: Hello world Goodbye world e

Ambas as funes usam printk ( ) para imprimir as mensagens. No entanto esta funo consiste num mecanismo de log para o kernel. De facto todas as mensagem impressas com printk so adicionadas ao registo que se encontra em /var/log, e que pode ser consultado com o comando dmesg. Assim as mensagens so impressas apenas se o mdulo for inserido ou removido numa consola TTY (modo texto). Se os comandos forem dados num terminal virtual como por exemplo o xterm as mensagens no so impressas, tendo de ser usado dmesg para as visualizar. O prtotipo de printk ( ) semelhante ao de printf:
int printk(const char * fmt, ...)

No entanto, como mostra o exemplo anterior, no nicio da string de formatao fmt, pode ser indicado um nvel de prioridade numrico, ou usada uma macro como definido em <linux/kernel.h>:
#define #define #define #define #define #define #define #define KERN_EMERG KERN_ALERT KERN_CRIT KERN_ERR KERN_WARNING KERN_NOTICE KERN_INFO KERN_DEBUG "<0>" "<1>" "<2>" "<3>" "<4>" "<5>" "<6>" "<7>" /* /* /* /* /* /* /* /* System is unusable action must be taken immediately critical conditions error conditions warning conditions normal but significant condition informational debug-level messages */ */ */ */ */ */ */

Se a prioridade for menor que o valor de int console_loglevel, a mensagem impressa na consola (terminal TTY), seno apenas adicionada ao registo (assumindo que os processos syslogd e klog) esto activos. Foi usada a prioridade KERN_ALERT para garantir que a mensagem apresentada na consola.
http://w3.ualg.pt/~hdaniel/pin PIN 2005-6 T05 - 4/15

Perifricos e Interfaces had

Para compilar os mdulos pode ser usada o makefile:


#Compilar hello.c para kernel 2.6 obj-m += hello.o #chama Makefile na directoria com as fontes do kernel all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules #Limpa os ficheiros gerados pela compilao, mantendo o cdigo fonte clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Se o ficheiro acima se chamar Makefile bastando executar o commando make na consola. Se tiver outro nome ters de ser especificado:
make f <nome do ficheiro>

Supondo que o modulo estava dividido em dois ficheiros, por exemplo a funo init_module() num ficheiro chamado init.c e a funo cleanup_module() num ficheiro chamado exit.c, bastaria alterar as linhas que indicam os ficheiros a compilar, no incio do makefile:
#Compilar hello.c para kernel 2.6 obj-m += hello.o hello-objs := init.o exit.o

Aps compilar o mdulo pode-se aceder a informao sobre este com o comando:
modinfo hello.ko filename: vermagic: depends: hello.ko 2.6.14-mini 386 gcc-3.3

Que indica o nome do ficheiro, a verso do kernel, o processador para o qual foi compilado (neste caso seria para 386 e sucessores), e qual o compilador em vermagic:. Na ltima linha indicaria dependncias se existissem. Para mdulos mais complexos este comando pode retornar mais informao. Agora ao inserir o mdulo, repare-se na mensagem impressa na consola:
hello: module license 'unspecified' taints kernel. Hello world

que indica que o kernel pode ter sido corrompido. Esta mensagem aparece porque no foi especificada uma licena no mdulo. Este mecanismo surgiu no kernel 2.4 para indicar se o cdigo aberto ou proprietrio. A licena pode ser indicada com a macro seguinte. Pode ser colocada logo aps a incluso dos headers no incio do cdigo, mas no obrigatrio:
MODULE_LICENSE("GPL"); ou MODULE_LICENSE("Proprietary");

http://w3.ualg.pt/~hdaniel/pin

PIN 2005-6

T05 - 5/15

Perifricos e Interfaces had

5.3.1 Informao dos mdulos Alm da informao sobre a licena referida no ponto anterior, o programador de um mdulo pode tambm indicar o autor, uma descrio do mdulo, a verso do mdulo, e uma nome alternativo:
MODULE_AUTHOR("Nome do autor"); MODULE_DESCRIPTION("Simples mdulo de demonstrao: Hello world"); MODULE_VERSION("1.0"); MODULE_ALIAS("Ol!");

As macros acima indicadas, e de um modo geral todas as comeadas por MODULE_ podem ser usadas em qualquer parte do cdigo, existe no entanto uma conveno de as colocar no fim do cdigo. conveniente que quando se desenvolvem mdulos as 3 primeiras macros sejam usada. A informao dada por modinfo ser:
filename: author: description: version: alias: vermagic: depends: srcversion: hello.ko Nome do autor Simples mdulo de demonstrao: Hello world 1.0 Ol 2.6.14-mini 386 gcc-3.3 6D52F7CBC1A3718D85C980E

O campo srcversion uma soma dos ficheiros que compe os mdulos. Isto ajuda quem desenvolve os cdigos a verificar se os ficheiros fonte disponveis foram os usados para compilar um dado mdulo, pois possvel que um programador faa alguma alterao num mdulo e no indique uma nova verso com MODULE_VERSION. 5.3.2 Exportao de smbolos Por defeito todos os smbolos de um mdulo, identificadores de variveis e funes, no so visveis no kernel, isto so locais ao mdulo. A nica necessidade de um mdulo exportar smbolos, para que outros mdulos possam us-los, i.e., para mdulos que dependem de outros mdulos.

Fig. 5-1: Empilhamento de mdulos do driver do porto paralelo (in Corbet et. al. 2005) Na figura anterior tm-se a representao esquemtica do empilhamento de drivers para o porto paralelo. Este empilhamento pode ser visto como uma decomposio do driver em camadas de abstraco de modo a simplificar o desenvolvimento do mesmo driver para hardware no totalmente compatvel. assim comum ter um modulo com funes de baixo nvel dependentes do hardware que exporta smbolos para mdulos de mais alto nvel.
http://w3.ualg.pt/~hdaniel/pin PIN 2005-6 T05 - 6/15

Perifricos e Interfaces had

No caso do porto paralelo, o mdulo de mais baixo nvel parport disponibiliza funes de baixo nvel para os mdulos de mais alto nvel: parport_pc e lp, o driver da impressora. Todos os mdulos exportam tambm smbolos para a API do kernel. Nestes casos conveniente usar o comando modprobe para carregar um mdulo como por exemplo lp, e todos os outros do qual este mdulo depende. Como j foi referido, as dependncias podem ser verificas com o comando modinfo ou no ficheiro /lib/modules/<verso do kernel/modules.dep. Quando um mdulo carregado, quaisquer smbolos exportados so colocados na tabela de smbolos do kernel (Kernel Symbol Table). Para exportar smbolos so usadas as seguintes macros: EXPORT_SYMBOL (identificador); EXPORT_SYMBOL_GPL (identificador); A segunda faz com que o smbolo seja acessvel fora do mdulo apenas para mdulos com licena GPL. Assim nem todas as funes da libc esto disponveis para mdulos. Estes usam apenas os smbolos disponibilizados pelo kernel. Estes smbolos podero ser listados com:
cat /proc/kallsyms

Repare-se que parte das funes da libc so wrappers para chamadas ao sistema, e no necessrio us-las em modo kernel. Por exemplo a funo puts() efectua a chamada write. Se se pretender determinar quais as chamadas ao sistema efectuadas por uma aplicao, pode-se usar o comando strace. Por exemplo compilando o programa seguinte:
/* hello.c */ #include <stdio.h> main () { puts(Hello world); }

E executando:
strace hello

Tm-se um traado das chamadas ao sistema. As primeiras tm a ver com a inicializao do processo pelo sistema operativo. No final encontra-se uma linha com a chamada write efectuada por puts().
execve("./hello", ["./hello"], [/* 13 vars */]) = 0 uname({sys="Linux", node="balanca", ...}) = 0 (...) write(1, "Hello world\n", 12Hello world ) = 12 munmap(0xb7f2f000, 4096) = 0 semget(12, 3086077511, IPC_CREAT|IPC_EXCL|IPC_NOWAIT|0xb7f1c000|0

Repare-se que a string impressa no meio do traado. O valor 12 retornado indica os caracteres que foram escritos. http://w3.ualg.pt/~hdaniel/pin PIN 2005-6 T05 - 7/15

Perifricos e Interfaces had

Para mdulos no possvel determinar este traado com strace. 5.3.3 Inicializao e encerramento J foi referido que o nome das funes de entrada e sada podem ser alteradas usando macros definidas em <init.h>. Neste header esto definidas ainda outras macros que podem ser usadas na inicializao e no encerramento do mdulo.

A definio da funo de inicializao, embora no seja obrigatrio, deve ser no formato:


static int __init initialization_function(void) { /* Cdigo de inicializao */ } module_init(initialization_function);

Como j foi visto por defeito no os smbolos locais dos mdulos no so exportados, por isso no mesmo que a funo no seja declarada como static no visvel fora do mdulo. O modificador __init, indica ao carregador de mdulos que aps o mdulo ter sido inicializado, a funo de inicializao pode removida da memria, deixando-a disponvel. Existe tambm uma etiqueta semelhante __initdata para libertar da memria dados utilizados apenas na incializao:
static int umInteiro __initdata = 1;

Deve-se tomar cuidado para no se utilizar __init e __initdata com funes e dados necessrios aps a inicializao. A funo de encerramento, que liberta recursos utilizados pelo mdulo antes deste ser descarregado, definida como:
static void __exit cleanup_function(void) { /* liberta recursos */ } module_exit(cleanup_function);

O modificador __exit, indica que se o mdulo for montado directamente no kernel ou se a opo de remoo de mdulos do kernel estiver desactivada, o cdigo da funo no carregado para a memria durante a insero do mdulo. Se o mdulo no pode ser removido no hs necessidade ocupar memria com a funo de encerramento.

5.4 Passagem de parmetros para os mdulos A operao do driver pode variar de acordo com o sistema onde ser instalado. Pode ser necessrio especificar os endereos de E/S de um dado perifrico ou os seus portos. Para que o driver se adapte ao sistema possvel passar-lhe argumentos na linha de comandos quando inserido com modprobe ou insmod:
insmod modulo.ko count = 10 str = "uma string"

Para que os argumentos sejam passados quando o mdulo carregado, os parmetros correspondentes devem ser declarados com a macro module_param, definida em http://w3.ualg.pt/~hdaniel/pin PIN 2005-6 T05 - 8/15

Perifricos e Interfaces had

<linux/moduleparam.h >. Esta macro recebe trs parmetros: o nome da varivel, o seu tipo e as permisses para expr os parmetros do mdulo numa entrada sysfs, se o valor for diferente de zero. Convm referir que sysfs um sistema de ficheiros virtual, que surgiu com o kernel 2.6, e que exporta informao sobre dispositivos e drivers para o espao de utilizador. Pode ser usado tambm para configurao. Se o valor deste ltimo parmetro for nulo no existe entrada em sysfs. Para dar permisses devem ser usadas as macros definidas em <linux/stat.h>. Por exemplo: S_IRUGO S_IRUGO | S_IWUSR torna o valor do parmetro visvel mas no altervel. torna o valor do parmetro visvel e altervel pelo root.

De qualquer modo alterar os parmetros do mdulo poder ter implicaes na sua funcionalidade, de modo que se no for estritamente necessrio aconselha-se a que no sejam dadas permisses, ou apenas visualizao dos valores dos parmetros com S_IRUGO. Ento para se poder aceder aos argumentos passados no exemplo anterior, o cdigo seria:
static int count = 0; static char *str = qq coisa; module_param(count, int, S_IRUGO); module_param(str, charp, 0); MODULE_PARM_DESC(str, Uma cadeia de caracteres);

Repare-se que os nomes dos parmetros da linha de comando tm de coincidir com as variveis do cdigo. Alm disso devem ser dados valores por defeito aos argumentos, pois o mdulo poder ser carregado sem que todos os parmetros sejam especificados na linha de comando. A macro MODULE_PARAM_DESC usada para descrever os parmetros. Esta informao pode ser acedida com modinfo. Para o exemplo acima seria apresentado:
(...) depends: parm: str:Uma cadeia de caracteres (charp) parm: count:int

Assim se no for usada esta macro apenas indicado o nome do parmetro e o seu tipo. Se for usada tambm apresentada a descrio. Vrios tipos de parmetros podem ser especificados: bool invbool charp int long short uint
http://w3.ualg.pt/~hdaniel/pin PIN 2005-6 T05 - 9/15

toma valores true ou false, e guarda-os numa varivel inteira. inverte o valor booleano passado. Um ponteiro para character. Memria alocada para as strings passadas pelo utilizador e o ponteiro aponta para o seu nicio

Perifricos e Interfaces had

ulong ushort

Inteiros. O prefixo u indica sem sinal.

Podem tambm ser definidos arrays como parmetros, onde os valores so separados por vrgulas:
insmod modulo.ko array = 10,3,4

Para declarar estes parmetros usada a macro module_param_array. Os primeiros dois parmetros e o ltimo so idnticos a module_param, o terceiro um ponteiro para uma varivel que guarda o nmero de itens do array passados na linha de comando.
static int arr_count = 0; static int array[3] = {-1, -1, -1}; module_param_array(array, int, &arr_count, 0); MODULE_PARM_DESC(array, Vector para um mximo de 3 inteiros);

Se o tentar utilizador passar mais itens do que a dimenso do array um erro apresentado na consola e o mdulo no carregado. Por isso talvez seja boa prtica descrever estes parmetros indicando tambm a sua capacidade. Seguidamente apresentado o exemplo de um mdulo completo:
/* modpar.c * Demonstrao de passagem de argumentos da linha de comandos para mdulo. */ #include <linux/module.h> /* Needed by all modules */ #include <linux/kernel.h> /* Needed for KERN_ALERT */ #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/stat.h> static static static static int count = 0; char *str = "qq coisa"; int arr_count = 0; int array[3] = {-1, -1, -1};

module_param(count, int, S_IRUGO); module_param(str, charp, S_IRUGO); module_param_array(array, int, &arr_count, 0); MODULE_PARM_DESC(count, "Inteiro 32 bits"); MODULE_PARM_DESC(str, "Cadeia de caracteres"); MODULE_PARM_DESC(array, "Vector inteiros com max 3 elementos"); static int __init start(void) { int i; printk(KERN_ALERT "modpar inserido\n"); printk(KERN_ALERT "count= %d\n", count); printk(KERN_ALERT "str = %s\n", str); printk(KERN_ALERT "itens no array = %d\nitems:", arr_count); for (i=0; i<arr_count; ++i) printk(" %d ", array[i]); return 0; } static void __exit stop(void) { printk(KERN_ALERT "modpar removido!\n"); } module_init(start); module_exit(stop); http://w3.ualg.pt/~hdaniel/pin PIN 2005-6 T05 - 10/15

Perifricos e Interfaces had MODULE_LICENSE("GPL"); MODULE_AUTHOR("Helder Daniel"); MODULE_VERSION("1.0"); MODULE_DESCRIPTION("Demonstrao comandos para mdulo.");

de

passagem

de

argumentos

da

linha

de

Se o mdulo for inserido com:


insmod modpar.ko count=4 str="Teste" array=1,2

Ter-se- no registo do sistema ou na consola:


modpar inserido count= 4 str = Teste itens no array = 2 items: 1 2 <1>modpar removido!

E a informao do mdulo dada por modinfo ser:


filename: license: author: version: description: para mdulo. vermagic: depends: srcversion: parm: parm: parm: modpar.ko GPL Helder Daniel 1.0 Demonstrao de passagem de argumentos da linha de comandos 2.6.14-mini 386 gcc-3.3 F22A0B1E5C7103268020556 array:Vector inteiros com max 3 elementos (array of int) str:Cadeia de caracteres (charp) count:Inteiro 32 bits (int)

Agora em sysfs possvel obter informao sobre o mdulo. Para visualizar o valor dos parmetros basta editar o contedo dos ficheiros com o mesmo nome, por exemplo::
cat /sys/module/modpar/parameters/count

5.5 Notas sobre programao do kernel A programao do kernel difere da programao no espao de utilizador de muitos modos, como ser referido ao longo do texto. Neste ponto sero indicadas algumas diferenas fundamentais. As aplicaes, i.e, processos em modo de utilizador, so mapeadas na memria virtual com uma pilha muito grande. Nesta pilha colocado no s o endereo de retorno das funes, mas tambm todas as variveis automticas criadas pelas funes, i.e., os argumentos das funes e as variveis declaradas localmente nas funes. O kernel por outro lado tem uma pilha que pode ser to pequena como uma nica pgina de 4KBytes. As funes de um mdulo partilham esta pilha com as funes de todos os outros mdulos e do restante cdigo do kernel. Por isso no se deve declarar variveis locais muito grandes, como por exemplo:
int init_module(void) { unsigned char localbuffer [1000]; // .... } http://w3.ualg.pt/~hdaniel/pin PIN 2005-6 T05 - 11/15

Perifricos e Interfaces had

A alocao de variveis desta natureza deve ser efectuada dinamicamente quando o mdulo for chamado para efectuar uma qualquer funo. O cdigo do kernel no pode efectuar operaes de vrgula flutuante. Refira-se que no so necessrias na programao do kernel e que a sua utilizao implica uma sobrecarga. fcil verificar a natureza desta sobrecarga quando um processo efectua uma chamada ao sistema. Antes de se entrar em modo kernel o contexto do processo deve ser preservado, pelo menos os registo do CPU, pois no possvel determinar quais registos o cdigo do kernel vai utilizar. Ao terminar a chamada, o contedo dos registos reposto antes de se devolver a execuo ao processo. Assim existiria uma perda de tempo desnecessria se se tivesse de guardar e repor tambm os valores dos registos de vrgula flutuante. Normalmente um mdulo, por exemplo um driver, executa uma dada tarefa a pedido de um processo que corre em modo utilizador. Este o caso de uma chamada ao sistema como open ou write. Um modo de se aceder a partir do mdulo ao descritor do processo que invocou a chamada, i.e, estrutura task_struct, definida em <linux/sched.h>, atravs do ponteiro global current, definido em <asm/sched.h>. O seguinte cdigo permite retornar o nome do processo corrente, i.e, os primeiros 15 caracteres do nome do ficheiro executvel, e o seu PID:
printk(KERN_INFO "The process is \"%s\" (pid %i)\n", current->comm, current->pid);

5.6 Compilao de mdulos para kernel pr-compilado Antes de mais deve-se referir que o sistema onde se pretende desenvolver mdulos deve ter um kernel compilado com suporte para insero de mdulos. Para configurar o kernel pode ser usado o comando:
make menuconfig

Sendo aberta a janela seguinte:

Fig. 5-2: Menu principal da configurao do kernel Escolhendo a opo indicada entra-se no menu de configurao do suporte para mdulos carregveis: http://w3.ualg.pt/~hdaniel/pin PIN 2005-6 T05 - 12/15

Perifricos e Interfaces had

Fig. 5-3: Configurao do suporte para mdulos carregveis Neste menu devem estar assinaladas as opes indicadas acima: Enable loadable module support para habilitar o suporte. Module unloading para que os mdulos possam ser removidos com o comando rmmod. Force Module unloading para que se possa fora a remoo de mdulos com rmmod f. Durante o desenvolvimento de mdulos possvel que devido a algum erro no cdigo o mdulo no possa ser removido. Se esta opo no for ligada a nica alternativa para a remoo do mdulo reinicializar o sistema. Automatic kernel module loading para que os mdulos possam ser automaticamente carregados pelo kernel se necessrio. Esta ltima opo no estritamente necessria mas poder ser util, mesmo que o sistema no seja usado para desenvolvimento de mdulos. Aps ser efectuada a configurao necessrio compilar o kernel usando a srie de comandos:
make install make module_install #compila o kernel #compila os mdulos

Para que se possa inserir um mdulo no kernel a verso do mdulo e do kernel devem ser iguais, mais precisamente a vermagic. A vermagic do kernel pode ser determinada com:
uname r

e a do mdulo com o comando modinfo. Para o exemplo anterior tinha-se:


vermagic: 2.6.14-mini 386 gcc-3.3

Sendo apenas a primeira string: 2.6.14-mini a que interessa em termos de verificao da possibilidade de insero do mdulo. Se forem iguais possvel inserir o mdulo com insmod. Se no forem pode-se usar o comando:
modprobe --force-vermagic hello.ko

Para que este comando possa ser utilizado, como j foi referido, pelo menos o caminho para mdulo ter de estar referido no ficheiro:
http://w3.ualg.pt/~hdaniel/pin PIN 2005-6 T05 - 13/15

Perifricos e Interfaces had

/lib/modules/2.6.14-mini/modules.dep

Alm disso este comando potencialmente inseguro, pois permite forar a instalao de um mdulo compilado para uma verso anterior do kernel presente no sistema. Um meio mais seguro consiste em instalar no sistema as fontes do kernel que est a ser executado alterar a vermagic para que seja igual. Repare-se que a vermagic original do kernel indica a sua verso, e to simples como por exemplo 2.6.14, no entanto algumas distribuies alteram a vermagic. Por exemplo a distribuio Mandrake adiciona mdk ao nmero da verso: 2.6.14-mdk. Para alterar a vermagic pode-se aceder configurao do kernel, como j foi referido, e no menu principal escolher a opo General Setup, entrando-se no menu:

Fig. 5-4: Configurao geral do kernel 2.6.14 A primeira opo permite que seja adicionada uma string verso do kernel. No exemplo ser mini, ficando 2.6.14-mini. Agora necessrio actualizar os ficheiros do cdigo fonte. Para isso basta executar make at que seja compilada a 3 linha SPLIT include/linux/autoconf.h -> include/config/*, interrompendo a compilao com <ctrl-c>:
CHK UPD SPLIT HOSTCC HOSTLD CC include/linux/version.h include/linux/version.h include/linux/autoconf.h -> include/config/* scripts/mod/modpost.o scripts/mod/modpost init/main.o

Pois o smbolo UTS_RELEASE do ficheiro /usr/src/linux/include/linux/version.h j foi actualizado nas duas primeiras linhas. Alternativamente pode-se editar este ficheiro e alterar a string UTS_RELEASE.

http://w3.ualg.pt/~hdaniel/pin

PIN 2005-6

T05 - 14/15

Perifricos e Interfaces had

Como nota final deve ser referido que a opo Module versioning support no menu de configurao apresentado na figura Fig. 5-3, possvel inserir alguns mdulos mesmo que as vermagics sejam diferentes. No entanto esta opo experimental, no garante que todos os mdulos possam ser inseridos. Tambm no existe garantia que um kernel pr-compilado, o tenha sido com esta opo habilitada.

5.7 Bibliografia Corbet, Jonathan, Alessandro Rubini e Kroah-Hartman, Greg (2005). Linux Device Drivers 3rd edition, OReilly, http://lwn.net/Kernel/LDD3/ Salzman, Peter Jay, Michael Burian and Ori Pomerantz (2005). The Linux Kernel Module Programming Guide, http://www.tldp.org/LDP/lkmpg/2.6/lkmpg.pdf Linux man pages

http://w3.ualg.pt/~hdaniel/pin

PIN 2005-6

T05 - 15/15

Você também pode gostar