Programação de Módulos para o Kernel do Linux
   
 

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

  1. Introdução
  2. Hello World
  3. Parâmetros na Inicialização
  4. Sistema de Arquivos /proc
  5. Substituindo o printk
  6. Comunicação entre Módulos
  7. Dispositivos de Caractere
  8. IOCTLs
  9. Chamadas de Sistema
  10. Manipulando Interrupções
  11. Gerenciamento de Memória
  12. Kernel Threads
  13. Apêndice
  14. 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


© 2001-2002 by the people in #kernel-br - Webdesign by Caio Begotti (caio1982) & Fábio Minami (sussumo)