美文网首页
向内核模块传参以及共享内核模块中的符号

向内核模块传参以及共享内核模块中的符号

作者: 付凯强 | 来源:发表于2022-06-26 20:22 被阅读0次

    向内核模块传参

    内核模块作为可扩展的动态模块,有时候需要根据不同的场景给内核模块传递不同的参数,让内核模块更加具有灵活性.在Linux内核中提供了宏module_param(name, type, perm)来实现模块的参数传递.

    #define module_param(name, type, perm)           \
       module_param_named(name, name, type, perm)
    
    1. 宏module_param(name, type, perm)用来实现向内核模块进行参数传递.一共有3个参数: name 表示参数名, type 表示参数类型, perm 参数在 sysfs 中的可见性.
    2. type 参数类型可以是byte, short, ushort, int, uint, long, ulong, charp, bool, invbool(不熟悉).
    3. perm 的值可以是 0 ,表示不会出现在 sysfs 文件系统中.也可以是 S_IRUGO(0444),表示可以被所有人读取,但是不能修改.也可以是 S_IRUGO|S_IWUSR(0644),表示可以让拥有root权限的用户修改参数.
    4. linux 内核也提供了宏 MODULE_PARM_DESC(_parm, desc) 来对参数进行描述.
    #define MODULE_PARM_DESC(_parm, desc) \
       __MODULE_INFO(parm, _parm, #_parm ":" desc)
    

    下面我们对上一篇文章中的helloworld的示例进行更改:通过传递的参数来决定是否输出日志.

    ...
    static int debug = 1;
    module_param(debug,int,0644);
    MODULE_PARM_DESC(debug,"enable debugging info");
    
    #define dprintk(args...) \
        if (debug) { \
            printk(KERN_DEBUG args); \
        }
        
    static int __init hello_init(void)
    {
            dprintk("hello World enter\n");
            return 0;
    }
    
    static void __exit hello_exit(void)
    {
            dprintk("hello World exit\n");
    }
    ...
    
    1. 增加了变量debug,作为传入的参数值的接收者,同时定义了其在文件系统中的可见性为0644.
    2. 通过变量debug,定义了宏dprintk(args...),如果debug的值为0则不输出日志,如果为1则输出日志.默认不打印.
    3. 用dprintk替代了printk.
      接下来 重新编译并卸载然后加载 helloworld模块.
    $make
    make -C /lib/modules/4.15.0-142-generic/build M=/media/fukaiqiang/Disk960/Codes/kernel/hello_world
    make[1]: Entering directory '/usr/src/linux-headers-4.15.0-142-generic'
      CC [M]  /media/fukaiqiang/Disk960/Codes/kernel/hello_world/helloworld.o
      Building modules, stage 2.
      MODPOST 1 modules
      CC      /media/fukaiqiang/Disk960/Codes/kernel/hello_world/helloworld.mod.o
      LD [M]  /media/fukaiqiang/Disk960/Codes/kernel/hello_world/helloworld.ko
    make[1]: Leaving directory '/usr/src/linux-headers-4.15.0-142-generic'
    
    sudo rmmod helloworld.ko
    
    sudo insmod helloworld.ko debug=1
    
    [32570.897361] hello World enter
    

    到此我们通过控制debug的参数值实现了关闭还是打开调试信息.看到这里,感兴趣的小伙伴,不妨立刻行动起来,去实现一个向内核模块传递参数的示例.

    补充: 在/sys/module/helloworld/parameters/中可以看到新增的参数debug.

    { fukaiqiang@superman /media/fukaiqiang/Disk960/Codes/kernel/hello_world }
    $cd /sys/module/helloworld/parameters/
    
    { fukaiqiang@superman /sys/module/helloworld/parameters }
    $ls
    debug
    

    内核模块符号共享

    一个模块如果想调用另外一个模块的接口函数,需要接口函数的提供者把这些接口函数进行共享,这里可以借助两个宏来实现.

    #define EXPORT_SYMBOL(name)             \
       __EXPORT_SYMBOL(name, KSYM_FUNC(name),)
    #define EXPORT_SYMBOL_GPL(name)             \
       __EXPORT_SYMBOL(name, KSYM_FUNC(name), _gpl)
    
    1. EXPORT_SYMBOL 将函数以符号的方式导出给内核中的其他模块使用.
    2. EXPORT_SYMBOL_GPL 只能包含GPL许可的模块,当然使用者也必须显式地声明模块为GPL.
      现在修改上面的helloworld的内核模块,增加以下代码,共享一个变量和一个函数.
    static int age = 18;
    static void say_my_age(void)
    {
        dprintk("my age is %d .", age);
    }
    EXPORT_SYMBOL(age);
    EXPORT_SYMBOL(say_my_age);
    

    然后重新编译并卸载和加载helloworld内核模块.
    这个时候会在helloworld的内核模块目录下的Module.symvers文件中发现共享的变量和函数.

    0x00000000  age /media/fukaiqiang/Disk960/Codes/kernel/hello_world/helloworld   EXPORT_SYMBOL
    0x00000000  say_my_age  /media/fukaiqiang/Disk960/Codes/kernel/hello_world/helloworld   EXPORT_SYMBOL
    

    接下来把Module.symvers文件复制到另外一起模块,我们起名字为hellobeijing.这个新模块是从helloworld修改而来,大同小异,在hellobeijing.c文件中增加以下代码:

    extern int age;
    extern void say_my_age(void);
    

    并在hello_init函数中进行调用:

    static int __init hello_init(void)
    {
        
        dprintk("global variable age = %d .",age);
        say_my_age();
        dprintk("hello beijing init\n");
        return 0;
    }
    

    完整代码如下:

    #include <linux/init.h>
    #include <linux/module.h>
    
    static int debug = 0;
    module_param(debug,int,0644);
    MODULE_PARM_DESC(debug,"enable debugging info");
    
    #define dprintk(args...) \
        if (debug) { \
            printk(KERN_DEBUG args); \
        }
    
    extern int age;
    extern void say_my_age(void);
    
    static int __init hello_init(void)
    {
        
        dprintk("global variable age = %d .",age);
        say_my_age();
        dprintk("hello beijing init\n");
        return 0;
    }
    
    module_init(hello_init);
    
    static void __exit hello_exit(void)
    {
        dprintk("hello beijing exit\n");
    }
    
    module_exit(hello_exit);
    
    MODULE_AUTHOR("fukaiqiang");//作者
    MODULE_LICENSE("GPL v2");//模块许可证声明,一般用GPL v2
    MODULE_DESCRIPTION("A simple hello beijing module");//模块描述
    MODULE_ALIAS("hb"); //别名
    

    然后进行编译并加载.

    $make
    make -C /lib/modules/4.15.0-142-generic/build M=/media/fukaiqiang/Disk960/Codes/kernel/hello_beijing
    make[1]: Entering directory '/usr/src/linux-headers-4.15.0-142-generic'
      CC [M]  /media/fukaiqiang/Disk960/Codes/kernel/hello_beijing/hellobeijing.o
      Building modules, stage 2.
      MODPOST 1 modules
      CC      /media/fukaiqiang/Disk960/Codes/kernel/hello_beijing/hellobeijing.mod.o
      LD [M]  /media/fukaiqiang/Disk960/Codes/kernel/hello_beijing/hellobeijing.ko
    make[1]: Leaving directory '/usr/src/linux-headers-4.15.0-142-generic'
    
    $sudo insmod hellobeijing.ko debug=1
    [sudo] fukaiqiang 的密码:
    

    现在看下日志内容:

    [ 3361.894465] global variable age = 18 .
    [ 3361.894466] my age is 18 .
    [ 3361.894467] hello beijing init
    

    到此,我们已经可以在一个模块中去使用另外一个模块的变量和函数.

    对以上两个小节内容感兴趣的小伙伴,现在就把代码敲起来吧.

    补充: 模块编译时,如何寻找使用的符号?

    1. 在本模块符号表中,寻找符号(函数或变量实现)
    2. 在内核全局符号表中寻找
    3. 在模块目录下的Module.symvers文件中寻找

    参考

    奔跑吧内核 入门篇第2版 第五章内核模块

    相关文章

      网友评论

          本文标题:向内核模块传参以及共享内核模块中的符号

          本文链接:https://www.haomeiwen.com/subject/uumgvrtx.html