当前位置: 首页 > news >正文

驱动-创建设备节点

字符设备相关的知识面:已经了解了 申请设备号、注册字符设备。 已经将字符设备添加进入内核了,那么如何使用这个字符设备呢?
Linux 里面一切皆文件,对内核中的字符设备进行文件操作如打开、关闭、读、写等,但是并不支持哦。 需要相应的设备文件作为桥梁进行设备的访问,
这里需要了解知识:创建设备节点

文章目录

  • 自动创建设备节点
    • udev 知识了解
      • 动态创建设备节点
      • 自动加载驱动和触发动作
      • 支持热插拔(Hotplug)
    • 创建设备节点
      • class_create() - 创建设备类
      • device_create() - 创建设备节点
      • 销毁 class_destroy
      • 销毁 device_destroy
  • 实验
    • 源码程序 chrdev_node
      • 源码分析
    • Makefile 编译文件
    • 运行测试
      • insmod dmesg | tail
      • cat proc/devices
      • ls /sys/class/ ls /sys/class/class_test/
      • ls /dev/device_test
  • 总结


自动创建设备节点

针对手动创建设备节点,这里不概述、总结,重点是自动创建设备节点。

udev 知识了解

udev 知识了解:
在 Linux 系统中,udev(用户空间设备管理,Userspace Device Manager)是一个动态设备管理工具,负责在 /dev 目录下自动管理设备节点。它是现代 Linux 系统的核心组件,取代了传统的静态 /dev 目录管理和早期的 devfs。

udev 的核心作用:

动态创建设备节点

  • 当硬件设备插入(如 USB、硬盘、网卡等)或系统启动时,udev 会根据内核发出的 uevent 动态创建对应的设备文件(如 /dev/sda、/dev/ttyUSB0)。
  • 设备移除时,自动清理对应的设备节点。

自动加载驱动和触发动作

  • 支持在设备插入时自动加载内核模块(驱动)。
  • 触发自定义脚本(如挂载磁盘、通知用户)。

支持热插拔(Hotplug)

实时响应硬件插拔事件,适用于移动设备、外接存储等。

创建设备节点

设备文件的自动创建是利用 udev(mdev)机制来实现, 多数情况下采用自动创建设备节点
的方式。 udev(mdev)可以检测系统中硬件设备状态, 可以根据系统中硬件设备状态来创建或者
删除设备文件。 在驱动中首先使用 class_create(…)函数对 class 进行创建, 这个类存放于
/sys/class/ 目录下, 之后使用 device_create(…)函数创建相应的设备, 在进行模块加载时, 用户
空间中的 udev 会自动响应 device_create()函数, 寻找对应的类从而创建设备节点

class_create() - 创建设备类

struct class *class_create(struct module *owner, const char *name);

功能:创建一个设备类,将在/sys/class/下出现对应的目录

参数:

  • owner:struct module 结构体类型的指针, 指向函数即将创建的这个 struct class 的模块。
    一般赋值为 THIS_MODULE
  • name:类名,将出现在/sys/class/下 ;char 类型的指针, 代表即将创建的 struct class 变量的名字
  • 返回:成功返回指向class结构的指针,失败返回ERR_PTR

device_create() - 创建设备节点

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

功能:在指定类下创建设备,触发udev在/dev下创建设备节点

参数:

  • cls:从class_create()返回的类指针 ,指定所要创建的设备所从属的类

  • parent:父设备,通常为NULL

  • devt:设备号(主设备号+次设备号)

  • drvdata:驱动私有数据,通常为NULL

  • fmt:设备名格式字符串(类似printf)

  • 返回:成功返回device指针,失败返回ERR_PTR;struct device * 类型结构体

销毁 class_destroy

用于删除设备的逻辑类, 即从 Linux 内核系统中删除设备的逻辑类。

销毁 device_destroy

extern void device_destroy(struct class *cls, dev_t devt);

用来删除 class 类中的设备属性文件, udev 会自动识别从而进行设备节点的删除。

实验

源码程序 chrdev_node


#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>


static dev_t dev_num;//定义dev_t类型(32位大小)的变量dev_num
struct cdev cdev_test;  //定义struct cdev 类型结构体变量cdev_test,表示要注册的字符设备

static struct file_operations cdev_test_ops = {
	.owner=THIS_MODULE,//将owner字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块
};//定义file_operations结构体类型的变量cdev_test_ops

struct class *class_test;//定于struct class *类型结构体变量class_test,表示要创建的类


static int __init chrdev_fops_init(void)//驱动入口函数
{
    int ret;//定义int类型的变量ret,用来判断函数返回值
    int major,minor;//定义int类型的主设备号major和次设备号minor

    ret=alloc_chrdev_region(&dev_num,0,1,"chardev_num"); //通过动态方式进行设备号注册
    
    if(ret < 0){
            printk("alloc_chrdev_region is error\n");
    }   
        printk("alloc_chrdev_region is ok\n");
        major=MAJOR(dev_num);//通过MAJOR()函数进行主设备号获取
        minor=MINOR(dev_num);//通过MINOR()函数进行次设备号获取
        printk("major is %d\n",major);
        printk("minor is %d\n",minor);
 
        使用cdev_init()函数初始化cdev_test结构体,并链接到cdev_test_ops结构体
       cdev_init(&cdev_test,&cdev_test_ops);
        cdev_test.owner = THIS_MODULE;//将owner字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块 

      ret= cdev_add(&cdev_test,dev_num,1);
        
    if(ret < 0 ){
        printk("cdev_add is error\n");
    }
    printk("cdev_add is ok\n");
    class_test  = class_create(THIS_MODULE,"class_test");//使用class_create进行类的创建,类名称为class_test
    device_create(class_test,NULL,dev_num,NULL,"device_test");//使用device_create进行设备的创建,设备名称为device_test

    return 0;
}
static void __exit chrdev_fops_exit(void)//驱动出口函数
{
    cdev_del(&cdev_test);//使用cdev_del()函数进行字符设备的删除
    unregister_chrdev_region(dev_num,1);//释放字符驱动设备号 
    device_destroy(class_test,dev_num);//删除创建的设备
    class_destroy(class_test);//删除创建的类


    printk("module exit \n");

}
module_init(chrdev_fops_init);//注册入口函数
module_exit(chrdev_fops_exit);//注册出口函数
MODULE_LICENSE("GPL v2");//同意GPL开源协议
MODULE_AUTHOR("wang fang chen "); //作者信息

源码分析

使用到了前面的基础知识点

  • dev_t dev_num :设备号变量声明,dev_t 类型
  • cdev cdev_test : 字符设备结构体声明
  • file_operations cdev_test_ops :声明 定义 函数指针的集合 结构体
  • alloc_chrdev_region :动态注册设备号
  • MAJOR 、MINOR 通过设备号获取主设备号和次设备号
  • cdev_init :初始化 字符设备
  • cdev_add :字符设备添加到内核
  • class_create :进行类的创建 : 位置 /sys/class/
  • device_create :进行设备的创建,在声明 类 目录下生成device 设备,同步通过udev 机制,自动在 /dev 下生成设备节点

Makefile 编译文件


#!/bin/bash
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
obj-m += chrdev_node.o
KDIR :=/home/wfc123/Linux/rk356x_linux/kernel
PWD ?= $(shell pwd)
all:
	make -C $(KDIR) M=$(PWD) modules

clean:
	make -C $(KDIR) M=$(PWD) clean

运行测试

insmod dmesg | tail

[root@topeet:/mnt/sdcard]# insmod  chrdev_node.ko
[root@topeet:/mnt/sdcard]# dmesg | tail
[29383.739997] dwc3 fcc00000.dwc3: device reset
[29383.845540] android_work: sent uevent USB_STATE=CONNECTED
[29383.888321] configfs-gadget gadget: high-speed config #1: b
[29383.888611] android_work: sent uevent USB_STATE=CONFIGURED
[29592.130493] adbd (13142): /proc/13142/oom_adj is deprecated, please use /proc/13142/oom_score_adj instead.
[30279.336659] chrdev_node: loading out-of-tree module taints kernel.
[30279.337604] alloc_chrdev_region is ok
[30279.337626] major is 236
[30279.337643] minor is 0
[30279.337662] cdev_add is ok

程序日志,内核打印如上

cat proc/devices

结果如下,如前面 字符设备注册和设备号申请内容,说明设备已经注册到内核了。
在这里插入图片描述

ls /sys/class/ ls /sys/class/class_test/

结果如下,

  • /sys/class/ 目录下生成了class_test 类
  • /sys/class/class_test/ 目录下生成了device_test 设备

在这里插入图片描述

ls /dev/device_test

结果如下:在dev 下面生成了device_test 设备,通过udev 机制自动生成的。
在这里插入图片描述

总结

  • 字符设备知识点中的 设备节点创建内容熟悉
  • 结合以前的知识,字符设备设备号申请,字符设备注册内容进一步熟悉
  • 设备注册、类、设备创建后 在驱动卸载的时候记得 卸载

相关文章:

  • Spring MVC与Spring Boot文件上传配置项对比
  • 什么是模型上下文协议(MCP)?
  • openEuler24.03 LTS下安装Flink
  • 搜索引擎是如何理解你的查询并提供精准结果的?
  • 学习笔记(C++篇)--- Day2
  • Rust 在汽车 MCU 编程中的进展及安全特性剖析
  • Zephyr、FreeRTOS、RT-Thread 任务创建对比分析
  • 项目范围蔓延的十大诱因及应对策略
  • 两台电脑之间实现文件互传-创建共享文件夹
  • 打破单一视角!融合红外和可见光,YOLO算法实现全天候无人机检测
  • apifox前置加密签名
  • 搜狗拼音输入法纯净优化版:去广告,更流畅输入体验15.2.0.1758
  • YOLO-LLTS:低光照实时交通标志检测算法详解
  • Maven的安装配置-项目管理工具
  • Linux 内核网络协议栈中的 struct packet_type:以 ip_packet_type 为例
  • dB,dBi, dBd, dBc,dBm,dBw释义及区别
  • 带约束的智能优化算法
  • 探讨一下STM32单片机中的中断
  • Windows 10 安装Mysql 8
  • 案例-流量统计