现在的位置: 首页 > 编程语言 > 正文

Linux内核模块:初始化、加载/卸载、模块参数、导出符号、错误处理、模块装载竞争

2018年10月02日 编程语言 ⁄ 共 3776字 ⁄ 字号 评论关闭

一、代码

       模块1导出了函数void  output(void),模块2调用了模块1中的函数。(先加载模块1,再加载模块2

       模块1:

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


//
static int int_var = 0;
module_param(int_var, int, 0644);
MODULE_PARM_DESC(int_var, "A integer variable");

static char* str_var = "default";
module_param_named(str_var_alias, str_var, charp, 0644);
MODULE_PARM_DESC(str_var_alias, "A string variable");

static char str_var2[10];
module_param_string(str_var2_alias, str_var2, 10, 0644);
MODULE_PARM_DESC(str_var2_alias, "A string variable");

static int int_arr[10];
int narr;
module_param_array(int_arr, int, &narr, 0644);
MODULE_PARM_DESC(int_arr, "A integer array");

static int int_arr2[10];
int narr2;
module_param_array_named(int_arr2_alias, int_arr2, int, &narr2, 0644);
MODULE_PARM_DESC(int_arr2_alias, "A integer array");


//
static void output(void)
{
    printk("hello world!\n");
}
EXPORT_SYMBOL_GPL(output);


static int __init init_marker(void)
{
    int i;

    printk("init marker.\n");

    //
    printk("int_var:%d\n", int_var);
    printk("str_var:%s\n", str_var);
    printk("str_var2:%s\n", str_var2);
    for (i=0; i<narr; ++i)
    {
        printk("int_arr[%d]=%d\n", i, int_arr[i]);
    }
    for (i=0; i<narr2; ++i)
    {
        printk("int_arr2[%d]=%d\n", i, int_arr2[i]);
    }

    //
    output();

    return 0;
}

static void __exit exit_marker(void)
{
    printk("exit marker.\n");
}


module_init(init_marker);
module_exit(exit_marker);


MODULE_VERSION("1.0.0_0");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("gwy");
MODULE_ALIAS("the alias of module name");
MODULE_DESCRIPTION("the description about the use of the module");
//MODULE_DEVICE_TABLE();

        模块2:

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


static void my(void)
{
    extern void output(void);
    output();
    printk("my\n");
}

static int __init init_marker(void)
{
    printk("init marker.\n");


        
    my();

    return 0;
}

static void __exit exit_marker(void)
{
    printk("exit marker.\n");
}


module_init(init_marker);
module_exit(exit_marker);


MODULE_VERSION("1.0.0_0");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("gwy");
MODULE_ALIAS("the alias of module name");
MODULE_DESCRIPTION("the description about the use of the module");
//MODULE_DEVICE_TABLE();

二、初始化

2.1  函数原型

        static int __init init_marker(void)

        { 

        }

        static void __exit exit_marker(void)

        {

        }

        初始化函数,在定义文件之外没有任何意义,所以应声明为static。

        __init(__exit)表名该函数只在初始化期间使用。在模块装载之后,模块装载器会将初始化函数丢掉,这样可以将函数占用的内存释放出来。

2.2 关于模块的描述

        MODULE_VERSION("1.0.0_0"); 

            模块版本号

        MODULE_LICENSE("GPL");

            模块开源协议

        MODULE_AUTHOR("gwy");

            模块作者

        MODULE_ALIAS("the alias of module name");

            模块别名

        MODULE_DESCRIPTION("the description about the use of the module");

            模块描述

        MODULE_PARM_DESC(int_var, "A integer variable");

            参数描述

查看模块信息:

三、加载/卸载

        insmod

        rmmod

        modprobe ( modprobe -r )

        lsmod(通过读取/proc/modules虚拟文件来获取模块信息)

四、模块参数

4.1 参数

        module_param(int_var, int, 0644);

            定义模块参数。

        module_param_named(str_var_alias, str_var, charp, 0644);

            外部参数名称与内部变量名称不同。

        module_param_string(str_var2_alias, str_var2, 10, 0644);

            拷贝字符串到分配好的内存。(不同于charp,先分配内存,再拷贝)

        module_param_array(int_arr, int, &narr, 0644);

            定义模块数组类型参数。

        module_param_array_named(int_arr2_alias, int_arr2, int, &narr2, 0644);

            数组类型 外部参数名称与内部变量名称不同。

        MODULE_PARM_DESC(int_var, "A integer variable");

            参数描述。

4.2 支持的类型

        bool

        invbool 

            布尔值(true或false),关联的变量是int型。

        charp 

            字符指针值。内核会为用户提供的字符串分配内存,并设置相应的指针。

        int、short、long、uint、ushort、ulong

4.3 访问权限

        module_param(int_var, int, 0644); 

        0644是访问权限,用数字表示,或者用S_IRUGO|S_IWUSR表示。

        查看访问权限:/sys/module/test/parameters

4.4 查看模块的参数

        modinfo查看,见2.2中图片。

五、导出符号表

        模块层叠技术

5.1 导出函数

        EXPORT_SYMBOL(func)

        EXPORT_SYMBOL_GPL(func) 

            EXPORT_SYMBOL_GPL导出的模块只能被GPL许可的模块使用。

5.2 查看导出的符号

        /proc/kallsyms

5.3 调用模块导出符号

        需要在模块2的Makefile中添加:KBUILD_EXTRA_SYMBOLS = /opt/test-kernel/Module.symvers(模块1的路径,必须是绝对路径), 否则会报错。

        参考资料:http://blog.csdn.net/hshl1214/article/details/8769112

六、错误处理

        如果可以继续,通过降低功能继续运转。

        如果不能继续,必须撤销所有已经注册的设备。

            goto、cleanup/flag。

            撤销顺序与加载顺序相反。

七、模块装载竞争

        模块还没初始化完的时候,内核就有可能来调用我们的模块了。

抱歉!评论已关闭.