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

Linux内核 -- 字符设备之read write poll基本实现

Linux字符设备:read、write和poll函数实现及完整代码

1. read函数

原型

ssize_t read(struct file *file, char __user *buf, size_t count, loff_t *pos);

实现步骤

  1. 检查用户缓冲区:使用copy_to_user将数据从内核空间复制到用户空间。
  2. 返回已读取的字节数:
    • 如果数据长度少于请求长度,返回实际读取的字节数。
    • 如果到达文件末尾,返回0

注意事项

  • 确保对用户缓冲区的访问是安全的。
  • 返回值必须是已成功读取的字节数或错误码(如-EFAULT)。
  • 考虑多线程情况下的并发访问,适当加锁。

示例代码

static ssize_t my_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{char data[] = "Hello, World!\n";size_t datalen = strlen(data);if (*pos >= datalen)return 0;if (count > datalen - *pos)count = datalen - *pos;if (copy_to_user(buf, data + *pos, count))return -EFAULT;*pos += count;return count;
}

2. write函数

原型

ssize_t write(struct file *file, const char __user *buf, size_t count, loff_t *pos);

实现步骤

  1. 验证用户数据:使用copy_from_user将数据从用户空间复制到内核空间。
  2. 处理写入数据:通常会将数据存储到设备的内存区域或触发硬件操作。

注意事项

  • 确保处理的是合法数据。
  • 如果设备有缓冲区,需检查空间是否足够。
  • 同样需要考虑多线程并发访问的问题。

示例代码

static ssize_t my_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{char kernel_buf[128];if (count > sizeof(kernel_buf) - 1)count = sizeof(kernel_buf) - 1;if (copy_from_user(kernel_buf, buf, count))return -EFAULT;kernel_buf[count] = '\0';printk(KERN_INFO "Received from user: %s\n", kernel_buf);*pos += count;return count;
}

3. poll函数

原型

unsigned int poll(struct file *file, struct poll_table_struct *wait);

实现步骤

  1. 注册等待队列:使用poll_wait将设备的等待队列添加到wait
  2. 返回设备状态:返回一个位掩码,指示设备的可读性、可写性等状态。

注意事项

  • 确保等待队列正确唤醒。
  • 返回值可以是POLLIN(可读)、POLLOUT(可写)等。
  • 处理阻塞和非阻塞模式。

示例代码

static unsigned int my_poll(struct file *file, struct poll_table_struct *wait)
{unsigned int mask = 0;poll_wait(file, &my_wait_queue, wait);if (data_available)mask |= POLLIN | POLLRDNORM; // 可读if (space_available)mask |= POLLOUT | POLLWRNORM; // 可写return mask;
}

4. 完整代码示例

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/poll.h>
#include <linux/wait.h>
#include <linux/sched.h>#define DEVICE_NAME "my_device"
#define BUFFER_SIZE 128static int major;
static char device_buffer[BUFFER_SIZE];
static size_t buffer_len = 0;static wait_queue_head_t my_wait_queue; // 声明等待队列
static int data_available = 0;         // 标志:是否有数据可供读取// read 实现
static ssize_t my_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{if (buffer_len == 0) {if (file->f_flags & O_NONBLOCK)return -EAGAIN;// 等待数据可用wait_event_interruptible(my_wait_queue, data_available);if (buffer_len == 0) // 防止被中断唤醒但没有数据return -EINTR;}if (count > buffer_len)count = buffer_len;if (copy_to_user(buf, device_buffer, count))return -EFAULT;buffer_len = 0; // 数据被读取后清空缓冲区data_available = 0;return count;
}// write 实现
static ssize_t my_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{if (count > BUFFER_SIZE - 1)count = BUFFER_SIZE - 1;if (copy_from_user(device_buffer, buf, count))return -EFAULT;device_buffer[count] = '\0';buffer_len = count;// 数据写入后唤醒等待的进程data_available = 1;wake_up_interruptible(&my_wait_queue);return count;
}// poll 实现
static unsigned int my_poll(struct file *file, struct poll_table_struct *wait)
{unsigned int mask = 0;poll_wait(file, &my_wait_queue, wait);if (buffer_len > 0)mask |= POLLIN | POLLRDNORM; // 数据可读if (buffer_len < BUFFER_SIZE)mask |= POLLOUT | POLLWRNORM; // 可写入更多数据return mask;
}// 文件操作结构
static struct file_operations my_fops = {.owner = THIS_MODULE,.read = my_read,.write = my_write,.poll = my_poll,
};// 模块初始化
static int __init my_init(void)
{major = register_chrdev(0, DEVICE_NAME, &my_fops);if (major < 0) {printk(KERN_ALERT "Failed to register character device\n");return major;}init_waitqueue_head(&my_wait_queue); // 初始化等待队列printk(KERN_INFO "Device registered with major number %d\n", major);return 0;
}// 模块退出
static void __exit my_exit(void)
{unregister_chrdev(major, DEVICE_NAME);printk(KERN_INFO "Device unregistered\n");
}module_init(my_init);
module_exit(my_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple character device with wait queue.");

相关文章:

Linux内核 -- 字符设备之read write poll基本实现

Linux字符设备&#xff1a;read、write和poll函数实现及完整代码 1. read函数 原型 ssize_t read(struct file *file, char __user *buf, size_t count, loff_t *pos);实现步骤 检查用户缓冲区&#xff1a;使用copy_to_user将数据从内核空间复制到用户空间。返回已读取的字…...

腾讯微信C++面试题及参考答案

64 匹马 8 个赛道,找出前四名,最少赛多少场 为了找出64匹马中的前四名,我们可以按照以下步骤来组织比赛,尽量减少所需要的比赛次数: 初步分组: 将64匹马分成8组,每组8匹马。分别为A、B、C、D、E、F、G、H这8组。每组进行一次比赛来确定各组的速度排序。每个组比赛一次总…...

如何查看内网设备访问互联网时的出口 IP 地址?

在企业VPC中我们通常是一个机房公用一个公网IP&#xff0c;也就是所有的设备共用同一个出口IP。 那么如何查看如何查看内网设备访问互联网时的出口 IP 地址呢&#xff1f; 要查看一台 Linux 内网设备访问互联网时的出口 IP 地址&#xff0c;可以使用以下几种方法&#xff1a;…...

ESP32-S3模组上跑通ES8388(24)

接前一篇文章:ESP32-S3模组上跑通ES8388(23) 二、利用ESP-ADF操作ES8388 2. 详细解析 上一回解析完了es8388_init函数中的第8段代码,本回继续往下解析。为了便于理解和回顾,再次贴出es8388_init函数源码,在components\audio_hal\driver\es8388\es8388.c中,如下: ​ …...

【AIGC系列】frequency_penalty如何通过控制参数提升文本生成的多样性与创造性

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

Python+OpenCV系列:图像的运算

文章目录 PythonOpenCV系列&#xff1a;图像的加权和、覆盖1. 图像加权和&#xff08;加权融合&#xff09;2. 图像覆盖&#xff08;区域叠加&#xff09;3. 应用场景4. 总结 PythonOpenCV系列&#xff1a;图像的加权和、覆盖 在图像处理中&#xff0c;图像的加权和与覆盖是两…...

【Unity技巧】Unity项目中哪些文件不用管理(.gitignore)

Unity的项目编译后一般都比较大&#xff0c;动辙几个G。这里面一般我们只需要把Assets, Packages, ProjectSettings这三个文件夹进行源代码管理就可以&#xff0c;其他文件就可以通过下面的.gitignore来忽略掉。 .gitignore文件的内容如下&#xff1a; # 将此 .gitignore 文件…...

ansible 自动化运维工具(三)playbook剧本

目录 Playbook的定义 Playbook组成 Playbook命令 Playbook剧本编写格式 基本组件 Handlers处理器 tags标签 Facts组件 Register&#xff1a;注册变量 Debug模块 Playbook循环 With_items循环 With_dict循环&#xff08;字典循环&#xff09; With_nested循环&…...

图论【Lecode_HOT100】

文章目录 1.岛屿数量No.2002.腐烂的橘子No.9943.课程表No.2074.实现Trie&#xff08;前缀树&#xff09;No.208 1.岛屿数量No.200 class Solution {public int numIslands(char[][] grid) {if (grid null || grid.length 0) {return 0;}int numIslands 0;int rows grid.len…...

day10性能测试(2)——Jmeter

【没有所谓的运气&#x1f36c;&#xff0c;只有绝对的努力✊】 目录 1、LoadRunner vs Jmeter 1.1 LoadRunner 1.2 Jmeter 1.3 对比小结 2、Jmeter 环境安装 2.1 安装jdk 2.2 安装Jmeter 2.3 小结 3、Jmeter 文件目录结构 4、Jmeter默认配置修改 5、Jmeter元件、组…...

Y3编辑器文档4:触发器

文章目录 一、触发器简介1.1 触发器界面1.2 ECA语句编辑及快捷键1.3 参数设置1.4 变量设置1.5 实体触发器1.6 函数库与触发器复用 二、触发器的多层结构2.1 子触发器&#xff08;在游戏内对新的事件进行注册&#xff09;2.2 触发器变量作用域2.3 复合条件2.4 循环2.5 计时器2.6…...

1. 机器学习基本知识(3)——机器学习的主要挑战

1.5 机器学习的主要挑战 1.5.1 训练数据不足 对于复杂问题而言&#xff0c;数据比算法更重要但中小型数据集仍然很普遍&#xff0c;获得额外的训练数据并不总是一件轻而易举或物美价廉的事情&#xff0c;所以暂时不要抛弃算法。 1.5.2 训练数据不具有代表性 采样偏差&#…...

prometheusgrafana实现监控告警

Prometheus负责集群数据的监控和采集&#xff0c;然后传递给grafana进行可视化&#xff0c;集成睿象云可实现监控报警&#xff0c;为了方便操作&#xff0c;可以通过iframe嵌套grafana到指定的页面。 文章目录 1.Grafana集成Prometheus2.iframe内嵌grafana3.监控告警 1.Grafana…...

Ubuntu防火墙管理(五)——ufw源规则解读与修改

firewalld与nftables 在 /etc/firewalld/firewalld.conf 文件中&#xff0c;FirewallBackend 选项用于指定 Firewalld 使用的防火墙后端实现。具体来说&#xff1a; nftables&#xff1a;这是当前的默认选项&#xff0c;表示 Firewalld 将使用 nftables 作为防火墙后端。nftab…...

Docker如何运行一个python脚本Hello World

Docker如何运行一个python脚本Hello World 1、编写Python的Hello World&#xff1a;script.py #!/usr/bin/python #_*_coding:utf-8_*_ print("Hello World") 2、Dockerfile文件 #拉取Docker环境 FROM python #设置工作目录 WORKDIR /app #将dockerfile同级文件copy到…...

人工智能-自动驾驶领域

目录 引言自动驾驶与人工智能的结合为什么自动驾驶领域适合发表文章博雅智信的自动驾驶辅导服务结语 引言 自动驾驶技术的崛起是当代交通行业的一场革命。通过结合先进的人工智能算法、传感器技术与计算机视觉&#xff0c;自动驾驶不仅推动了技术的进步&#xff0c;也使得未来…...

[ubuntu18.04]ubuntu18.04安装json-c操作说明

ubuntu18.04安装json-c 代码下载 rootw1804-virtual-machine:/home/w1804/tr069# git clone https://github.com/json-c/json-c.git Cloning into /opt/git/json-c... remote: Enumerating objects: 6398, done. remote: Counting objects: 100% (1067/1067), done. remote:…...

华为eNSP:VRRP

一、VRRP背景概述 在现代网络环境中&#xff0c;主机通常通过默认网关进行网络通信。当默认网关出现故障时&#xff0c;网络通信会中断&#xff0c;影响业务连续性和稳定性。为了提高网络的可靠性和冗余性&#xff0c;采用虚拟路由冗余协议&#xff08;VRRP&#xff09;是一种…...

Linux--top系统资源命令查看--详解

top命令用法 图&#xff1a; top命令用法&#xff1a; top命令经常用来监控linux的系统状况&#xff0c;是常用的性能分析工具&#xff0c;能够实时显示系统中各个进程的资源占用情况。 top的使用方式&#xff1a; top [-d number] | top [-bnp] top参数解释&#xff1a; -…...

es的join是什么数据类型

在 Elasticsearch 中,parent 并不是一个独立的数据类型,而是与 join 数据类型一起使用的一个概念。join 数据类型用于在同一个索引中建立父子文档之间的关系,允许你在一个索引内表示层级结构或关联关系。通过 join 字段,你可以定义不同类型的文档(如父文档和子文档),并指…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施&#xff0c;由雇主和个人按一定比例缴纳保险费&#xff0c;建立社会医疗保险基金&#xff0c;支付雇员医疗费用的一种医疗保险制度&#xff0c; 它是促进社会文明和进步的…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...