中断底半部
简介与原理
内核在处理中断请求时,要求在单位时间内可以处理尽可能多的中断,也就是系统要求处理中断的吞吐率要尽可能地大。这就要求中断处理程序要尽可能地短小精悍,并且不能有耗时操作。但是大多数的中断处理程序是很复杂的,很难在短时间内处理完毕。为了提高系统的响应能力和并发能力,需要解决平衡中断处理程序时间要求短和工作量要大的问题,SylixOS 将中断处理分为两个阶段,也就是顶半部和底半部:
- 顶半部完成的一般是紧急的硬件操作,一般包括读取寄存器中的中断状态,清除中断标志,将底半部处理程序挂到底半部的执行队列中去。
- 底半部完成大部分的耗时操作,并且可以被新的中断打断。
如果中断处理程序足够简单,则不需要分为顶半部和底半部,直接在顶半部完成即可。SylixOS 实现底半部的机制是工作队列。
驱动开发中的使用
以下程序展示了 SylixOS 中断底半部在驱动开发中的使用,内核模块在装载时创建了一个不带有延迟执行功能的 SylixOS 工作队列和一个线程,线程用于设置某个 GPIO 的中断处理函数并使能中断,当产生对应的按键中断时,中断处理函数会清除中断,并将耗时操作插入到工作队列中,卸载内核模块时清除该 GPIO 的相关设置并删除工作队列。
#define __SYLIXOS_STDIO
#define __SYLIXOS_KERNEL
#include <SylixOS.h>
#include <module.h>
#define KEY_NUM 36
PVOID _G_pvWorkQueue;
static INT _G_iIrqNum;
static VOID __workHandler(VOID)
{
printk("work handler function start.\n");
API_TimeSSleep(5);
printk("work handler function stop.\n");
}
static irqreturn_t GpioIsr (INT iGpioNum, ULONG ulVector)
{
API_GpioClearIrq(iGpioNum);
API_WorkQueueInsert(_G_pvWorkQueue,
0,
__workHandler,
LW_NULL,
LW_NULL,
LW_NULL,
LW_NULL,
LW_NULL,
LW_NULL);
return (ERROR_NONE);
}
static PVOID __keyThread (PVOID pvArg)
{
INT iError;
iError = API_GpioRequestOne(KEY_NUM, LW_GPIOF_IN, "KEY");
if (iError != ERROR_NONE) {
printk("failed to request gpio %d!\n", KEY_NUM);
return (NULL);
}
_G_iIrqNum = API_GpioSetupIrq(KEY_NUM, LW_FALSE, 0);
if (_G_iIrqNum == PX_ERROR) {
printk("failed to setup gpio %d irq!\n", KEY_NUM);
return (NULL);
}
iError = API_InterVectorConnect((ULONG)_G_iIrqNum,
(PINT_SVR_ROUTINE)GpioIsr,
(PVOID)KEY_NUM,
"GpioIsr");
if (iError != ERROR_NONE) {
printk("failed to connect GpioIsr!\n");
return (NULL);
}
API_InterVectorEnable(_G_iIrqNum);
return (NULL);
}
void module_init (void)
{
LW_CLASS_THREADATTR threadattr;
printk("interrupt_module init!\n");
API_ThreadAttrBuild(&threadattr,
4 * LW_CFG_KB_SIZE,
LW_PRIO_NORMAL,
LW_OPTION_THREAD_STK_CHK,
LW_NULL);
_G_pvWorkQueue = API_WorkQueueCreate("t_workqueue",
10,
FALSE,
0,
&threadattr);
if (_G_pvWorkQueue == LW_NULL) {
printk("WorkQueue create failed.\n");
return;
}
API_ThreadCreate("t_key",
(PTHREAD_START_ROUTINE)__keyThread,
LW_NULL,
LW_NULL);
}
void module_exit (void)
{
API_InterVectorDisconnect((ULONG)_G_iIrqNum,
(PINT_SVR_ROUTINE)GpioIsr,
(PVOID)KEY_NUM);
API_GpioFree(KEY_NUM);
API_WorkQueueDelete(_G_pvWorkQueue);
printk("interrupt_module exit!\n");
}
在 SylixOS Shell 下装载模块,然后通过按下指定的按键触发中断:
#insmod ./interrupt.ko
interrupt_module init!
module interrupt.ko register ok, handle: 0x13338f0
work handler function start.
work handler function stop.
在 SylixOS Shell 下卸载模块:
#rmmod interrupt.ko
interrupt_module exit!
module /lib/modules/interrupt.ko unregister ok.