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

linux创建设备节点

创建设备节点

Linux 系统中,所有硬件设备都会被当作文件来管理。这些设备对应的特殊文件叫做设备节点,都存放在/dev文件夹里。它们的作用就像桥梁:一边连接着系统内部识别的硬件设备,另一边让应用程序能像操作普通文件一样读写设备数据。这样程序就能通过访问这些特殊文件,间接控制硬件完成操作。

一、SYSFS 文件系统

sysfsLinux 内核提供的一个虚拟文件系统,通常挂载在 /sys 目录。它以目录和属性文件的形式,展示了系统中各类设备、内核模块、内核参数等信息。

像一扇窗口,让你不用深入内核代码即可直观看到所有硬件和驱动的情况。驱动注册时,如果用 class_create 等接口,会自动在 /sys/class/ 下生成相关的 class 目录(比如 /sys/class/myclass/),方便查看和自动生成 /dev/ 设备节点。

二、自动创建设备文件的工具

Linux 新内核中,设备节点的创建主要借助于 udevUser Device),它是一种用户空间的设备管理工具。udev 能自动监控内核发出的硬件变化事件(如插入或移除设备),并智能地在 /dev/ 目录下为所有类型的设备创建设备节点文件。

udev的优点:

  • 自动识别设备,自动创建设备文件;
  • 支持对设备文件命名和权限进行自定义规则设置;
  • 不必手动用 mknod 创建节点,方便又安全。

手动创建(使用命令)

可以使用命令:

sudo mknod /dev/设备名 [c|b] 主设备号 次设备号
  • 设备名:自己定义,比如 mydev
  • c:表示字符设备(b 表示块设备)
  • 主设备号、次设备号:与驱动程序中注册时保持一致

示例: 创建一个 mydev ,设定为主设备号为 240,次设备号为 0:

mknod /dev/mydev c 240 0chmod 666 /dev/mydevls -l /dev/mydev

image-20260201225757117

危险操作:删除设备节点使用 sudo rm /dev/mydev

四、自动创建设备节点

推荐使用自动创建设备节点的方法。只需在驱动代码里调用内核提供的 class_createdevice_create 函数,就能让内核自动在 /dev/ 下生成设备文件,并配合 udev 正确识别。

例子(与驱动注册部分配合):

struct class *cls;
cls = class_create(THIS_MODULE, "myclass");
device_create(cls, NULL, devno, NULL, "mydev");

这样会自动生成 /dev/mydev

卸载驱动时记得清理:

device_destroy(cls, devno);
class_destroy(cls);

五、创建设备节点实验

1、源码编写

写一个简单的字符设备驱动,它在加载时自动创建设备节点 /dev/mydev

首先我们创建一个 04_mk_device_node/ 目录,并在其中创建一个 mydev_mk.c 源码文件,编写下面的驱动:

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>static dev_t devno;
static struct cdev my_cdev;
static struct class *my_class;// 设备操作函数
static int mydev_open(struct inode *inode, struct file *file) { return 0; }
static int mydev_release(struct inode *inode, struct file *file) { return 0; }static struct file_operations mydev_fops = {.owner = THIS_MODULE,.open = mydev_open,.release = mydev_release,
};static int __init mydev_init(void)
{int ret;// 自动分配设备号ret = alloc_chrdev_region(&devno, 0, 1, "mydev");if (ret < 0) return ret;// 注册cdevcdev_init(&my_cdev, &mydev_fops);cdev_add(&my_cdev, devno, 1);// 创建设备类和设备节点my_class = class_create(THIS_MODULE, "myclass");device_create(my_class, NULL, devno, NULL, "mydev");printk("mydev driver installed!\n");return 0;
}static void __exit mydev_exit(void)
{device_destroy(my_class, devno);class_destroy(my_class);cdev_del(&my_cdev);unregister_chrdev_region(devno, 1);printk("mydev driver removed!\n");
}module_init(mydev_init);
module_exit(mydev_exit);
MODULE_LICENSE("GPL");

makefile

export ARCH=arm64# 交叉编译器绝对路径前缀
CROSS_COMPILE ?= aarch64-linux-gnu-
# 和源文件名一致
obj-m += mydev_mk.o# 内核源码目录
KDIR := $(HOME)/Desktop/SDK/kernel
PWD ?= $(shell pwd)all:make -C $(KDIR) M=$(PWD) modulesclean:make -C $(KDIR) M=$(PWD) clean
~                                        

创建设备节点

在虚拟机中

在SDK路径下(编译过源码的文件夹)

/home/p/Desktop/SDK/user_project/04_mk_device_node

创建user_project/04_mk_device_node目录用于存放创建设备节点代码的目录

主要文件

mydev_mk.c

/* 字符设备结构体* cdev 是 Linux 内核中描述“字符设备”的核心对象,* 用于把 设备号(dev_t) 和 file_operations 绑定在一起*/
static struct cdev my_cdev;/* 设备类指针* class 用于在 /sys/class 下创建一个设备类,* 结合 udev,可自动在 /dev 下生成设备节点*/
static struct class *my_class;/* ================== 设备操作函数 ================== *//* open 回调函数* 当用户空间调用 open("/dev/mydev2", ...) 时进入* inode : 描述设备节点的 inode* file  : 本次打开对应的 file 结构*/
static int mydev_open(struct inode *inode, struct file *file)
{/* 当前只是一个空实现* 返回 0 表示 open 成功*/return 0;
}/* release 回调函数* 当用户空间调用 close(fd) 时进入*/
static int mydev_release(struct inode *inode, struct file *file)
{/* 同样为空实现,仅表示关闭成功 */return 0;
}/* 文件操作集* 描述该字符设备支持哪些系统调用* 内核通过该结构体将用户态操作映射到驱动函数*/
static struct file_operations mydev_fops = {.owner   = THIS_MODULE,     // 指定模块所有者,防止模块被错误卸载.open    = mydev_open,      // 对应 open().release = mydev_release,   // 对应 close()
};/* ================== 模块初始化函数 ================== */
/* __init 表示该函数只在模块加载阶段使用,* 加载完成后该函数占用的内存可以被释放*/
static int __init mydev_init(void)
{int ret;/* 动态申请字符设备号* devno : 返回的设备号(包含主设备号 + 次设备号)* 0     : 起始次设备号* 1     : 申请的设备个数* "mydev2" : 设备名(显示在 /proc/devices 中)*/ret = alloc_chrdev_region(&devno, 0, 1, "mydev2");if (ret < 0)return ret;     // 申请失败直接返回/* 初始化 cdev 结构体* 将字符设备和 file_operations 关联起来*/cdev_init(&my_cdev, &mydev_fops);/* 向内核注册字符设备* devno : 设备号* 1     : 设备数量*/cdev_add(&my_cdev, devno, 1);/* 创建设备类* 会在 /sys/class/myclass2/ 下生成目录*/my_class = class_create(THIS_MODULE, "myclass2");/* 创建设备节点* udev 会自动在 /dev/ 下生成 /dev/mydev2*/device_create(my_class, NULL, devno, NULL, "mydev2");printk("mydev driver installed!\n");return 0;
}/* ================== 模块退出函数 ================== */
/* __exit 表示该函数仅在模块卸载时使用 */
static void __exit mydev_exit(void)
{/* 删除设备节点 /dev/mydev2 */device_destroy(my_class, devno);/* 删除设备类 /sys/class/myclass2 */class_destroy(my_class);/* 从内核中删除字符设备 */cdev_del(&my_cdev);/* 释放之前申请的设备号 */unregister_chrdev_region(devno, 1);printk("mydev driver removed!\n");
}/* 指定模块加载和卸载函数 */
module_init(mydev_init);
module_exit(mydev_exit);/* 指定许可证,GPL 表示使用 GPL 协议 */
MODULE_LICENSE("GPL");

/* ================== 模块初始化函数 ================== */

/* __init 表示该函数只在模块加载阶段使用,* 加载完成后该函数占用的内存可以被释放*/
static int __init mydev_init(void)
{int ret;/* 动态申请字符设备号* devno : 返回的设备号(包含主设备号 + 次设备号)* 0     : 起始次设备号* 1     : 申请的设备个数* "mydev2" : 设备名(显示在 /proc/devices 中)*/ret = alloc_chrdev_region(&devno, 0, 1, "mydev2");if (ret < 0)return ret;     // 申请失败直接返回/* 初始化 cdev 结构体* 将字符设备和 file_operations 关联起来*/cdev_init(&my_cdev, &mydev_fops);/* 向内核注册字符设备* devno : 设备号* 1     : 设备数量*/cdev_add(&my_cdev, devno, 1);/* 创建设备类* 会在 /sys/class/myclass2/ 下生成目录*/my_class = class_create(THIS_MODULE, "myclass2");/* 创建设备节点* udev 会自动在 /dev/ 下生成 /dev/mydev2*/device_create(my_class, NULL, devno, NULL, "mydev2");printk("mydev driver installed!\n");return 0;
}

初始化流程

1、动态申请字符设备号

alloc_chrdev_region(&devno, 0, 1, "mydev2");

2、初始化 cdev 结构体,将字符设备和 file_operations 关联起来

cdev_init(&my_cdev, &mydev_fops);

3、 向内核注册字符设备

cdev_add(&my_cdev, devno, 1);

4、创建设备类,会在 /sys/class/myclass2/ 下生成目录

my_class = class_create(THIS_MODULE, "myclass2");

5、创建设备节点, udev 会自动在 /dev/ 下生成 /dev/mydev2

device_create(my_class, NULL, devno, NULL, "mydev2");

/* ================== 模块退出函数 ================== */

/* ================== 模块退出函数 ================== */
/* __exit 表示该函数仅在模块卸载时使用 */
static void __exit mydev_exit(void)
{/* 删除设备节点 /dev/mydev2 */device_destroy(my_class, devno);/* 删除设备类 /sys/class/myclass2 */class_destroy(my_class);/* 从内核中删除字符设备 */cdev_del(&my_cdev);/* 释放之前申请的设备号 */unregister_chrdev_region(devno, 1);printk("mydev driver2 removed!\n");
}

1、删除设备节点 /dev/mydev2

device_destroy(my_class, devno);

2、删除设备类 /sys/class/myclass2
class_destroy(my_class);

3、从内核中删除字符设备
cdev_del(&my_cdev);

4、 释放之前申请的设备号

unregister_chrdev_region(devno, 1);

编写Makefile

export ARCH=arm64
# 交叉编译器绝对路径前缀
CROSS_COMPILE ?= aarch64-linux-gnu-
# 和源文件名一致
obj-m += mydev_mk.o
#obj-m 表示 模块(module)
#如果是内核内建,会用 obj-y# 内核源码目录(存放编译镜像SDK生成的kernel目录)
KDIR := $(HOME)/Desktop/SDK/kernel
#指向:已经配置并编译过的内核源码目录
PWD ?= $(shell pwd)#获取当前 Makefile 所在路径,传给内核编译系统all:make -C $(KDIR) M=$(PWD) modulesclean:make -C $(KDIR) M=$(PWD) clean

编译

make

将驱动模块拷贝至rk3566

image-20260202222235524

验证

使用adb,将模块拷贝

adb -s “设备” push “PC路径” “存放在rk3566的路径”

挂载模块

sudo insmod mydev_mk.ko

查看日志

dmesg | grep -E 'mydev'

image-20260202222730367

卸载模块

sudo rmmod mydev_mk

image-20260202222821082

查看设备节点文件

ls -l /dev/mydev

image-20260202222911288

http://www.jsqmd.com/news/339263/

相关文章:

  • 首款全AI驱动恶意软件VoidLink登场:7天速成,瞄准云原生基础设施
  • 三电平逆变器并板控制器设计过程(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码
  • 找深圳文旅街区改造升级设计公司 看这篇就够了【2026年】
  • 3542. 查找
  • 基于多目标粒子群算法冷热电联供综合能源系统运行优化(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码
  • 连中一区TOP!多模态图学习新范式!高分论文创新点都在这
  • Java开发者转型大模型:两步走,不丢Java本事,轻松拥抱AI
  • 【毕业设计】基于web的影院订票系统设计与开发(源码+文档+远程调试,全bao定制等)
  • 【无人机任务分配】基于matlab三维山地地形生成+随机任务点采样+K-means任务聚类+任务点排序的无人机航迹规划【含Matlab源码 15058期】
  • 数字图像处理篇---非锐化掩膜
  • 全域未来乡村数字化建设与共富运营规划方案深度解读:打造数字乡村“中国样本“的完整方法论(PPT)
  • 工业恒温箱温度控制系统设计(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码
  • 基于微信小程序的家校沟通管理系统的设计与实现
  • 从零开始掌握RAG技术:让大模型突破知识局限的终极指南
  • 【无人机控制】基于matlab无人机系统制导与导航控制【含Matlab源码 15055期】
  • 2026年上半年软考高项是报班还是自学?
  • 【算法基础篇】(五十三)隔板法指南:从 “分球入盒” 到不定方程,组合计数的万能解题模板
  • 数字图像处理篇---边缘检测
  • 大额沃尔玛购物卡回收,这些平台安全又可靠 - 京顺回收
  • 流量入口Nginx动态发现K8s Ingress Controller实操指南
  • Flutter for OpenHarmony:构建一个 Flutter 单词拼图游戏,深入解析状态驱动 UI、交互式字母操作与教育类应用设计
  • 【无人机控制】基于matlab反步控制和滑模控制SMC提升四旋翼在存在风扰的动态环境中的稳定性【含Matlab源码 15054期】
  • Python技术应用工程师:互联网行业技能赋能者
  • 亚马逊裁员VS AI疯抢:大模型学习指南,让你不被时代淘汰(建议收藏)
  • 邦芒干货:想要跳槽成功得拼这6项
  • 【无人机控制】基于matlab多旋翼无人机横向动力学的鲁棒控制【含Matlab源码 15051期】
  • BEAR基准深度解析:多模态大语言模型的体现能力评估与提升指南
  • 数字图像处理篇---拉普拉斯锐化
  • 破解大模型交付困境:从“烧钱“到“赚钱“的转型指南
  • 【无人机控制】基于matlab T-S模糊模型的四旋翼无人机非线性系统跟踪控制【含Matlab源码 15052期】