1 使用背景
对定时器做相关配置,使得每隔时间T,触发定时器中断,可以在定时器中断处理函数处理算法,这样就可以周期性的执行特定的任务。但如果不想在定时器中断处理函数中添加算法,比如说用户只想在应用程序里面执行他们的任务,那么钩子函数就是一个不错的选择。
2 钩子函数的原理
本章以定时器中断为例说明SylixOS钩子的使用方法。
2.1 API_InterVectorIsr函数
函数原型如程序清单 2.1
程序清单 2.1
#include <SylixOS>
irqreturn_t API_InterVectorIsr (ULONG ulVector);
API_InterVectorIsr该内核接口是向量中断的总服务入口,根据中断号得到对应的中断服务函数链表,找到具体中断服务函数。流程图如图 2.1 所示。
图 2.1 中断向量处理流程
从流程图可以看到,该函数入口处调用LW_CPU_INT_ENTER_HOOK。该宏的定义如程序清单 2.2所示。这就是钩子函数的调用流程,其实就是调用一个函数指针指向的函数。
程序清单 2.2
#define LW_CPU_INT_ENTER_HOOK(ulVector, ulNesting) \
if (_K_hookKernel.HOOK_CpuIntEnter) { \
_K_hookKernel.HOOK_CpuIntEnter(ulVector, ulNesting); \
}
2.2 钩子的设置函数
SylixOS内核提供了钩子的设置接口API_KernelHookSet,函数的实现如程序清单 2.3所示。
程序清单 2.3
LW_API
ULONG API_KernelHookSet (LW_HOOK_FUNC hookfuncPtr, ULONG ulOpt)
{
INTREG iregInterLevel;
iregInterLevel = __KERNEL_ENTER_IRQ(); /* 进入内核同时关闭中断 */
switch (ulOpt) {
case LW_OPTION_THREAD_CREATE_HOOK: /* 线程建立钩子 */
_K_hookKernel.HOOK_ThreadCreate = hookfuncPtr;
break;
case LW_OPTION_THREAD_DELETE_HOOK: /* 线程删除钩子 */
_K_hookKernel.HOOK_ThreadDelete = hookfuncPtr;
break;
case LW_OPTION_THREAD_SWAP_HOOK: /* 线程切换钩子 */
_K_hookKernel.HOOK_ThreadSwap = hookfuncPtr;
break;
case LW_OPTION_THREAD_TICK_HOOK: /* 系统时钟中断钩子 */
_K_hookKernel.HOOK_ThreadTick = hookfuncPtr;
break;
case LW_OPTION_THREAD_INIT_HOOK: /* 线程初始化钩子 */
_K_hookKernel.HOOK_ThreadInit = hookfuncPtr;
break;
case LW_OPTION_THREAD_IDLE_HOOK: /* 空闲线程钩子 */
_K_hookKernel.HOOK_ThreadIdle = hookfuncPtr;
break;
case LW_OPTION_KERNEL_INITBEGIN: /* 内核初始化开始钩子 */
_K_hookKernel.HOOK_KernelInitBegin = hookfuncPtr;
break;
case LW_OPTION_KERNEL_INITEND: /* 内核初始化结束钩子 */
_K_hookKernel.HOOK_KernelInitEnd = hookfuncPtr;
break;
case LW_OPTION_KERNEL_REBOOT: /* 内核重新启动钩子 */
_K_hookKernel.HOOK_KernelReboot = hookfuncPtr;
break;
case LW_OPTION_WATCHDOG_TIMER: /* 看门狗定时器钩子 */
_K_hookKernel.HOOK_WatchDogTimer = hookfuncPtr;
break;
case LW_OPTION_OBJECT_CREATE_HOOK: /* 创建内核对象钩子 */
_K_hookKernel.HOOK_ObjectCreate = hookfuncPtr;
break;
case LW_OPTION_OBJECT_DELETE_HOOK: /* 删除内核对象钩子 */
_K_hookKernel.HOOK_ObjectDelete = hookfuncPtr;
break;
case LW_OPTION_FD_CREATE_HOOK: /* 文件描述符创建钩子 */
_K_hookKernel.HOOK_FdCreate = hookfuncPtr;
break;
case LW_OPTION_FD_DELETE_HOOK: /* 文件描述符删除钩子 */
_K_hookKernel.HOOK_FdDelete = hookfuncPtr;
break;
case LW_OPTION_CPU_IDLE_ENTER: /* CPU 进入空闲模式 */
_K_hookKernel.HOOK_CpuIdleEnter = hookfuncPtr;
break;
case LW_OPTION_CPU_IDLE_EXIT: /* CPU 退出空闲模式 */
_K_hookKernel.HOOK_CpuIdleExit = hookfuncPtr;
break;
case LW_OPTION_CPU_INT_ENTER: /* CPU 进入中断(异常)模式 */
_K_hookKernel.HOOK_CpuIntEnter = hookfuncPtr;
break;
case LW_OPTION_CPU_INT_EXIT: /* CPU 退出中断(异常)模式 */
_K_hookKernel.HOOK_CpuIntExit = hookfuncPtr;
break;
case LW_OPTION_STACK_OVERFLOW_HOOK: /* 堆栈溢出 */
_K_hookKernel.HOOK_StkOverflow = hookfuncPtr;
break;
case LW_OPTION_FATAL_ERROR_HOOK: /* 致命错误 */
_K_hookKernel.HOOK_FatalError = hookfuncPtr;
break;
case LW_OPTION_VPROC_CREATE_HOOK: /* 进程建立钩子 */
_K_hookKernel.HOOK_VpCreate = hookfuncPtr;
break;
case LW_OPTION_VPROC_DELETE_HOOK: /* 进程删除钩子 */
_K_hookKernel.HOOK_VpDelete = hookfuncPtr;
break;
default:
__KERNEL_EXIT_IRQ(iregInterLevel); /* 退出内核同时打开中断 */
_ErrorHandle(ERROR_KERNEL_OPT_NULL);
return (ERROR_KERNEL_OPT_NULL);
}
__KERNEL_EXIT_IRQ(iregInterLevel); /* 退出内核同时打开中断 */
return (ERROR_NONE);
}
该函数可以设置各种类型钩子函数,由此可见,只要调用API_KernelHookSet设置中断相关的钩子函数,当进入中断入口的函数的入口时都会调用钩子。
2.3 钩子函数的定义
假设设置了中断的钩子函数,那么任何中断都会调用这个钩子函数。现在只关心定时器中断,因此需要用中断号来过滤不关心的中断。例程如程序清单 2.4所示。
程序清单 2.4
#define TIMER_INTVECTOR 69
void HookFun(ULONG ulVector,ULONG ulnest)
{
if(TIMER_INTVECTOR != ulVector)
{
return;
}
/* 用户在此添加自己算法*/
/* 注意:不要调用中断上下文不能执行的语句,比如sleep,printf等*/
}
这样,系统会周期性的进入定时器中断,处理 HookFun函数。只要知道中断号,就可以不用知晓定时器驱动的实现机制,直接挂接想要处理的任务。
3 参考资料
《SylixOS_driver_usermanual》
原文地址:http://blog.51cto.com/12142768/2162820