In continuation of the previous text 第10章:中断处理-8: Enabling and Disabling Interrupts, let's GO ahead.
Top and Bottom Halves
One of the main problems with interrupt handling is how to perform lengthy tasks within a handler. Often a substantial amount of workmust be done in response to a device interrupt, but interrupt handlers need to finish up quickly and not keep inter rupts blocked for long. These two needs (work and speed) conflict with each other, leaving the driver writer in a bit of a bind.
中断处理的核心问题之一,是如何在处理程序中执行耗时任务。设备中断往往需要完成大量工作,但中断处理程序又必须快速结束,不能长时间阻塞中断。这两种需求(完成工作与保证速度)相互冲突,给驱动开发者带来了困扰。
Linux (along with many other systems) resolves this problem by splitting the inter rupt handler into two halves. The so-called top half is the routine that actually responds to the interrupt—the one you register with request_irq. The bottom half is a routine that is scheduled by the top half to be executed later, at a safer time. The big difference between the top-half handler and the bottom half is that all interrupts are enabled during execution of the bottom half—that’s why it runs at a safer time. In the typical scenario, the top half saves device data to a device-specific buffer, sched ules its bottom half, and exits: this operation is very fast. The bottom half then per forms whatever other work is required, such as awakening processes, starting up another I/O operation, and so on. This setup permits the top half to service a new interrupt while the bottom half is still working.
Linux(以及许多其他系统)通过将中断处理程序拆分为两个部分来解决此问题:所谓的 “上半部” 是实际响应中断的程序 —— 即通过 request_irq 注册的处理程序;“下半部” 是由上半部调度、在更安全的时机执行的程序。上半部与下半部的核心区别是:下半部执行期间,所有中断均处于启用状态 —— 这也是它 “更安全” 的原因。
典型场景中,上半部仅完成三项快速操作:将设备数据保存到设备专属缓冲区、调度下半部执行、退出;而唤醒进程、启动下一次 I/O 操作等其他工作,均由下半部完成。这种设计允许下半部工作时,上半部仍能响应新的中断。
Almost every serious interrupt handler is split this way. For instance, when a net workinterface reports the arrival of a new packet, the handler just retrieves the data and pushes it up to the protocol layer; actual processing of the packet is performed in a bottom half.
几乎所有复杂的中断处理程序都采用这种拆分方式。例如,当网络接口报告新数据包到达时,上半部仅接收数据并推送至协议层,数据包的实际处理则交给下半部完成。
The Linux kernel has two different mechanisms that may be used to implement bot tom-half processing, both of which were introduced in Chapter 7. Tasklets are often the preferred mechanism for bottom-half processing; they are very fast, but all tasklet code must be atomic. The alternative to tasklets is workqueues, which may have a higher latency but that are allowed to sleep.
Linux 内核提供两种实现下半部处理的机制(均在第 7 章介绍):
-
Tasklet(小任务):通常是下半部处理的首选,执行速度极快,但所有 tasklet 代码必须是原子操作(不可睡眠);
-
Workqueue(工作队列):延迟可能更高,但允许在执行过程中睡眠,适合处理需睡眠的耗时任务。
The following discussion works, once again, with the short driver. When loaded with a module option, short can be told to do interrupt processing in a top/bottom-half mode with either a tasklet or workqueue handler. In this case, the top half executes quickly; it simply remembers the current time and schedules the bottom half pro cessing. The bottom half is then charged with encoding this time and awakening any user processes that may be waiting for data.
以下讨论仍以 short 驱动为例:加载模块时指定参数,可让 short 以 “上半部 + 下半部” 模式工作,下半部既可用 tasklet 实现,也可用 workqueue 实现。此时上半部执行速度极快,仅记录当前时间并调度下半部;编码时间戳、唤醒等待数据的用户进程等工作,均由下半部负责。
补充说明:
-
上半部的核心原则:快进快出
上半部必须极简,仅完成 “无法延迟” 的核心操作:如清除硬件中断标志、读取设备寄存器数据、保存关键状态。任何可延迟的工作(如数据处理、用户进程交互)都必须交给下半部,避免阻塞中断。
-
Tasklet 与 Workqueue 的核心差异
特性
Tasklet
Workqueue
执行上下文
软中断上下文(不可调度)
内核线程上下文(可调度)
睡眠允许
不允许(原子操作)
允许(可调用睡眠函数)
执行速度
极快(适合短任务)
较慢(适合长任务)
适用场景
无睡眠需求的快速处理
需睡眠的耗时处理(如内存分配、同步等待)
-
下半部调度的时机
下半部由上半部通过特定函数调度(如
tasklet_schedule调度 tasklet、queue_work调度 workqueue),实际执行时机由内核决定:通常在中断处理完成后、返回用户空间前,或内核空闲时(如软中断处理阶段)。 -
避免下半部滥用
若中断处理逻辑极简单(如仅记录时间戳),无需拆分 —— 过度拆分反而增加内核调度开销;仅当中断处理包含耗时操作(如大量数据拷贝、复杂计算)时,才需要拆分上下半部。
1891

被折叠的 条评论
为什么被折叠?



