• 第四季-专题5-内核模块开发


    专题5-内核模块开发

    第1课-内核模块基础

    什么是内核模块?

    Linux内核的整体结构非常庞大,其中包含的组件也非常多,如何使用这些组件呢,方法1:把所有的组件都编译到内核文件中,即:zImage或者bzImage,但是这样会导致一个问题:内存占用过多。

    有没有一种机制能让内核文件本身并不包含某组件,而是在该组件需要被使用的时候,动态地添加到正在运行的内核呢?

    内核模块本身并不会被编译到内核文件(zImage或者bzImage);可以根据需求,在内核运行期间动态的安装护着卸载。

    如何使用内核模块

    安装:insmod   例如:insmod /home/dnw_usb.ko(usb下载线驱动程序)

    卸载:rmmod   例如:rmmod dnw_usb

    查看:lsmod    例如:lsmod

    第2课-内核模块设计  

    1. 范例代码分析

    #include <linux/init.h>

    #include <linux/module.h>

    static int hello_init()

    {

        printk(KERN_WARNING"Hello world! ");

        return 0;      

    }

    static void hello_exit()

        printk(KERN_WARNING"hello exit! ");

    }

    module_init(hello_init);

    module_exit(hello_exit);

    范例代码中没有main函数,在我们正常的应用程序中,main函数起到的是一个入口的作用。我们内核函数中没有面函数,但是也是同样有入口的。这里用module_init这个宏定义来表示入口,module_init这里指向hello_init,当我们输入insmod时就会调用这一个函数了。module_exit这个宏定义表示出口,或者卸载函数(也称为出口函数)。这是内核函数的两个要素。第三个要素就是我们用的两个头文件,任何模块都是要用的。

    头文件:linux/init.h; linux/module.h

    加载函数:module_init

    写在函数:module_exit

    操作注意:

    当我们把模块导入开发板后,使用命令:insmod hellowordl.ko 进行模块安装,这是会显示hello world!,说明安装正确。但是在卸载时却会出问题,这是因为在/lib/modules下没有对应的文件夹,我们使用命令:mkdir –p /lib/modules/$(uname -r)进行建立即可。这样使用命令:rmmod helloworld就会显示hello exit!,说明模块正常卸载。

         

    知识点插入:网关是什么意思?

    大家都知道,从有个房间走到另一个房间,必然经过一扇门。同样,从一个网络向另一个网络发送信息,也必须经过一道“关口”,这道关口就是网关。顾名思义,网关(Gateway)就是一个网络连接到另一个网络的“关口”。

    按照不同的分类标准,网关有很多种。TCP/IP协议里的网关是最常用的,在这里我们所讲的“网关”均指TCP/IP协议下的网关。

    那么网关到底是什么呢?网关实质上是一个网络通向其他网络的IP地址。比如有网络A和网络B,网络A的IP地址范围:“192.168.1.1~192.168.1.154”,子网掩码为:“255.255.255.0”。网络B的IP地址范围是:“192.168.2.1~192.168.2.254”,子网掩码为:“255.255.255.0”。在没有路由器的情况下,两个网络之间是不能进行TCP/IP通讯的,即使是两个网络连接在同一台交换机(或者集线器)上,TCP/IP协议也会根据子网掩码(255.255.255.0)判定两个网络中的主机处在不同的网络里。而要实现两个网络之间的通信,就必须通过网关。如果网络A中的主机发现数据包额目的的主机不在本地网络中,就必须把数据转发给他自己的网关,再有网关转发给网络B的网关,网络B的网关在转发给网络B的某个主机。这就是网络之间的通讯。

    所以说,只有设置好网关的IP地址,TCP/IP协议才能实现不同网络之间的通信。那么这个IP地址是哪台机器的IP地址呢?网关的IP地址是具有路由器功能的设备的IP地址,具有路由器功能的设备有路由器、启动了路由协议的服务器(实质上相当于一台路由器)、代理服务器(也相当于一台路由器)。

    1. 编译内核模块

    编写makefile,编译内核模块

    obj-m := helloworld.o           

    %hello-objs :=file1.0 file2.0 file3.o(在有多个文件时使用这条指令,hello对应这个模块的名字)%

    KDIR := /home/free/part2/linux   %来发版上运行这个代码的位置,特指电脑的根文件系统的存储代码的位置%

    all:

           make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm

    clean:

           rm -f *.o *.ko *.order *.aymvers

    第3课-内核模块可选项

    1. 模块申明

    (1)MODULE_LICENSE(“遵守的协议”)

    申明该模块遵守的许可证协议,如:”GPL”、”GPL v2”等

    (2)MODULE_AUTHOR(“作者”)

    申明模块的作者

    (3)MODULE_DESCRIPTION(“模块的功能描述")

    申明模块的功能

    (4)MODULE_VERSION("V1.0")

    申明模块的版本

    例子:

    在我们之前编译的helloworld.ko内核模块中,我们当运行它的时候会显示一些提示信息,主要指的是协议不合法等,我们在头文件下面加上:

    MODULE_LICENSE(“GPL”);

    再编译运行后,就不会显示之前的提示信息,这样的模块就是合法的了。

    2. 模块参数

    在应用程序中

    int main(int argc, char** argv)

    argc表示命令行输入的参数个数,argv中保存输入的参数

    当然在内核模块中也可以通过命令来传递参数,而且也有保存的位置。通过宏module_param指定保存模块参数的变量。模块参数用于在加载模块时传递参数给模块。

    module_param(name,type,perm)

    name:变量的名称

    type:变量类型,bool:布尔型int:整型charp:字符串型

    perm是访问权限。S_IRUGO:读权限S_IWUSR:写权限

    例子:

    int a = 3;

    char *st;

    module_param(a,int, S_IRUGO);

    module_param(st,charp, S_IRUGO);

    例子:

    #include <linux/init.h>

    #include <linux/module.h>

    MODULE_LICENSE(“GPL”);

    int a = 3;

    module_param(a,int, S_IRUGO| S_IRUGO); //两个参数一起使用

    /* int *p;

    module_param(p,charp, S_IRUGO| S_IRUGO);

    static int hello_init()

    {

        printk(KERN_WARNING"Hello world! ");

           printk("a=%d ",a);

       // printk("p=%s ",p);

        return 0;      

    }

    static void hello_exit()

    {

        printk(KERN_WARNING"hello exit! ");

    }

    module_init(hello_init);

    module_exit(hello_exit);

    在运行新的模块的时候,可以直接运行:insmod helloworld.ko a=10,这样在下面就会多显示a=10。

    1. 符号输出

    内核符号的导出使用宏

    EXPORT_SYMBOL(符号名)

    EXPORT_SYMBOL_GPL(符号名

    其中EXPORT_SYMBOL_GPL只能用于包含GPL许可证的模块。

    例子-      导出内核模块

    我们再创建一个模块add.ko做简单的加法运算:

    #include <linux/init.h>

    #include <linux/module.h>

    MODULE_LICENSE("GPL");

    Int add(int a,int b)

    {

           return a+b;

    }

    static int add_init()

    {

        return 0;      

    }

    static void add_exit()

    {

    }

    EXPORT_SYMBOL(add); //必须有这不,这才能显示它导出供其他模块使用。

    module_init(hello_init);

    module_exit(hello_exit);

    对应的helloworld.ko改为:

    #include <linux/init.h>

    #include <linux/module.h>

    static int hello_init()

    {

        printk(KERN_WARNING"Hello world! ");

        return 0;      

    }

    static void hello_exit()

    {

           add(1,5);

        printk(KERN_WARNING"hello exit! ");

    }

    module_init(hello_init);

    module_exit(hello_exit);

    makefile文件变为:

    obj-m := helloworld.o add.o          

    %hello-objs :=file1.0 file2.0 file3.o(在有多个文件时使用这条指令,hello对应这个模块的名字)%

    KDIR := /home/free/part2/linux   %来发版上运行这个代码的位置,特指电脑的根文件系统的存储代码的位置%

    all:

           make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm

    clean:

           rm -f *.o *.ko *.order *.aymvers

    我们将编译好的两个模块都导入到开发板中,先安装add.ko,再安装helloworld.ko,我们会看到正常运行,否则后面的模块是安装不上去的。

  • 相关阅读:
    springMvc
    计算机网络
    Mybatis 总结
    Spring 面试总结
    Java IO 流
    自定义类加载器
    缓存一致性协议
    dfs、bfs序
    7.26
    Trick
  • 原文地址:https://www.cnblogs.com/free-1122/p/10902176.html
Copyright © 2020-2023  润新知