1:驱动开发的准备工作
(1)正常运行linux系统的开发板。要求开发板中的linux的zImage必须是自己编译的,不能是别人编译的。
(2)内核源码树,其实就是一个经过了配置编译之后的内核源码。这里使用的是九鼎官方提供的kener,因为这个内核已经是移植好的,所以只需要直接编译即可。首先make distclean进行清理,然后make x210ii_qt_defconfig进行配置,最后make 编译得到Zimage(在/arch/arm/boot)目录下。
(3)nfs挂载的rootfs,主机ubuntu中必须搭建一个nfs服务器。
2:常用的模块操作命令
(1)lsmod(list module 将模块列表显示):功能是打印出当前内核中已经安装的模块列表
(2)insmod(install module 安装模块):功能是向当前内核中去安装一个模块,用法是insmod xxx.ko
(3)modinfo(module information 模块信息):功能是打印出一个内核模块的自带信息。用法是modinfo xxx.ko
(4)rmmod(remove module 删除模块):功能是从当前内核中卸载一个已经安装了的模块,用法是rmmod xxx(注意卸载模块时只需要输入模块名即可,有些低版本的内核中如果加了后缀.ko会报错)
3:模块的安装(模块的卸载类似 module _exit与rmmod对应)
(1)先lsmod再insmod看安装前后系统内模块记录。实践测试标明内核会将最新安装的模块放在lsmod显示的最前面。
(2)insmod与module_init宏。模块源代码中用module_init宏声明了一个函数(在我们这个例子里是chrdev_init函数),作用就是指定chrdev_init这个函数和insmod命令绑定起来,也就是说当我们insmod module_test.ko时,insmod命令内部实际执行的操作就是帮我们调用执行chrdev_init函数。因为我们当前的驱动是安装在ubuntu中的,由于ubuntu拦截了我们安装时的打印信息,所以我们不能直接看到chrdev_init函数中的printk打印的信息,需要使用dmesg命令来查看。
(3)模块安装时insmod内部除了帮我们调用module_init宏所声明的函数外,实际还做了一些别的事
4:模块的版本信息
(1)使用modinfo查看模块的版本信息
(2)内核zImage中也有一个确定的版本信息
(3)insmod时模块的vermagic必须和内核的相同,否则不能安装,报错信息为:insmod: ERROR: could not insert module module_test.ko: Invalid module format
(4)模块的版本信息是为了保证模块和内核的兼容性,是一种安全措施
(5)如何保证模块的vermagic和内核的vermagic一致?编译模块的内核源码树就是我们编译正在运行的这个内核的那个内核源码树即可。说白了就是模块和内核要同出一门。
5:模块中常用的宏
(1)MODULE_LICENSE,模块的许可证。一般声明为GPL许可证,而且最好不要少,否则可能会出现莫名其妙的错误(譬如一些明显存在的函数提升找不到)。
(2)MODULE_AUTHOR:描述模块的作者
(3)MODULE_DESCRIPTION:描述模块的介绍信息
(4)MODULE_ALIAS:描述模块的别名
这些来自于内核驱动部分的代码,其余的也可以在内核驱动部分去寻找
6:函数修饰符
(1)__init:本质上是个宏定义,在内核源代码中就有#define __init xxxx。这个__init的作用就是将被他修饰的函数放入.init.text段中去(本来默认情况下函数是被放入.text段中)。整个内核中的所有的这类函数都会被链接器链接放入.init.text段中,所以所有的内核模块的__init修饰的函数其实是被统一放在一起的。内核启动时统一会加载.init.text段中的这些模块安装函数,加载完后就会把这个段给释放掉以节省内存。
(2)__exit:和__init类似
注:前面加一个_表示这是给内核使用的,加两个__表示是给内核内部使用的,加三个_表示很靠近内核的核心,基本不用自己去修改的