1、linux设备驱动的模块
1.1 内核模块的三要素(面试题)
入口:在入口函数中申请资源
出口:在出口函数中释放资源
许可证:遵循GPL的开源协议
1.2 内核模块代码的编写
// 头文件的包含可以使用vi -t 函数名/宏名 查看最终的头文件
// 在linux驱动中的头文件一般都是<linux/头文件名.h>
#include <linux/init.h>
#include <linux/module.h>
// 入口函数:申请资源
static int __init demo_init(void)
{
// static : 静态的成员函数,只可以在本文件中使用,其他文件不可以使用
// int : 返回类型
// __init : 修饰demo_init函数,给编译器使用,
// 告诉编译器将demo_init函数编译时放到__init段,
// 当加载驱动时直接从__init段查找对应的入口函数,最终加载驱动,提高查找的效率。
/*
vi -t __init ----> 查看__init在哪里定义
50 #define __init __section(".init.text") // include/linux/init.h
在linux内核的连接脚本文件中:arch/arm/kernel/vmlinux.lds得到以下内容。
32 . = ALIGN(8); .init.text : AT(ADDR(.init.text) - 0)
*/
// demo_init : 入口函数的名字,自己随意编写,符合函数名的命名规则
// (void) : 函数的参数无
return 0;
}
// 出口函数:释放资源
static void __exit demo_exit(void)
{
// __exit : 修饰出口函数,给编译器使用的,
// 告诉编译器将出口函数都存到__exit对应的代码段。
}
// module_init修饰入口函数,当加载驱动时,最终会调用module_init修饰的函数
module_init(demo_init);
// module_exit修饰出口函数,当卸载驱动时,最终会调用module_exit修饰的函数
module_exit(demo_exit);
// 许可证:遵循GPL的开源协议
MODULE_LICENSE("GPL");
1.3 对模块化的驱动进行编译
1.3.1 采用内部编译的方式
1> 将驱动文件拷贝到drivers/char目录下
2> 修改drivers/char目录下的Makefile文件
obj-$(CONFIG_****) += ****.o
3> 修改drivers/char目录下的Kconfig文件
config ****(配置选项的名称)
tristate "菜单选项的名称"
default y/n/m
help
帮助信息
4> 执行make menuconfig配置为模块化的方式进行编译
5> 执行make modules模块化编译驱动生成.ko驱动文件
6> 拷贝.ko文件到跟文件系统中
7> 启动开发板,使用insmod rmmod lsmod加载驱动,卸载驱动,查看驱动
1.3.2 采用外部编译的方式
# 1. 外部编译模块化驱动需要借助于内核源码中的Makefile文件完成驱动的编译
# 2. 内核源码必须执行过make modules命令。
# 指定ubuntu系统的内核源码目录
KERNELDIR := /lib/modules/$(shell uname -r)/build
# 指定当前的驱动的路径
CURRENTDIR := $(shell pwd)
all:
make -C $(KERNELDIR) M=$(CURRENTDIR) modules
@ # -C : 进入到内核源码目录下,借助内核源码目录下的Makefile文件,
@ # 根据内核源码目录下模块化编译驱动的规则完成对当前目录下的模块化驱动的编译
@ # 进入内核源码目录下执行make modules
@ # M=$(CURRENTDIR) : 只对当前目录下的模块化驱动进行模块化的编译
clean:
make -C $(KERNELDIR) M=$(CURRENTDIR) clean
obj-m := demo.o
1.4 模块化驱动相关的命令
linux@ubuntu:01modules$ insmod demo.ko
insmod: ERROR: could not insert module demo.ko: Operation not permitted
linux@ubuntu:01modules$ sudo insmod demo.ko ---> 加载驱动
linux@ubuntu:01modules$ lsmod | grep "demo" ---> 查看驱动
linux@ubuntu:01modules$ sudo rmmod demo ---> 卸载驱动
2、linux内核中的打印语句的使用
2.1 printk函数的使用
用法:
printk(打印级别 “格式化字符串”, 可变参数);
打印级别:过滤打印信息的。
2.2 printk支持的打印级别的分析
vi -t KERN_ERR
8 #define KERN_EMERG KERN_SOH "0" /* system is unusable */
9 #define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */
10 #define KERN_CRIT KERN_SOH "2" /* critical conditions */
11 #define KERN_ERR KERN_SOH "3" /* error conditions */
12 #define KERN_WARNING KERN_SOH "4" /* warning conditions */
13 #define KERN_NOTICE KERN_SOH "5" /* normal but significant condition */
14 #define KERN_INFO KERN_SOH "6" /* informational */
15 #define KERN_DEBUG KERN_SOH "7" /* debug-level messages */
printk函数最多支持8个打印级别,编号越小打印的级别越高。
只有打印几倍大于终端的打印级别时,打印信息才会在终端进行回显。
2.3 过滤信息的作用
在linux内核中的打印信息,在终端有其自己的打印的级别,当内核中的打印信息的级别大于终端的
打印级别时,内核的打印信息才会在终端回显。
如何查看终端信息的打印级别:cat /proc/sys/kernel/printk
4 ----------------- 4 ------------------ 1 --------------- 7
终端打印级别 打印消息的默认级别 终端消息的最大级别 终端消息的最小级别
2.4 打印函数的练习
#include <linux/init.h>
#include <linux/module.h>
// 入口函数:申请资源
static int __init demo_init(void)
{
// 消息的打印级别大于终端的默认级别
printk(KERN_ERR "%s:%s:%d\n", __FILE__, __func__, __LINE__);
// 使用消息的默认打印的级别
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
// 消息的打印级别小于终端的默认级别
printk(KERN_INFO "%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
// 出口函数:释放资源
static void __exit demo_exit(void)
{
printk(KERN_ERR "%s:%s:%d\n", __FILE__, __func__, __LINE__);
}
// module_init修饰入口函数,当加载驱动时,最终会调用module_init修饰的函数
module_init(demo_init);
// module_exit修饰出口函数,当卸载驱动时,最终会调用module_exit修饰的函数
module_exit(demo_exit);
// 许可证:遵循GPL的开源协议
MODULE_LICENSE("GPL");
2.5 加载驱动,查看打印信息
1. 直接加载驱动
linux@ubuntu:02printk$ sudo insmod demo.ko
[sudo] password for linux:
linux@ubuntu:02printk$
终端并没有打印任何的消息,原因是终端将所有的消息全部都屏蔽。
2. 查看驱动的打印的消息的方式1:打开ubuntu系统的虚拟的终端
打开ubuntu系统的虚拟终端:fn + ctrl + alt + (F2-F7)
退出ubuntu系统的虚拟终端:fn + ctrl + alt + F1
.png" alt=“image-20220808115728525” style />
3. 使用dmesg命令查看内核中的打印的信息
dmesg : 查看内核启动之后的所有的打印的信息
dmesg -C : 不回显清除所有的打印的信息
dmesg -c : 先回显在清除所有的打印的信息
2.6 设置printk默认的打印级别大于终端的打印级别
1. ubuntu系统
sudo echo "4 3 1 7" > /proc/sys/kernel/printk
2. 开发板中
开发板系统启动之后,执行以下命令:
echo "4 3 1 7" > /proc/sys/kernel/printk
只要开发板一重启, /proc/sys/kernel/printk文件中的内容又恢复默认
将echo "4 3 1 7" > /proc/sys/kernel/print