嵌入式Linux驱动开发(一)——Hello Module驱动程序开发

学习目标:

  • 理解内核是怎么加载和卸载驱动
  • 理解相关的头文件
  • 理解内核打印函数printk以及日志等级
  • 理解__init和__exit修饰的作用
  • makefile相关语法解析

一、代码

hellomodule.c

#include "linux/init.h"
#include "linux/module.h"
#include "linux/kernel.h"


/*
	入口函数
*/
static int __init hellomodule_init(void)
{
	printk(KERN_EMERG "[ KERN_EMERG ]  Hello  Module Init\n");
    printk( "[ default ]  Hello  Module Init\n");
	return 0;
}

/*
	出口函数
*/
static void __exit hellomodule_exit(void)
{
	printk(KERN_DEBUG "[ KERN_DEBUG ]  Hello  Module exit\n");
	printk("[ default ]   Hello  Module Exit\n");
}


module_init(hellomodule_init);		// 注册入口函数
module_exit(hellomodule_exit); // 注册出口函数


MODULE_AUTHOR("LingTu <luo_lin_lai_sheng@163.com>");  			// 设置编写驱动的作者信息
MODULE_DESCRIPTION("LingTu hello module test code");			// 设置驱动的描述信息
MODULE_LICENSE("GPL");											// 设置驱动遵循的开源协议

Makefile

KERNEL_DIR=../../ebf_linux_kernel/build_image/build

ARCH=arm
CROSS_COMPILE=arm-linux-gnueabihf-
export  ARCH  CROSS_COMPILE

obj-m := hellomodule.o
all:
	$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules

.PHONE:clean copy

clean:
	$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean	

copy:
	sudo  cp  *.ko  /home/embedfire/workdir

二、相关知识点

1. 内核模块头文件
  • #include <linux/module.h>:包含内核模块信息声明的相关函数:MODULE_AUTHOR、MODULE_DESCRIPTION、MODULE_ALIAS
  • #include <linux/init.h>:包含了 module_init()和 module_exit()函数的声明
  • #include <linux/kernel.h>:包含内核提供的各种函数,如printk
2. __init和__exit修饰的作用

想要理解这两个宏的作用,那么我们应该找到这两个宏的定义处,在include\linux下的init.h中定义的。
在这里插入图片描述
从该定义中可以猜测出,使用__init和__exit是用于规定代码存放的段

3. 内核模块打印函数
  • printf:glibc实现的打印函数,工作于用户空间,只能在应用层使用。
  • printk:内核模块无法使用glibc库函数,内核自身实现的一个类printf函数,但是需要指定打印等级。
    • #define KERN_EMERG “<0>” 通常是系统崩溃前的信息
    • #define KERN_ALERT “<1>” 需要立即处理的消息
    • #define KERN_CRIT “<2>” 严重情况
    • #define KERN_ERR “<3>” 错误情况
    • #define KERN_WARNING “<4>” 有问题的情况
    • #define KERN_NOTICE “<5>” 注意信息
    • #define KERN_INFO “<6>” 普通消息
    • #define KERN_DEBUG “<7>” 调试信息

查看当前系统printk打印等级:cat /proc/sys/kernel/printk
显示:7 7 1 7
分别代表以下含义:

  • 当前控制台日志级别: 也就是目前控制台能输出的信息的等级
  • 默认消息日志级别 : 也就是设置当使用printk未加修饰符的打印信息级别
  • 最小的控制台级别: 控制台最小的级别
  • 默认控制台日志级别:默认时控制台的日志级别

一般我们常设置当前控制台日志级别和 默认消息日志级别,当当前控制台日志级别级别<默认消息日志级别时,默认消息日志才能显示,数字越小,级别越高!
比如当7 7 1 7时,由于默认消息日志级别(7)不大于当前控制台日志级别(7),所以不打印默认日志信息。
设置打印等级:echo "7 6 1 7" > /proc/sys/kernel/printk
设置为上述后就可以打印了。

打印内核所有打印信息:dmesg

  • 内核log缓冲区大小有限制,缓冲区数据可能被冲掉

在代码中可以人为的指定打印等级,比如下面所示的KERN_EMERG

printk(KERN_EMERG "[ KERN_EMERG ]  Hello  Module Init\n");

可以在printk后面加上修饰符KERN_EMERG、 KERN_ALERT等等,用于设置当前的日志等级,这些宏是在include\linux下的kern_levels.h中定义的,如下图所示:
在这里插入图片描述

4. Makefile分析
  • KERNEL_DIR:指向linux内核具体路径

  • export:导出变量给子Makefile使用

  • obj-m := <模块名>.o:定义要生成的模块

  • $(MAKE):Makefile的默认变量,值为make

  • 选项”-C”:让make工具跳转到linux内核目录下读取顶层Makefile

  • M=:表示内核模块源码目录

  • $(CURDIR):Makefile默认变量,值为当前目录所在路径

  • make modules:执行Linux顶层Makefile的伪目标,它实现内核模块的源码读取并编译为.ko文件

三、实验分析

1. 编译内核模块
make

在这里插入图片描述

2. 把生成的内核模块拷贝到nfs共享目录
make copy

在这里插入图片描述

3. 开发板加载内核模块
insmod hellomodule.ko

在这里插入图片描述
由于我们设置的默认消息日志为6,而当前控制台日志等级为7所以,default的日志消息打印了,同时KERN_EMERG日志等级为0,所以这个消息日志也打印了。
从日志信息中可以看出,运行insmod命令时运行了入口函数

4. 查看是否有相关驱动
lsmod

在这里插入图片描述

5. 卸载驱动
lsmod

在这里插入图片描述
由于我们当前控制台日志等级为7,而KERN_DEBUG日志等级为7,所以在hellomodule_exit函数中的

printk(KERN_DEBUG "[ KERN_DEBUG ]  Hello  Module exit\n");

这句打印信息没有打印,从日志信息中可以看出,运行rmmod命令时运行了出口函数

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

零涂

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值