Cơ chế và quản lý module nhân Linux

Lập trình module nhân Linux đòi hỏi sự cẩn trọng cao độ, vì lỗi trong module có thể dẫn đến hệ thống treo hoàn toàn, khác với lập trình ứng dụng người dùng nơi lỗi segment fault chỉ ảnh hưởng cục bộ.

Bảng ký hiệu nhân

Bảng ký hiệu nhân lưu trữ các ký hiệu mà mọi module đều có thể truy cập cùng địa chỉ tương ứng. Module khi được nạp sẽ chèn các ký hiệu toàn cục của nó vào bảng này. Nhân sử dụng bảng này để xác định địa chỉ các hàm và biến cần thiết cho hoạt động.

Thông tin trong bảng có thể xem tại /proc/kallsyms với định dạng:

Mã địa chỉ Thuộc tính Tên ký hiệu [Tên module]

Các ký hiệu từ module sẽ có thêm tên module ở cột thứ tư, còn ký hiệu từ nhân chính thức thì không.

Vị trí của bảng trong đoạn mã là _ksymtab, giới hạn bởi hai ký hiệu do trình biên dịch tạo ra: __start___ksymtab và __stop___ksymtab.

Sự phụ thuộc giữa các module

Khi một module A tham chiếu tới ký hiệu từ module B, ta nói A phụ thuộc vào B. Việc nạp A yêu cầu B phải được nạp trước đó, nếu không liên kết sẽ thất bại. Đây gọi là mối quan hệ phụ thuộc giữa các module.

Phân tích cấu trúc dữ liệu

Cấu trúc mô tả mỗi module nằm trong tệp include/linux/module.h:

struct module {
    enum module_state state;
    struct list_head list;
    char name[MODULE_NAME_LEN];
    struct module_kobject mkobj;
    struct module_param_attrs *param_attrs;
    const char *version;
    const char *srcversion;
    const struct kernel_symbol *syms;
    unsigned int num_syms;
    const unsigned long *crcs;
    const struct kernel_symbol *gpl_syms;
    unsigned int num_gpl_syms;
    const unsigned long *gpl_crcs;
    unsigned int num_exentries;
    const struct exception_table_entry *extable;
    int (*init)(void);
    void *module_init;
    void *module_core;
    unsigned long init_size, core_size;
    unsigned long init_text_size, core_text_size;
    struct mod_arch_specific arch;
    int unsafe;
    int license_gplok;
    int gpgsig_ok;
#ifdef CONFIG_MODULE_UNLOAD
    struct module_ref ref[NR_CPUS];
    struct list_head modules_which_use_me;
    struct task_struct *waiter;
    void (*exit)(void);
#endif
#ifdef CONFIG_KALLSYMS
    Elf_Sym *symtab;
    unsigned long num_symtab;
    char *strtab;
    struct module_sect_attrs *sect_attrs;
#endif
    void *percpu;
    char *args;
};
  • state: Trạng thái hiện tại của module (MODULE_STATE_LIVE, MODULE_STATE_COMING, MODULE_STATE_GOING)
  • name: Tên của module
  • syms: Bảng ký hiệu xuất khẩu
  • init/exit: Hàm khởi tạo và dọn dẹp
  • modules_which_use_me: Danh sách các module phụ thuộc vào module này

Cấu trúc kernel_symbol chứa thông tin về ký hiệu:

struct kernel_symbol {
    unsigned long value;
    const char *name;
};

Hàm thực thi chính

Hệ thống cung cấp hai lời gọi hệ thống để quản lý module:

Nạp module

asmlinkage long sys_init_module(void __user *umod, unsigned long len, const char __user *uargs)
{
    struct module *mod;
    int ret = 0;

    if (!capable(CAP_SYS_MODULE))
        return -EPERM;

    if (down_interruptible(&module_mutex) != 0)
        return -EINTR;

    mod = load_module(umod, len, uargs);
    if (IS_ERR(mod)) {
        up(&module_mutex);
        return PTR_ERR(mod);
    }

    stop_machine_run(__link_module, mod, NR_CPUS);
    up(&module_mutex);

    down(¬ify_mutex);
    notifier_call_chain(&module_notify_list, MODULE_STATE_COMING, mod);
    up(¬ify_mutex);

    if (mod->init != NULL)
        ret = mod->init();
    if (ret < 0) {
        mod->state = MODULE_STATE_GOING;
        synchronize_sched();
        if (!mod->unsafe) {
            module_put(mod);
            down(&module_mutex);
            free_module(mod);
            up(&module_mutex);
        }
        return ret;
    }

    down(&module_mutex);
    mod->state = MODULE_STATE_LIVE;
    module_put(mod);
    module_free(mod, mod->module_init);
    mod->module_init = NULL;
    mod->init_size = 0;
    mod->init_text_size = 0;
    up(&module_mutex);

    return 0;
}

Gỡ bỏ module

asmlinkage long sys_delete_module(const char __user *name_user, unsigned int flags)
{
    struct module *mod;
    char name[MODULE_NAME_LEN];
    int ret, forced = 0;

    if (!capable(CAP_SYS_MODULE))
        return -EPERM;

    if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0)
        return -EFAULT;
    name[MODULE_NAME_LEN-1] = '\0';

    if (down_interruptible(&module_mutex) != 0)
        return -EINTR;

    mod = find_module(name);
    if (!mod) {
        ret = -ENOENT;
        goto out;
    }

    if (!list_empty(&mod->modules_which_use_me)) {
        ret = -EWOULDBLOCK;
        goto out;
    }

    if (mod->state != MODULE_STATE_LIVE) {
        ret = -EBUSY;
        goto out;
    }

    if ((mod->init != NULL && mod->exit == NULL) || mod->unsafe) {
        forced = try_force_unload(flags);
        if (!forced) {
            ret = -EBUSY;
            goto out;
        }
    }

    mod->waiter = current;
    ret = try_stop_module(mod, flags, &forced);
    if (ret != 0)
        goto out;

    if (!forced && module_refcount(mod) != 0)
        wait_for_zero_refcount(mod);

    if (mod->exit != NULL) {
        up(&module_mutex);
        mod->exit();
        down(&module_mutex);
    }
    free_module(mod);

out:
    up(&module_mutex);
    return ret;
}

Sử dụng công cụ module

Module có thể được nạp bằng lệnh insmod hoặc tự động qua modprobe. Để gỡ bỏ dùng lệnh rmmod.

Tập tin Makefile đơn giản

TARGET = hello
KDIR = /usr/src/linux
PWD = $(shell pwd)
obj-m += $(TARGET).o

default:
	make -C $(KDIR) M=$(PWD) modules

Biên dịch nhiều file

Với nhiều file nguồn, ví dụ start.c và stop.c:

#define __NO_VERSION__
#include <linux/kernel.h>
#include <linux/module.h>

int init_module()
{
    printk("Hello, world!\n");
    return 0;
}

File stop.c:

#define __NO_VERSION__
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>

void cleanup_module()
{
    printk("Bye!\n");
}

Makefile cho dự án đa file:

TARGET = hello
KDIR = /usr/src/linux
PWD = $(shell pwd)
obj-m += $(TARGET).o
$(TARGET)-y := start.o stop.o

default:
	make -C $(KDIR) M=$(PWD) modules

Thẻ: linux-kernel module-programming kernel-development embedded-systems

Đăng vào ngày 25 tháng 9 lúc 08:32