中断上半部和中断下半部的介绍
在Linux内核中,中断处理机制被设计成“中断上半部(Top Half)”和“中断下半部(Bottom Half)”两个部分,这种设计主要目的是提高系统的中断响应效率,同时减少中断处理对内核其他操作的影响。
中断上半部(Top Half)
-
概念:中断上半部是指中断发生后,硬件触发的中断处理程序(ISR, Interrupt Service Routine)。
-
特点:
- 运行在中断上下文中,不能睡眠。
- 优先级高,必须尽快完成,以便处理其他可能的中断。
- 通常只完成最必要的工作,例如清除中断源、读取硬件状态等。
- 阻塞所有同级或更低优先级的中断。
-
局限性:由于不能睡眠且运行时间要求短,复杂的工作不能放在上半部。
中断下半部(Bottom Half)
-
概念:中断下半部通常是在中段上半部中指定的任务,当中断上半部完成后,会执行在中断下半部任务,这些任务没有中断上半部那样紧急。它运行在普通进程上下文中,可以执行相对复杂的逻辑。
-
特点:
- 在合适的时机由内核调度运行,不会阻塞中断。
- 可以睡眠,因为运行在普通的进程上下文中。
- 通常处理较为复杂的任务,例如数据处理、通知用户空间等。
-
实现方式:
Linux 提供了多种机制来实现中断下半部:- 软中断(SoftIRQ):
- 用于非常高效的下半部实现,主要用于网络包处理等。
- 不能睡眠。
- 比如之前在博文 https://blog.csdn.net/wenhao_ir/article/details/145281064 中实现的内核定时器其实就是属于中断下半部的软中断。
- 任务队列(Tasklet):
- 通过软中断实现的高级接口,适合较轻量的任务。
- 也不能睡眠。
- 工作队列(Workqueue):
- 运行在内核线程中,可以睡眠,适合复杂的任务处理。例子见 https://blog.csdn.net/wenhao_ir/article/details/145321621
- 线程化的中断处理:
- 利用函数
request_threaded_irq
在注册中断请求时,为该中断也注册一个属于这个中断的线程,当中断的处理函数(上半部)运行完毕后,唤醒这个线程,进而处理中断的下半部。例子见 https://blog.csdn.net/wenhao_ir/article/details/145326705
- 利用函数
- 软中断(SoftIRQ):
上半部和下半部的关系
- 上半部的任务是快速响应硬件,通知内核某个事件发生,并触发下半部的处理。
- 下半部完成真正的处理工作,将中断对内核的影响降到最低。
举例说明
- 情景:按键中断
- 上半部:读取硬件寄存器,获取按键的状态,并将事件放入缓冲区,同时触发下半部。
- 下半部:将按键事件处理为具体的逻辑,比如按键消抖、通知用户空间等。
这种分工明确的设计,使得系统既能快速响应中断,又能在合适的时机完成复杂任务,从而提高整体性能和实时性。
本文代码在哪个基础上修改而成?
本文是在博文 https://blog.csdn.net/wenhao_ir/article/details/145228617 的代码基础上修改而成。
完整源代码
驱动程序gpio_key_drv.c
中的代码
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
struct gpio_key{
int gpio;
struct gpio_desc *gpiod;
int flag;
int irq;
struct tasklet_struct tasklet;
} ;
static struct gpio_key *gpio_keys_100ask;
/* 主设备号 */
static int major = 0;
static struct class *gpio_key_class;
static int g_key = 0;
static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);
/* 环形缓冲区 */
#define BUF_LEN 128
static int g_keys[BUF_LEN];
static int r, w;
#define NEXT_POS(x) ((x+1) % BUF_LEN)
static int is_key_buf_empty(void)
{
return (r == w);
}
static int is_key_buf_full(void)
{
return (r == NEXT_POS(w));
}
static void put_key(int key_value)
{
if (!is_key_buf_full())
{
g_keys[w] = key_value;
w = NEXT_POS(w);
}
}
static int get_key(void)
{
int key_value = 0;
if (!is_key_buf_empty())
{
key_value = g_keys[r];
r = NEXT_POS(r);
}
return key_value;
}
/* 实现文件操作结构体中的read函数 */