ARM Linux 驱动开发篇---新版led驱动实验原理(2)--基于 mdev 机制实现设备节点自动创建及--利用私有数据结构体管理设备属性-- Ubuntu20.04

ARM Linux 驱动开发篇---新版led驱动实验原理(2)--基于 mdev 机制实现设备节点自动创建及--利用私有数据结构体管理设备属性-- Ubuntu20.04
🎬 渡水无言个人主页渡水无言

专栏传送门linux专栏
⭐️流水不争先,争的是滔滔不绝

 📚博主简介:第二十届中国研究生电子设计竞赛全国二等奖 |国家奖学金 | 省级三好学生

| 省级优秀毕业生获得者 | ZEEKLOG新星杯TOP18 | 半导纵横专栏博主 | 211在读研究生

在这里主要分享自己学习的linux嵌入式领域知识;有分享错误或者不足的地方欢迎大佬指导,也欢迎各位大佬互相三连

目录

前言

一、自动创建设备节点

1.1、mdev 机制

1.2、创建和删除类

1.3、创建设备

1.4、示例

1.5、总结流程图

二、设置文件私有数据

2.1具体原理代码

2.2总体流程框图

总结


前言

上一期介绍了一些新的分配和释放设备号,字符设备注册方法,接下来本期博客将介绍一下自动创建设备节点,设置文件私有数据的方法。


一、自动创建设备节点

在上一期的 Linux LED驱动实验中,当我们使用 modprobe 加载驱动程序以后还需要使用命令

“mknod”手动创建设备节点。接下来就来介绍一下如何实现自动创建设备节点,在驱动中实现自动创建设备节点的功能以后,使用 modprobe 加载驱动模块成功的话就会自动在/dev 目录下创建对应的设备文件。

1.1、mdev 机制

在 Linux 系统中,udev 是一个用户态守护进程,负责动态管理 /dev 目录下的设备节点文件。它通过监听内核事件,实时检测硬件设备的插拔与状态变化,并自动完成设备文件的创建与删除。

例如:当使用 modprobe 命令成功加载驱动模块时,udev 会自动在 /dev 目录下生成对应的设备节点;反之,使用 rmmod 卸载驱动后,相关设备节点也会被自动清理。

在嵌入式 Linux 场景中,为了适应资源受限的环境,通常使用 BusyBox 提供的轻量级工具 mdev。mdev 是 udev 的简化实现,同样承担着设备节点自动管理与热插拔事件处理的核心职责。在基于 BusyBox 构建的根文件系统中,mdev 的初始化配置通常位于 /etc/init.d/rcS 脚本中。

相关语句如下:

echo /sbin/mdev > /proc/sys/kernel/hotplug

上述命令设置热插拔事件由 mdev 来管理。接下来我们详细介绍一下如何通过 mdev 来实现设备文件节点的自动创建与删除。

1.2、创建和删除类

在 Linux 驱动开发中,设备节点的自动创建工作,通常是在驱动程序的入口函数中完成的,具体来说,我们一般会在 cdev_add 函数调用之后,补充自动创建设备节点的相关代码。

要实现这一功能,首先需要创建一个 class 类 ——class 是 Linux 内核中用于管理设备的核心结构体,其定义位于内核头文件 include/linux/device.h 中。

而创建这个类的核心接口是 class_create,需要注意的是,class_create 并非一个普通函数,而是内核封装的宏定义,其具体的宏展开内容如下:

#define class_create(owner, name) \ ({ \ static struct lock_class_key __key; \ __class_create(owner, name, &__key); \ }) struct class *__class_create(struct module *owner, const char *name, struct lock_class_key *key)

class_create 一共有两个参数,参数 owner 一般为 THIS_MODULE,参数 name 是类名字。 返回值是个指向结构体 class 的指针,也就是创建的类。

卸载驱动程序的时候需要删除掉类,类删除函数为 class_destroy,函数原型如下:

void class_destroy(struct class *cls);

参数 cls 就是要删除的类。

1.3、创建设备

创建好类以后还不能实现自动创建设备节点,我们还需要在这个类下创建一个设备。使用device_create 函数在类下面创建设备,device_create 函数原型如下:

struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...)

class:指定该设备要归属的设备类(即创建在哪个类目录下),需传入之前通过 class_create 创建的 struct class 指针,决定了设备节点在 /sys/class 下的归属。
parent:指向父设备的指针,用于标识设备的层级关系,绝大多数场景下无需指定父设备,直接传 NULL 即可。
devt:设备号(包含主设备号和次设备号),是设备在系统中的唯一标识,需与 cdev_add 中注册的设备号保持一致。
drvdata:指向设备私有数据的指针,可用于传递驱动需要的自定义数据,若无特殊需求,通常传 NULL。
fmt:设备名称的格式化字符串(可变参数的核心),比如设置 fmt = "xxx",系统会自动在 /dev 目录下生成 /dev/xxx 这个设备节点文件。

同样的,卸载驱动的时候需要删除掉创建的设备,设备删除函数为 device_destroy,函数原型如下:

void device_destroy(struct class *class, dev_t devt)

参数 class 是要删除的设备所处的类,参数 devt 是要删除的设备号。

1.4、示例

在驱动入口函数里面创建类和设备,在驱动出口函数里面删除类和设备,示例如下:

struct class *class; /* 类 */ struct device *device; /* 设备 */ dev_t devid; /* 设备号 */ /* 驱动入口函数 */ static int __init led_init(void) { /* 创建类 */ class = class_create(THIS_MODULE, "xxx"); /* 创建设备 */ device = device_create(class, NULL, devid, NULL, "xxx"); return 0; } /* 驱动出口函数 */ static void __exit led_exit(void) { /* 删除设备 */ device_destroy(newchrled.class, newchrled.devid); /* 删除类 */ class_destroy(newchrled.class); } module_init(led_init); module_exit(led_exit);

1.5、总结流程图

左侧是驱动加载 + 自动创建设备节点正向流程,右侧是驱动卸载 + 自动删除设备节点反向流程

注意:

红色背景(keyStep):驱动开发中需要手动编写代码的核心步骤(创建 / 删除类、设备);

蓝色背景(sysStep):系统层面自动执行的步骤(mdev 监听、热插拔事件处理)。

二、设置文件私有数据

2.1具体原理代码

在嵌入式 Linux 驱动开发中,我们经常需要管理设备的各种属性,如主设备号、cdev 结构体、设备类、设备指针等。如果将这些属性零散地定义为全局变量,不仅代码不专业,还会导致维护困难和线程安全问题。就像下面这种写法:

// 零散的全局变量,弊端显著 dev_t devid; /* 设备号 */ struct cdev test_cdev; /* 字符设备结构体 */ struct class *class; /* 设备类 */ struct device *device; /* 设备实例 */ int major; /* 主设备号 */ int minor; /* 次设备号 */

所以有必要使用私有数据结构体来封装设备属性,并在open函数中将其作为私有数据挂载到文件结构体中,实现高效、安全的设备管理。如下所示:

​ /* 设备结构体 */ struct test_dev{ dev_t devid; /* 设备号 */ struct cdev cdev; /* cdev */ struct class *class; /* 类 */ struct device *device; /* 设备 */ int major; /* 主设备号 */ int minor; /* 次设备号 */ }; struct test_dev testdev; /* open函数 */ static int test_open(struct inode *inode, struct file *filp) { filp->private_data = &testdev; /* 设置私有数据 */ return 0; } ​

重点在于open 函数挂载私有数据

filp->private_data = &testdev;

这是整个设计的 “枢纽”,将文件实例与设备结构体绑定;
后续所有文件操作函数(read/write/ioctl)都能通过filp->private_data访问设备属性。

2.2总体流程框图


总结

本期博客主要介绍了自动创建设备节点,设置文件私有数据的方法。

Read more

本地AI助手上线!老项目秒变新架构,就用飞算JavaAI

本地AI助手上线!老项目秒变新架构,就用飞算JavaAI

本地AI助手上线!老项目秒变新架构,就用飞算JavaAI 文章目录 * 本地AI助手上线!老项目秒变新架构,就用飞算JavaAI * 前言 * 一:飞算AI安装流程 * 二:飞算AI功能介绍 * 三:案例:多角色用户管理模块(适用:智能引导 + 模块化生成) * 四:小飞算标 * 代码解释功能 * 生成代码注释 * 优化建议功能 * 五、开发者实测数据对比 * 📈 预测依据说明: * 结束语 * 上一篇推荐: * 下一篇推荐: * 下一篇推荐: 前言 飞算AI 是一个集成于 IntelliJ IDEA 的智能插件,它将原本需要在浏览器中跳转、复制粘贴代码向 AI 提问的繁琐流程,变成了在本地开发环境中即可与 AI 直接对话的高效体验。开发者无需离开 IDE,就能通过飞算AI进行代码生成、逻辑分析、错误排查、注释补全等智能操作,大幅降低上下文切换带来的效率损耗。

By Ne0inhk

终极JByteMod-Beta安装指南:5分钟快速上手Java字节码编辑

终极JByteMod-Beta安装指南:5分钟快速上手Java字节码编辑 【免费下载链接】JByteMod-BetaJava bytecode editor 项目地址: https://gitcode.com/gh_mirrors/jb/JByteMod-Beta JByteMod-Beta是一款功能强大的Java字节码编辑器,支持语法高亮、实时反编译和方法图绘制。本指南将带领您快速完成安装配置,轻松开始Java字节码编辑之旅 🚀 一键环境准备清单 在开始安装JByteMod-Beta之前,请确保您的系统满足以下基本要求: ✅ Java开发环境:JDK 8或更高版本 ✅ Maven构建工具:用于项目编译和打包 ✅ 系统内存:建议至少2GB可用内存 ✅ 磁盘空间:需要约100MB的可用空间 图形化安装步骤 步骤一:获取项目源码 git clone https://gitcode.com/gh_mirrors/jb/JByteMod-Beta.git cd JByteMod-Beta 步骤二:构建项目包 使用Maven命令进行项目构建: mvn

By Ne0inhk
Java 包装类:基本类型与引用类型的桥梁详解

Java 包装类:基本类型与引用类型的桥梁详解

🏠个人主页:黎雁 🎬作者简介:C/C++/JAVA后端开发学习者 ❄️个人专栏:C语言、数据结构(C语言)、EasyX、JAVA、游戏、规划、程序人生 ✨ 从来绝巘须孤往,万里同尘即玉京 文章目录 * Java 包装类:基本类型与引用类型的桥梁详解 📦 * 📝 文章摘要 * 🧠 前两篇知识回顾 * 一、包装类核心概念 🤔 * 1. 什么是包装类? * 2. 为什么需要包装类? * 二、8种基本数据类型与包装类对应关系 📋 * 三、包装类的使用(以 Integer 为例) 📌 * (一)JDK5 以前:手动装箱与拆箱(了解) * 1. 手动装箱(基本类型 → 包装类) * 常用方法(Integer) * 代码示例 * 2. 手动拆箱(

By Ne0inhk
Java 注解与反射实战:手把手实现自定义日志与参数校验注解

Java 注解与反射实战:手把手实现自定义日志与参数校验注解

前言:为什么需要自定义注解? 在日常开发中,我们经常遇到两类重复工作: 日志记录:每个重要方法都要写 "开始执行"、"参数是 xxx"、"执行结束" 的代码;参数校验:判断输入是否为 null、年龄是否在合理范围、手机号格式是否正确等。 这些工作机械且冗余,而注解 + 反射正是解决这类问题的 "银弹"—— 用注解标记需要处理的地方,用反射自动执行逻辑,实现 "一次定义,多处复用"。 本文将带你从零实现两个实用案例: 1. 自定义日志注解@Log:自动记录方法调用细节; 2. 自定义参数校验注解@NotNull、@Range:自动校验方法参数合法性。 全程实战,代码可直接运行,搭配图解帮你吃透底层逻辑。 案例一:自定义日志注解@

By Ne0inhk