LDD3--字符设备驱动

发布时间:2026/9/29 11:51:27

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/9/26 22:39:33

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

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

2026/9/28 8:17:57

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

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

2026/9/29 8:28:27

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

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

2026/9/29 11:49:44

Tarjan算法

我们先来了解一下Tarjan算法的作用 Tarjan算法解决的是:在有向图里找连通分量的问题 连通分量,听起来很高大上对吧,但是实际上他就是一堆点,它们两两之间可以互相到达 像这样: 1 -> 2 -> 3 -> 4 ^ | | …

2026/9/29 11:49:44

服装智能制造大会上的AI质检案例分享

1. AI服装制造场景 在服装智能制造大会上,AI质检成为最受关注的议题之一。传统人工质检依赖老师傅的经验与肉眼判断,效率低、漏检率高、招工难,已成为制约服装工厂产能与品质的瓶颈。随着计算机视觉与深度学习技术的成熟,AI质检正…

2026/9/29 11:44:44

Qwen模型遥感智能解译实战:LoRA微调与地物分割全流程

1. 遥感智能解译为什么值得用Qwen模型重做一遍遥感影像的智能解译这几年变化很快。早些年大家做地物分类,基本是手工设计特征加随机森林、SVM那一套,后来深度学习起来,SegFormer、U-Net这类分割网络成了主流。但真正在一线做过项目的人都知道…

2026/9/29 11:07:23

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/9/28 6:05:15

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/29 7:00:49

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/29 0:04:04

AI Evals实战指南:从零搭建LLM应用评估体系与CI/CD集成

1. 为什么AI Evals值得你花时间搞明白做LLM应用的人,迟早会撞上同一堵墙:模型输出飘忽不定,今天答得好好的,明天换个问法就胡说八道。你改了一版提示词,感觉好像好了点,但到底好了多少?说不清。…

2026/9/29 0:04:04

Java采购管理系统实战:从数据库设计到事务一致性

简介:这是一套面向Java Web初学者与课程设计者的采购管理系统完整源码,采用JSP技术搭建,配合MySQL数据库,用于解决企业采购信息的管理问题,适合作为毕业设计、课程大作业或进销存类项目的参考模板。系统实现了用户登录…

2026/9/29 3:53:39

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

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

2026/9/29 9:46:12

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

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

2026/9/29 6:36:14

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

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

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑