【系列文章】Linux系统中断的应用06-中断线程化
【系列文章】Linux系统中断的应用06-中断线程化
该文章为系列文章:Linux系统中断的应用
中的第06篇
该系列的导航页连接:
【系列文章】Linux系统中断的应用-导航页
文章目录
- 【系列文章】Linux系统中断的应用06-中断线程化
- 一、什么是中断线程化
- 二、中断线程化接口函数
- 三、实验程序的编写
- 3.1驱动程序编写
- 3.2运行测试
中断线程化是实时 Linux 项目开发的一个新特性,目的是降低中断处理对系统实时延迟的影响。本文章我们来一项新技术——中断线程化。
一、什么是中断线程化
中断线程化是一种优化技术,用于提高多线程程序的性能。
想象一下,你正在做一项任务,但是总是被别人的打扰所中断,每次都要停下手头的工作去处理别人的事情。这样频繁的中断会让你的工作效率变低,因为你需要反复切换任务,无法专心做好自己的工作。
在多线程程序中,也存在类似的问题。有时硬件或其他事件会发出中断信号,打断正在执行的线程,需要切换到中断处理程序去处理这些事件。这种频繁的中断切换会导致额外的开销和延迟,影响程序的性能。
为了解决这个问题,中断线程化提出了一种优化方案。它将中断处理程序从主线程中独立出来,创建一个专门的线程来处理这些中断事件。这样,主线程就不再受到中断的干扰,可以专注于自己的工作,不再频繁地被打断。
中断线程化的核心思想是将中断处理和主线程的工作分开,让它们可以并行执行。中断线程负责处理中断事件,而主线程负责执行主要的工作任务。这样一来,不仅可以减少切换的开销,还可以提高整个程序的响应速度和性能。
需要注意的是,中断线程化还需要处理线程之间的同步和数据共享问题。因为中断线程和主线程可能会同时访问和修改共享的数据,所以需要合理地进行同步操作,确保数据的一致性和正确性。
总而言之,中断线程化是一种优化技术,通过将中断处理和主线程的工作分开,提高多线程程序的性能。让主线程不再频繁被中断,可以专注于自己的工作,从而提高程序的效率和响应速度。
中断线程化的处理仍然可以看作是将原来的中断上半部分和中断下半部分。上半部分还是用来处理紧急的事情,下半部分也是出路比较耗时的操作,但是下半部分会交给一个专门的内核线程来处理。这个内核线程只用于这个中断。当发生中断的时候,会唤醒这个内核线程,然后由这个内核线程来执行中断下半部分的函数。
二、中断线程化接口函数
request_threaded_irq 是 Linux 内核中用于请求并注册一个线程化的中断处理函数的函数。
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id);
irq:中断号,表示要请求的中断线路。handler:是在发生中断时首先要执行的处理程序,非常类似于顶半部,该函数最后会返回 IRQ_WAKE_THREAD 来唤醒中断,一般 handler 设为 NULL,用系统提供的默认处理。thread_fn:线程化的中断处理函数,非常类似于底半部。如果此处设置为 NULL 则表示没有使用中断线程化。irqflags:中断标志,用于指定中断的属性和行为。devname:中断的名称,用于标识中断请求的设备。dev_id:设备标识符,用于传递给中断处理函数的参数。函数返回值函数返回一个整数值,表示中断请求的结果。如果中断请求成功,返回值为 0,否则返回一个负数错误代码。
三、实验程序的编写
3.1驱动程序编写
本实验将实现注册显示屏触摸中断,每按当触摸 LCD 显示屏就会触发中断服务函数,在中断服务函数中提交工作项到工作队列中,打印“This id test_interrupt”,并打印“Thisis test_work”。
我们要实现一个简单的中断处理的例子,用于展示中断的顶半部和底半部处理的概念,并通过线程化的工作队列实现了底半部的延时处理。编写完成的 interrupt.c 代码如下所示
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/workqueue.h>int irq;
// 中断处理函数的底半部(线程化中断处理函数)
irqreturn_t test_work(int irq, void *args)
{// 执行底半部的中断处理任务msleep(1000);printk("This is test_work\n");return IRQ_RETVAL(IRQ_HANDLED);
}// 中断处理函数的顶半部
irqreturn_t test_interrupt(int irq, void *args)
{printk("This is test_interrupt\n");// 将中断处理工作推迟到底半部return IRQ_WAKE_THREAD;
}static int interrupt_irq_init(void)
{int ret;irq = gpio_to_irq(101); // 将 GPIO 映射为中断号printk("irq is %d\n", irq);// 用于请求并注册一个线程化的中断处理函数ret = request_threaded_irq(irq, test_interrupt, test_work, IRQF_TRIGGER_RISING, "test", NULL);if (ret < 0){printk("request_irq is error\n");return -1;}return 0;
}static void interrupt_irq_exit(void)
{free_irq(irq, NULL); // 释放中断printk("bye bye\n");
}module_init(interrupt_irq_init);
module_exit(interrupt_irq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("XXX");
3.2运行测试
可以看到申请的中断号被打印了出来,然后用手触摸连接的 LVDS 7 寸屏幕,打印如下图
我们按一下屏幕,立即输入“ps -aux | grep test_workqueue”,可以看到工作线程,如下图所示,u 代表无绑定的工作队列。