码迷,mamicode.com
首页 > 系统相关 > 详细

Linux Kernel Module(LKM) Init、Delete Code Principle Learning

时间:2014-08-18 23:22:03      阅读:587      评论:0      收藏:0      [点我收藏+]

标签:des   style   blog   http   color   使用   os   io   

目录

1. Linux模块(LKM)简介
2. 使用Linux模块
3. LKM模块加载原理
4. LKM模块卸载原理

 

1. Linux模块(LKM)简介

模块是一种向linux内核添加"设备驱动程序"、"文件系统"、"其他组件"的有效方法,而无须重新编译内核或重启系统,这消除了许多限制,同时带来了很多的优点

1. 通过使用模块,内核程序员能够预先编译大量驱动程序,而不会致使内核映像的尺寸发生膨胀。在自动检测硬件或用户提示后,安装例程会选择适当的模块并将其添加到内核中
2. 内核开发者可以将试验性的代码打包到模块中,模块可疑卸载,修改代码或重新打包后再重新加载,这使得可以快速测试新特性,无需每次都重启系统
3. 模块(LKM)可疑无缝地插入到内核中,同时模块也可以导出一些函数,可以由其他核心模块(以及持久编译到内核中的代码)使用。在模块代码需要卸载时,模块和内核剩余部分之间的关联会自动终止

bubuko.com,布布扣

0x1: 模块的依赖关系和引用

如果模块B使用了模块A提供的函数,那么模块A和模块B之间就存在关系,可以从两个方面来看这种关系

1. 模块B依赖模块A
除非模块A已经驻留在内核内存,否则模块B无法装载

2. 模块B引用模块A
除非模块B已经移除,否则模块A无法从内核移除,在内核中,这种关系称之为"模块B使用模块A"

"struct module_use"和"struct module->module_which_use_me"这两个结果共同组合和保证了内核模块中的依赖关系。
如果模块B使用了模块A提供的函数,那么模块A和模块B之间就存在关系,可以从两个方面来看这种关系

1. 模块B依赖模块A
除非模块A已经驻留在内核内存,否则模块B无法装载

2. 模块B引用模块A
除非模块B已经移除,否则模块A无法从内核移除,在内核中,这种关系称之为"模块B使用模块A"

对每个使用了模块A中函数的模块B,都会创建一个module_use结构体实例,该实例将被添加到模块A(被依赖的模块)的module实例中的modules_which_use_me链表中,modules_which_use_me指向模块B的module实例

bubuko.com,布布扣

我们在编写并加载LKM模块的时候,一定要注意模块间的依赖关系,有时候还需要分步骤单独进行指定模块的加载,才能保证模块间的依赖关系的正确连接

 

2. 使用Linux模块

0x1: 模块的添加

从用户的角度来看,模块可以通过以下两个"指令”进行添加

1. modprobe
它考虑了各个模块之间可能出现的依赖性(在一个模块依赖于一个或多个合作者模块的功能时),modprobe在识别出目标模块所依赖的模块之后,在内核也会使用insmod(即modprobe只是对insmod的一个包装)

2. insmod
insmod只加载一个单一的模块到内核中,且该模块只信赖内核中已经存在的代码(不管是通过模块动态加载的、还是持久编译到内核中的)

从内核系统的角度来看,模块的加载可以通过以下方法完成

1. init_module()
init_module()是一个系统调用,用户空间的工具只需要提供二进制数据,所有其他工作(重定位、解决引用问题)由内核自身完成

2. request_module()
request_module()不是系统调用,它用于从内核端加载模块,它不仅用于加载模块,还用于实现热插拔功能

0x2: 模块的移除

从用户的角度来看,模块可以通过以下"指令”进行删除

1. rmmod

从内核系统的角度来看,模块的卸载可以通过以下方法完成

1. delete_module()
delete_module()是一个系统调用,它用于从内核移除一个模块,前提是该模块代码不再使用,且其他模块也不再使用该模块导出的函数(即不能有依赖关系)

 

3. LKM模块加载原理

LKM模块的加载的大部分逻辑都在init_module()中,ring3的insmod、modprobe仅仅负责传递一个二进制数据,本文只关注init_module这个系统调用的代码逻辑,关于整个insmod模块加载过程的原理分析,请参阅

http://files.cnblogs.com/LittleHann/Modultils%E5%B7%A5%E5%85%B7%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E2%80%94%E2%80%94insmod%E7%AF%87.pdf

0x1: 代码流程

init_module()系统调用是用户空间和内核之间用于装载新模块的接口,它的大致流程如下

bubuko.com,布布扣

 

0x2: 内核代码分析

\linux-2.6.32.63\kernel\module.c
SYSCALL_DEFINE3(init_module, void __user *, umod, unsigned long, len, const char __user *, uargs)
    1. *umod
    指向用户地址空间中的区域,表示模块的名字
    2. len
    该区域的长度
    3. *uargs
    指向字符串的指针,指定了模块的参数

\linux-2.6.32.63\kernel\module.c

/* This is where the real work happens */
SYSCALL_DEFINE3(init_module, void __user *, umod, unsigned long, len, const char __user *, uargs)
{
    struct module *mod;
    int ret = 0;

    /*
    Must have permission 
    确保有插入和删除模块不受限制的权利,并且模块没有被禁止插入或删除
    */
    if (!capable(CAP_SYS_MODULE) || modules_disabled)
    {
        return -EPERM;
    }
        

    /* Only one module load at a time, please */
    if (mutex_lock_interruptible(&module_mutex) != 0)
        return -EINTR;

    /* 
    Do all the hard work 
    分配,加载模块,并创建相关的sysfs文件
    */
    mod = load_module(umod, len, uargs);
    if (IS_ERR(mod)) 
    {
        mutex_unlock(&module_mutex);
        return PTR_ERR(mod);
    }

    /* Drop lock so they can recurse */
    mutex_unlock(&module_mutex);

    /*
    通知内核通知链module_notify_list上的监听者,模块状态变为MODULE_STATE_COMING
    关于module的状态信息,请参阅http://www.cnblogs.com/LittleHann/p/3865490.html,搜索: struct module
         enum module_state
        {
            MODULE_STATE_LIVE,    //模块当前正常使用中(存活状态) 
            MODULE_STATE_COMING,    //模块当前正在被加载
            MODULE_STATE_GOING,    //模块当前正在被卸载
        };
    */
    blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_COMING, mod);

    //调用本模块的所有构造器
    do_mod_ctors(mod);
    /* 
    Start the module 
    调用模块的init方法
    */
    if (mod->init != NULL)
        ret = do_one_initcall(mod->init);
    if (ret < 0)
    {
        /* Init routine failed: abort.  Try to protect us from
                   buggy refcounters. */
        mod->state = MODULE_STATE_GOING;
        synchronize_sched();
        module_put(mod);
        blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_GOING, mod);
        mutex_lock(&module_mutex);
        free_module(mod);
        mutex_unlock(&module_mutex);
        wake_up(&module_wq);
        return ret;
    }
    if (ret > 0) 
    {
        printk(KERN_WARNING
"%s: ‘%s‘->init suspiciously returned %d, it should follow 0/-E convention\n"
"%s: loading module anyway...\n",
               __func__, mod->name, ret,
               __func__);
        dump_stack();
    }

    /* 
    Now it‘s a first class citizen!  Wake up anyone waiting for it.  
    */
    mod->state = MODULE_STATE_LIVE;
    //唤醒module_wq 队列上等待本模块初始化的所有任务
    wake_up(&module_wq);
    //通知内核通知链module_notify_list上的监听者,模块状态变为MODULE_STATE_LIVE
    blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_LIVE, mod);

    /* 
    We need to finish all async code before the module init sequence is done 
    等待所有的异步函数调用完成
    */
    async_synchronize_full();

    //获得module_mutex锁,module_mutex作用之一就是保护全局的模块链表
    mutex_lock(&module_mutex);
    /* Drop initial reference. */
    module_put(mod);
    trim_init_extable(mod);
#ifdef CONFIG_KALLSYMS
    mod->num_symtab = mod->core_num_syms;
    mod->symtab = mod->core_symtab;
    mod->strtab = mod->core_strtab;
#endif
    /*
    释放与模块初始化相关的节区所占的内存
    这点和windows下的驱动加载是类似的,仅仅用于驱动加载的那部分"派遣初始化函数"被分配到"可换页内存"区域中,当驱动加载完毕后就立即释放
    */
    module_free(mod, mod->module_init);
    mod->module_init = NULL;
    mod->init_size = 0;
    mod->init_text_size = 0;
    mutex_unlock(&module_mutex);

    return 0;
}

Relevant Link:

http://bbs.chinaunix.net/thread-2194837-1-1.html
http://blog.csdn.net/wuhui_gdnt/article/details/5316616
http://blog.csdn.net/muge0913/article/details/7518568
http://blog.csdn.net/ganggexiongqi/article/details/6823960

 

4. LKM模块卸载原理

我们输入指令rmmod,最终在系统内核中需要调用sys_delete_module进行实现

SYSCALL_DEFINE2(delete_module, const char __user *, name_user, unsigned int, flags)
    1. name_user
    待卸载的模块名称
    2. flags

\linux-2.6.32.63\kernel\module.c

SYSCALL_DEFINE2(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) || modules_disabled)
        return -EPERM;

    //获得从用户空间传递到内核空间的模块名字 
    if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0)
        return -EFAULT;
    name[MODULE_NAME_LEN-1] = \0;

    /* Create stop_machine threads since free_module relies on
     * a non-failing stop_machine call. */
    ret = stop_machine_create();
    if (ret)
        return ret;

    //获得module_mutex锁 
    if (mutex_lock_interruptible(&module_mutex) != 0) {
        ret = -EINTR;
        goto out_stop;
    }

    //得到要卸载的模块的指针
    mod = find_module(name);
    if (!mod) {
        ret = -ENOENT;
        goto out;
    }

    /*
    检查,确认没有其他模块依赖要卸载的模块
    关于linux下模块间的依赖性以及相关数据结构,请参阅另一篇文章
    http://www.cnblogs.com/LittleHann/p/3865490.html,搜索: struct module_use
    */
    if (!list_empty(&mod->modules_which_use_me)) {
        /* Other modules depend on us: get rid of them first. */
        ret = -EWOULDBLOCK;
        goto out;
    }

    /* 
    Doing init or already dying? 
    检查模块的状态是否是 MODULE_STATE_LIVE 
    */
    if (mod->state != MODULE_STATE_LIVE) {
        /* FIXME: if (force), slam module count and wake up
                   waiter --RR */
        DEBUGP("%s already dying\n", mod->name);
        ret = -EBUSY;
        goto out;
    }

    /* If it has an init func, it must have an exit func to unload */
    if (mod->init && !mod->exit) {
        forced = try_force_unload(flags);
        if (!forced) {
            /* This module can‘t be removed */
            ret = -EBUSY;
            goto out;
        }
    }

    /* 
    Set this up before setting mod->state 
    设置等待本模块退出 的进程为current
    */
    mod->waiter = current;

    /* Stop the machine so refcounts can‘t move and disable module. */
    ret = try_stop_module(mod, flags, &forced);
    if (ret != 0)
        goto out;

    /* 
    Never wait if forced. 
    等待模块的引用计数变为0
    */
    if (!forced && module_refcount(mod) != 0)
    {
        wait_for_zero_refcount(mod);
    } 

    //释放module_mutex锁 
    mutex_unlock(&module_mutex);
    /* 
    Final destruction now noone is using it. 
    调用模块本身的exit函数
    */
    if (mod->exit != NULL)
    {
        mod->exit();
    } 
    //告诉通知链module_notify_list上的监听者,模块状态 变为 MODULE_STATE_GOING
    blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_GOING, mod);
    //等待所有的异步函数调用完成
    async_synchronize_full();
    mutex_lock(&module_mutex);
    /* Store the name of the last unloaded module for diagnostic purposes */
    strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module));
    free_module(mod);

 out:
    mutex_unlock(&module_mutex);
out_stop:
    stop_machine_destroy();
    return ret;
}

 

Copyright (c) 2014 LittleHann All rights reserved

 

Linux Kernel Module(LKM) Init、Delete Code Principle Learning,布布扣,bubuko.com

Linux Kernel Module(LKM) Init、Delete Code Principle Learning

标签:des   style   blog   http   color   使用   os   io   

原文地址:http://www.cnblogs.com/LittleHann/p/3920387.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!