• 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #3 如何编写内核模块


    HACK #3 如何编写内核模块

    本节将介绍向Linux内核中动态添加功能的结构—内核模块的编写方法。
    内核模块
    Linux内核是单内核(monolithic kernel),也就是所有的内核功能都集成在一个内核空间内。但是内核具有模块功能,可以将磁盘驱动程序、文件系统等独立的内核功能制作成模块,并动态添加到内核空间或者删除。
    内核模块是可以动态添加到Linux内核空间的二进制文件,文件扩展名为ko。
    内核模块的编写方法大致有两种。一种是将内核源码树带有的功能编写为模块的方法(参考Hack #2),另一种是将内核源码树中所没有的特有功能编写为模块的方法。
    通过内核配置编写模块
    把内核源代码文件中CONFIG_*=m的项目所对应的驱动程序编写为模块。编写生成的模块一般安装在/lib/modules/内核版本/kernel下。
    以RHEL6为例

    # ls /lib/modules/2.6.32-71.29.1.el6.x86_64/kernel/
    arch  crypto  drivers  fs  kernel  lib  mm  net  sound
    

    编写特有的内核模块
    下面将介绍如何编写内核源码树中所没有的特有内核模块。
    以mymod模块为例说明,请将下面的代码以mymod.c为文件名保存。

    #include <linux/module.h>
    #include <linux/timer.h>
    #include <linux/errno.h>
    
    static int sec = 5;
    module_param(sec, int, S_IRUGO|S_IWUSR);
    MODULE_PARM_DESC(sec, "Set the interval.");
    
    static void mymod_timer(unsigned long data);
    
    static DEFINE_TIMER(timer, mymod_timer, 0, 0);
    
    static void mymod_timer(unsigned long data)
    {
        printk(KERN_INFO "mymod: timer
    ");
        mod_timer(&timer, jiffies + sec * HZ);
    }
    
    static int mymod_init(void)
    {
        printk(KERN_INFO "mymod: init
    ");
    
        if (sec <= 0) {
            printk(KERN_INFO "Invalid interval sec=%d
    ", sec);
            return -EINVAL;
        }
    
        mod_timer(&timer, jiffies + sec * HZ);
    
        return 0;
    }
    
    static void mymod_exit(void)
    {
        del_timer(&timer);
        printk(KERN_INFO "mymod: exit
    ");
    }
    
    module_init(mymod_init);
    module_exit(mymod_exit);
    
    MODULE_AUTHOR("Hiroshi Shimamoto");
    MODULE_LICENSE("GPL");
    MODULE_DESCRIPTION("My module");
    

    在模块的源代码中包含(include)头文件linux/module.h。
    名为module_int()和module_exit()的宏,可以调用回调(callback)函数来进行初始化和终止模块的处理。在模块的源文件中进行如下描述,就可以在添加模块时调用初始化函数,在删除模块时调用终止函数。
    module_init(初始化函数名);
    module_exit(终止函数名);
    在这个例子模块的情形下调用的分别是mymod_init()和mymod_exit()。
    初始化函数为了表示初始化已正常完成,需要返回0。按照Linux内核中的写法,发生错误(error)时将返回一个值为负数的错误代码。在这个例子中,如果设定值出错,则处理为-EINVAL(非法值)。
    下面先用3个宏对模块进行定义,但在模块编写中并不是必需的。
    image

    这个例子模块还用到了模块参数。模块参数可以使用module_param()宏来生成。
    module_param(参数名,参数类型,权限(permission));
    在例子模块中,sec定义为int类型的模块参数。
    另外,还可以使用MODULE_PARM_DESC()宏来对模块参数进行说明。
    先简单介绍一下这个例子的运行过程。当添加模块时,会调用指定为初始化函数的mymod_init()。在mymod_init()中首先通过printk()输出:
    mymod: init
    然后确认模块参数sec是否正常。在模块参数sec的值为0以下的异常情形时,会返回EINVAL错误代码并终止程序。在判断模块参数sec正常后,将内核计时器设置为sec秒后启动超时(timeout)函数mymod_timer()。在每隔sec秒启动的mymod_timer()中,首先使用printk()输出:

    mymod: timer
    

    再次设置sec秒的内核计时器,然后终止。当删除模块时,会调用mymod_exit()函数,删除内核计时器,通过printk()输出:

    mymod: exit
    

    于是模块终止。
    接下来需要准备编写模块所需的Makefile。由于是使用内核的创建框架来生成,因此Makefile的内容非常简单。

    obj-m :=mymod.o
    

    最后执行下列make命令,通过当前目录(current directory)的源代码和Makefile生成模块mymod.ko。

    # make -C /lib/modules/'uname 杛'/build M='pwd'
    

    通过使用modinfo命令,可以看到所生成模块mymod.ko的信息。从这里可以看到使用MODULE_*宏所指定的内容。

    # modinfo mymod.ko
    filename:       mymod.ko
    description:    My module
    license:        GPL
    author:         Hiroshi Shimamoto
    srcversion:     61A3BB7CFC0C89B8344F5A5
    depends:        
    vermagic:       2.6.32-71.29.1.el6.x86_64 SMP mod_unload modversions 
    parm:           sec:Set the interval. (int)
    

    添加内核模块
    添加内核模块需要用到insmod命令或modprobe命令。
    通过执行insmod命令把生成的mymod.ko模块添加进来。

    # insmod mymod.ko
    

    使用dmesg命令,可以看到例子模块mymod.ko的输出内容。

    # dmesg | tail
           :
    mymod: init
    

    作为模块初始化函数mymod_init()所调用的printk()的输出内容会在最后一行显示。
    使用lsmod可以显示目前添加到内核中的模块列表。

    # lsmod
    Module                Size   Used by
    mymod                 1482   0
    
       :

    可以看到,mymod行存在,模块已添加。
    要将已添加的模块从内核空间删除时,可以使用rmmod命令。

    # rmmod mymod
    

    执行rmmod命令后,模块将从内核空间内删除,使用lsmod命令就不会再输出mymod行。
    此外,使用dmesg命令还可以看到终止模块的处理中printk()输出的信息mymod: exit。

    # dmesg | tail
           :
    mymod: exit
    

    下面针对模块参数作一些介绍。在添加模块后,就会在/sys/module下生成对应的目录和文件。

    # ls /sys/module/mymod/
    

    holders initstate notes parameters refcnt sections srcversion
    可以确认在parameters下生成的模块mymod中所定义的参数sec。

    # ls -l /sys/module/mymod/parameters/sec
    

    -rw-r--r--. 1 root root 4096 May 15 06:34 /sys/module/mymod/parameters/sec
    其内容应当是初始值5。

    # cat /sys/module/mymod/parameters/sec
    

    5
    模块参数可以在使用insmod添加模块时对值进行指定。

    # insmod mymod.ko sec=10
    

    进行上述操作后,添加mymod.ko时模块参数sec就为10,默认间隔5秒的超时变成间隔10秒。
    小结
    本节介绍了内核模块的编写方法。编写特有内核模块是Kernel构建的入门级操作,你也可以尝试一下。
    参考文献
    Documentation/kbuild/modules.txt
    —Hiroshi Shimamoto

  • 相关阅读:
    kafka集群搭建
    数据导入 xls --》mysql
    Spark --RDD算子
    Spark集群搭建
    【已解决】 IDEA运行spark程序报错:GC overhead limit exceeded?
    Spring Boot 配置 ---02
    Spring Boot 入门 ---01
    Nginx 推流 拉流 --- 点播直播
    【转】JS内置对象方法
    MapReduce 简单数据统计
  • 原文地址:https://www.cnblogs.com/tcicy/p/8552644.html
Copyright © 2020-2023  润新知