71 模块编程之新增一个字符设备

前言

这个 主要是 最开始的时候了解驱动的时候, 看到的一系列的 case, 这里 来大致剖析一下 相关的道理

这些模块 是怎么和内核交互的, 内核的这些业务是怎么实现的 

这里主要是一个模块来注册了一个字符设备 

然后这个字符设备 可读可写, 基于的是分配的一段空间 

 

 

测试用例

测试模块如下, 模块主要是来自于 某git仓库, 这里未记录信息, 感谢原作者 

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/string.h>
#include <linux/errno.h>MODULE_LICENSE("Dual BSD/GPL");static char data[] = "0123456789\r\n";
/* The lock for device data. */
static rwlock_t lock;static int example_open(struct inode *inode, struct file *filp) {printk(KERN_DEBUG "EXAMPLE: open\n");/* Initial lock. */rwlock_init(&lock);/* Map the data location to the file data pointer. */filp->private_data = data;return 0;
}static int example_close(struct inode *inode, struct file *filp) {printk(KERN_DEBUG "EXAMPLE: close\n");/* Release the mapping of file data address. */if(filp->private_data) {filp->private_data = NULL;}return 0;
}static ssize_t example_read(struct file *filp, char __user *buf, size_t size, loff_t *f_pos) {size_t count;uint8_t byte;uint8_t *data_p;printk(KERN_DEBUG "EXAMPLE: read (size=%zu)\n", size);data_p = filp->private_data;/* Get the lock for reading. */read_lock(&lock);/* Read from the device data to user space. */for(count = 0; (count < size) && (*f_pos) < strlen(data); ++(*f_pos), ++count) {byte = data_p[*f_pos];if(copy_to_user(buf + count, &byte, 1) != 0) {break;}printk(KERN_DEBUG "EXAMPLE: read (buf[%zu]=%02x)\n", count, (unsigned)byte);}/* Release the lock for reading. */read_unlock(&lock);return count;
}static ssize_t example_write(struct file *filp, const char __user *buf, size_t size, loff_t *f_pos) {size_t count;ssize_t ret;uint8_t byte;uint8_t *data_p;printk(KERN_DEBUG "EXAMPLE: write (size=%zu)\n", size);data_p = filp->private_data;/* Get the lock for writing. */write_lock(&lock);/* Write from user space to the device. */for(count = 0; (count < size) && (*f_pos) < strlen(data); ++(*f_pos), ++count) {if(copy_from_user(&byte, buf + count, 1) != 0) {break;}data_p[*f_pos] = byte;printk(KERN_DEBUG "EXAMPLE: write (buf[%zu]=%02x)\n", count, (unsigned)byte);}/* Release the lock for writing. */write_unlock(&lock);if((count == 0) && ((*f_pos) >= strlen(data))) {ret = -ENOBUFS;}else {ret = count;}return ret;
}static struct file_operations example_fops = {.open = example_open,.release = example_close,.read = example_read,.write = example_write,
};#define EXAMPLE_NAME   "example"static unsigned int example_major;
static unsigned int example_devs = 2;
static struct cdev example_cdev;
static struct class *example_sys_class = NULL;static int example_init(void) {dev_t dev;int alloc_ret, cdev_err;printk(KERN_DEBUG "EXAMPLE: init\n");/* Allocate a character device. */alloc_ret = alloc_chrdev_region(&dev, 0, example_devs, EXAMPLE_NAME);if(alloc_ret) {printk(KERN_DEBUG "EXAMPLE: Failed to allocate a character device\n");return -1;}/* Initial the character device ddriver. */example_major = MAJOR(dev);cdev_init(&example_cdev, &example_fops);example_cdev.owner = THIS_MODULE;/* Add the character device driver into system. */dev = MKDEV(example_major, 0);cdev_err = cdev_add(&example_cdev, dev, example_devs);if(cdev_err) {printk(KERN_DEBUG "EXAMPLE: Failed to register a character device\n");/* Release the allocated character device. */if(alloc_ret == 0) {unregister_chrdev_region(dev, example_devs);}return -1;}printk(KERN_DEBUG "EXAMPLE: %s driver(major %d) installed.\n", EXAMPLE_NAME, example_major);/* Create device class. */example_sys_class = class_create(THIS_MODULE, EXAMPLE_NAME);if(IS_ERR(example_sys_class)) {printk(KERN_DEBUG "EXAMPLE: Failed to create a class of device.\n");/* Release the added character device. */if(cdev_err == 0)cdev_del(&example_cdev);/* Release the allocated character device. */if(alloc_ret == 0)unregister_chrdev_region(dev, example_devs);return -1;}printk(KERN_DEBUG "EXAMPLE: %s class created.\n", EXAMPLE_NAME);/* Create device node. */device_create(example_sys_class, NULL, dev, NULL, EXAMPLE_NAME);printk(KERN_DEBUG "EXAMPLE: %s device node created.\n", EXAMPLE_NAME);return 0;
}static void example_exit(void) {dev_t dev = MKDEV(example_major, 0);printk(KERN_DEBUG "EXAMPLE: exit\n");/* Destory device nodes. */device_destroy(example_sys_class, dev);/* Delete device class. */class_destroy(example_sys_class);/* Delete the character device driver from system. */cdev_del(&example_cdev);/* Unregister the allocated character device. */unregister_chrdev_region(dev, example_devs);printk(KERN_DEBUG "EXAMPLE: %s driver removed.\n", EXAMPLE_NAME); 
}module_init(example_init);
module_exit(example_exit);

 

 

创建 dev, cdev

分为创建 dev, 初始化 cdev, 关联 dev, cdev

创建设备节点 

 

创建 char_device_struct, 将 dev 和 char_device_sturct 关联起来, 通过 major, baseminor 关联

 

这里是初始化 cdev, 以及 cdev->ops 为传入的 example_fops

 

将 dev 关联到 cdev 上面

创建 probe, 关联 dev, cdev, module 并且添加到 cdev_map 中 

 

创建当前 module 下面的 example 的 class 

 

 

创建 device 以及设备文件 

创建 device 相关

创建 kobject, 注册到 sysfs, 创建设备文件 等等

 

创建 device 对象并初始化 dev_initialize 中主要是初始化各类链表 

device_add 中处理的相关核心业务 

 

根据 dev.kobj 在 sysfs 中注册 sysfs 文件 

dev, subsystem, group, power 相关 

 

创建 /dev 中设备文件的地方, 这里放入 任务队列

 

真实异步创建 /dev 中设备文件的地方

 

创建的设备文件对应的 inode 的 f_ops 初始化如下 

open 函数为 chrdev_open

 

  

 device 设备文件的使用

然后 open 的时候, 会获取到 dev 对应的 cdev, 进而获取到 cdev->ops[驱动中注册的 example_fops]

然后 替换掉 file 中的 f_ops, 作为 真实业务读写的 f_ops

这里可以对比一下 上面 cdev_init 初始化的时候, 传入的 example_fops 的地址信息, 发现 差不多是能够对上的, 地址有一些差异 主要是 两次截图的调试不是在同一个 insmod 但是实际上这里获取到的 f_ops 就是 cdev_init 的时候传入的 example_fops

代理了一层之后, 使用代理的 f_ops->open 再处理了一次 

 

其后, read, write, ioctl 就是基于代理的 f_ops 来进行处理了 

这里以 read 为例来进行调试 

 

 

完 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/91693.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/91693.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小众创新方向!多传感器融合与视觉惯性导航,定位精度和效率大幅提升!

多传感器融合与视觉惯性导航技术&#xff08;VINS&#xff09;取得了显著进展。近期&#xff0c;研究人员通过优化视觉与惯性传感器数据的融合算法、引入深度学习技术以及改进系统架构&#xff0c;显著提升了VINS在复杂环境下的定位精度和鲁棒性。基于深度学习的特征提取方法能…

超简单linux上部署Apache

1.Apache是什么&#xff1f;Apache 是世界上最流行的 ​​开源Web服务器软件​​&#xff0c;由 Apache 软件基金会维护。​​主要功能​​&#xff1a;接收客户端&#xff08;如浏览器&#xff09;的HTTP请求&#xff0c;返回网页、图片等静态/动态资源。​​特点​​&#xf…

前端 SSE 实战应用:用最简单的方式实现实时推送

前端 SSE 实战应用&#xff1a;用最简单的方式实现实时推送 &#x1f4cc; 点赞收藏关注不迷路&#xff01; 在前端项目中&#xff0c;我们常听到“实时通信”这个需求 —— 聊天、进度、状态变化、系统消息。 但提到实时&#xff0c;大家首先想到的是 WebSocket&#xff0c;对…

第16章 基于AB实验的增长实践——验证想法:AB实验实践

​一、AB实验全流程框架​实验分为5个核心环节&#xff1a;实验假设​ → 实验设计​ →实验运行​ → 实验分析​ → 实验决策​​二、各环节核心要点详解​​1. 实验假设​​原则​&#xff1a;目标性、可归因、可复用&#xff08;前两者必选&#xff09;​&#xff08;1&…

解决【软件安装路径】失败的方法

出现问题上图所示问题为&#xff1a;你的临时目录路径中包含 Unicode 字符&#xff0c;这可能会导致安装损坏。请参阅故障排除指南以获取解决方法。出现问题的原因&#xff1a;添加路径下存在中文&#xff0c;导致系统文件无法识别。解决方法步骤一&#xff1a;创建Temp(临时文…

FreeRTOS学习笔记——总览

考虑到RTOS能够提升单片机开发能力&#xff0c;也是开发复杂任务的必经之路&#xff0c;还是有必要学习的。 FreeRTOS教程多&#xff0c;免费开源&#xff0c;是个不错的选择。后续可以考虑继续学习RT-Thread等。 参考1&#xff1a;FreeRTOS(教程非常详细&#xff09;——作者&…

Clip微调系列:《coOp: learning to prompt for vision-language models》

论文链接&#xff1a;arxiv.org/pdf/2109.01134v1 推荐视频(clip_coop的代码逻辑讲解&#xff0c;代码简单&#xff0c;有助于理解)&#xff1a;CLIP和CoOp工作的简单Pytorch复现和理解_哔哩哔哩_bilibili 其他参考链接&#xff1a;CoOp - CLIP 自适应Prompt工程 【一】_coop…

[论文阅读] 人工智能 + 软件工程 | 开源软件中的GenAI自白:开发者如何用、项目如何管、代码质量受何影响?

开源软件中的GenAI自白&#xff1a;开发者如何用、项目如何管、代码质量受何影响&#xff1f; 论文&#xff1a;Self-Admitted GenAI Usage in Open-Source SoftwarearXiv:2507.10422 Self-Admitted GenAI Usage in Open-Source Software Tao Xiao, Youmei Fan, Fabio Calefato…

AI绘画版权问题全解析:你的作品真的属于你吗?

AI绘画版权问题全解析:你的作品真的属于你吗? 关键词:AI绘画、版权归属、生成式AI、训练数据、独创性、法律合规、知识产权 摘要:当你用MidJourney生成一张“赛博朋克风格的熊猫”,或用Stable Diffusion画出“梵高笔触的星空咖啡馆”时,你是否想过:这张图的版权属于你、…

深入理解Linux文件I/O:系统调用与标志位应用

目录 一、引入 二、标志位 1、什么是标志位&#xff1f; 2、标志位传递示例 输出结果分析 关键点解释 三、文件描述符(File Descriptor)&#xff08;先大概了解&#xff09; 四、接口介绍&#xff1a;open()函数 1、命令查看 2、头文件 3、函数原型 4、参数说明 …

海康线扫相机通过采集卡的取图设置

目录 1、扫描高度小于65000行 1.1 软触发 1、采集卡设置项 2、相机设置项 1.2 硬触发 1、采集卡设置项 2、相机设置项 2、扫描高度大于65000行 1.1 软触发 1、采集卡设置项 2、相机设置 1.2 硬触发 1、采集卡设置项 2、相机设置 2.1 帧扫描 2.2 行扫描 3、注意…

InfluxDB 3与Apache Parquet:打造高性能时序数据存储与分析解决方案

在当今数据驱动的时代&#xff0c;各行业产生的数据量呈爆炸式增长&#xff0c;如何高效存储和管理海量数据成为企业和开发者面临的重大挑战。对于时序数据而言&#xff0c;其具有数据量大、写入频繁、查询模式多样等特点&#xff0c;对存储系统的性能和效率提出了更高的要求。…

20250718-4-Kubernetes 应用程序生命周期管理-Pod对象:实现机制_笔记

一、Pod对象&#xfeff;&#xfeff;1. 资源共享实现机制1&#xff09;共享网络&#xfeff;基本概念实现方式&#xff1a;通过将业务容器网络加入到负责网络的容器&#xff08;infra container&#xff09;实现网络共享核心特点&#xff1a;共享网络协议栈&#xff08;包括TC…

防爆手机是什么?能用普通手机改装吗?

在石油开采平台的井架之上&#xff0c;在化工车间的反应釜旁&#xff0c;在煤矿深达千米的巷道中&#xff0c;一群特殊的工作人员正使用着看似普通的通讯设备。这些设备外壳上醒目的Ex防爆认证标志&#xff0c;揭示着其与众不同的身份——防爆手机。这类专为易燃易爆环境设计的…

gem install报错解析

报错内容 [rootlocalhost ~]# gem install bundler Fetching: bundler-2.6.9.gem (100%) ERROR: Error installing bundler:bundler requires Ruby version > 3.1.0. The current ruby version is 2.5.0.解决方案&#xff08;任选其一&#xff09; 这个错误表明你当前的 Ru…

css 如何实现大屏4个占位 中屏2个 小屏幕1个

1、 使用grid.container {display: grid;grid-template-columns: repeat(4, 1fr);gap: 20px;border: 1px solid red;width: 400px;height: 400px;}media (max-width: 768px) {.container {grid-template-columns: 1fr;}}media (min-width: 768px) and (max-width: 992px) {.con…

Redis学习系列之—— JDHotKey 热点缓存探测系统

一、为什么需要热点缓存探测 在回答这个问题前&#xff0c;我们先考虑一下&#xff1a;为什么光用 Redis 还不够&#xff0c;还需要使用本地缓存&#xff1f; 一般来说&#xff0c;Redis 集群的性能能抗住几十万并发&#xff0c;能够应付大部分情况。但对于一些头部 APP&#x…

Linux 安全加固

Linux 安全加固需要从​​用户权限、系统服务、网络防护、日志审计、文件系统、访问控制​​等多个维度入手&#xff0c;目标是减少攻击面、限制未授权访问、提升系统健壮性。以下是​​详细步骤实操示例​​&#xff0c;覆盖主流 Linux 发行版&#xff08;如 CentOS/Ubuntu&am…

【Docker#2】容器历史发展 | 虚拟化实现方式

一、前言 – 容器技术发展史 容器技术是现今计算技术的重要组成部分&#xff0c;其发展历程可以追溯到很早的计算机系统提供的进程隔离工具。以下是容器技术的发展历程&#xff0c;其中涵盖了从早期的进程隔离技术到现代云计算和云原生的演变&#xff1a; ① Jail 时代 1979 年…

React + Mermaid 图表渲染消失问题剖析及 4 种代码级修复方案

Mermaid 是一个流行的库&#xff0c;它可以将文本图表&#xff08;例如 graph LR; A-->B;&#xff09;转换为 SVG 图表。在静态 HTML 页面中&#xff0c;Mermaid 会查找 <pre class"mermaid"> 代码块&#xff0c;并在页面加载时将它们替换为渲染后的图表。它…