Kernel e Internals

Módulos do kernel: carregando, descarregando e escrevendo módulos básicos

• 6 min de leitura

Módulos do kernel: carregando, descarregando e escrevendo módulos básicos
Módulos do kernel são pedaços de código que podem ser carregados e descarregados do kernel Linux em execução, sem necessidade de recompilar ou reiniciar o sistema. Eles permitem estender funcionalidades do kernel de forma dinâmica, adicionando suporte a novos dispositivos, sistemas de arquivos ou chamadas de sistema.

Módulos do kernel: carregando, descarregando e escrevendo módulos básicos

1. Introdução aos Módulos do Kernel Linux

Módulos do kernel são pedaços de código que podem ser carregados e descarregados do kernel Linux em execução, sem necessidade de recompilar ou reiniciar o sistema. Eles permitem estender funcionalidades do kernel de forma dinâmica, adicionando suporte a novos dispositivos, sistemas de arquivos ou chamadas de sistema.

O kernel Linux adota uma abordagem híbrida: embora seja monolítico, seu design modular permite que componentes sejam compilados separadamente como módulos. O ciclo de vida de um módulo é simples: ele é carregado na memória, executado (geralmente registrando-se para atender a eventos do sistema) e, quando não for mais necessário, descarregado.

2. Estrutura de um Módulo Básico

Todo módulo do kernel precisa incluir cabeçalhos fundamentais:

#include <linux/module.h>    // macros e funções para módulos
#include <linux/kernel.h>    // funções do kernel como printk()
#include <linux/init.h>      // macros module_init() e module_exit()

As funções de entrada e saída são declaradas com as macros module_init() e module_exit(). A função de inicialização retorna 0 em sucesso ou um código de erro negativo. A função de saída libera recursos alocados.

Informações do módulo são declaradas com macros de licenciamento:

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Seu Nome");
MODULE_DESCRIPTION("Um módulo exemplo");

A licença GPL é obrigatória para distribuição de módulos que usam símbolos exportados como GPL-only.

3. Compilação e Construção de Módulos

Módulos do kernel não são compilados como programas comuns. Eles usam o sistema de build do kernel (Kbuild). Um Makefile típico:

obj-m := meu_modulo.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
    $(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
    $(MAKE) -C $(KDIR) M=$(PWD) clean

A variável obj-m lista os módulos a serem compilados. KDIR aponta para a árvore de fontes do kernel em execução. O comando make invoca o sistema de build do kernel para compilar o módulo. O resultado é um arquivo .ko (kernel object).

4. Carregando e Descarregando Módulos

Para carregar um módulo, use insmod:

sudo insmod meu_modulo.ko

Para descarregar:

sudo rmmod meu_modulo

O comando modprobe é mais inteligente: resolve dependências automaticamente e carrega módulos adicionais necessários:

sudo modprobe meu_modulo

Para ver módulos carregados:

lsmod

Ou examine /proc/modules:

cat /proc/modules

lsmod mostra o nome, tamanho, uso e módulos dependentes.

5. Parâmetros de Módulo e Comunicação com o Usuário

Módulos podem aceitar parâmetros na linha de comando durante o carregamento. Use module_param() para parâmetros simples e module_param_array() para arrays:

#include <linux/moduleparam.h>

static int valor = 0;
static char *nome = "padrao";
static int arr[3];
module_param(valor, int, 0644);
module_param(nome, charp, 0644);
module_param_array(arr, int, NULL, 0644);

O terceiro argumento define as permissões do arquivo em /sys/module/<nome>/parameters/. Para passar parâmetros:

sudo insmod meu_modulo.ko valor=42 nome="teste"

6. Registro de Dispositivos e Interfaces com o Kernel

Para criar um dispositivo virtual, use a API de char devices. Exemplo mínimo:

#include <linux/cdev.h>
#include <linux/fs.h>

static dev_t dev_num;
static struct cdev meu_cdev;

static int meu_open(struct inode *inode, struct file *filp) {
    return 0;
}

static ssize_t meu_read(struct file *filp, char __user *buf, size_t len, loff_t *off) {
    return 0;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = meu_open,
    .read = meu_read,
};

static int __init init_modulo(void) {
    alloc_chrdev_region(&dev_num, 0, 1, "meu_dev");
    cdev_init(&meu_cdev, &fops);
    cdev_add(&meu_cdev, dev_num, 1);
    return 0;
}

static void __exit exit_modulo(void) {
    cdev_del(&meu_cdev);
    unregister_chrdev_region(dev_num, 1);
}

alloc_chrdev_region() aloca dinamicamente um número de dispositivo. cdev_init() e cdev_add() registram as operações do dispositivo.

7. Boas Práticas e Depuração de Módulos

Use printk() para depuração, com níveis de log:

printk(KERN_INFO "Módulo carregado com sucesso\n");
printk(KERN_ERR "Erro ao alocar memória\n");

As mensagens aparecem no buffer do kernel. Visualize com:

dmesg

Ou acompanhe em tempo real:

dmesg -w

Cuidados importantes:
- Nunca use bibliotecas padrão C (glibc) dentro do kernel
- Gerencie memória com kmalloc()/kfree(), não malloc()/free()
- Evite deadlocks usando spinlocks ou mutexes corretamente
- Sempre libere recursos na função de saída
- Teste o descarregamento com rmmod forçado apenas em último caso: sudo rmmod -f modulo

8. Exemplo Completo: Módulo "Hello World" com Parâmetros

Código fonte (hello.c):

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/moduleparam.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Exemplo");
MODULE_DESCRIPTION("Módulo Hello World com parâmetros");

static int vezes = 1;
static char *mensagem = "Olá, Kernel!";
module_param(vezes, int, 0644);
module_param(mensagem, charp, 0644);

static int __init hello_init(void) {
    int i;
    for (i = 0; i < vezes; i++) {
        printk(KERN_INFO "Hello: %s (iteração %d)\n", mensagem, i+1);
    }
    return 0;
}

static void __exit hello_exit(void) {
    printk(KERN_INFO "Hello: módulo descarregado\n");
}

module_init(hello_init);
module_exit(hello_exit);

Makefile:

obj-m := hello.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
    $(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
    $(MAKE) -C $(KDIR) M=$(PWD) clean

Passo a passo:

  1. Compile: make
  2. Carregue com parâmetros: sudo insmod hello.ko vezes=3 mensagem="Teste"
  3. Verifique as mensagens: dmesg | tail -5
  4. Saída esperada no dmesg:
[12345.678901] Hello: Teste (iteração 1)
[12345.678902] Hello: Teste (iteração 2)
[12345.678903] Hello: Teste (iteração 3)
  1. Descarregue: sudo rmmod hello
  2. Confirme: dmesg | tail -1 deve mostrar Hello: módulo descarregado

Módulos do kernel são ferramentas poderosas para estender o Linux. Dominar seu carregamento, descarregamento e escrita permite criar desde pequenos utilitários de depuração até drivers completos de dispositivos.

Referências

💬 Comentários
Mais em Kernel e Internals