瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工智能应用。RK3568 支持安卓 11 和 linux 系统,主要面向物联网网关、NVR 存储、工控平板、工业检测、工控盒、卡拉 OK、云终端、车载中控等行业。
【公众号】迅为电子
【粉丝群】824412014(加群获取驱动文档+例程)
【视频观看】嵌入式学习之Linux驱动(第五期_中断_全新升级)_基于RK3568
【购买链接】迅为RK3568开发板瑞芯微Linux安卓鸿蒙ARM核心板人工智能AI主板
中断线程化是实时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,否则返回一个负数错误代码。
在下一小节中将使用上述API进行相应的实验,利用中断线程化相关知识来对共享工作队列实验进行优化。
本实验对应的网盘路径为:iTOP-RK3568开发板【底板V1.7版本】\03_【iTOP-RK3568开发板】指南教程\02_Linux驱动配套资料\04_Linux驱动例程\39_request_threaded_irq\module。
本实验将实现注册显示屏触摸中断,每按当触摸LCD显示屏就会触发中断服务函数,在中断服务函数中提交工作项到工作队列中,打印“This id test_interrupt”,并打印“This is test_work”。
我们要实现一个简单的中断处理的例子,用于展示中断的顶半部和底半部处理的概念,并通过线程化的工作队列实现了底半部的延时处理。编写完成的interrupt.c代码如下所示,添加的代码已加粗表示。
#include
#include
#include
#include
#include
#include
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("topeet");
在上一小节中的interrupt.c代码同一目录下创建 Makefile 文件,Makefile 文件内容如下所示:
export ARCH=arm64#设置平台架构
export CROSS_COMPILE=aarch64-linux-gnu-#交叉编译器前缀
obj-m += interrupt.o #此处要和你的驱动源文件同名
KDIR :=/home/topeet/Linux/linux_sdk/kernel #这里是你的内核目录
PWD ?= $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules #make操作
clean:
make -C $(KDIR) M=$(PWD) clean #make clean操作
对于Makefile的内容注释已在上图添加,保存退出之后,来到存放interrupt.c和Makefile文件目录下,如下图(图49-1)所示:
图 49-1
然后使用命令“make”进行驱动的编译,编译完成如下图(图49-2)所示:
图 49-2
编译完生成interrupt.ko目标文件,如下图(图49-3)所示:
图 49-3
至此驱动模块就编译成功了,接下来进行测试。
开发板启动之后,使用以下命令进行驱动模块的加载,如下图(图49-4)所示:
insmod interrupt.ko
图49-4
可以看到申请的中断号被打印了出来,然后用手触摸连接的LVDS 7寸屏幕,打印如下图(49-5)所示:
图 49-5
我们按一下屏幕,立即输入“ps -aux | grep test_workqueue”,可以看到工作线程,如下图(49-6)所示,u代表无绑定的工作队列。
图 49-6
最后可以使用以下命令进行驱动的卸载,如下图(图49-7)所示:
rmmod interrupt
图 49-7
至此,中断线程化实验就完成了。