news 2026/6/26 7:19:03

input 设备 - kernel 和 应用数据 交互

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
input 设备 - kernel 和 应用数据 交互

我们先看些一个sensor report input 的 数据流程

[用户程序] ──ioctl(START)──> [内核态 驱动逻辑 sensor_enable]


[内核态 驱动逻辑 schedule_delayed_work] ──每隔30ms──> [report]

┌───────────┬───────────┬───────────┬───────┘
▼ ▼ ▼ ▼
[ABS_X] [ABS_Y] [ABS_Z] [SYN]
└───────────┴───────────┴───────────┘


[内核态 驱动逻辑------输入子系统队列]


[用户态程序 read()] <── 循环读取每个 event

┌──────────────────────────────────────────────────────┐
│ 1. 用户调用 ioctl(GSENSOR_IOCTL_START) │
└────────────────────┬───────────────────────────────┘


┌──────────────────────────────────────────────────────┐
│ 2. sensor_enable() → 启动定时任务 │
│ schedule_delayed_work(每 poll_delay_ms 执行) │
└────────────────────┬───────────────────────────────┘


┌──────────────────────────────────────────────────────┐
│ 3. sensor_delaywork_func() → report() │
│ └── 读取硬件数据 → input_report_abs() ×3 │
│ └── input_sync() → 帧结束 │
└────────────────────┬───────────────────────────────┘


┌──────────────────────────────────────────────────────┐
│ 4. 用户 read() → 获取 input_event │
│ └── 解析 ev.type/code/value │
└──────────────────────────────────────────────────────┘

核心流程(一句话)

用户通过 ioctl 启动传感器 → 内核定时读取硬件数据 → 通过 input_report_abs/input_sync 上报 → 用户 read() 获取数据

用户如何 获取数据的你 read eventX 这个设备

/* 打开传感器控制设备 */

gsensor_fd = open(GSENSOR_DEV, O_RDONLY);

/* 启动传感器 */

ioctl(gsensor_fd, GSENSOR_IOCTL_START)

input_fd = open(INPUT_DEV, O_RDONLY | O_NONBLOCK);

/* 主循环读取数据 */

while (running) {

ssize_t n = read(input_fd, &ev, sizeof(ev));

if (ev.type == EV_ABS) {

switch (ev.code) {

case ABS_X:

x = ev.value * SCALE; break;

case ABS_Y:

y = ev.value * SCALE; break;

case ABS_Z:

z = ev.value * SCALE; break;

}

} else if (ev.type == EV_SYN) {

// 收到syn 后,同步显示一次结果

printf("\r%.2f %.2f %.2f", x, y, z);

fflush(stdout);

}

}

------------------------------------------------------------------------------------

#include <linux/input.h>

struct input_event {
struct timeval time; // 时间戳(秒 + 微秒)
__u16 type; // 事件类型(如 EV_ABS、EV_SYN)
__u16 code; // 事件代码(如 ABS_X、ABS_Y)
__s32 value; // 事件值(传感器数据)
};

为什么每次读取都是固定大小

原因1:内核保证数据完整性

驱动调用input_report_abs()时,内核会将数据封装成完整的input_event结构:

// 驱动中的操作 input_report_abs(input_dev, ABS_X, 1234); // 生成一个 input_event input_sync(input_dev); // 生成另一个 input_event

每个事件都是独立的、完整的 24 字节数据块

原因2:输入子系统的队列机制

内核维护一个事件队列,每个队列元素都是完整的input_event

内核事件队列 ────────────── ┌─────────────────────────────┐ │ input_event #1 (24字节) │ ← read() 读取这里 ├─────────────────────────────┤ │ input_event #2 (24字节) │ ├─────────────────────────────┤ │ input_event #3 (24字节) │ └─────────────────────────────┘

原因3:用户程序的读取方式

struct input_event ev; ssize_t n = read(input_fd, &ev, sizeof(ev)); // 读取 24 字节 if (n == sizeof(ev)) { // 读取成功,ev 包含完整的事件数据 printf("type=%d, code=%d, value=%d\n", ev.type, ev.code, ev.value); }

每次读取恰好是一个完整的input_event结构体


数据读取的时序

驱动端 用户端 ─────── ────── │ │ │ input_report_abs(ABS_X, 1234) │ │ ↓ │ │ 写入队列: [time][EV_ABS][ABS_X][1234] │ │ │ input_report_abs(ABS_Y, -56) │ │ ↓ │ │ 写入队列: [time][EV_ABS][ABS_Y][-56] │ │ │ input_sync() │ │ ↓ │ │ 写入队列: [time][EV_SYN][0][0] │ │ │ read(&ev, 24) → ev = [time][EV_ABS][ABS_X][1234] │ │ │ read(&ev, 24) → ev = [time][EV_ABS][ABS_Y][-56] │ │ │ read(&ev, 24) → ev = [time][EV_SYN][0][0]

在你的代码中的应用

struct input_event ev; // 固定大小 24 字节 while (running) { ssize_t n = read(input_fd, &ev, sizeof(ev)); // 每次读取 24 字节 if (n != sizeof(ev)) continue; // 确保读取完整 // 解析固定格式的数据 if (ev.type == EV_ABS) { switch (ev.code) { case ABS_X: x = ev.value * SCALE; break; case ABS_Y: y = ev.value * SCALE; break; case ABS_Z: z = ev.value * SCALE; break; } } }

总结

是的!struct input_event是固定大小的结构体(24字节),每次读取都是按照固定格式。

这是 Linux 输入子系统的设计原则:

  1. 统一格式:所有输入设备(键盘、鼠标、传感器)都使用相同的input_event结构
  2. 固定大小:保证数据的完整性和可预测性
  3. 顺序读取:用户程序按顺序读取每个完整的事件

这就是为什么你可以直接用read(input_fd, &ev, sizeof(ev))来读取传感器数据!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/26 7:18:45

迅雷资源搜索教程:从入门到高效使用

写在前面 迅雷作为国内老牌下载工具&#xff0c;很多人只用到了它的下载功能&#xff0c;其实迅雷自带的资源搜索能力被严重低估了。这篇文章从一个普通用户的角度&#xff0c;分享一下我日常使用迅雷搜索资源的方法和技巧。一、准备工作 首先确保手机上安装了最新版的 迅雷App…

作者头像 李华
网站建设 2026/6/26 7:17:24

【软工方法论34】微服务拆分策略与边界确定

【软工方法论34】微服务拆分策略与边界确定 微服务拆分策略与边界确定 你有没有遇到过这种情况? 微服务拆分后: 改一个功能要改5个服务 服务之间循环依赖 数据一致性难保证 团队之间天天扯皮 微服务拆分是个技术活,拆不好比不拆还糟糕。 一、什么时候需要微服务? 1. 微…

作者头像 李华
网站建设 2026/6/26 7:16:26

当线粒体基因组遇到PacBio HiFi数据:MitoHiFi的完美解决方案

当线粒体基因组遇到PacBio HiFi数据&#xff1a;MitoHiFi的完美解决方案 【免费下载链接】MitoHiFi Find, circularise and annotate mitogenome from PacBio assemblies 项目地址: https://gitcode.com/gh_mirrors/mi/MitoHiFi 你是否曾为线粒体基因组组装而烦恼&#…

作者头像 李华
网站建设 2026/6/26 7:14:36

计算机毕业设计之基于SSM的流浪动物领养救助平台的设计与实现

随着流浪动物领养救助的推进&#xff0c;该系统成为促进流浪动物领养救助发展的重要工具。为此开发了流浪动物领养救助平台&#xff0c;以满足该用户的需求。本研究构建了一个基于JSP和SSM技术的流浪动物领养救助平台&#xff0c;该系统与MySQL数据库紧密集成&#xff0c;以实现…

作者头像 李华
网站建设 2026/6/26 7:14:23

OpenAI收购Ona:Codex持久化沙箱如何改变Agent开发范式

2026年6月11日&#xff0c;OpenAI 在估值 8520 亿美元的 IPO 前夜投下了一颗重磅炸弹——正式宣布收购 Ona&#xff08;前身为 Gitpod&#xff09;。同一天&#xff0c;OpenAI 还宣布了与 Oracle OCI 的云基础设施合作。如果把这颗棋子和前一天 Oracle 合作的消息放在一起看&am…

作者头像 李华