中断底半部

更新时间:
2024-05-14
下载文档

中断底半部

简介与原理

内核在处理中断请求时,要求在单位时间内可以处理尽可能多的中断,也就是系统要求处理中断的吞吐率要尽可能地大。这就要求中断处理程序要尽可能地短小精悍,并且不能有耗时操作。但是大多数的中断处理程序是很复杂的,很难在短时间内处理完毕。为了提高系统的响应能力和并发能力,需要解决平衡中断处理程序时间要求短和工作量要大的问题,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.
文档内容是否对您有所帮助?
有帮助
没帮助