Chapter 31: Interrupt, DMA, Device Driver, Hardware Event
手机系统里的硬件事件通常先表现为一个用户可见现象:手指落到屏幕上,应用收到一次点击;相机传感器输出一帧,预览画面刷新;耳机播放声音,音频周期稳定推进;网络控制器收到数据包,应用 socket 变为可读。本章要追踪这类现象从硬件到 App 的转换路径,定位每一层负责什么、保存什么状态、把什么结果交给下一层。
硬件事件进入系统时,Kernel 先承担“事件接收者”和“资源所有者”的角色。设备通过 interrupt 通知 CPU 有状态变化,driver 在受限时间内确认事件来源,随后把耗时处理移动到延迟执行路径。高速数据通常通过 DMA 写入内存,driver 负责建立 DMA 地址、buffer 所有权和 completion 语义。上层系统服务接收 driver 暴露的事件或 buffer,再转换成 framework callback、队列消息、输入事件、媒体帧或网络可读状态。
贯穿本章的主线是一条触摸事件路径:触控控制器采样到手指位置,触发 interrupt,触摸 driver 读取控制器状态,把坐标转换成 input event,系统输入服务分发给前台窗口,应用回调最终收到 ACTION_DOWN 或等价事件。相机、音频和网络路径会在后续小节中作为对照:它们共享 interrupt、driver、buffer、service 的责任链,但在数据量、时延、DMA 和上层调度方式上有不同取舍。
本章的结论是:硬件事件进入 App 的路径是一组跨层状态交接,单纯函数调用链只能覆盖其中很小一段。interrupt 只负责把事件带入 Kernel;driver 负责把硬件状态翻译成内核对象;DMA 负责大块数据搬运;system service 负责权限、生命周期、路由和用户可见语义;framework 负责把系统结果转成 App 能处理的事件。
31.1 Interrupt 作为硬件事件进入 Kernel 的机制
Interrupt 是硬件向 Kernel 报告“某个设备状态发生变化”的入口机制。CPU 正在执行应用线程、系统服务线程或 idle 代码时,外设控制器可以通过中断控制器提交一个 IRQ。架构相关入口代码保存最小执行现场,进入 Kernel 的 IRQ 分发路径,再根据 IRQ 编号找到对应描述符、flow handler 和 driver 注册的处理函数。Linux 的 generic IRQ handling 把 driver API、IRQ flow handler 和 chip-level 硬件封装分成三层,这个分层说明了中断处理同时涉及 driver 逻辑、IRQ 触发语义和中断控制器细节。
从触摸事件看,触控控制器按固定采样周期或状态变化产生中断。这个中断本身携带的信息很少,通常只表示“控制器里有新状态可读”。driver 需要通过 I2C、SPI 或 memory-mapped register 读取坐标、压力、触点数量和设备状态。Kernel 此时看到的是一个 IRQ 编号和一个 device driver,上层 input service 还没有收到用户手势,App 也无法直接知道这次硬件事件。
这条路径可以按输入、处理、输出拆开:输入是设备电信号或控制器状态变化;处理是中断控制器路由、Kernel IRQ 分发、driver handler 执行;输出是 driver 更新内核对象、唤醒等待队列、提交延迟任务或标记 buffer 完成。Kernel 接收 interrupt 后,第一目标是快速确认事件和维持系统响应,完整业务语义会交给后续层完成。
中断进入 Kernel 后还会受到电源状态影响。屏幕关闭、SoC 进入低功耗状态、外设 runtime suspend 时,只有被配置成 wakeup source 的中断才会把系统唤醒。触摸唤醒、充电状态变化、modem 通知和某些传感器事件会被纳入电源策略;普通后台数据到达可能被批处理、合并或推迟。由此可以得到一个判断顺序:先判断该事件是否能触发 IRQ,再判断该 IRQ 是否被 driver 注册,再判断当前电源状态是否允许它唤醒或进入普通处理路径。
这张图只表达事件上行的主干。真实系统还会加入电源域、锁、队列、权限、进程调度和错误码。阅读移动 OS 的硬件事件路径时,先把这条主干固定下来,再补充平台差异和设备差异,能减少术语干扰。
31.2 Interrupt Handler、Bottom Half 与延迟处理
Interrupt handler 的工作边界由时间约束决定。硬中断上下文适合完成确认事件、清除中断状态、读取少量寄存器、保存必要状态和安排后续工作。复杂解析、内存分配、用户态通知、长时间持锁和跨进程通信会放大中断关停时间或调度延迟,所以常被移动到 bottom half、softirq、workqueue、threaded IRQ 或设备子系统自己的 worker 中。
Linux 文档中的 request_irq() 与 request_threaded_irq() 体现了这个边界:driver 可以注册普通中断处理,也可以把主要处理放到 IRQ thread。threaded IRQ 让处理逻辑运行在可调度上下文中,适合 I2C 读取、睡眠等待、复杂状态机和较长的设备访问。移动设备上,触摸、传感器、充电、音频 codec 等慢速外设经常需要这种拆分,因为硬件总线访问本身可能需要等待。
Bottom half 是一类延迟处理责任的统称。网络收包路径常用 softirq/NAPI 把大量包处理从逐包 interrupt 转为批量 poll;块设备和存储控制器会在 completion 后推动请求队列;音频驱动会在周期中断后唤醒音频线程或更新 ring buffer 指针;触摸 driver 会把坐标报告给 input subsystem,再由用户态输入分发服务继续处理。不同子系统的名称不同,但共同目标是把“确认硬件事件”和“处理系统语义”拆成两个阶段。
触摸路径中,硬中断 handler 可能只做三件事:确认触摸控制器产生了中断,屏蔽或清除设备状态位,调度 threaded handler。threaded handler 再从控制器读取坐标数据,调用 input subsystem 的接口报告事件。这个输入事件进入内核输入队列后,用户态输入服务从 device node 读取,结合窗口焦点、显示旋转、触摸区域、手势策略和安全限制决定交给哪个 App。
延迟处理也带来失败边界。硬中断丢失可能表现为设备失联或事件断续;延迟处理排队过长会表现为触摸迟滞、音频 underrun、网络吞吐下降或相机帧延迟;worker 卡住会导致系统服务长时间收不到事件。排查时应按顺序定位:IRQ 是否增长、driver 是否确认 completion、延迟队列是否积压、用户态服务是否及时读取、App 主线程是否按时消费 callback。
31.3 DMA 与高速数据传输路径
DMA(Direct Memory Access)是设备在 CPU 逐字节搬运之外直接访问内存的传输机制。触摸坐标这类小数据可以通过寄存器读取完成;相机 frame、音频 PCM、网络 packet、存储块和 GPU buffer 这类高频大块数据需要 DMA。DMA 的核心问题是地址、权限、缓存一致性和 buffer 所有权:设备看到的是 bus address 或 DMA address,CPU 看到的是虚拟地址,IOMMU 可能在二者之间建立映射。
Linux 的 Dynamic DMA mapping Guide 明确区分 CPU virtual address、CPU physical address 和 bus address。driver 使用 dma_map_single()、dma_map_sg() 等接口把 CPU 可访问的内存映射成设备可使用的 DMA 地址,再把这个地址写入设备寄存器或队列描述符。传输完成后,driver 根据方向和 buffer 生命周期执行 unmap 或同步操作,随后把 completion 上报给子系统。
手机相机路径是 DMA 的典型场景。Camera driver 或底层 ISP 相关 driver 准备一组 frame buffer,把 buffer 描述符交给硬件队列。传感器和 ISP 产生一帧后,硬件通过 DMA 写入 buffer,再触发 completion interrupt。driver 收到 completion 后更新 buffer 状态,HAL 或 camera daemon 取到可用 frame,系统相机服务再把它交给预览、编码、拍照或机器视觉 pipeline。App 看到的是 preview callback、image reader buffer 或 capture result,底层的 DMA 地址和 IOMMU 映射仍由系统持有。
DMA 需要明确 buffer ownership。buffer 归设备所有时,CPU 应按平台 DMA API 的同步规则访问;buffer 归 CPU 或上层服务所有时,设备需要停止写入或使用下一块 buffer。scatter-gather 进一步允许一个逻辑传输由多个物理片段组成,driver 把 scatterlist 映射给设备,硬件按描述符链访问内存。这个设计让大 buffer 和碎片化内存可以一起工作,但 completion、unmap 和错误恢复必须严格配对。
DMA 失败在用户侧通常表现为相机黑帧、预览卡顿、音频爆音、网络丢包、存储 I/O 错误或系统重启。复盘时应把现象放回 buffer 路径:buffer 是否成功分配,DMA 地址是否建立,设备是否获得队列描述符,completion interrupt 是否返回,cache 同步是否匹配,system service 是否按约定释放或复用 buffer。
31.4 Device Driver 对硬件寄存器、Buffer、Queue 的控制
Device driver 是把硬件控制面转换成 Kernel 可管理对象的第一层软件。它通过寄存器配置设备模式,通过 IRQ 注册接收硬件事件,通过 DMA API 和内存管理组织 buffer,通过队列把请求提交给设备,通过 completion 或错误中断回收结果。应用看到的是触摸、相机、音频、网络能力;driver 看到的是寄存器位、状态机、ring buffer、descriptor queue 和硬件超时。
寄存器控制承担配置责任。触摸 driver 可能写采样率、手势唤醒开关和中断模式;音频 codec driver 可能写采样率、增益、路由和电源状态;网络 driver 可能写 RX/TX ring 地址、队列长度和中断合并参数;相机相关 driver 可能配置 MIPI、CSI、ISP 或 vendor-specific 控制寄存器。寄存器写入的效果会受到设备电源状态、时钟、reset 状态和 firmware 协议影响。
Buffer 控制承担数据所有权责任。driver 要知道哪个 buffer 已经交给硬件、哪个 buffer 完成、哪个 buffer 交给上层、哪个 buffer 可以复用。音频路径中,ring buffer 的读写指针决定播放或录音周期;网络路径中,RX ring 持有可接收 packet 的 buffer;相机路径中,多帧 buffer 支撑 pipeline 延迟和并行处理。buffer ownership 错误会表现为旧帧重复、数据损坏、音频断续或 packet 内容异常。
Queue 控制承担并发和顺序责任。高速设备通常用 descriptor queue 或 ring 让硬件连续工作。driver 提交请求时把 buffer 地址、长度、方向和控制位写入队列,更新 doorbell 或 producer index,设备处理完成后更新 consumer index 或生成 completion。队列深度越大,吞吐越稳定;队列越深,延迟和取消成本也会上升。移动系统会在交互延迟、功耗、吞吐和内存占用之间选择队列策略。
下面的简化伪代码只展示 driver 如何把硬件事件拆成“确认中断”和“处理状态”两个阶段。它不对应某个真实设备,目的在于固定责任边界。
irqreturn_t touch_irq_handler(int irq, void *data)
{
struct touch_device *dev = data;
ack_device_irq(dev);
schedule_work(&dev->report_work);
return IRQ_HANDLED;
}
void touch_report_work(struct work_struct *work)
{
struct touch_device *dev = container_of(work, struct touch_device, report_work);
struct touch_sample sample;
read_touch_controller(dev, &sample);
report_input_position(dev->input, sample.x, sample.y, sample.pressure);
input_sync(dev->input);
}
这个例子说明 driver 的输出是内核输入子系统可以理解的事件,App 事件还需要继续经过系统分发。App 事件需要系统输入服务、窗口管理、framework 分发和应用线程共同完成。相同思路也适用于 camera buffer completion、audio period elapsed 和 network RX completion:driver 交付的是子系统对象,上层再把它转换成平台语义。
31.5 Touch、Camera、Audio、Network 的硬件事件路径
Touch、Camera、Audio、Network 都通过硬件事件进入系统,但它们的主数据形态不同。Touch 以低带宽坐标事件为主,Camera 以高带宽 frame buffer 为主,Audio 以固定周期 ring buffer 为主,Network 以 packet 队列和协议栈为主。比较这些路径时,应使用同一组维度:硬件触发、driver 输入、数据搬运、内核对象、system service 交接和 App 可见结果。
| 路径 | 硬件触发 | Driver 主要工作 | 数据形态 | 上层交接 | App 可见结果 |
|---|---|---|---|---|---|
| Touch | 触控控制器中断 | 读取坐标,报告 input event | 小事件 | 输入服务读取 device node | 点击、滑动、手势回调 |
| Camera | frame completion interrupt | 管理 sensor/ISP 状态和 frame buffer | DMA frame | HAL、camera service、buffer queue | 预览帧、拍照结果、录像数据 |
| Audio | period interrupt 或 DMA completion | 更新 ring buffer 指针,唤醒音频线程 | PCM buffer | audio HAL、audio server、mixer | 播放连续性、录音 callback |
| Network | RX/TX interrupt 或 NAPI poll | 回收 descriptor,提交 packet 给协议栈 | packet buffer | network stack、socket、connectivity service | socket 可读、请求完成、网络变化 |
触摸路径重视低延迟和分发准确性。硬件采样后,driver 把坐标事件交给输入子系统;用户态输入服务结合 display viewport、窗口焦点、触摸目标、手势策略和安全策略,把事件发送给对应应用。失败表现通常是触摸迟滞、误触、断触或前台应用收不到事件。排查证据会围绕 IRQ、input event、输入服务队列、窗口焦点和 App 主线程展开。
相机路径重视吞吐、buffer 生命周期和权限控制。硬件事件通常只是 frame done,真正的数据已经通过 DMA 写入 buffer。camera HAL 或 daemon 需要把 frame metadata、sensor timestamp、3A 状态、buffer handle 和 capture request 对齐。系统相机服务再检查调用者权限、前后台状态、独占访问和多路输出约束。App 看到的 preview 卡顿,可能来自传感器、ISP、DMA buffer、HAL、camera service、图形合成或 App 消费速度中的任一环。
音频路径重视稳定周期。播放时,上层音频服务和 HAL 提供 PCM 数据,driver 或硬件按 period 消耗 buffer;录音时,硬件按 period 填充 buffer,driver 通知上层读取。interrupt 过密会增加 CPU 开销,period 过大又会增加延迟。音频系统通常把低延迟、功耗和稳定性放在同一组约束中处理。用户听到爆音或断续时,可能是 buffer underrun、调度延迟、音频路由切换或 driver completion 异常。
网络路径重视批处理和协议栈协作。高速网络设备如果每个 packet 都触发一次 interrupt,CPU 会被频繁打断。Linux 网络路径常通过 NAPI 思路在高负载下切换到 poll,批量处理 RX ring 中的 packet。移动系统还会叠加蜂窝省电、Wi-Fi 省电、后台传输限制和弱网策略。应用层的网络超时需要沿 socket、协议栈、driver queue、modem/Wi-Fi firmware 和连接策略逐层判断,单看硬件事件证据覆盖不了完整原因。
31.6 Driver 与 System Service 之间的数据交接
Driver 与 system service 之间的交接把内核对象转换成平台能力状态。交接方式可以是 device node 读取、ioctl() 控制、netlink 事件、shared memory、HAL callback、daemon event、Binder/XPC 消息或框架内部队列。交接点决定了权限在哪里检查、buffer 生命周期由谁持有、错误码如何返回、后台策略在哪里生效。
Android 的典型结构是 Kernel driver 暴露设备节点或子系统接口,vendor HAL 或 native daemon 持有硬件细节,system_server 中的 system service 持有平台能力状态,framework API 给 App 提供受控入口。Android 官方 HAL overview 将 HAL 定位为硬件相关实现与系统框架之间的接口,并说明 Android 13 起 HIDL 已弃用,HAL 新实现应使用 AIDL,旧 HIDL HAL 仍被支持。这个版本边界影响移动 OS 阅读:同一类硬件事件在不同 Android 版本和设备上可能经由 AIDL HAL、HIDL HAL、legacy HAL 或 vendor daemon。
Apple 平台的公开可验证边界更窄。公开文档可以确认 DriverKit、IOKit、XPC、daemon、entitlement 和 sandbox 是设备访问控制的重要组成部分;具体设备路径、私有 daemon 名称和内部消息协议通常属于平台私有实现。写作和排查时应把 Apple 路径表述为公开组件与平台行为的组合:driver 或 driver extension 管理设备,系统 daemon 持有能力状态,framework 通过受控 API 和 entitlement 把结果交给 App。
交接方式可以按控制面和数据面拆开。控制面负责配置:打开相机 session、选择音频路由、设置网络接口、注册输入监听、设置采样率。数据面负责持续传输:相机 frame buffer、音频 ring buffer、网络 packet、传感器 batch、触摸事件队列。控制面通常经过权限和生命周期检查;数据面通常强调 buffer ownership、队列时序和 backpressure。
相机可以作为完整例子。App 请求打开 camera,framework 进入 camera service,service 检查权限、前台状态、设备占用和策略;HAL 配置 sensor/ISP pipeline;driver 准备 buffer 和硬件队列;frame completion 后,HAL 回调 camera service,service 把 buffer 和 metadata 交给 App 或图形系统。若 App 退到后台,权限和生命周期策略可能在 camera service 层关闭 session;若硬件超时,driver/HAL 返回设备错误;若 App 消费慢,buffer queue 产生 backpressure。每一种失败对应不同责任主体。
31.7 硬件事件到 App 可见事件的跨层转换
硬件事件变成 App 可见事件,需要完成三次转换。第一次转换发生在 Kernel 内:driver 把设备状态转换成内核对象,例如 input event、network packet、audio buffer completion 或 video buffer done。第二次转换发生在 system service 或 daemon:系统把内核对象和平台策略结合,形成输入分发、相机会话、音频路由、网络连接状态。第三次转换发生在 framework 和 runtime:系统把结果放进 App 的 callback、message queue、event loop、listener 或异步 future。
触摸事件的完整责任链可以这样复盘:硬件触控控制器报告状态变化;Kernel IRQ 路径调用触摸 driver;driver 读取坐标并报告 input event;用户态输入服务读取事件并结合显示、窗口和安全策略;framework 把事件投递到前台应用窗口;应用主线程从事件队列取出并触发 UI 回调。App 收到一次点击时,前面已经完成了中断、driver、输入服务、窗口分发和 runtime 调度。
这个转换链解释了移动系统为什么很少把原始设备直接交给应用。应用真正需要的是“用户点击了按钮”“相机返回了一帧”“音频播放可继续写入”“网络请求完成”这类能力结果。系统必须在中间层处理权限、隐私指示器、前后台生命周期、设备独占、电源状态、热限制和错误恢复。硬件事件只提供事实输入,平台服务负责把事实输入转换成可授权、可调度、可审计的用户体验。
跨层复盘时,建议按固定顺序提问。第一,硬件是否产生事件,证据是 IRQ、completion、timestamp 或设备状态。第二,driver 是否把事件转成内核对象,证据是 input event、buffer done、packet 入栈或 period 更新。第三,system service 是否接收并通过策略检查,证据是服务状态、权限状态、session 状态和错误码。第四,framework 是否把结果投递到目标 App,证据是 callback、event queue、window focus 或 socket readiness。第五,App 是否及时处理,证据是主线程负载、消费速度和生命周期状态。
Android 与 Apple 在命名、IPC、驱动边界和平台私有程度上不同,但责任链可以用同一组角色阅读:App 发出意图或等待事件;framework 提供公开能力表面;system service 或 daemon 持有平台状态;driver 持有设备状态;Kernel 持有中断、内存和调度资源;硬件产生事件或搬运数据。这个共同模型能帮助读者把平台术语放回系统路径,判断一个用户可见故障应该从哪一层开始定位。
最小自检任务
假设一个视频通话应用在前台运行时出现两种现象:屏幕触摸按钮有时延迟响应,同时摄像头预览偶尔卡住 1 到 2 帧。请分别写出触摸事件和相机预览从硬件到 App 的路径,标出 interrupt、driver、DMA、system service 和 framework 各自承担的责任,并说明至少三个可能失败点及其用户可见结果。
答案要点
触摸路径应从触控控制器中断开始,经 Kernel IRQ 分发进入触摸 driver。driver 读取坐标并向 input subsystem 报告 input event,用户态输入服务读取事件后结合窗口焦点、显示坐标和安全策略分发给前台应用,framework 再把事件放入 App 主线程事件队列。可能失败点包括 IRQ 处理或 threaded handler 延迟导致触摸迟滞,输入服务队列积压导致事件分发晚到,App 主线程繁忙导致按钮回调执行延迟。
相机路径应从 sensor/ISP frame completion 开始。硬件通过 DMA 把 frame 写入 buffer,并通过 interrupt 或 completion 通知 driver。driver 更新 buffer 状态,HAL 或 camera daemon 取得 frame 和 metadata,camera service 根据权限、前台状态、session、buffer queue 和多路输出策略把结果交给预览 pipeline,framework 或图形系统最终让 App 看到预览帧。可能失败点包括 DMA buffer 紧张导致 frame 复用延迟,driver/HAL completion 延迟导致帧晚到,camera service 或 App 消费速度不足导致 buffer queue backpressure。用户可见结果分别是预览卡顿、黑帧、帧时间不稳定或视频通话画面短暂停顿。
关键边界是:interrupt 只把事件带入 Kernel;driver 负责设备状态和内核对象;DMA 负责相机这类大块数据进入内存;system service 负责权限、生命周期、设备独占和队列策略;framework 负责把系统结果转换成 App 事件或 callback。判断时先确认硬件事件和 driver completion,再看系统服务策略,最后看 framework 分发和 App 消费速度。
本章知识点总结
- Interrupt 入口:硬件事件通过中断控制器进入 Kernel,IRQ 分发路径把事件交给对应 driver。
- 事件语义:中断通常只表示设备状态变化,完整用户语义由 driver、service 和 framework 逐层生成。
- 处理拆分:硬中断适合快速确认事件,耗时逻辑应移动到 threaded IRQ、workqueue、softirq 或子系统 worker。
- 延迟边界:触摸迟滞、音频断续和网络吞吐下降常来自延迟处理排队、worker 卡住或上层消费速度不足。
- DMA 地址:DMA 需要区分 CPU 虚拟地址、物理地址和设备可见地址,IOMMU 可能建立额外映射。
- Buffer 所有权:高速数据路径必须明确 buffer 在设备、driver、HAL、service 和 App 之间的归属和复用时机。
- Driver 职责:driver 控制寄存器、IRQ、DMA、buffer、queue 和 completion,把硬件状态转换成内核对象。
- Queue 取舍:设备队列提升吞吐和连续性,同时增加延迟、取消成本和内存占用。
- Touch 路径:触摸以低带宽事件为主,核心责任链是 IRQ、driver、input subsystem、输入服务、窗口分发和 App 事件队列。
- Camera 路径:相机以 DMA frame buffer 为主,camera service 和 HAL 需要同时处理权限、session、metadata 和 buffer queue。
- Audio 路径:音频以固定周期 ring buffer 为主,period、调度延迟和 buffer underrun 共同决定播放或录音稳定性。
- Network 路径:网络以 packet queue 和协议栈为主,高负载下会通过批处理降低 interrupt 开销。
- Service 交接:device node、ioctl、netlink、shared memory、HAL callback 和 daemon event 都是 driver 到系统服务的常见交接方式。
- 平台边界:Android 通常通过 driver、HAL、system service、framework 暴露硬件能力,Apple 需要区分公开组件和私有实现推断。
- 复盘顺序:先看硬件事件,再看 driver 对象,再看 system service 策略,再看 framework 投递,最后看 App 消费。