本文已参与「新人创作礼」活动,一起开启掘金创作之路。
一、驱动模块命令
在linux里面,当一个驱动程序以模块化的模式编译之后,它并不是随着系统启动而直接活跃的,而是静静的躺在内核的某个角落里,此时是不可用的。应用程序想要调用该驱动,则必须先加载,然后才能使用。而且,使用完还存在一个卸载的过程。 如果玩过驱动的都知道,编译完驱动过后会有一个KO(kernel object)文件,该文件的意义就是把内核的一些功能移动到内核外边, 需要的时候插入内核,不需要时卸载。 所以有一些命令,会方便我们去操作内核模块 (1)lsmod(list module,显示模块列表)功能是打印出内核中已经安装的模块列表 (2)insmod(install module,安装模块)安装一个模块,用法 insmod xxx.ko (3)modinfo(module infomation,显示模块信息 )打印出内核模块自带的信息,包括许可证、版本号、内核目录等等,用法 modinfo xxx.ko (4)rmmod(remove module,卸载模块)将安装的模块卸载,用法 rmmod xxxx(没有后缀,不加ko)
二、简单的驱动模块
我们来写一个简单的驱动来展示我们刚才的一些命令 hello.c
#include <linux/module.h>
#include <linux/init.h>
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("XHH");
MODULE_DESCRIPTION ("A test module");
static int hello_init(void) {
printk(KERN_DEBUG "hello world\n");
return 0;
}
static void hello_exit(void) {
printk(KERN_DEBUG"goodbye my world\n");
}
module_init(hello_init);
module_exit(hello_exit);
Makefile
KEVN := $(shell uname -r)
PWD := $(shell pwd)
KERN_DIR := /lib/modules/$(KEVN)/build
obj-m := hello.o
all:
make -C $(KERN_DIR) M=$(PWD) modules
clean:
make -C $(KERN_DIR) M=$(PWD) clean
这里需要注意的Makefile的编写
make 这里是TAB -C $(KERN_DIR) M=$(PWD) modules //M=$(PWD)中间不能有空格
开始的时候
make编译后
我们可以用 lsmod 查看我们现在的驱动模块有那些、注意这些是倒叙,最新的在最上面
可以明显看到hello.ko没有安装
我们用insmod hello.ko 加载看后再看看
这里会有提示,没有操作权限,直接切换到root权限就好了
这里就已经安装好了
不用的时候卸载掉了
加载了的驱动通过modinfo 可以看到相关信息
由于在ubuntu里面我们看不到我们 printk的信息,所以我们需要用dmesg 来查看信息
最新加载、卸载的驱动都在最下面,我们可以直接看到
三、代码解释
#include <linux/module.h>
#include <linux/init.h>
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("XHH");
MODULE_DESCRIPTION ("A test module");
static int hello_init(void) {
printk(KERN_DEBUG "hello world\n");
return 0;
}
static void hello_exit(void) {
printk(KERN_DEBUG"goodbye my world\n");
}
module_init(hello_init);
module_exit(hello_exit);
首先说明头文件: linux/module.h 是指linux下一级目录下的module.h这个文件,主要是代码里面有MODULE_LICENSE这些module相关的函数 linux/init.h 是指linux下下一级目录下的init.h这个文件,主要作用的代码里面 _init 和 _exit 相关的东西,下划线不能少,相当于直接写入.init.exit.这个文档里面去。 多重目录下头文件也可以改变 linux/xxx/yyy/ooo.h
MODUE_LICENSE
MODULE_AUTHOR
MODULE_DESCRIPTION
这些都是一下信息,module的协议,作者、简单描述等等
printk(KERN_DEBUG "hello world\n");
printk是内核里面的函数,而我们printf是C库里面的,所以这里的printf是不起作用的,因为我们驱动在内核里面。
KERN_DEBUG 是错误等级,应该一共有8级,数字越小,证明错误越大。看下图
我们甚至可以写成这样
//printk("<7>""hello world\n")
module_init(hello_init);
module_exit(hello_exit);
最后这个和我们开始MODULE_LICENSE一样都是标准,内核已经给我定义号接口了,我们只需要去用就行
static也是要加的,因为内核里面的驱动实在太多,你不能保证你的名字和别人的不一样,所以最好是加上static