内核模块基础--特点及其命令使用
1、模块本身并不被编译进内核文件(zImage或bzImage)
2、可以根据需要在内核运行时动态加载、卸载----》进而达到节省空间的目的
命令详解(以下载驱动DNW为例):
insmod 模块名称(注意有.ko后缀)--安装
insmod dnw_usb.ko
lsmod-->查看安装的内核模块
rmmod 模块名称(注意没有.ko后缀)---->卸载内核模块
rmmod dnw_usb
内核模块设计--简单的模块编写
根据上图范例代码,比较应用程序代码的区别
与应用程序代码区别(主要涉及三个要素-->缺一不可)
1、引出内核模块的入口在哪里?(内核模块函数没有main函数)
内核模块函数的入口是由宏module_init指明的,,其参数即是其函数入口。当使用insmod加载模块时,有宏module_init指定的初始化函数被调用。
2、出口(卸载函数)---宏module_exit指出。当使用rmmod卸载函数时,由module_exit指定的模块清除函数被调用。
3、头文件 #include<linux/init.h> #include <linux/module.h>
注:应用程序由main函数指定程序入口。
除了以上使用module_init、module_exit指定初始化、卸载函数外。也可以给初始化函数与卸载函数取固定的名字:
初始化------>int init_module(void)
卸载函数---->void cleanup_module(void)
一、根据以上三要素编写简单的驱动
二、编写该模块的Makefile。。(其格式较为固定)内容如下:
obj-m := helloworld.o //obi-m指明生成的内核模块名称
helloworld-objs := file1.o file2.o file3.o//多文件生成一个内核模块时的书写格式 ,
KDIR := /home/kindlyde/Desktop/arm-5/lesson3_3/linux-ok6410 //编译依赖的内核的路径
all:
make -C ${KDIR} M=${PWD} modules ARCH=arm CROSS_COMPILE=arm-linux- //选项M代表需编译的内核模块的位置
rm:
rm -f *.o *.ko *.order *.symvers
三、之后编译make;生成helloworld.ko文件,,把该文件放置到NFS文件系统中。。
四、挂载nfs文件系统之后,安装helloworld.ko内核模块
五、卸载helloworld内核模块
内核模块可选项:
1、模块声明
1.1MODULE_LICENSE(“遵守的协议”)----“GPL“、”GPL v2‘
申明该模块遵守的许可证协议
1.2MODULE_AUTHOR(“作者”)
申明模块的作者
1.3MODULE_DESCRIPTION(“模块的功能描述”)
申明模块的功能
1.4MODULE_VERSION(“v1.0”)
申明模块的版本
以申明模块遵守的协议为例:
声明之后安装该内核模块(不会出现 "module license 'unspecified' taints kernel"字样-->):
2、向内核模块的传入参数
主要通过宏module_param指定保存模块参数的变量;然后模块参数在加载模块时传递参数给模块
形式如下: module_param(name,type,perm)
参数:name: 变量的名称
type:变量的类型,bool:布尔型 int:整型 charp:字符串型
perm:访问权限。S_IRUGO:读权限 S_IWUSR:写权限
3、符号导出
---->在不同内核模块之间实现函数调用
使用符号导出所使用的宏:(注意导出语句的位置,应在该变量定义之后。)
EXPORT_SYMBOL(符号名)
EXPORT_SYMBOL_GPL(符号名)
注:EXPORT_SYMBOL_GPL只能用于包含GPL许可证的模块
建立另一个add.c文件,在helloworld内核模块中实现调用
helloworld文件如下:
add.c文件如下:
修改Makefile(生成两个独立的模块)
编译生成两个内核模块: helloworld.ko 和 add.ko
先安装add.ko驱动后安装helloworld.ko驱动。。调试出现以下结果,。。
注--调试时遇到的错误
1、调试printk时未打印。
如下:画线的printk变量未打印。。
制作的文件系统的关于日志消息的默认设置为 7 7 1 7:如下
这四个值是在kernel/printk.c 中被定义的,如下:
int console_printk[4] = {
DEFAULT_CONSOLE_LOGLEVEL,/* console_loglevel */-->控制台日志级别
DEFAULT_MESSAGE_LOGLEVEL,/* default_message_loglevel */--->默认消息日志级别
MINIMUM_CONSOLE_LOGLEVEL,/* minimum_console_loglevel */-->最低控制台日志级别
DEFAULT_CONSOLE_LOGLEVEL, /* default_console_loglevel */--->控制台默认优先级
};
以上关于四个数字的意义:
控制台日志级别:优先级高于该值的消息将被打印至控制台
默认的消息日志级别:将用该优先级来打印没有优先级的消息--->(即没有指定优先级的日志默认为优先级为7,,由上知等于控制台优先级。。则不打印消息。。)
最低的控制台日志级别:控制台日志级别可被设置的最小值(最高优先级)
默认的控制台日志级别:控制台日志级别的缺省值
更改默认消息级别低于7后。打印出消息;如下
设置不同级别的日志的意义:
更好地控制不同级别的信息显示在控制台上,内核设置了控制台的日志级别console_loglevel。printk日志级别的作用是打印一定级别的消息,与之类似,控制台只显示一定级别的消息。
2、安装内核模块驱动时遇见如下错误:
原因:内核模块函数写错了。。缺少入口函数。。