linux下编写驱动模块的概念与代码组成

1、概念(为什么需要模块)

1.1  我们得到一个内核镜像后,想要增加功能时,两种方式:

        (1)一种方式是在配置选项中添加模块,重新编译内核,这样会很麻烦

        (2)找一个默认配置添加了全部功能的内核,这样内核太大了。 

1.2  有没有一种机制使得编译出的内核本身并不需要包含所有功能,而在这些功能需要被使用的时候,其对应的代码被动态地加载到内核中呢?
        答:Linux提供了这样的一种机制,这种机制被称为模块(Module)。模块具有这样的特点:
                (1) 模块本身不被编译入内核映像,这控制了内核的大小。
                (2) 模块一旦被加载,它就和内核中的其它部分完全一样。

2、模块代码组成

2.1  模块加载函数(必须)

        2.1.1 当通过insmod或modprobe命令加载内核模块时,模块的加载函数会自动被内核执行,完成本模块的相关初始化工作。
           (1)modprobe命令比insmod命令要强大,它在加载某模块时,会同时加载该模块所依赖的其它模块。使用modprobe命令加载的模块若以“modprobe -r filename”的方式卸载将同时卸载其依赖的模块。
            (2) 使用modinfo <模块名>命令可以获得模块的信息,包括模块作者、模块的说明、模块所支持的参数以及vermagic:
[root@localhost driver_study]# modinfo hello.ko
filename:      全路径/ hello.ko
license:        Dual BSD/GPL
author:          作者
description:   描述
alias:          a simplest module
vermagic:       2.6.15.5 686 gcc-3.2  版本号 内核版本和模块版本必须相同才能安装
depends:         依赖(这个模块是否依赖其他模块,kconfig中指定)   
         
            (3)Linux内核模块加载函数一般用static 关键字声明为内部链接,__init,本质上是个宏定义,在内核源代码中就有#define __init xxxx。这个__init的作用就是将被他修饰的函数放入.init.text段中去(本来默认情况下函数是被放入.text段中)。整个内核中的所有的这类函数都会被链接器链接放入.init.text段中,所以所有的内核模块的__init修饰的函数其实是被统一放在一起的。内核启动时统一会加载.init.text段中的这些模块安装函数,加载完后就会把这个段给释放掉以节省内存。在驱动,内核中出现__init,应用代码不会有。
         解释:init段、静态、动态方式加载模块?
                代码段: 本来函数是被放在.text段,使用__init后,把我们放在了init.text段,它在内核的init.h中有定义
                静   态:就是menuconfig配置功能时,添加进去,再编译进内核,静态加载
                动   态:就是以模块的形式动态加载,比如在rootfs命令行下 insmod

   (3)模块加载函数必须以module_init(函数名) 的形式被指定。它返回整型值,若初始化成功,返回0。初始化失败时,应该返回错误编码。内核的错误码是一个负数,在中定义,形如ENODEV等。

        (4)代码如下:

                    1. static void __exit foo_exit(void)

                  2. {

                  3. //...

                  4. }

                  5. module_exit(foo_exit);

扫描二维码关注公众号,回复: 165543 查看本文章

2.2  模块卸载函数(必须)

(1)当通过rmmod命令卸载某模块时,模块的卸载函数会自动被内核执行,完成与模块加载函数相反的功能。

(2)Linux内核模块卸载函数一般用static 关键字声明为内部链接,并以__exit 标识。和__init 一样, __exit 也可以使对应函数在运行完成后自动回收内存。具体可以查看内核代码中__init 和__exit 这两个宏的定义。

(3)模块卸载函数必须以module_exit(函数名) 的形式指定,不返回任何值。

示例代码如下:

1. static void __exit foo_exit(void)

2. {

3. //...

4. }

5. module_exit(foo_exit);


2.3  模块许可证声明(必须)

        (1)模块许可证(LICENSE)声明描述内核模块的许可权限,如果不声明 LICENSE,模块被加载时,将收到内核被污染(kernel tainted)的警告,因为内核按照LICENSE标准定义,你写的模块也必须标注相同LICENSE,内核才让你使用。在Linux2.6内核中,可接受的 LICENSE包括“GPL”,“GPL v2”,“GPL and additonal rights”,“Dual BSD/GPL”,“Dual MPL/GPL”和“Proprietary”。

        (2)Linux2.6内核模块最常见的是声明代码如下(3.4内核用的GPL v2):

                 MODULE_LICENSE("Dual BSD/GPL");

2.4  模块vermagic信息

        (1)vermagic:       2.6.15.5 686 gcc-3.2  版本号 内核版本和模块版本必须相同才能安装,比如我们在模块的makefile中指定KERN_DIR = /root/driver/kernel时,这个内核版本与我们的模块默认的内核版本不匹配,安装会出现格式错误。

详细细节请看下面博主:

https://blog.csdn.net/bugouyonggan/article/details/9097201



猜你喜欢

转载自blog.csdn.net/qq_40334837/article/details/79915705