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

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.dtcms.com/a/285577.html

相关文章:

  • Proto文件从入门到精通——现代分布式系统通信的基石(含实战案例)
  • 标题 “Python 网络爬虫 —— selenium库驱动浏览器
  • 光伏电站工业通信网络解决方案:高可靠工业通信架构与设备选型
  • 开源短链接工具 Sink 无需服务器 轻松部署到 Workers / Pages
  • 西门子工业软件全球高级副总裁兼大中华区董事总经理梁乃明先生一行到访庭田科技
  • ArcGIS Pro+PS 实现地形渲染效果图
  • WinDbg命令
  • FastAdmin框架超级管理员密码重置与常规admin安全机制解析-卓伊凡|大东家
  • 本地部署DeepSeek-R1并打通知识库
  • 数字地与模拟地隔离
  • 【C语言】深入理解柔性数组:特点、使用与优势分析
  • Cursor替代,公测期间免费使用Claude4
  • 首个直播流扩散(LSD)AI模型:MirageLSD,它可以实时把任意视频流转换成你的自定义服装风格——虚拟换装新体验
  • mpiigaze的安装过程一
  • 【后端】.NET Core API框架搭建(10) --配置163邮件发送服务
  • 【锂电池剩余寿命预测】TCN时间卷积神经网络锂电池剩余寿命预测(Pytorch完整源码和数据)
  • C#之线程Thread
  • ARCS系统机器视觉实战(直播回放)
  • Huber Loss(胡贝损失)详解:稳健回归的秘密武器 + Python实现
  • Unity 堆栈分析实战指南 C#
  • Copula 回归与结构方程模型:R 语言构建多变量因果关系网络
  • 机器视觉的布料丝印应用
  • React条件渲染
  • 用 React-Three-Fiber 实现雪花下落与堆积效果:从零开始的 3D 雪景模拟
  • jvm分析篇---1、先认识下dump文件
  • Linux系统安装Docker及部署Node.js 20.15.0(含pnpm、pm2)完整指南
  • Docker部署前后端分离项目——多项目共享环境部署
  • GEV/POT/Markov/点过程/贝叶斯极值全解析;基于R语言的极值统计学
  • Camera相机人脸识别系列专题分析之十七:人脸特征检测FFD算法之libhci_face_camera_api.so 296点位人脸识别检测流程详解
  • vue2 面试题及详细答案150道(81 - 90)