当前位置: 首页 > 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 字段,你可以定义不同类型的文档(如父文档和子文档),并指…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

测试微信模版消息推送

进入“开发接口管理”--“公众平台测试账号”&#xff0c;无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息&#xff1a; 关注测试号&#xff1a;扫二维码关注测试号。 发送模版消息&#xff1a; import requests da…...

Leetcode 3577. Count the Number of Computer Unlocking Permutations

Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接&#xff1a;3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯&#xff0c;要想要能够将所有的电脑解锁&#x…...

测试markdown--肇兴

day1&#xff1a; 1、去程&#xff1a;7:04 --11:32高铁 高铁右转上售票大厅2楼&#xff0c;穿过候车厅下一楼&#xff0c;上大巴车 &#xffe5;10/人 **2、到达&#xff1a;**12点多到达寨子&#xff0c;买门票&#xff0c;美团/抖音&#xff1a;&#xffe5;78人 3、中饭&a…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

微信小程序云开发平台MySQL的连接方式

注&#xff1a;微信小程序云开发平台指的是腾讯云开发 先给结论&#xff1a;微信小程序云开发平台的MySQL&#xff0c;无法通过获取数据库连接信息的方式进行连接&#xff0c;连接只能通过云开发的SDK连接&#xff0c;具体要参考官方文档&#xff1a; 为什么&#xff1f; 因为…...