鸿蒙轻内核M核源码分析系列十七(2) 异常钩子函数的注册操作

发布于:2024-09-18 ⋅ 阅读:(112) ⋅ 点赞:(0)

本文中所涉及的源码,以OpenHarmony LiteOS-M内核为例,均可以在开源站点https://gitee.com/openharmony/kernel_liteos_m 获取。鸿蒙轻内核异常钩子模块代码主要在components\exchook目录下。异常钩子函数的注册、解注册、异常钩子类型定义在utils\los_debug.h|.c


1、异常钩子函数节点结构体和异常钩子函数节点数组

在文件components\exchook\los_exchook.c定义异常钩子的一些宏、函数节点结构体和一些全局变量函数节点数组。⑴处定义的宏设置当前系统支持的钩子函数的个数。⑵处定义的钩子函数节点结构体,每个节点除了异常钩子函数定义还有一个指向下一个节点的指针。⑶处定义的全局变量数组g_excNodes,注册的每一个异常钩子函数都使用一个节点来维护。⑷处定义异常钩子函数节点指针数组,数组的每个元素指针指向对应类型的异常钩子节点单向链表,而最后一个元素g_excHeads[EXC_TYPE_END]指向空闲的钩子函数节点链表。

    #ifndef LOSCFG_BASE_EXC_HOOK_LIMIT
⑴  #define LOSCFG_BASE_EXC_HOOK_LIMIT  16
    #endif

⑵  struct Node {
        ExcHookFn excHookFn;
        struct Node *next;
    };

⑶  STATIC struct Node g_excNodes[LOSCFG_BASE_EXC_HOOK_LIMIT];
⑷  STATIC struct Node *g_excHeads[EXC_TYPE_END + 1]; /* EXC_TYPE_END is used for the free list. */

2、异常钩子函数的注册操作

文件components\exchook\los_exchook.c中主要定义了异常钩子函数的注册LOS_RegExcHook和解除注册LOS_UnRegExcHook对外接口函数。接下来,我们分析注册操作的源代码。

2.1 注册LOS_RegExcHook

在分析注册钩子函数的函数之前,我们先分析下如何通过调用GetFreeNode()获取钩子函数空闲节点,代码如下。g_excHeads[EXC_TYPE_END]指向钩子函数空闲节点单向链表的第一个节点。⑴处如果为空说明未初始化或者空闲节点使用完毕。⑵处如果第1个空闲节点g_excNodes[0]被使用,说明已经没有空闲节点,返回NULL即可。否则执行⑶初始化空闲节点链表,初始化后g_excHeads[EXC_TYPE_END]执行g_excNodes数组的最后一个元素,然后数组的每个元素执行前一个元素,g_excNodes[0]是最后一个空闲节点,如下图所示。然后执行⑷处把函数DoExcHook注册为全局异常钩子函数g_excHook

如果空闲节点链表不为空,执行⑸获取第一个空闲节点,然后g_excHeads[EXC_TYPE_END]指向下一个空闲节点。

STATIC struct Node *GetFreeNode(VOID)
{
    struct Node *node = NULL;
    int i;
⑴  if (g_excHeads[EXC_TYPE_END] == NULL) {
⑵      if (g_excNodes[0].excHookFn != NULL) {
            /* no free node now */
            return NULL;
        } else {
            /* Initialize the free list */
⑶          for (i = 0; i < LOSCFG_BASE_EXC_HOOK_LIMIT; ++i) {
                g_excNodes[i].next = g_excHeads[EXC_TYPE_END];
                g_excHeads[EXC_TYPE_END] = &g_excNodes[i];
            }
⑷          OsExcHookRegister(DoExcHook);
        }
    }

⑸  node = g_excHeads[EXC_TYPE_END];
    g_excHeads[EXC_TYPE_END] = node->next;
    return node;
}

下面我们接着看注册异常钩子函数的LOS_RegExcHook函数的源代码。⑴处先判断传入参数的合法性,⑵处获取空闲钩子函数节点,如果获取节点为空,则无法注册钩子函数。⑶处设置节点的钩子函数成员变量为传入的钩子函数,然后设置其下一个节点为该异常类型对应的钩子函数链表的第一个节点,然后把最后注册的钩子函数节点设置为第一个节点。因此,同一个异常类型可以注册多个钩子函数,他们维护为单向链表,第一个节点为g_excHeads[excType]。后注册的在链表的靠近链表头部。如下图所示。

UINT32 LOS_RegExcHook(EXC_TYPE excType, ExcHookFn excHookFn)
{
    UINT32 intSave;
    struct Node *node = NULL;
⑴  if (excType >= EXC_TYPE_END || excHookFn == NULL) {
        return LOS_ERRNO_SYS_PTR_NULL;
    }

    intSave = LOS_IntLock();
⑵  node = GetFreeNode();
    if (node == NULL) {
        LOS_IntRestore(intSave);
        return LOS_ERRNO_SYS_HOOK_IS_FULL;
    }

⑶  node->excHookFn = excHookFn;
    node->next = g_excHeads[excType];
    g_excHeads[excType] = node;
    LOS_IntRestore(intSave);
    return LOS_OK;
}

2.2 解除注册LOS_UnRegExcHook

我们再看看解除注册钩子函数的LOS_UnRegExcHook函数的源码,⑴处先判断传入参数的合法性。我们已经知道,同一个类型的异常钩子函数节点使用单向链表维护,我们需要执行
⑵处的循环进行遍历。遍历时,判断是否遍历到了要解除注册的函数,如果遍历到了,⑶成立,执行后续的代码。如果⑷成立,说明遍历到的节点不是第一个,把之前的节点执行当前要解除注册的节点的下一个节点,这样把要解除注册的节点从链表中进行删除。否则说明遍历到的节点为第一个,则执行⑸,把遍历到的下一个节点作为第一个节点,这样把要解除注册的节点从链表中进行删除。⑹处置空解除注册的节点,把该节点释放后作为空闲节点插入到钩子函数空闲节点链表g_excHeads[EXC_TYPE_END]的第一个位置上。

UINT32 LOS_UnRegExcHook(EXC_TYPE excType, ExcHookFn excHookFn)
{
    UINT32 intSave;
    struct Node *node = NULL;
    struct Node *preNode = NULL;
⑴  if (excType >= EXC_TYPE_END || excHookFn == NULL) {
        return LOS_ERRNO_SYS_PTR_NULL;
    }

    intSave = LOS_IntLock();
⑵  for (node = g_excHeads[excType]; node != NULL; node = node->next) {
⑶      if (node->excHookFn == excHookFn) {
⑷          if (preNode) {
                preNode->next = node->next;
            } else {
⑸              g_excHeads[excType] = node->next;
            }
⑹          node->excHookFn = NULL;
            node->next = g_excHeads[EXC_TYPE_END];
            g_excHeads[EXC_TYPE_END] = node;
        }
        preNode = node;
    }
    LOS_IntRestore(intSave);
    return LOS_OK;
}

3、异常钩子函数的执行

从上文中,我们了解到初始化钩子函数空闲链表后,通过语句OsExcHookRegister(DoExcHook)注册了异常钩子函数。现在我们就来分析下该函数的源码。DoExcHook函数会调用DoExcHookInRegOrder函数递归运行钩子函数链表上的所有节点维护的钩子函数。⑴处的代码调用函数DoExcHookInRegOrder,除了传入异常类型,还传入该异常类型对应的钩子函数节点链表的第一个节点。⑵处如果链表节点不为空,则递归调用该节点的下一个节点。⑶处执行异常钩子函数。因此,可以看得出,注册早的钩子函数在链表的尾部最先执行,注册晚的函数在链表的头部,最后执行。

STATIC VOID DoExcHookInRegOrder(EXC_TYPE excType, struct Node *node)
{
    if (node != NULL) {
⑵      DoExcHookInRegOrder(excType, node->next);
⑶      node->excHookFn(excType);
    }
}

STATIC VOID DoExcHook(EXC_TYPE excType)
{
    UINT32 intSave;
    if (excType >= EXC_TYPE_END) {
        return;
    }
    intSave = LOS_IntLock();
⑴  DoExcHookInRegOrder(excType, g_excHeads[excType]);
    LOS_IntRestore(intSave);
}


小结

本文介绍了异常钩子模块的对外注册函数LOS_RegExcHook和解除注册函数LOS_UnRegExcHook,对内部维护的钩子函数节点链表有了更深的理解。

经常有很多小伙伴抱怨说:不知道学习鸿蒙开发哪些技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?

为了能够帮助到大家能够有规划的学习,这里特别整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。

在这里插入图片描述

《鸿蒙 (Harmony OS)开发学习手册》(共计892页):https://gitcode.com/HarmonyOS_MN/733GH/overview

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

开发基础知识:

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

在这里插入图片描述

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

在这里插入图片描述

鸿蒙开发面试真题(含参考答案):https://gitcode.com/HarmonyOS_MN/733GH/overview

在这里插入图片描述

OpenHarmony 开发环境搭建

图片

《OpenHarmony源码解析》:https://gitcode.com/HarmonyOS_MN/733GH/overview

  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……
  • 系统架构分析
  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……

图片

OpenHarmony 设备开发学习手册:https://gitcode.com/HarmonyOS_MN/733GH/overview

图片
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到