三、linux字符驱动详解
在上一节完成NFS开发环境的搭建后,本节将探讨Linux字符设备驱动的开发。字符设备驱动作为Linux内核的重要组成部分,主要负责管理与字符设备(如串口、键盘等)的交互,并为用户空间程序提供统一的读写操作接口。
驱动代码
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/cdev.h> #define DEVICE_NAME "hello_chrdev"
#define BUFFER_SIZE 100// 设备结构体
typedef struct {char buffer[BUFFER_SIZE];struct class *class;struct device *device;dev_t dev_num;struct cdev cdev;
} HelloDevice;static HelloDevice hello_dev;// 打开设备
static int hello_open(struct inode *inode, struct file *filp) {printk(KERN_INFO "Hello device opened\n");return 0;
}// 读取设备
static ssize_t hello_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {size_t len = strlen(hello_dev.buffer);if (*f_pos >= len) {return 0;}if (count > len - *f_pos) {count = len - *f_pos;}if (copy_to_user(buf, hello_dev.buffer + *f_pos, count)) {return -EFAULT;}*f_pos += count;return count;
}// 写入设备
static ssize_t hello_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {if (count > BUFFER_SIZE - 1) {count = BUFFER_SIZE - 1;}if (copy_from_user(hello_dev.buffer, buf, count)) {return -EFAULT;}hello_dev.buffer[count] = '\0';*f_pos += count;return count;
}// 关闭设备
static int hello_release(struct inode *inode, struct file *filp) {printk(KERN_INFO "Hello device closed\n");return 0;
}// 文件操作结构体
static struct file_operations hello_fops = {.owner = THIS_MODULE,.open = hello_open,.read = hello_read,.write = hello_write,.release = hello_release,
};// 模块初始化函数
static int __init hello_init(void) {int ret;// 分配设备号ret = alloc_chrdev_region(&hello_dev.dev_num, 0, 1, DEVICE_NAME);if (ret < 0) {printk(KERN_ERR "Failed to allocate character device number\n");return ret;}// 创建类hello_dev.class = class_create(THIS_MODULE, DEVICE_NAME);if (IS_ERR(hello_dev.class)) {unregister_chrdev_region(hello_dev.dev_num, 1);printk(KERN_ERR "Failed to create class\n");return PTR_ERR(hello_dev.class);}// 创建设备hello_dev.device = device_create(hello_dev.class, NULL, hello_dev.dev_num, NULL, DEVICE_NAME);if (IS_ERR(hello_dev.device)) {class_destroy(hello_dev.class);unregister_chrdev_region(hello_dev.dev_num, 1);printk(KERN_ERR "Failed to create device\n");return PTR_ERR(hello_dev.device);}// 初始化 cdev 结构体cdev_init(&hello_dev.cdev, &hello_fops);hello_dev.cdev.owner = THIS_MODULE;// 添加字符设备到系统ret = cdev_add(&hello_dev.cdev, hello_dev.dev_num, 1);if (ret < 0) {device_destroy(hello_dev.class, hello_dev.dev_num);class_destroy(hello_dev.class);unregister_chrdev_region(hello_dev.dev_num, 1);printk(KERN_ERR "Failed to add character device\n");return ret;}printk(KERN_INFO "Hello device initialized. Major: %d, Minor: %d\n", MAJOR(hello_dev.dev_num), MINOR(hello_dev.dev_num));return 0;
}// 模块卸载函数
static void __exit hello_exit(void) {cdev_del(&hello_dev.cdev);device_destroy(hello_dev.class, hello_dev.dev_num);class_destroy(hello_dev.class);unregister_chrdev_region(hello_dev.dev_num, 1);printk(KERN_INFO "Hello device removed\n");
}module_init(hello_init);
module_exit(hello_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple hello world character device driver");
函数接口详解
1. 模块初始化与退出相关函数
alloc_chrdev_region
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
- 功能:动态分配一组连续的字符设备号。
- 参数
dev:用于存储分配到的设备号。baseminor:起始的次设备号。count:要分配的设备号数量。name:设备的名称,用于在/proc/devices中显示。
- 返回值:成功返回 0,失败返回负数错误码。
class_create
struct class *class_create(struct module *owner, const char *name);
- 功能:在
/sys/class目录下创建一个设备类。 - 参数
owner:指向模块的指针,通常为THIS_MODULE。name:类的名称。
- 返回值:成功返回指向
struct class的指针,失败返回错误指针。
device_create
struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);
- 功能:在
/sys/class/<class_name>目录下创建设备节点,并在/dev目录下创建对应的设备文件。 - 参数
class:指向设备类的指针。parent:父设备指针,通常为NULL。devt:设备号。drvdata:设备驱动数据,通常为NULL。fmt:设备名称的格式化字符串。
- 返回值:成功返回指向
struct device的指针,失败返回错误指针。
cdev_init
void cdev_init(struct cdev *cdev, const struct file_operations *fops);
- 功能:初始化字符设备结构体
struct cdev,并关联文件操作结构体struct file_operations。 - 参数
cdev:指向struct cdev的指针。fops:指向struct file_operations的指针。
cdev_add
int cdev_add(struct cdev *p, dev_t dev, unsigned count);
- 功能:将字符设备添加到内核中。
- 参数
p:指向struct cdev的指针。dev:设备号。count:设备数量。
- 返回值:成功返回 0,失败返回负数错误码。
module_init 和 module_exit
module_init(hello_init);
module_exit(hello_exit);
- 功能:分别指定模块加载和卸载时调用的函数。
2. 文件操作相关函数
-
在 Linux 内核中,
struct file_operations结构体是字符设备驱动与用户空间进行交互的关键桥梁,其中open、read、write和release是比较常用的操作函数。struct file_operations` 结构体中相关成员介绍
open函数int (*open) (struct inode *inode, struct file *filp);- 功能:当用户空间使用
open()系统调用打开设备文件时,内核会调用驱动中注册的open函数。该函数通常用于执行设备的初始化操作,如分配资源、检查设备状态等。 - 参数
struct inode *inode:指向文件对应的索引节点,包含了文件的元信息,如文件类型、权限等。struct file *filp:指向文件对象,代表了一个打开的文件实例,包含了文件的当前状态、偏移量等信息。
- 返回值:成功时返回 0,失败时返回负数错误码。
read函数ssize_t (*read) (struct file *filp, char __user *buf, size_t count, loff_t *f_pos);- 功能:当用户空间使用
read()系统调用从设备文件读取数据时,内核会调用驱动中的read函数。该函数负责将设备中的数据复制到用户空间的缓冲区。 - 参数
struct file *filp:指向文件对象。char __user *buf:用户空间的缓冲区指针,用于存储从设备读取的数据。size_t count:用户请求读取的字节数。loff_t *f_pos:文件的当前偏移量指针,可通过修改该指针来更新文件的读写位置。
- 返回值:成功时返回实际读取的字节数,返回 0 表示已到达文件末尾,失败时返回负数错误码。
write函数ssize_t (*write) (struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);- 功能:当用户空间使用
write()系统调用向设备文件写入数据时,内核会调用驱动中的write函数。该函数负责将用户空间缓冲区中的数据复制到设备中。 - 参数
struct file *filp:指向文件对象。const char __user *buf:用户空间的缓冲区指针,包含了要写入设备的数据。size_t count:用户请求写入的字节数。loff_t *f_pos:文件的当前偏移量指针。
- 返回值:成功时返回实际写入的字节数,失败时返回负数错误码。
release函数int (*release) (struct inode *inode, struct file *filp);- 功能:当用户空间使用
close()系统调用关闭设备文件时,内核会调用驱动中的release函数。该函数通常用于执行设备的清理操作,如释放资源、关闭设备等。 - 参数
struct inode *inode:指向文件对应的索引节点。struct file *filp:指向文件对象。
- 返回值:成功时返回 0,失败时返回负数错误码。
- 功能:当用户空间使用
3. 模块卸载相关函数
cdev_del
void cdev_del(struct cdev *p);
- 功能:从内核中移除字符设备。
- 参数
p:指向struct cdev的指针。
device_destroy
void device_destroy(struct class *class, dev_t devt);
- 功能:销毁
/sys/class/<class_name>目录下的设备节点和/dev目录下的设备文件。 - 参数
class:指向设备类的指针。devt:设备号。
class_destroy
void class_destroy(struct class *cls);
- 功能:销毁
/sys/class目录下的设备类。 - 参数
cls:指向struct class的指针。
unregister_chrdev_region
void unregister_chrdev_region(dev_t from, unsigned count);
- 功能:释放之前分配的字符设备号。
- 参数
from:起始的设备号。count:要释放的设备号数量。
编译和测试
编写 Makefile
obj-m += helloworld.o
KDIR := linux-5.15.18/
PWD := $(shell pwd)
default:$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:$(MAKE) -C $(KDIR) M=$(PWD) clean
将 linux-5.15.18/ 替换为实际的 Linux 5.15.18 内核源码路径。
编译驱动
在终端中执行 make 命令编译驱动模块。
测试驱动
在 QEMU 终端中:
- 使用
insmod helloworld.ko加载驱动模块。 - 使用 echo “Hello World” > /dev/helloworld 向设备写入数据。
- 使用 cat /dev/helloworld 从设备读取数据。
- 使用
rmmod hello_chrdev.ko卸载驱动模块。

相关文章:
三、linux字符驱动详解
在上一节完成NFS开发环境的搭建后,本节将探讨Linux字符设备驱动的开发。字符设备驱动作为Linux内核的重要组成部分,主要负责管理与字符设备(如串口、键盘等)的交互,并为用户空间程序提供统一的读写操作接口。 驱动代码…...
【Research Proposal】基于提示词方法的智能体工具调用研究——研究问题
博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: AIGC | ChatGPT 文章目录 💯前言💯研究问题1. 如何优化提示词方法以提高智能体的工具调用能力?2. 如何解决提示词方法在多模态任务中的挑战?3. 如何通过提示词优化智能体…...
【从0做项目】Java文档搜索引擎(9)烧脑终章!
阿华代码,不是逆风,就是我疯 你们的点赞收藏是我前进最大的动力!! 希望本文内容能够帮助到你!! 目录 文章导读 零:项目结果展示 一:导入 二:问题引入 1:情…...
python: SQLAlchemy (ORM) Simple example using mysql in Ubuntu 24.04
mysql sql script: create table School 表 (SchoolId char(5) NOT NULL comment主鍵primary key,學校編號,SchoolName nvarchar(500) NOT NULL DEFAULT comment 學校名稱,SchoolTelNo varchar(8) NULL DEFAULT comment電話號碼,PRIMARY KEY (SchoolId) #主…...
如何为自己的 PDF 文件添加密码?在线加密 PDF 文件其实更简单
随着信息泄露和数据安全问题的日益突出,保护敏感信息变得尤为重要。加密 PDF 文件是一种有效的手段,可以确保只有授权用户才能访问或修改文档内容。本文将详细介绍如何使用 CleverPDF 在线工具为你的 PDF 文件添加密码保护,确保其安全性。 为…...
echarts 折线图动态基准线设置超出基准线标红
基准线属性:markLine 线条标红关键属性:visualMap 小于: lt (less than) 大于:gt (greater than) 小于等于:lte (Less than or equal to) 大于等于:gte (Greater than or equal to) 1、基础应用——2条基准…...
Part 3 第十二章 单元测试 Unit Testing
概述 第十二章围绕单元测试展开,阐述了单元测试的实践与重要性,通过对比其他测试类型,突出其特点,还介绍了单元测试的最佳实践、避免的反模式以及与测试替身相关的内容,为编写高质量单元测试提供指导。 章节概要 1…...
C++与Python:两种编程语言的区别
C和Python都是当今编程领域广泛使用的语言,它们各有特色,适用于不同的开发场景。本文将从语言特性、性能、学习难度、应用领域等多个方面探讨C与Python之间的区别。 一、语言特性 类型系统: C:是一种静态类型语言…...
Springboot 高频面试题
以下是Spring Boot的高频面试题及答案和底层原理解释: 基础概念 什么是Spring Boot,其主要特点是什么? 答案: Spring Boot本质上是一个建立在Spring框架之上的快速应用开发框架。其主要特点包括: 启动器:一…...
常用电脑,护眼软件推荐 f.lux 3400K | 撰写论文 paper
常用电脑?平均每天用 5 个小时?你就要考虑用一个护眼软件了,对皮肤也好。因为电脑屏幕有辐射,比如蓝光。 f.lux 作为一款专业护眼软件,值得使用。之前用了三年的 Iris Pro,现在 f.lux 做的更好了。 使用…...
MacOS下使用Ollama本地构建DeepSeek并使用本地Dify构建AI应用
目录 1 大白话说一下文章内容2 作者的电脑配置3 DeepSeek的本地部署3.1 Ollamal的下载和安装3.2 选择合适的deepseek模型3.3 安转deepseek 4 DifyDeepSeek构建Al应用4.1 Dify的安装4.1.1 前置条件4.1.2 拉取代码4.1.3 启动Dify 4.2 Dify控制页面4.3 使用Dify实现个“文章标题生…...
如何有效利用MYSQL的连接数
连接数配置2500~3000 依然发现连接不够用? -- 查看当前最大连接数 SHOW VARIABLES LIKE MAX_CONNECTIONS; -- 查看当前总链接数 SHOW STATUS LIKE Threads_connected; -- 查看当前进程明细 SHOW PROCESSLIST; 合理设置以下参数: 1. MySQL 的参数设置 …...
调用click.getchar()时Windows PyCharm无法模拟键盘输入
文章目录 问题描述解决方案参考文献 问题描述 调用 click.getchar() 时,Windows PyCharm 无法模拟键盘输入 解决方案 Run → Edit Configurations… → Modify options → Emulate terminal in output console 参考文献 Terminal emulator | PyCharm Documentati…...
使用Hardhat实现ERC20 代币合约详解
ERC20 代币合约详解 💰 1. 合约概览 // SPDX-License-Identifier: MIT pragma solidity ^0.8.20;import "openzeppelin/contracts/token/ERC20/ERC20.sol";contract MyToken is ERC20 {constructor() ERC20("MyToken", "MTK") {_min…...
清华大学《DeepSeek与AI幻觉》(无套路免费分享)
随着人工智能技术的飞速发展,以DeepSeek为代表的国产大模型正逐渐成为各行各业的重要工具。然而,AI在生成内容时常常会出现“幻觉”——即生成与事实不符、逻辑断裂或脱离上下文的内容。 清华大学新闻与传播学院与人工智能学院联合推出的这篇教程《Deep…...
代码随想录算法【Day52】
Day51 101. 孤岛的总面积 思路 从周边找到陆地然后 通过 dfs或者bfs 将周边靠陆地且相邻的陆地都变成海洋,然后再去重新遍历地图 统计此时还剩下的陆地 代码 #include <iostream> #include <vector> using namespace std; int dir[4][2] {-1, 0, …...
Spark 和 Flink
Spark 和 Flink 都是目前流行的大数据处理引擎,但它们在架构设计、应用场景、性能和生态方面有较大区别。以下是详细对比: 1. 架构与核心概念 方面Apache SparkApache Flink计算模型微批(Micro-Batch)为主,但支持结构…...
Unity结合Vuforia虚拟按键实现AR机械仿真动画效果
零、最终效果 待上传 一、资源准备 1、Vuforia Vuforia版本不能高于10.17.4(往上的版本虚拟按键功能被删除) 2、Unity Unity版本必须要高于2022.3.x,不然使用Vuforia插件时会出现bug 二、主要内容 1、添加虚拟按钮 2、为虚拟按钮设置…...
PL/SQL 异常处理
目录 一、命名的系统异常 1.常见命名的系统异常 2.预定义的系统异常数量以及描述 3.处理命名的系统异常的一般步骤 二、程序员定义的异常 1.概念 2.自定义异常的定义与抛出 3.自定义异常的处理 三、未命名的系统异常 1.概述 2.处理未命名的系统异常的方法 3.使用场景 …...
【自学笔记】Spring Boot框架技术基础知识点总览-持续更新
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 Spring Boot框架技术基础知识点总览一、Spring Boot简介1.1 什么是Spring Boot?1.2 Spring Boot的主要特性 二、Spring Boot快速入门2.1 搭建Spring Boo…...
问题解决:Ubuntu 下 IDEA 复制中文变问号乱码
问题描述 在 Ubuntu 系统中(Wayland 桌面协议),使用最新版的 IntelliJ IDEA 2026.1 开发时,遇到了一个极其诡异的复制粘贴问题:从 IDEA 中复制一段包含中文的代码或文字,粘贴到 Chrome 浏览器、VS Code 时…...
如何专业配置游戏优化工具:DLSS Swapper终极性能提升指南
如何专业配置游戏优化工具:DLSS Swapper终极性能提升指南 【免费下载链接】dlss-swapper 项目地址: https://gitcode.com/GitHub_Trending/dl/dlss-swapper DLSS Swapper是一款专为游戏玩家设计的显卡性能优化工具,通过智能管理DLSS、FSR和XeSS超…...
Nucleus Co-Op终极指南:如何为任何单机游戏添加本地分屏多人功能
Nucleus Co-Op终极指南:如何为任何单机游戏添加本地分屏多人功能 【免费下载链接】nucleuscoop Starts multiple instances of a game for split-screen multiplayer gaming! 项目地址: https://gitcode.com/gh_mirrors/nu/nucleuscoop 你是否曾经想过和朋友…...
Oracle EBS 的 E-Business Tax (eBTax) 主要用于流转税(间接税)计税
Oracle EBS 的 E-Business Tax (eBTax) 主要用于流转税(间接税)计税,但也支持部分直接税场景。一、核心定位:交易型税种(流转税)eBTax 设计初衷是处理交易层面的税务计算,与采购、销售、发票、付…...
2026届毕业生推荐的六大AI辅助写作神器横评
Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 在学术写作范围内,针对紧迫的截止时间以及繁重的文献整理任务,研究人…...
在 SAP S/4HANA 里把经典事务 BP 挂进 Fiori,并支持参数传递
先把名字叫对,很多项目里说的 Transaction launcher,落到 SAP 官方对象上,其实是 tile 加 target mapping 在 SAP 官方术语里,真正负责把导航意图解析到目标应用的,不是一个孤立的 Transaction launcher 对象,而是 SAP Fiori launchpad 里的 target mapping。target map…...
BGE-Reranker-v2-m3推理延迟高?量化压缩部署方案
BGE-Reranker-v2-m3推理延迟高?量化压缩部署方案 在实际RAG系统落地过程中,不少团队反馈:BGE-Reranker-v2-m3虽然排序精度高,但单次推理耗时普遍在300–600ms(A10显卡),批量处理10个候选文档就…...
标准库 vs HAL库:从零为STM32F103新建工程,我为什么劝新手先别碰HAL库?
标准库 vs HAL库:STM32F103工程搭建的技术路线选择 第一次接触STM32开发的新手,往往会在标准库和HAL库之间陷入选择困难。这两种开发方式代表了不同的技术路线,而选择哪种作为入门路径,直接影响着学习曲线和后续开发效率。本文将深…...
零刻EQ12 N100双网口AIO实战:从ESXI部署到多系统融合
1. 零刻EQ12 N100双网口AIO方案解析 第一次接触零刻EQ12 N100这款小主机时,我就被它的双2.5G网口设计吸引了。这种配置在家庭网络改造和轻量级数据中心建设中简直就是神器。AIO(All In One)方案的核心思想就是把路由、存储、虚拟化等功能整合…...
如何解决JuiceFS Python SDK缓存目录配置难题:从内存限制到高性能存储方案
如何解决JuiceFS Python SDK缓存目录配置难题:从内存限制到高性能存储方案 【免费下载链接】juicefs JuiceFS is a distributed POSIX file system built on top of Redis and S3. 项目地址: https://gitcode.com/GitHub_Trending/ju/juicefs JuiceFS是一个基…...
