|
O Programação de Módulos para o Kernel do Linux é um
documento livre protegido pela GNU Free Documentation License (FDL) publicada pela Free Software Foundation. É permitido
copiar, modificar e distribuir este documento, porém
os termos desta licença devem ser respeitados.
Este guia é destinado às pessoas que querem começar
a escrever seus módulos e aprender sobre o kernel do
Linux. Esperamos que este trabalho seja útil para estas
pessoas. Para compreender melhor este guia você precisa
conhecer a linguagem de programação C. Se você já tem
estes conhecimentos, boa leitura.
Este guia é aberto e encontra-se em construção. Colaboradores
são bem-vindos , entre em contato com a equipe
do kernel-br.
Changelog
09/04/2002 - incluído comunicação entre módulos.
01/04/2002 - incluído procfs.c e tty.c.
26/03/2002 - Kernel threads por Thobias.
25/03/2002 - Introdução e Hello World
por Thobias. Parâmetros na inicialização
por polo.
Sumário
- Introdução
- Hello World
- Parâmetros na Inicialização
- Sistema de Arquivos /proc
- Substituindo o printk
- Comunicação entre Módulos
- Dispositivos de Caractere
- IOCTLs
- Chamadas de Sistema
- Manipulando Interrupções
- Gerenciamento de Memória
- Kernel Threads
- Apêndice
- Bibliografia
1. Introdução
Módulos são partes do kernel que não precisam estar
sempre em memória. Eles podem ser carregados quando
se fazem necessários pelo kernel e ausentes no restante
do tempo. Na maioria das vezes são drivers para dispositivos.
Imagine por exemplo os drivers para o seu CD-ROM, placa
de rede ou som, eles não precisam estar sempre em memória
e assim ocupando espaço que poderia estar sendo utilizado
por alguma aplicação. Em vez disso, eles podem ser compilados
como módulos do kernel, ou seja, só vão ser carregados
para memória quando você acessar o dispositivo, e removidos
quando não for mais necessário. Para isto o Linux executa
periodicamente, via crontab, o comando /sbin/rmmod -as, o qual tenta remover os módulos que não
estão mais sendo usandos. Com eles você pode adicionar
certas funcionalidade no kernel sob demanda, ou seja,
so quando for necessário, e deste modo não precisa recompilar
o seu kernel para adicionar alguma nova funcionalidade,
etc.
Módulos são arquivos ELF. Eles são linkados
com o kernel através do comando /sbin/insmod
e removidos via /sbin/rmmod.
Para mais detalhes, consulte as páginas de manual: man
insmod e man rmmod.
Cada módulo tem um usage counter, que é incrementado
quando uma operação envolvendo o módulo inicia e decrementado
quando a operação termina. Um exemplo é quando você
tem um sistema de arquivos compilado como módulo. Quando
o módulo é linkado com o kernel este contador
é 0, se você monta uma partição com este sistema de
arquivos o contador é incrementado com 1, se você desmontar
esta partição ele é decrementado. Este número também
é incrementado também quando você tem um módulo que
depende de outro. Os módulos carregados e seus contadores
podem ser obtidos através do comando /sbin/lsmod.
2. Hello World
Vamos agora fazer nosso primeiro programa, o famoso
"Hello World". Os módulos do kernel tem pelo menos duas
funções: init_module que
é chamada quando o módulo é linkado com o kernel
e cleanup_module que é chamada
antes do módulo ser removido.
Os nomes destas funções podem ser modificados através
das macros module_init e
module_exit. A macro module_init
define qual função será chamada quando o módulo for
inserido no kernel. A macro module_exit
define qual função será chamada na remoção do módulo.
/* Arquivos de header necessários */
#include <linux/module.h>
#include <linux/kernel.h>
int init_module(void)
{
/*
* printk() é uma função semelhante ao printf, porém programação em
* kernel-space é muito diferente de user-space. O kernel não é linkado
* com a libc ou qualquer outra biblioteca, deste modo ele tem suas próprias
* funções. printk você pode usar um nível de priproridade que vai de 0 a 7.
* Estes níveis podem serem encontrados em include/linux/kernel.h
*/
printk(KERN_ALERT "Hello World!\n");
/*
* Se você retornar um valor diferente de zero, significa
* que ini_module falhou e o módulo do kernel não pode ser carregado.
*/
return 0;
}
void cleanup_module(void)
{
printk(KERN_ALERT "Goodbye !\n");
}
Nosso primeiro módulo esta pronto! Para compilar o
módulo utilizamos o comando :
gcc -c -o hello.o hello.c -D__KERNEL__ -DMODULE -DLINUX -O2 -Wall
-I/usr/src/linux/include
- -D__KERNEL__: headers do kernel, este código vai
rodar em kernel-space
- -DMODULE: arquivo de header para módulo, definições
para um módulo do kernel
- -O2: nível de otimização, códigos inline
- -Wall: mostra todos os erros e warnings
- -I/usr/src/linux/include: procura pelos includes
neste diretório (kernel includes)
Podemos automatizar a compilação do módulo por meio
do Makefile. Um exemplo de Makefile para compilar o
nosso Hello World:
#Makefile para compilar um módulo de kernel
all: hello.o
SEARCHDIR = -I. -I/usr/src/linux/include
KCFLAGS = -D__KERNEL__ -DMODULE -DLINUX -O2 -Wall ${SEARCHDIR}
hello.o: hello.c
gcc $(KCFLAGS) -c -o $@ $<
clean:
- rm -f hello.o
Para testar o módulo, utilize o comando insmod
hello.o e para remover rmmod
hello.
3. Parâmetros na Inicialização
O módulo do exemplo anterior é simples, note como ele
é "fixo" e não é possível configurar nenhum
parâmetro.
Um modo de passar argumentos que sejam necessários
antes de um programa ou módulo do kernel iniciar é através
de parâmetros na linha de comando. Em módulos de kernel
você não pode usar argc e argv. Você define variáveis
globais no módulo as quais o /sbin/insmod irá preencher.
Para isto nós utilizamos a macro MODULE_PARM para avisar
o insmod que experamos parâmetros, com um determinado
nome e tipo. Os tipos de dados aceitos pelo MODULE_PARM
são:
b byte
h short
i int
l long
s string
Para mais detalhes, dê uma olhada neste arquivo: include/linux/module.h.
O próximo exemplo mostra como fazer para os módulos
retornarem os valores de strings predefinidas.
/* Guilherme Polo <gpolo@nl.linux.org>
* Licença: GPL
* Módulo para tópico: Parametros na Inicialização */
#include <linux/kernel.h>
#include <linux/module.h>
char *p1, *p2; /* parametros */
MODULE_PARM(p1, "s");
MODULE_PARM(p2, "s");
/* Inicialização do módulo */
int init_module()
{
if (p1 == NULL || p2 == NULL)
{
/* Mensagem que retorna se algum dos parametros for vazio */
printk("Você não especificou parametros suficientes. \n
Você deve usar o insmod com os parametros: p1=<algo> p2=<algo>\n");
return 1;
} else
/* Retorna a mensagem com valor dos parametros */
printk("p1=%s p2=%s\n", p1,p2);
return 0;
}
/* Descarregar o modulo */
void cleanup_module()
{
printk(KERN_ALERT "Fui-me \n");
}
Para compilar este módulo você pode utilizar o mesmo
exemplo do anterior. Para inserir o módulo: insmod
modulo.o p1=kernel p2=modulo.
4. Sistema de Arquivos /proc
O sistema de arquivo /proc,
também chamado de procfs, é um sistema de arquivo especial
para o kernel do Linux. É um sistema de arquivo virtual,
não é associado a nenhum block device e existe somente
na memória. Os arquivos do /proc
existem para permitir que programas no espaço do usuário
acessem certas informações do kernel, como informações
de processos /proc/[0-9]+/
e também para depuração (debug).
Note que os arquivos do /proc/sys
são arquivos sysctl e são
gerenciados por uma API diferente. Estes arquivos permitem
a inspeção e reconfiguração instantânea de certos recusos
do sistema, sem a necessidade de editar código-fonte,
recompilar e reinicializar o sistema.
Para mais informações veja o Kernel API book em
Documentation/DocBook/kernel-api na árvore do
kernel. As permissões dos arquivos em /proc
são:
S_IRWXU = 0700
S_IRUSR = 0400
S_IWUSR = 0200
S_IXUSR = 0100
S_IRWXG = 0070
S_IRGRP = 0040
S_IWGRP = 0020
S_IXGRP = 0010
S_IRWXO = 0007
S_IROTH = 0004
S_IWOTH = 0002
S_IXOTH = 0001
S_IRWXUGO = (S_IRWXU|S_IRWXG|S_IRWXO)
S_IRUGO = (S_IRUSR|S_IRGRP|S_IROTH)
S_IWUGO = (S_IWUSR|S_IWGRP|S_IWOTH)
S_IXUGO = (S_IXUSR|S_IXGRP|S_IXOTH)
Você pode usar S_ALGO ou
números para permissões. Os números geralmente são usados
pois são mais faceis de lembrar e mais práticos. Por
exemplo: 0644 é igual a S_IWUSR
| S_IRUGO. O módulo abaixo mostra um exemplo
de como manipular arquivos no /proc.
/*
* procfs.c
*
*
*
*/
/* Headers necessárias para esse módulo */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#define NOME "exemplo de procfs"
/* Nome do diretorio a criar no /proc */
#define PROCDIR "exemplo"
#define ARQUIVO_T 8
struct fb_data_t {
char nome[ARQUIVO_T + 1];
char valor[ARQUIVO_T + 1];
};
static struct proc_dir_entry *diretorio, *normal,
*vazio, *dispositivo, *link;
struct fb_data_t normal_data;
/* funcao para ler arquivo normal */
static int ler_normal(char *page, char **start,
off_t off, int count,
int *eof, void *data)
{
int tam;
struct fb_data_t *fb_data = (struct fb_data_t *)data;
MOD_INC_USE_COUNT;
tam = sprintf(page, "%s = '%s'\n",
fb_data->nome, fb_data->valor);
MOD_DEC_USE_COUNT;
return tam;
}
/* funcao para escrever no arquivo normal */
static int escrever_normal(struct file *file,
const char *buffer,
unsigned long count,
void *data)
{
int tam;
struct fb_data_t *fb_data = (struct fb_data_t *)data;
MOD_INC_USE_COUNT;
if(count > ARQUIVO_T)
tam = ARQUIVO_T;
else
tam = count;
if(copy_from_user(fb_data->valor, buffer, tam)) {
MOD_DEC_USE_COUNT;
return -EFAULT;
}
fb_data->valor[tam] = '\0';
MOD_DEC_USE_COUNT;
return tam;
}
/* Inicializacao */
static int __init iniciar_procfs(void)
{
/* criar diretorio */ /* NULL = /proc */
diretorio = proc_mkdir(PROCDIR, NULL);
diretorio->owner = THIS_MODULE;
/* criar arquivo de nome 'normal' com permissao 0644
no diretorio exemplo */
normal = create_proc_entry("normal", 0644, diretorio);
strcpy(normal_data.nome, "normal");
strcpy(normal_data.valor, "1");
normal->data = &normal_data;
normal->read_proc = ler_normal;
normal->write_proc = escrever_normal;
normal->owner = THIS_MODULE;
/* criar arquivo vazio */
vazio = create_proc_entry("vazio", 0644, diretorio);
/* criar dispositivo ttyS0 */
dispositivo = proc_mknod("ttyS0", S_IFCHR | 0666,
diretorio, MKDEV(4, 64));
dispositivo->owner = THIS_MODULE;
/* criar um symlink */
link = proc_symlink("link", diretorio,
"normal");
link->owner = THIS_MODULE;
/* mensagem pos-inicio */
printk(KERN_INFO "%s inicializado\n", NOME);
return 0;
}
static void __exit limpar_procfs(void)
{
/* Remover arquivos criados no proc */
remove_proc_entry("link", diretorio);
remove_proc_entry("ttyS0", diretorio);
remove_proc_entry("vazio", diretorio);
remove_proc_entry("normal", diretorio);
/* Remover diretorio */
remove_proc_entry(PROCDIR, NULL);
/* Enviar mensagem */
printk(KERN_INFO "%s desregistrado!\n",
NOME);
}
/* Iniciar */
module_init(iniciar_procfs);
/* Finalizar */
module_exit(limpar_procfs);
/* Informacoes do Modulo */
MODULE_AUTHOR("Guilherme Polo");
MODULE_DESCRIPTION("Sistema de Arquivos /proc");
EXPORT_NO_SYMBOLS;
5. Substituindo o printk
O printk envia as mensagens
para o console, mas as vezes é necessário enviar mensagens
para o tty. Por exemplo, quando você está em um xterm
e inicializa ou retira um módulo do kernel, as mensagens
de erro não aparecerão no seu xterm.
Mas você pode escrever direto no tty.
Isto é feito usando o current,
que é um ponteiro para o processo rodando. Com o current,
consegue-se acessar a estutura do tty
do processo corrente.
O módulo abaixo mostra um exemplo de como escrever
no tty corrente.
/*
* tty.c
*
* Este módulo escreve no tty atual
*
* A função print_string tem os mesmos parâmetros de um printf.
*
* Para mais informações sobre as va_list: man va_arg
*
* 28/03/2002
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/tty.h>
#include <linux/mm.h>
#include <linux/slab.h>
#define MAX_LEN 50
int print_string(char *fmt, ...)
{
struct tty_struct *my_tty;
va_list args;
char *buffer=kmalloc(MAX_LEN, GFP_KERNEL);
int printed_len;
my_tty = current->tty;
/*
* Lendo do tty atual
* Esta mensagen nao aparecera no tty.
* Estou usando printk entao /var/log/messages
*/
printk(KERN_ALERT "tty name = %s\n",my_tty->driver.name);
va_start(args, fmt);
printed_len = vsnprintf(buffer, MAX_LEN, fmt, args);
va_end(args);
if (printed_len<0) {
printk(KERN_ALERT "ERRO : Tamanho maximo da string excedido [%d]\n",MAX_LEN);
return -1;
}
/*
* Escrevendo no tty atual
*/
if (my_tty != NULL) {
(*(my_tty->driver).write)(my_tty,0,buffer,printed_len);
(*(my_tty->driver).write)(my_tty,0,"\015\012",2);
}
else
printk(KERN_ALERT "tty = NULL\n");
return 1;
}
int tty_init_module(void)
{
int i=100, j;
char *string = "Linux Kernel";
printk(KERN_ALERT "Hello !\n");
j=print_string("Teste de tty: %s Valor = [%d]",string,i);
if (j<0)
printk(KERN_ALERT "ERRO no print_string\n");
return 0;
}
void tty_cleanup_module(void)
{
printk(KERN_ALERT "Goodbye !\n");
}
module_init(tty_init_module);
module_exit(tty_cleanup_module);
MODULE_DESCRIPTION("Exemplo, escreve no tty");
MODULE_AUTHOR("Thobias Salazar Trevisan");
MODULE_LICENSE("GPL");
EXPORT_NO_SYMBOLS;
6. Comunicação entre Módulos
Quando criamos módulos mais complexos, torna-se mais
fácil dividir o mesmo em pequenos módulos. Mas a comunicação
entre eles é diferente de user space.
O kernel utiliza a "The Kernel Symbol Table". Esta
tabela contém endereços de funções e variáveis globais
do kernel. Quando se roda o comando insmod os "undefined symbols" são resolvidos contra a tabela
de símbolos públicos do kernel. Esta tabela pode ser
acessada através do arquivo /proc/ksyms
Quando um novo módulo é carregado, qualquer símbolo
exportado pelo mesmo torna-se parte desta tabela, e
você poderá vê-lo em /proc/ksyms, ou através da saída do comando ksyms. Como isto funciona:
Como vimos antes, um módulo é um arquivo objeto ELF.
Um arquivo objeto ELF consiste de várias seções. Algumas
são partes básicas do arquivo objeto. Mas podem ser
adicionadas outras seções que você queira para ser usada
por programas especiais. Para ver as seções de um módulo
utiliza-se o comando:
objdump modulo.o --section-headers
Em módulos do kernel existem uma seção chamada .modinfo. Nesta seção existem informações sobre o módulo,
sendo uma delas a versão para qual ele foi compilado.
Pode-se ver estas informações através do comando:
objdump modulo.o --full-contents --section=.modinfo
Outras informações contidas nesta seção são as macros
definidas no arquivo fonte, como por exemplo:
MODULE_PARM();
MODULE_AUTHOR("xx");
MODULE_DESCRIPTION("xx");
MODULE_LICENSE("xx");
...
Pode-se utilizar o comando modinfo
para interpretar o conteúdo da seção .modinfo
Para mais detalhes: man modinfo
Em kernel modules que explicitamente exportaim símbolos
são adicionadas algumas seções, como por exemplo, __ksymtab
and .kstrtab No nosso módulo
de exemplos podemos ver o conteúdo de .kstrtab
objdump servidor.o --full-contents --section=.kstrtab
servidor.o: file format elf32-i386
Contents of section .kstrtab:
0000 74657374 655f5238 61313664 64616500 teste_R8a16ddae.
Aqui temos então o nosso símbolo exportado teste.
Deste modo o kernel sabe quais funções ou variáveis
do módulo são exportadas.
O kernel ainda usa um sistema de versões. Deste modo,
é atachado uma versão em todos os símbolos exportados.
Estas versões são calculadas através do comando genksyms
Olhe o Makefile. Mais detalhes: man
genksyms
Se você quiser que não seja exportado nenhum símbolo
do seu módulo, use a macro EXPORT_NO_SYMBOLS
Para exportar um símbolo use a macro EXPORT_SYMBOL
Deste modo, podemos inserir nossos módulos que exportam
símbolos que serão usados por outros inseridos depois,
como uma pilha.
No nosso exemplo, segue esta ordem:
insmod servidor.o
insmod cliente.o
rmmod cliente
rmmod servidor
OBS: como uma pilha. Veja a seguir
um simples exemplo de como realizar a comunicacao entre
modulos:
/* cliente.c */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/init.h>
#include "servidor.ver"
extern int teste();
int cliente_inter_init_module(void)
{
int i;
printk(KERN_ALERT "cliente Hello\n");
i = teste();
printk(KERN_ALERT "Valor retornado %d\n",i);
return 0;
}
void cliente_inter_cleanup_module(void)
{
printk(KERN_ALERT "cliente Goodbye\n");
}
module_init(cliente_inter_init_module);
module_exit(cliente_inter_cleanup_module);
MODULE_AUTHOR("Thobias Salazar Trevisan");
MODULE_DESCRIPTION("Intercomunicacao entre modulos. \
Modulo que chama a funcao exportada pelo server");
MODULE_LICENSE("GPL");
EXPORT_NO_SYMBOLS;
/* servidor.c */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/modversions.h>
#include "servidor.ver" //arquivo gerado pelo genksyms que contem informacoes sobre versao
#define MODVERSIONS
#ifndef EXPORT_SYMTAB
# define EXPORT_SYMTAB /* necessario para exportar symbols */
#endif
int teste(void);
int teste(void)
{
printk(KERN_ALERT "servidor Fui chamado! hip hip...\n");
return 10;
}
int servidor_inter_init_module(void)
{
printk(KERN_ALERT "servidor Hello\n");
return 0;
}
void servidor_inter_cleanup_module(void)
{
printk(KERN_ALERT "servidor Goodbye\n");
}
module_init(servidor_inter_init_module);
module_exit(servidor_inter_cleanup_module);
EXPORT_SYMBOL (teste); // exporta simbolo teste
MODULE_AUTHOR("Thobias Salazar Trevisan");
MODULE_DESCRIPTION("Intercomunicacao entre modulos, modulo que exporta a funcao teste");
MODULE_LICENSE("GPL");
#
# Makefile para compilar modulo de intercomunicacao
#
SEARCHDIR = -I. -I/usr/src/linux/include
ver = /usr/src/linux/include/linux/modversions.h
CFLAGS = -D__KERNEL__ -DMODULE -DMODVERSIONS -include $(ver) -DEXPORT_SYMTAB -DLINUX -O2 -Wall ${SEARCHDIR}
GENKSYMS = /sbin/genksyms
NAME1 = servidor.o
NAME2 = cliente.o
CC = gcc
all: servidor.o cliente.o
servidor.o cliente.o: servidor.ver
servidor.ver: servidor.c
$(CC) -I$(SEARCHDIR) $(CFLAGS) -E -D__GENKSYMS__ $^ | \
$(GENKSYMS) -k 2.4.0 > $@
cliente.o: cliente.c
$(CC) $(CFLAGS) -c -o $(NAME2) cliente.c
clean:
- rm -f *.{o,ver}
12. Kernel Threads
Kernel thread são processos que rodam somente em modo
kernel. A função kernel_thread
cria uma nova kernel thread. Seus parâmetros são a função
a ser executada, argumentos para esta função e flags.
As flags são as cloning flags que podem ser encontradas
em include/linux/sched.h.
Um exemplo de kernel threads criadas pelo Linux são:
- bdflush: executa flush das páginas marcadas como
"dirty" dos buffers para o disco;
- kupdated: executa flush das páginas marcadas como
"dirty" antigas dos buffers para o disco. Para evitar
inconsistência do sistema de arquivo;
- kswapd: testa o número de páginas de memória livre,
quando este chega a um mínimo, esta kernel thread
tenta liberar memória.
O módulo abaixo é um simples servidor HTTP. Ele cria
NUM_THREAD kernel threads, que ficam respondendo pedidos
na porta 80 ou qualquer outra. Para terminá-las basta
enviar requisições para a página exit.html. Algo como
:
lynx http://localhost/exit.html
Esta requisição tem que ser enviada para cada uma das
kernel threads criadas. Para ve-las utilize o comando
:
ps -aux | grep www_server
Para compreender melhor este módulo é necessário que
você tenha conhecimento sobre sockets.
/* www.c */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/net.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <net/tcp.h>
#include <net/ip.h>
#include <linux/inet.h>
#include <net/protocol.h>
#include <net/sock.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <asm/page.h>
#include <linux/string.h>
#define NUM_THREAD 5 // Número de kernel threads a ser criado
#define ROOT_DIR "/tmp/www/html" // Caminho onde estao os arquivos html
struct socket *MainSocket=NULL;
int pid[NUM_THREAD];
int ServerPort = 80; //Número da porta
/*
* Monta a string com o nome do arquivo, ie, monta o PATH do arquivo requisitado
* Para compreender melhor mande imprimir o buffer, que e o conteudo da
* requisicao feita pelo browser
*/
char * acha_arquivo(char *buffer)
{
char *start_name, *end_name, *arquivo;
arquivo=kmalloc(25, GFP_KERNEL);
start_name = strstr(buffer, "/");
end_name = strstr(start_name, "HTT");
*(end_name - 1) = 0;
strcpy(arquivo, ROOT_DIR);
// Caso venha uma requisicao para o /, ou seja, lynx http://localhost/
// mandamos o index.html
if (*(start_name+1) == 0)
strcat(arquivo, "/index.html");
else
strcat(arquivo, start_name);
return arquivo;
}
/* Função que todas as kernel threads irão executar */
int Accept(void *unused)
{
struct socket *newsock;
struct file *fd;
int error,i=0,tamanho;
struct msghdr msg;
struct iovec iov;
mm_segment_t oldfs;
char *fonte;
char buf[4096], *sair = NULL;
/*
* Header necessário de respostas HTTP
*/
char header_200[]= "HTTP/1.1 200 OK\r\nContent-type: text/html\r\n\r\n";
char header_404[]= "HTTP/1.1 404 Not Found\r\nContent-type: text/html\r\n\r\n";
char pagina_404[]="<HTML><HEAD><TITLE>Server WWW 2.0</TITLE> \
</HEAD><BODY><H1>404\
Not Found</BODY></HTML>";
char *pagina, *nome;
daemonize();
sprintf(current->comm,"www_server");
while(!sair){
newsock = sock_alloc();
if (newsock==NULL) {
printk(KERN_ALERT "kernel_thread : Erro durante criacao newsock\n");
return -1;
}
newsock->type = MainSocket->type;
newsock->ops = MainSocket->ops;
error = MainSocket->ops->accept(MainSocket, newsock, O_RDWR);
if (error<0) {
printk(KERN_ALERT "kernel_thread : ERRO accept\n");
return -1;
}
newsock->sk->reuse = 1;
if (MainSocket->sk->state != TCP_LISTEN) {
printk(KERN_ALERT "kernel_thread : TCP DON´T LISTEN\n");
return -1;
}
fonte = in_ntoa(newsock->sk->daddr);
printk(KERN_ALERT "kernel_thread : fonte = %s\n",fonte);
printk(KERN_ALERT "kernel_thread : pid = %d\n",current->pid);
buf[0] = 0;
iov.iov_base = (void *)buf;
iov.iov_len = sizeof(buf);
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
oldfs = get_fs(); set_fs(KERNEL_DS);
error = sock_recvmsg(newsock, &msg, sizeof(buf), 0);
set_fs(oldfs);
if (error<0)
printk(KERN_ALERT "ERRO recv mesg");
nome = acha_arquivo(buf);
printk(KERN_ALERT "abrir arquivo [%s]\n",nome);
/*
* Tentamos abrir o arquivo, caso nao exista enviamos erro 404
*/
fd = filp_open(nome, O_RDONLY, 0);
if (IS_ERR(fd)) {
printk(KERN_ALERT "Erro nao pode abrir arquivo [%s]\n",nome);
/* Configuramos e enviamos o header de erro */
iov.iov_base = (void *)header_404;
iov.iov_len = sizeof(header_404);
msg.msg_iov = &iov;
oldfs = get_fs(); set_fs(KERNEL_DS);
error = sock_sendmsg(newsock, &msg, sizeof(header_404));
set_fs(oldfs);
if (error<0)
printk(KERN_ALERT "ERRO durante envio header erro = %d\n",error);
/* Configuramos e enviamos a pagina de erro */
iov.iov_base = (void *)pagina_404;
iov.iov_len = sizeof(pagina_404);
msg.msg_iov = &iov;
oldfs = get_fs(); set_fs(KERNEL_DS);
error = sock_sendmsg(newsock, &msg, sizeof(pagina_404));
set_fs(oldfs);
if (error<0)
printk(KERN_ALERT "ERRO durante envio pagina de erro = %d\n",error);
}
else {
/*
* Configuramos e enviamos o header HTTP
*/
iov.iov_base = (void *)header_200;
iov.iov_len = sizeof(header_200);
msg.msg_iov = &iov;
oldfs = get_fs(); set_fs(KERNEL_DS);
error = sock_sendmsg(newsock, &msg, sizeof(header_200));
set_fs(oldfs);
if (error<0)
printk(KERN_ALERT "ERRO durante envio header = %d\n",error);
/* Configuramos e enviamos a pagina do arquivo lido */
// tamnho contem o tamho do arquivo em bytes
tamanho = fd->f_dentry->d_inode->i_size;
pagina = kmalloc(tamanho+1, GFP_KERNEL);
fd->f_pos = 0;
oldfs = get_fs(); set_fs(KERNEL_DS);
i = generic_file_read(fd, pagina, tamanho, &fd->f_pos);
set_fs(oldfs);
if (i<0)
printk(KERN_ALERT "ERRO ao ler arq %d\n",i);
iov.iov_base = (void *)pagina;
iov.iov_len = tamanho+1;
msg.msg_iov = &iov;
oldfs = get_fs(); set_fs(KERNEL_DS);
error = sock_sendmsg(newsock, &msg, tamanho+1);
set_fs(oldfs);
if (error<0)
printk(KERN_ALERT "ERRO durante envio pagina = %d\n",error);
else
printk(KERN_ALERT "Send msg ok\n");
i = filp_close(fd, NULL);
kfree(pagina);
}
/*
* Testa para ver se foi feito a requisição para a página exit.html
*/
sair = strstr(buf, "/exit.html");
sock_release(newsock);
kfree(nome);
}
printk(KERN_ALERT "Kernel_Thread exit\n");
return 0;
}
/*
* Cria o socket principal que será compartilhado por todas as kernel threads
*/
int Socket_Create(void)
{
struct sockaddr_in server;
int error;
printk(KERN_ALERT "Socket_Create : Hello\n");
error = sock_create(PF_INET,SOCK_STREAM,IPPROTO_TCP,&MainSocket);
if (error<0) {
printk(KERN_ALERT "Socket_Create : Error durante a criacao socket; terminating\n");
return -1;
}
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons((unsigned short)(ServerPort));
MainSocket->sk->reuse = 1;
error = MainSocket->ops->bind(MainSocket,(struct sockaddr*)&server,sizeof(server));
if (error<0) {
printk(KERN_ALERT "Socket_Create : Error binding socket\n");
return 1;
}
error = MainSocket->ops->listen(MainSocket,1);
if (error!=0) {
printk(KERN_ALERT "Socket_Create : Error listening on socket\n");
return -1;
}
else
printk(KERN_ALERT "Socket_Create : Listening Socket OK \n");
printk(KERN_ALERT "Socket_Create : Server state %d\n",MainSocket->sk->state);
return 0;
}
int www_init_module(void)
{
int binding, i;
printk(KERN_ALERT "Servidor www 1.0\n");
binding = Socket_Create();
if (binding==0) {
for (i=0;i<NUM_THREAD;i++)
{
/*
* Cria as n kernel threads
*/
pid[i] = kernel_thread(Accept, NULL, CLONE_SIGHAND);
if (pid[i] < 0)
printk(KERN_ALERT "www: \
ERRO during kernel thread creation = %d\n",i);
else
printk(KERN_ALERT "www : kernel thread ok pid = %d\n",pid[i]);
}
}
else
printk(KERN_ALERT "ERRO www: binding, no threads\n");
return 0;
}
void www_cleanup_module(void)
{
sock_release(MainSocket);
printk(KERN_ALERT "Servidor www. Bye\n");
}
module_init(www_init_module);
module_exit(www_cleanup_module);
MODULE_DESCRIPTION("Exemplo de kernel thread e sockets");
MODULE_AUTHOR("Thobias Salazar Trevisan");
#ifndef MODULE_LICENSE
#define MODULE_LICENSE(a)
#endif
MODULE_LICENSE("GPL");
EXPORT_NO_SYMBOLS;
14. Bibliografia
-
Linux Kernel Module Programming Guide
Ori Pomerantz
www.linuxdoc.org/LDP/lkmpg/mpg.html
Linux Device Drivers, 2nd Edition
Alessandro Rubini, Jonathan Corbet
www.oreilly.com/catalog/linuxdrive2
Linux Kernel Procfs Guide
Erik Mouw
kernelnewbies.org/documents/kdoc/procfs-guide/lkprocfsguide.html
Understanding the Linux Kernel
Daniel P. Bovet & Marco Cesati
www.ora.com/catalog/linuxkernel
|