- linux内核模块开发
- 字符设备驱动
- 内核中并发和竞态的解决方法
- IO模型
- 设备树
- GPIO子系统,内核定时器
- 中断子系统
- platform总线驱动
- i2c总线驱动
- spi总线驱动
- 块设备驱动
- 网卡设备驱动
- camera驱动
目录
一、内核模块的三要素
入口:在入口中分配资源
出口:在出口中释放资源
许可证:遵从GPL协议
二、内核模块的编写
#include <linux/init.h> #include <linux/module.h> //入口 static int __init demo_init(void) { //static:限定作用域 //int:返回值类型 //__init:(给编译器使用) //#define __init __section(".init.text") //内核的链接脚本vmlinux.lds==> = ALIGN(8); .init.text //将demo_init函数放在.init.text段中 //demo_init:驱动的入口函数的名字(随便写) //(void):不能传递参数 return 0; } //出口 static void __exit demo_exit(void) { //__exit:告诉编译器将demo_exit函数放在.exit.text段中 } //将入口函数的地址告诉给内核 module_init(demo_init); //将出口函数的地址告诉给内核 module_exit(demo_exit); //许可证 MODULE_LICENSE("GPL");
三、驱动模块的编写
1. 内部编译:
Kconfig .config Makefile
2.外部编译
#KERNELDIR:= /home/linux/linux-5.10.61 #在开发板上安装
KERNELDIR:= /lib/modules/$(shell uname -r)/build #在ubuntu上安装
PWD:=$(shell pwd)
all:
make -C $(KERNELDIR) M=$(PWD) modules
@#从当前目录切换到内核的顶层目录下
@#读取内核顶层目录下的Makefile文件
@#执行make
@#make M=$(PWD) modules
@#make modules:模块化编译的命令,M指定只编译当前目录下的目录
clean:
make -C $(KERNELDIR) M=$(PWD) clean
@#清除编译的中间文件
obj-m:=demo.o
#指定编译的模块名是demo
= :在给var2赋值的时候会将var1最后一次赋值的结果赋值给var2
var1=123
var2=$(var1)
var1=456
all:
@echo $(var2) #456
:= :立即赋值
var1=123
var2:=$(var1)
var1=456
all:
@echo $(var2) #123
+= :附加赋值
var1=123
var1 +=456
all:
@echo $(var1) #123 456
?= :询问赋值
#var1=123
var1?=456 #询问var1之前是否被赋值给,如果被赋值给本次赋值不成立,否则成立
all:
@echo $(var1)
通用的makefile
arch?=x86
modname?=demo
ifeq ($(arch),arm)
KERNELDIR:= /home/linux/linux-5.10.61
else
KERNELDIR:= /lib/modules/$(shell uname -r)/build
endif
PWD:=$(shell pwd)
all:
make -C $(KERNELDIR) M=$(PWD) modules
clean:
make -C $(KERNELDIR) M=$(PWD) clean
obj-m:=$(modname).o
编译之后:
sudo insmod demo.ko //安装模块
lsmod //查看模块
sudo rmmod demo //卸载模块
四、编写进行测试
#include <linux/init.h>
#include <linux/module.h>
static int __init mycdev_init(void)
{
//1.打印级别是3,大于终端的4级别,消息应该会在终端上回显
printk(KERN_ERR "hello DC22021 everyone!\n");
//2使用的默认消息级别是4,终端级别也是4,所以消息不会在终端上回显
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
static void __exit mycdev_exit(void)
{
//1.打印级别是3,大于终端的4级别,消息应该会在终端上回显
printk(KERN_ERR "bye DC22021 everyone!\n");
//2使用的默认消息级别是4,终端级别也是4,所以消息不会在终端上回显
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
写好之后按照上面的程序依次进行编译、安装 然后:
需要在ubuntu中打开虚拟终端,
打开:ctrl+alt+(fn)+{F2 ~ F6} ===>用户名:linux 密码:
安装卸载驱动看现象:
在init和exit中只显示了第一句话
退出:ctrl+alt+(fn)+F1