一、简介:
Linux内核是一个整体结构,并且通过内核模块的形式向开发人员提供了一种动态加载程序到内核的能力。通过内核模块嵌入式 linux驱动,开发人员可以访问内核的资源,内核还向开发人员提供了访问底层硬件和总线的插口。为此,Linux系统的驱动是通过内核模块实现的。
Linux内核模块是一种可以被内核动态加载和卸载的可执行程序。通过内核模块可以扩充内核的功能,一般内核模块被用于设备驱动、文件系统等。若果没有内核模块,须要向内核添加功能就须要更改代码、重新编译内核、安装新内核等步骤,除了繁杂,并且容易出错,不便于调试。
二、常用的模块操作命令:
(1)lsmod(listmodule,将模块列表显示),功能是复印出当前内核中早已安装的模块列表
(2)insmod(installmodule,安装模块),功能是向当前内核中去安装一个模块,用法是insmodxxx.ko
(3)modinfo(moduleinformation,模块信息),功能是复印出一个内核模块的自带信息。
(4)rmmod(removemodule,卸载模块),功能是从当前内核中卸载一个早已安装了的模块,用法是rmmodxxx(注意卸载模块时只须要输入模块名即可,不能加.ko后缀)
三、编写一个最简单的的内核模块
module_test.c
#include // module_init module_exit
#include // __init __exit
// 模块安装函数
static int __init chrdev_init(void)
{
printk(KERN_INFO "chrdev_init helloworld initn");
return 0;
}

// 模块卸载函数
static void __exit chrdev_exit(void)
{
printk(KERN_INFO "chrdev_exit helloworld exitn");
}
module_init(chrdev_init);//注册模块的初始化函数
module_exit(chrdev_exit);//注册模块的退出函数
// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("liyijun"); // 描述模块的作者
MODULE_DESCRIPTION("module test"); // 描述模块的介绍信息
MODULE_ALIAS("clark"); // 描述模块的别名信息
module_init的浅显理解:
模块源代码中用module_init宏申明了一个函数(在我们这个事例里是chrdev_init函数),作用就是指定chrdev_init这个函数和insmod命令绑定上去,也就是说当我们insmodmodule_test.ko时,insmod命令内部实际执行的操作就是帮我们调用chrdev_init函数。模块安装时insmod内部不仅帮我们调用module_init宏所申明的函数外,实际还做了一些别的事(例如lsmod能见到多了一个模块也是insmod帮我们在内部做了记录),而且我们就不用管了。
module_init的深入理解:
四、编译内核模块
Makefile
#ubuntu的内核源码树,如果要编译在ubuntu中安装的模块就打开这2个
KERN_VER = $(shell uname -r)
KERN_DIR = /lib/modules/$(KERN_VER)/build
obj-m += module_test.o
all:
make -C $(KERN_DIR) M=`pwd` modules
.PHONY: clean
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
Makefile解释:
1、uname-r:显示操作系统的发行版号linux怎么读,所以在我的计算机上KERN_VER变量就是4.15.0-64-generic
KERN_DIR变量就是/lib/modules/4.4.0-62-generic/build
2、obj-m+=module_test.o,这一行就表示我们要将module_test.c文件编译成一个模块,不会编译到内核,并且会生成一个独立的”module_test.ko”文件;
如果是obj-y则表示把module_test.o文件编译进内核;
3、make-C$(KERN_DIR)M=`pwd`modules该命令是makemodules命令的扩充,拿来实际编译模块,大致意思就是:借助make-C(步入到某个目录下编译)步入到我们指定的内核源码树目录下永久免费linux服务器,之后在源码目录树下借用内核源码中定义的模块编译规则(makefile)去编译这个模块,M=`pwd`表明之后返回到当前目录继续读入、执行当前的Makefile。
编译完成以后嵌入式 linux驱动,生成module_test.ko和几个临时文件:
五、安装与卸载内核模块
在执行完安装命令后并没有将chrdev_inithelloworldinit信息复印,这是由于ubuntu中这个printk的复印级别控制无法实践,ubuntu中不管你把级别如何设置都不能直接复印下来,必须dmesg命令去查看。
关于tail命令参考以下:
我们对模块进行卸载:
六、内核模块的传参
驱动程序常须要在加载的时侯提供一个或则多个参数,内模块提供了设置参数的能力。通过module_param()宏可以为内核模块设置一个参数。定义如下:
module_param(参数名称,类型,属性)其中,参数名称是加载内核模块时使用的参数名称,在内核模块中须要有一个同名的变量与之对应;类型是参数的类型,内核支持C语言常用的基本类型;属性是参数的访问权限。
代码示例:
#include // module_init module_exit
#include // __init __exit
#include
static int initValue = 0;
static char* initName = NULL;
module_param(initValue,int,S_IRUGO);
module_param(initName,charp,S_IRUGO);
// 模块安装函数
static int __init chrdev_init(void)
{
printk(KERN_INFO "initValue = %d,initName = %sn",initValue,initName);
printk(KERN_INFO "chrdev_init helloworld initn");
return 0;
}
// 模块卸载函数
static void __exit chrdev_exit(void)
{
printk(KERN_INFO "chrdev_exit helloworld exitn");
}
module_init(chrdev_init);//注册模块的初始化函数
module_exit(chrdev_exit);//注册模块的退出函数
// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("liyijun"); // 描述模块的作者
MODULE_DESCRIPTION("module test"); // 描述模块的介绍信息
MODULE_ALIAS("clark"); // 描述模块的别名信息
Makefile文件同上
安装模块的时侯一齐传递参数:
使用modinfo命令查看内核模块的信息:
REF:
朱友鹏讲义
《ARM嵌入式linux系统开发解读》