1、常用的模块操作命令
(1)lsmod(list module,将模块列表显示),功能是打印出当前内核中已经安装的模块列表
(2)insmod(install module,安装模块),功能是向当前内核中去安装一个模块,用法是insmod xxx.ko
(3)modinfo(module information,模块信息),功能是打印出一个内核模块的自带信息。,用法是modinfo xxx.ko,注意要加.ko,也就是说是一个静态的文件形式。
(4)rmmod(remove module,卸载模块),功能是从当前内核中卸载一个已经安装了的模块,用法是rmmod xxx.ko rmmod xxx都可以
(5)剩下的后面再说,暂时用不到(如modprobe、depmod等)
2、模块的安装
(1)insmod与module_init宏。模块源代码中用module_init宏声明了一个函数(在我们这个例子里是chrdev_init函数),作用就是指定chrdev_init这个函数和insmod命令绑定
起来,也就是说当我们insmod module_test.ko时,insmod命令内部实际执行的操作就是帮我们调用chrdev_init函数。
(3)模块安装时insmod内部除了帮我们调用module_init宏所声明的函数外,实际还做了一些别的事(譬如lsmod能看到多了一个模块也是insmod帮我们在内部做了记录,也就是
将我们的模块加入到内核的一个数据结构中去),但是我们就不用管了。
3、模块的卸载
(1)module_exit和rmmod的对应关系
当我们执行rmmod命令的时候,就会执行模块的module_exit宏声明的函数,同样也会将我们这个模块信息从我们内核的模块管理的数据结构中将其删除。
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"),模块的许可证。一般声明为GPL许可证,而且最好不要少,否则可能会出现莫名其妙的错误(譬如一些明显存在的函数提升找不到)。
(2)MODULE_AUTHOR("tao Deng <> 773904075@qq.com"),用来添加模块的作者信息
(3)MODULE_DESCRIPTION("xxx"),用来添加模块的描述信息
(4)MODULE_ALIAS("xxxx"),用来添加模块的别名
以上的这些东西,我们通过modinfo命令查看模块的时候是可以看到的
6、函数修饰符
(1)__init,本质上是个宏定义,在内核源代码中就有#define __init xxxx。这个__init的作用就是将被他修饰的函数放入.init.text段中去(本来默认情况下函数是被
放入.text段中)。 实质就是在C高级中讲到的attribute的用法。
整个内核中的所有的这类函数都会被链接器链接放入.init.text段中,所以所有的内核模块的__init修饰的函数其实是被统一放在一起的。内核启动时统一会加载.init.text
段中的这些模块安装函数,加载完后就会把这个段给释放掉以节省内存。
(2)__exit
7、printk函数详解
(1)printk在内核源码中用来打印信息的函数,用法和printf非常相似。
(2)printk和printf最大的差别:printf是C库函数,是在应用层编程中使用的,不能在linux内核源代码中使用;printk是linux内核源代码中自己封装出来的一个打印函数,
是内核源码中的一个普通函数,只能在内核源码范围内使用,不能在应用编程中使用。
(3)printk相比printf来说还多了个:打印级别的设置。printk的打印级别是用来控制printk打印的这条信息是否在终端上显示的。应用程序中的调试信息要么全部打开要么
全部关闭,一般用条件编译来实现(DEBUG宏),但是在内核中,因为内核非常庞大,打印信息非常多,有时候整体调试内核时打印信息要么太多找不到想要的要么一个没有没
法调试。所以才有了打印级别这个概念。
(4)操作系统的命令行中也有一个打印信息级别属性,值为0-7。当前操作系统中执行printk的时候会去对比printk中的打印级别和我的命令行中设置的打印级别,小于我的
命令行设置级别的信息会被放行打印出来,大于的就被拦截的。譬如我的ubuntu中的打印级别默认是4,那么printk中设置的级别比4小的就能打印出来,比4大的就不能打印出来。
可以通过这个命令查看 : cat /proc/sys/kernel/printk
(5)ubuntu中这个printk的打印级别控制没法实践,ubuntu中不管你把级别怎么设置都不能直接打印出来,必须dmesg命令去查看。
8、关于驱动模块中的头文件
(1)驱动源代码中包含的头文件和原来应用编程程序中包含的头文件不是一回事。应用编程中包含的头文件是应用层的头文件,是应用程序的编译器带来的(譬如gcc的头文件路
径在 /usr/include下,这些东西是和操作系统无关的)。驱动源码属于内核源码的一部分,驱动源码中的头文件其实就是内核源代码目录下的include目录下的头文件。
9、用开发板来调试模块
(1)设置bootcmd使开发板通过tftp下载自己建立的内核源码树编译得到的zImage: set bootcmd 'tftp 0x30008000 zImage;bootm 0x30008000'
(2)设置bootargs使开发板从nfs去挂载rootfs(内核配置时需要使其支持挂载NFS文件系统,这个在uboot中已经说过)
setenv bootargs root=/dev/nfs nfsroot=192.168.1.141:/root/porting_x210/rootfs/rootfs ip=192.168.1.10:192.168.1.141:192.168.1.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC2,115200
(3)修改Makefile中的KERN_DIR使其指向自己建立的内核源码树
(4)将自己编译好的驱动.ko文件放入nfs共享目录下去
(5)开发板启动后使用insmod、rmmod、lsmod等去进行模块实验
参考: 《朱友鹏嵌入式Linux开发\5.Linux驱动开发\5.2.字符设备驱动基础》