发布时间:2026/9/2 5:50:49
LDD3--字符设备驱动 设备号通过访问设备节点可以调用字符设备驱动程序设备节点通常位于 /dev 目录下。 ls -l 命令输出权限字符串的首字母 c 来标记字符设备同时在原本显示文件大小的位置会显示出该设备的主设备号和次设备号。 主设备号与特定的字符设备驱动程序绑定用户空间对设备节点进行的打开、读写等操作最终会被内核路由调用该驱动程序对应的函数。 次设备号由驱动程序内部用于区分同一驱动管理下的不同设备实例驱动通过次设备号来定位具体的物理或逻辑设备。手动创建设备节点#1.加载模块 sudo insmod scull.ko #2.自动获取主设备号并赋值给变量 scull_major$(awk$2scull {print $1}/proc/devices)#3.创建设备节点 sudo mknod/dev/scull0 c $scull_major0#4.修改节点权限让普通用户也能读写否则默认只有 root 能访问 sudo chmod666/dev/scull0 #5.手动创建的节点也需要手动删除 sudo rmmod scull sudo rm/dev/scull0file_operations file inode1、struct file_operations驱动的能力清单核心定位它是驱动向内核暴露功能的接口定义了驱动程序支持哪些操作如 open、read、write 等。本质这是一个函数指针的集合。用面向对象编程的术语来说我们可以将 file 视为“对象”而 file_operations 中的函数就是作用于该对象的“方法”。关键机制结构体中的每个字段必须指向驱动中实现特定操作的函数。对于不支持的操作可以将其设为 NULL。内核在遇到 NULL 指针时会根据具体函数执行不同的默认行为例如 read 为 NULL 时会返回 -EINVAL 错误。第一个成员通常是 owner一般被初始化为 THIS_MODULE用于防止模块在操作还在被使用时被意外卸载。2、inode设备文件元信息载体当用户程序调用 fd open(“/dev/mydev”, O_RDWR|O_NONBLOCK) 1、VFS虚拟文件系统拿到字符串 “/dev/mydev”在目录树中找到对应的目录项并提取出该文件对应的 inode 结构体。2、VFS 发现 inode 属于字符设备通过 inode-i_mode然后从 inode 中读取之前存好的设备号 dev_t dev inode-i_rdev;3、VFS 拿着提取出来的 dev_t通过内核全局字符设备映射表kobj_map 中进行查找对应的 cdev 指针。4、VFS 从 cdev 中取出 file_operations将其赋值给当前打开的文件结构体file-f_op cdev-ops。如果 fops 里定义了 open 函数VFS 就会调用驱动的 open 函数并且把第 1 步找到的 inode 和当前的 file 作为参数传递进去driver_open(struct inode *inode, struct file *filp)核心链路节点文件 ➔ inode ➔ 设备号 ➔ cdev ➔ 操作方法 (fops)3、struct file 用户与设备的交互会话当用户程序调用 fd open(“/dev/mydev”, O_RDWR|O_NONBLOCK) 内核不仅会找到对应的 inode还会在内存中动态分配一个全新的 struct file 。file 只关心进程 “怎么操作这个设备”。 它记录了这次打开操作的所有状态信息。结构体核心字段1、f_mode 和 f_flagsf_flags直接来自用户态 open(fd, flags) 中的 flags 参数如 O_NONBLOCK 供驱动开发者使用用于判断用户的具体操作意图和状态。f_mode内核 VFS 拿到 f_flags 后解析、转换生成的内部标志如 FMODE_READ 供内核 VFS 使用用于底层权限校验。。2、f_pos文件偏移量记录当前读写操作到了设备的哪个位置偏移量。作用在 read 和 write 函数中必须根据 filp-f_pos 来决定从设备的哪个地址开始读/写数据操作完后还要更新 f_pos。3、private_data在 open 函数中我们通过 inode 找到了具体的硬件设备结构体比如 struct my_dev *dev然后把它赋值给 filp-private_data dev;。这样在后续的 read/write/release 函数中只需通过 filp-private_data 就能瞬间找回这个设备的上下文。4、f_op操作方法指针VFS 在权限校验通过并创建 file 结构体后会将底层 cdev 中绑定的 file_operations 指针赋值给 file-f_op。VFS 最终通过 file-f_op-open(inode, file) 这一函数指针将控制权正式移交给驱动开发者编写的 open 函数。驱动 open 函数接收的两个参数正是 VFS 一路解析得到的 inode包含设备号和刚刚实例化的 file包含操作上下文。open(“/dev/mydev”, O_RDWR|O_NONBLOCK) 完整链路路径解析与权限校验VFS 层VFS 根据路径 /dev/mydev 逐层解析最终找到设备节点对应的 inode。VFS 提取 flags 中的读写意图转换为 f_modeFMODE_READ | FMODE_WRITE并与 inode 的权限位i_mode进行严格校验。校验失败则直接返回 -EACCES。实例化 file 与设备映射VFS 层校验通过后内核分配并创建 file 结构体将原始的 flags含 O_NONBLOCK存入 file-f_flags将 f_mode 存入 file-f_mode。VFS 从 inode-i_rdev 中取出完整的设备号dev_t通过字符设备映射表kobj_map查找到对应的 cdev 指针。VFS 将 cdev-ops即驱动注册的 file_operations赋值给 file-f_op。回调驱动接口驱动层VFS 最终通过函数指针 file-f_op-open(inode, filp)将找到的 inode 和刚创建的 filefilp作为参数将控制权正式移交给驱动开发者编写的 open 函数完成整个调用闭环。#includelinux/module.h#includelinux/init.h#includelinux/types.h#includelinux/kdev_t.h#includelinux/cdev.h#includelinux/fs.h#includelinux/device.h#includelinux/mutex.h#includelinux/uaccess.h#includelinux/slab.h#includelinux/version.h#includelinux/kernel.h#defineSCULL_DEVICES4#defineSCULL_NAMEscull#defineQSET_SIZE1000#defineQUANTUM4000structscull_qset{void**data;structscull_qset*next;};structscull_dev{structscull_qset*qset_head;unsignedintquantum;unsignedintqset_size;unsignedsize;structmutexlock;dev_tdevno;structcdevcdev;};staticunsignedintscull_major0;staticstructclass*scull_class;structscull_devscull_devs[SCULL_DEVICES];staticvoidscull_trim(structscull_dev*dev){inti;structscull_qset*tmp_next,*dptr;for(dptrdev-qset_head;dptr!NULL;dptrtmp_next){tmp_nextdptr-next;for(i0;idev-qset_size;i){if(dptr-data[i]!NULL){kfree(dptr-data[i]);}}kfree(dptr-data);kfree(dptr);}dev-qset_headNULL;dev-size0;printk(KERN_INFOscull_trim successed!\n);}staticintscull_open(structinode*inode,structfile*filp){structscull_dev*devcontainer_of(inode-i_cdev,structscull_dev,cdev);filp-private_datadev;if(filp-f_flags(O_RDWR|O_WRONLY)){if(filp-f_flagsO_TRUNC){mutex_lock(dev-lock);scull_trim(dev);mutex_unlock(dev-lock);}}printk(KERN_INFOscull_open succeed!\n);return0;}staticintscull_release(structinode*inode,structfile*filp){printk(KERN_INFOscull_release successed!\n);return0;}staticssize_tscull_read(structfile*filp,char__user*buf,size_tcount,loff_t*f_pos){structscull_dev*devfilp-private_data;structscull_qset*dptr;unsignedintitem_sizedev-quantum*dev-qset_size;intitem,s_pos,q_pos,rest;ssize_tretval0;if(mutex_lock_interruptible(dev-lock))return-EINTR;if(*f_posdev-size)gotoout;if(countdev-size-*f_pos)countdev-size-*f_pos;item*f_pos/item_size;rest*f_pos%item_size;q_posrest/dev-quantum;s_posrest%dev-quantum;dptrdev-qset_head;while(item--dptr){dptrdptr-next;}if(countdev-quantum-s_pos){countdev-quantum-s_pos;}if(dptrdptr-datadptr-data[q_pos]){if(copy_to_user(buf,dptr-data[q_pos]s_pos,count)){retval-EFAULT;gotoout;}*f_poscount;retvalcount;}else{gotoout;}out:mutex_unlock(dev-lock);returnretval;}staticssize_tscull_write(structfile*filp,constchar__user*buf,size_tcount,loff_t*f_pos){structscull_dev*devfilp-private_data;structscull_qset*dptr;unsignedintitem_sizedev-quantum*dev-qset_size;intitem,s_pos,q_pos,rest;ssize_tretval0;if(mutex_lock_interruptible(dev-lock))return-EINTR;item*f_pos/item_size;rest*f_pos%item_size;q_posrest/dev-quantum;s_posrest%dev-quantum;dptrdev-qset_head;if(dptrNULL){dptrkmalloc(sizeof(structscull_qset),GFP_KERNEL);if(!dptr)gotoout;memset(dptr,0,sizeof(structscull_qset));dev-qset_headdptr;}while(item--0){if(!dptr-next){dptr-nextkmalloc(sizeof(structscull_qset),GFP_KERNEL);if(!dptr-next)gotoout;memset(dptr-next,0,sizeof(structscull_qset));}dptrdptr-next;}if(!dptr-data){dptr-datakmalloc(dev-qset_size*sizeof(char*),GFP_KERNEL);if(!dptr-data)gotoout;memset(dptr-data,0,dev-qset_size*sizeof(char*));}if(!dptr-data[q_pos]){dptr-data[q_pos]kmalloc(dev-quantum,GFP_KERNEL);if(!dptr-data[q_pos])gotoout;memset(dptr-data[q_pos],0,dev-quantum);}if(countdev-quantum-s_pos){countdev-quantum-s_pos;}if(copy_from_user(dptr-data[q_pos]s_pos,buf,count)){retval-EFAULT;gotoout;}*f_poscount;retvalcount;if(dev-size*f_pos){dev-size*f_pos;}out:mutex_unlock(dev-lock);returnretval;}staticconststructfile_operationsscull_fops{.ownerTHIS_MODULE,.openscull_open,.releasescull_release,.readscull_read,.writescull_write,};staticint__initscull_init(void){intret,i;dev_tdevno;retalloc_chrdev_region(devno,0,SCULL_DEVICES,SCULL_NAME);if(ret){printk(KERN_ERRalloc_chrdev_region failed \n);returnret;}scull_majorMAJOR(devno);printk(KERN_INFOscull_major %d,scull_major);#ifLINUX_VERSION_CODEKERNEL_VERSION(6,4,0)scull_classclass_create(SCULL_NAME);#elsescull_classclass_create(THIS_MODULE,SCULL_NAME);#endifif(IS_ERR(scull_class)){retPTR_ERR(scull_class);printk(KERN_ERRscull failed create class ret %d \n,ret);gotoerr_unregister_region;}for(i0;iSCULL_DEVICES;i){structscull_dev*devscull_devs[i];dev-qset_sizeQSET_SIZE;dev-quantumQUANTUM;dev-size0;dev-devnoMKDEV(scull_major,i);mutex_init(dev-lock);dev-qset_headNULL;cdev_init(dev-cdev,scull_fops);dev-cdev.ownerTHIS_MODULE;device_create(scull_class,NULL,dev-devno,NULL,scull%d,i);retcdev_add(dev-cdev,dev-devno,1);if(ret){while(i--){cdev_del(scull_devs[i].cdev);device_destroy(scull_class,scull_devs[i].devno);}printk(KERN_ERRfailed to add scull_cdev %d\n,ret);gotoerr_create_class;}}printk(KERN_INFOscull_init succeed!\n);return0;err_create_class:class_destroy(scull_class);err_unregister_region:unregister_chrdev_region(devno,SCULL_DEVICES);returnret;}staticvoid__exitscull_exit(void){inti;dev_tdevnoMKDEV(scull_major,0);for(i0;iSCULL_DEVICES;i){structscull_dev*devscull_devs[i];device_destroy(scull_class,dev-devno);cdev_del(dev-cdev);scull_trim(dev);}class_destroy(scull_class);unregister_chrdev_region(devno,SCULL_DEVICES);printk(KERN_INFOscull module unloaded!);}MODULE_LICENSE(GPL);module_init(scull_init);module_exit(scull_exit);字符设备驱动程序通过以下步骤实现驱动程序的 init分配设备号 创建设备实例 将设备实例与 file_operations 绑定 将设备实例与设备号绑定登记内核映射表 创建设备类 根据设备号与设备类创建设备节点

相关新闻

2026/8/31 14:40:09

想找靠谱的辽宁别墅入户门公司?这些要点很关键!

引言在辽宁地区,随着别墅建设的增多,别墅入户门的市场需求也日益增长。寻找一家靠谱的辽宁别墅入户门公司并非易事。下面将为大家介绍选择时的一些关键要点,并分析该行业的相关情况。辽宁别墅入户门行业现状行业报告显示,近年来辽…

2026/9/1 20:31:00

微信缓存清理终极指南:用 CleanMyWechat 一键释放几十 GB 磁盘空间

微信缓存清理终极指南:用 CleanMyWechat 一键释放几十 GB 磁盘空间 【免费下载链接】CleanMyWechat 自动删除 Windows 和 Mac 电脑端微信缓存数据,包括从所有聊天中自动下载的大量文件、视频、图片等数据内容,解放你的空间。 项目地址: htt…

2026/9/1 12:27:51

告别录屏抓狂:m3u8-downloader 一学就会的 M3U8 视频下载指南

告别录屏抓狂:m3u8-downloader 一学就会的 M3U8 视频下载指南 【免费下载链接】m3u8-downloader 一个M3U8 视频下载(M3U8 downloader)工具。跨平台: 提供windows、linux、mac三大平台可执行文件,方便直接使用。 项目地址: https://gitcode.com/gh_mirrors/m3u8d/…

2026/9/2 13:10:05

img2.5模型落地前怎么测?从单图到批量压测的完整指南

img2.5 的消息刚传出来时,很多人的第一反应就是找测试样例跑一轮。新模型版本能不能打,确实不能只看发布说明里的功能列表,要把单图、批量、不同输入内容逐一验证过,才能判断它是不是能接进现在的流程。这篇文章不追具体参数表&am…

2026/9/2 13:10:05

Art-Pi开发板实战:TouchGFX在RT-Thread上的移植与字体图标应用指南

简介:在嵌入式系统开发中,图形用户界面(GUI)的性能直接影响产品体验。随着微控制器性能提升,通过叠加实时操作系统与专用GUI框架,开发者可在资源受限设备上实现流畅动画与交互。TouchGFX作为STM32生态下的高…

2026/9/2 13:10:05

无加速测试:帧率与触控延迟的真实关系

1. 为什么坚持做“无加速”测试 在聊联想拯救者 Y700 五代之前,先说一个我自己的真实经历。 有一阵子我拿平板玩游戏,游戏内置帧率显示一直稳定在 120 上下,画面也确实顺滑,但我总感觉操作有点“隔着一层东西”:手指已…

2026/9/2 13:10:05

STM32外挂字库芯片GT32L32S0140实战:汉字显示与SPI移植指南

简介:这是一份面向嵌入式开发者的字库芯片GT32L32S0140配套例程与库文件资源包,适合需要驱动该芯片实现汉字及字符显示的中级开发者。包内提供基于STM32F103C8T6微控制器、KEIL5环境编写的完整工程示例,涵盖库文件、头文件、SPI硬件参考代码、…

2026/9/2 13:05:05

PowerShell 安装指南:Windows、Linux 与 macOS 三步就位

PowerShell 安装指南:Windows、Linux 与 macOS 三步就位 【免费下载链接】PowerShell PowerShell for every system! 项目地址: https://gitcode.com/GitHub_Trending/po/PowerShell 刚接触 PowerShell 的人最常卡在三处:包下错了(x64…

2026/9/1 16:02:17

vSound小提琴数字处理器实操指南:从接线到演出的完整配置

电小提琴或者原声小提琴插电演出,第一个绕不开的坎就是声音难听。原声琴的共鸣和空气感一旦进了拾音器,出来的往往是一坨干瘪、发尖、带着奇怪塑料味的信号。我当初第一次把琴接上乐队调音台,直接被主唱吐槽"你这声音像在锯钢丝"。…

2026/9/2 9:00:32

传感器接口IC如何攻克生物化学传感的微弱信号难题?

1. 从电极到比特流:为什么生物化学传感必须依赖专用接口IC 做生物化学传感的人都有过类似的经历:明明传感器本身性能很好,信号输出却一塌糊涂——噪声大、漂移明显、重复性差,怎么调都达不到预期。很多时候问题并不在传感器&#…

2026/9/2 8:41:06

STM32F411CEU6多通道ADC采集:扫描模式+DMA实现详解

1. 多通道 ADC 的用武之地把“Multichannel ADC”和“STM32F411CEU6”这两个关键字放在一起,其实就是嵌入式开发里最常遇到的一类需求:用一块不算贵的 MCU,同时采集多路模拟信号。STM32F411CEU6 是 48 引脚的 Cortex-M4F 主控,主频…

2026/9/2 0:03:41

单片机毕业设计-基于单片机与蓝牙通讯的输液状态监测终端设计与开发 基于 STM32 或 51 单片机的液位‑滴速‑温度多参数输液监护装置设计(024005)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

2026/9/2 0:03:41

DeepSeek字幕翻译实战:从API调用到批量SRT转中文的完整方案

这次我们来看一个很实用的 DeepSeek 落地场景:用 DeepSeek 把英文视频字幕自动翻译成中文。具体案例是《恶魔君》1989 年第 28 集的英转中字幕任务,标题写得很直白,但背后其实是一整套可以复用的技术流程:字幕解析、模型调用、批量…

2026/9/2 0:03:41

用Python搭建搞笑语音助手:从语音识别到语音合成全教程

当你家里摆着一台天猫精灵,却总希望语音助手偶尔“不正经”一点,不用官方腔回答问题,而是张口就接几句搞笑段子,会是什么体验?我最近动手验证了一下这个想法——没有去改装任何市面上现有的智能音箱,而是直…

2026/9/2 1:15:22

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/2 1:15:22

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/2 1:15:20

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…