zoukankan      html  css  js  c++  java
  • 鸿蒙轻内核定时器Swtmr:不受硬件和数量限制,满足用户需求

    摘要:本文通过分析鸿蒙轻内核定时器模块的源码,掌握定时器使用上的差异。

    本文分享自华为云社区《鸿蒙轻内核M核源码分析系列十四 软件定时器Swtmr》,作者:zhushy 。

    软件定时器(Software Timer)是基于系统Tick时钟中断且由软件来模拟的定时器。当经过设定的Tick数后,会触发用户自定义的回调函数。硬件定时器受硬件的限制,数量上不足以满足用户的实际需求。鸿蒙轻内核提供了软件定时器功能可以提供更多的定时器,满足用户需求。

    本文通过分析鸿蒙轻内核定时器模块的源码,掌握定时器使用上的差异。本文中所涉及的源码,以OpenHarmony LiteOS-M内核为例,均可以在开源站点https://gitee.com/openharmony/kernel_liteos_m 获取。

    接下来,我们看下定时器的结构体,定时器初始化,定时器常用操作的源代码。

    1、定时器结构体定义和常用宏定义

    1.1 定时器结构体定义

    在文件kernelincludelos_swtmr.h定义的定时器控制块结构体为SWTMR_CTRL_S,结构体源代码如下。定时器状态.ucState取值OS_SWTMR_STATUS_UNUSED、OS_SWTMR_STATUS_CREATED或OS_SWTMR_STATUS_TICKING,定时器模式.mode取值LOS_SWTMR_MODE_ONCE、LOS_SWTMR_MODE_PERIOD或LOS_SWTMR_MODE_NO_SELFDELETE。其他结构体成员的解释见注释部分。

    typedef struct tagSwTmrCtrl {
        struct tagSwTmrCtrl *pstNext;       /* 指向下一个定时器结构体的指针       */
        UINT8               ucState;        /* 定时器状态,取值枚举SwtmrState    */
        UINT8               ucMode;         /* 定时器模式,取值枚举enSwTmrType   */
    #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1)
        UINT8               ucRouses;       /* 唤醒开关                         */
        UINT8               ucSensitive;    /* 对齐开关                         */
    #endif
        UINT32              usTimerID;      /* 定时器编号Id                     */
        UINT32              uwCount;        /* 定时器运行的次数                  */
        UINT32              uwInterval;     /* 周期定时器超时间隔 (单位: tick)   */
        UINT32              uwArg;          /* 定时器超时回调函数参数            */
        SWTMR_PROC_FUNC     pfnHandler;     /* 定时器超时回调函数                */
        SortLinkList        stSortList;     /* 定时器排序链表                    */
    } SWTMR_CTRL_S;

    另外,还对回调函数及其参数单独定义了一个结构体SwtmrHandlerItem,如下:

    typedef struct {
        SWTMR_PROC_FUNC handler;    /**< 定时器超时回调函数    */
        UINTPTR arg;                /**< 定时器超时回调函数参数 */
    } SwtmrHandlerItem;

    1.2 定时器常用宏定义

    定时器头文件kernelincludelos_swtmr.h中还提供了相关的枚举和宏,从定时器池里获取定时器控制块的宏定义OS_SWT_FROM_SID如下:

    #define OS_SWT_FROM_SID(swtmrId)    ((SWTMR_CTRL_S *)g_swtmrCBArray + ((swtmrId) % LOSCFG_BASE_CORE_SWTMR_LIMIT))
    头文件中定义的定时器几个枚举如下:
    enum SwtmrState {
        OS_SWTMR_STATUS_UNUSED,     /**< 定时器未使用    */
        OS_SWTMR_STATUS_CREATED,    /**< 定时器已创建     */
        OS_SWTMR_STATUS_TICKING     /**< 定时器计时中     */
    };
    
    #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1)
    
    enum enSwTmrRousesType {
        OS_SWTMR_ROUSES_IGNORE, /* 定时器不能唤醒系统 */
        OS_SWTMR_ROUSES_ALLOW,  /* 定时器能唤醒系统 */
    };
    
    enum enSwTmrAlignSensitive {
        OS_SWTMR_ALIGN_SENSITIVE,   /* 定时器不需要对齐 */
        OS_SWTMR_ALIGN_INSENSITIVE, /* 定时器需要对齐 */
    };
    #endif
    
    enum EnSwTmrType {
        LOS_SWTMR_MODE_ONCE,            /* 一次性定时器, 值为0. */
        LOS_SWTMR_MODE_PERIOD,          /* 周期定时器,值为 1. */
        LOS_SWTMR_MODE_NO_SELFDELETE,   /* 一次性定时器,不会自删除,值为2 */
        LOS_SWTMR_MODE_OPP,             /* 一次性定时器完成后,使能周期性定时器。该模式暂不支持。值为3 */
    };

    2、定时器初始化

    定时器在内核中默认开启,用户可以通过宏LOSCFG_BASE_CORE_SWTMR进行关闭。开启定时器的情况下,在系统启动时,在kernelsrclos_init.c中调用OsSwtmrInit()进行定时器模块初始化。下面,我们分析下定时器初始化的代码。

    ⑴处如果开启定时器对齐宏LOSCFG_BASE_CORE_SWTMR_ALIGN,清零g_swtmrAlignID数组。定时器的数量由宏LOSCFG_BASE_CORE_SWTMR_LIMIT定义,⑵处计算定时器池需要的内存大小,然后为定时器申请内存,如果申请失败,则返回错误。⑶初始化空闲定时器链表g_swtmrFreeList,维护未使用的定时器。循环每一个定时器进行初始化,为每一个定时器节点指定索引timerId,定时器控制块依次指向下一个定时器控制块。

    ⑷处代码为定时器创建队列,队列的消息大小OS_SWTMR_HANDLE_QUEUE_SIZE等于定时器的数量LOSCFG_BASE_CORE_SWTMR_LIMIT,消息内容的最大大小sizeof(SwtmrHandlerItem)。后文分析定时器队列读取写入消息的时候具体来看是什么消息。⑸处调用函数OsSwtmrTaskCreate()创建定时器任务,定时器任务优先级最高,任务的入口函数为OsSwtmrTask(),后文会分析该函数。⑹处初始化定时器排序链表,源码分析系列之前的文章分析过,可以阅读下排序链表数据结构章节。⑺处注册定时器扫描函数OsSwtmrScan。

    LITE_OS_SEC_TEXT_INIT UINT32 OsSwtmrInit(VOID)
    {
        UINT32 size;
        UINT16 index;
        UINT32 ret;
    
    #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1)
        // Ignore the return code when matching CSEC rule 6.6(1).
    ⑴  (VOID)memset_s((VOID *)g_swtmrAlignID, sizeof(SwtmrAlignData) * LOSCFG_BASE_CORE_SWTMR_LIMIT,
                       0, sizeof(SwtmrAlignData) * LOSCFG_BASE_CORE_SWTMR_LIMIT);
    #endif
    
    ⑵  size = sizeof(SWTMR_CTRL_S) * LOSCFG_BASE_CORE_SWTMR_LIMIT;
        SWTMR_CTRL_S *swtmr = (SWTMR_CTRL_S *)LOS_MemAlloc(m_aucSysMem0, size);
        if (swtmr == NULL) {
            return LOS_ERRNO_SWTMR_NO_MEMORY;
        }
        // Ignore the return code when matching CSEC rule 6.6(3).
        (VOID)memset_s((VOID *)swtmr, size, 0, size);
        g_swtmrCBArray = swtmr;
    ⑶  g_swtmrFreeList = swtmr;
        swtmr->usTimerID = 0;
        SWTMR_CTRL_S *temp = swtmr;
        swtmr++;
        for (index = 1; index < LOSCFG_BASE_CORE_SWTMR_LIMIT; index++, swtmr++) {
            swtmr->usTimerID = index;
            temp->pstNext = swtmr;
            temp = swtmr;
        }
    
    ⑷  ret = LOS_QueueCreate((CHAR *)NULL, OS_SWTMR_HANDLE_QUEUE_SIZE,
                              &g_swtmrHandlerQueue, 0, sizeof(SwtmrHandlerItem));
        if (ret != LOS_OK) {
            (VOID)LOS_MemFree(m_aucSysMem0, swtmr);
            return LOS_ERRNO_SWTMR_QUEUE_CREATE_FAILED;
        }
    
    ⑸  ret = OsSwtmrTaskCreate();
        if (ret != LOS_OK) {
            (VOID)LOS_MemFree(m_aucSysMem0, swtmr);
            return LOS_ERRNO_SWTMR_TASK_CREATE_FAILED;
        }
    
    ⑹  g_swtmrSortLinkList = OsGetSortLinkAttribute(OS_SORT_LINK_SWTMR);
        if (g_swtmrSortLinkList == NULL) {
            (VOID)LOS_MemFree(m_aucSysMem0, swtmr);
            return LOS_NOK;
        }
    
        ret = OsSortLinkInit(g_swtmrSortLinkList);
        if (ret != LOS_OK) {
            (VOID)LOS_MemFree(m_aucSysMem0, swtmr);
            return LOS_NOK;
        }
    
    ⑺  ret = OsSchedSwtmrScanRegister((SchedScan)OsSwtmrScan);
        if (ret != LOS_OK) {
            (VOID)LOS_MemFree(m_aucSysMem0, swtmr);
            return LOS_NOK;
        }
    
        return LOS_OK;
    }

    我们再看一下定时器任务的入口函数为OsSwtmrTask()。⑴进行for永久循环,队列读取不到数据时会阻塞,因为优先级比较高,定时器队列有数据时该任务就会执行。从定时器队列中读取定时器处理函数地址放入指针地址&swtmrHandle,读取的长度为sizeof(SwtmrHandlerItem)。成功读取后,获取定时器回调函数及其参数,然后⑵处执行定时器回调函数。记录定时器回调函数的执行时间,⑶处判断执行时间是否超时,如果超时,打印警告信息。

    LITE_OS_SEC_TEXT VOID OsSwtmrTask(VOID)
    {
        SwtmrHandlerItem swtmrHandle;
        UINT32 readSize;
        UINT32 ret;
        UINT64 tick;
        readSize = sizeof(SwtmrHandlerItem);
    
        for (;;) {
    ⑴      ret = LOS_QueueReadCopy(g_swtmrHandlerQueue, &swtmrHandle, &readSize, LOS_WAIT_FOREVER);
            if ((ret == LOS_OK) && (readSize == sizeof(SwtmrHandlerItem))) {
                if (swtmrHandle.handler == NULL) {
                    continue;
                }
    
                tick = LOS_TickCountGet();
    ⑵          swtmrHandle.handler(swtmrHandle.arg);
                tick = LOS_TickCountGet() - tick;
    
    ⑶          if (tick >= SWTMR_MAX_RUNNING_TICKS) {
                    PRINT_WARN("timer_handler(%p) cost too many ms(%d)
    ",
                               swtmrHandle.handler,
                               (UINT32)((tick * OS_SYS_MS_PER_SECOND) / LOSCFG_BASE_CORE_TICK_PER_SECOND));
                }
            }
        }
    }

    3、定时器常用操作

    3.1 定时器创建

    我们分析下创建定时器函数LOS_SwtmrCreate()的代码。先不考虑定时器对齐LOSCFG_BASE_CORE_SWTMR_ALIGN的情况。先看下函数参数,interval是定时器执行时间间隔,mode是创建的定时器模式,handler、arg是定时器回调函数及其参数。swtmrId是定时器编号。

    ⑴处对传入参数定时器超时间隔、定时器模式、回调函数,定时器编号进行校验。⑵判断空闲定时器池是否为空,为空则返回错误,无法创建定时器。⑶处如果定时器不为空,则获取定时器控制块swtmr。⑷处对定时器控制块信息进行初始化。⑸处把该定时器排序链表节点的响应时间responseTime初始化为-1。

    #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1)
    LITE_OS_SEC_TEXT_INIT UINT32 LOS_SwtmrCreate(UINT32 interval,
                                                 UINT8 mode,
                                                 SWTMR_PROC_FUNC handler,
                                                 UINT32 *swtmrId,
                                                 UINT32 arg,
                                                 UINT8 rouses,
                                                 UINT8 sensitive)
    #else
    LITE_OS_SEC_TEXT_INIT UINT32 LOS_SwtmrCreate(UINT32 interval,
                                                 UINT8 mode,
                                                 SWTMR_PROC_FUNC handler,
                                                 UINT32 *swtmrId,
                                                 UINT32 arg)
    #endif
    {
        SWTMR_CTRL_S  *swtmr = NULL;
        UINT32 intSave;
    
    ⑴  if (interval == 0) {
            return LOS_ERRNO_SWTMR_INTERVAL_NOT_SUITED;
        }
    
        if ((mode != LOS_SWTMR_MODE_ONCE) &&
            (mode != LOS_SWTMR_MODE_PERIOD) &&
            (mode != LOS_SWTMR_MODE_NO_SELFDELETE)) {
            return LOS_ERRNO_SWTMR_MODE_INVALID;
        }
    
        if (handler == NULL) {
            return LOS_ERRNO_SWTMR_PTR_NULL;
        }
    
        if (swtmrId == NULL) {
            return LOS_ERRNO_SWTMR_RET_PTR_NULL;
        }
    
    #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1)
        if ((rouses != OS_SWTMR_ROUSES_IGNORE) && (rouses != OS_SWTMR_ROUSES_ALLOW)) {
            return OS_ERRNO_SWTMR_ROUSES_INVALID;
        }
    
        if ((sensitive != OS_SWTMR_ALIGN_INSENSITIVE) && (sensitive != OS_SWTMR_ALIGN_SENSITIVE)) {
            return OS_ERRNO_SWTMR_ALIGN_INVALID;
        }
    #endif
    
        intSave = LOS_IntLock();
    ⑵  if (g_swtmrFreeList == NULL) {
            LOS_IntRestore(intSave);
            return LOS_ERRNO_SWTMR_MAXSIZE;
        }
    
    ⑶  swtmr = g_swtmrFreeList;
        g_swtmrFreeList = swtmr->pstNext;
        LOS_IntRestore(intSave);
    ⑷  swtmr->pfnHandler    = handler;
        swtmr->ucMode        = mode;
        swtmr->uwInterval    = interval;
        swtmr->pstNext       = (SWTMR_CTRL_S *)NULL;
        swtmr->uwCount       = 0;
        swtmr->uwArg         = arg;
    #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1)
        swtmr->ucRouses      = rouses;
        swtmr->ucSensitive   = sensitive;
    #endif
        swtmr->ucState       = OS_SWTMR_STATUS_CREATED;
        *swtmrId = swtmr->usTimerID;
    ⑸  SET_SORTLIST_VALUE(&swtmr->stSortList, OS_SORT_LINK_INVALID_TIME);
    
        return LOS_OK;
    }

    3.2 定时器删除

    我们可以使用函数LOS_SwtmrDelete(UINT32 swtmrId)来删除定时器,下面通过分析源码看看如何删除定时器的。

    ⑴处判断定时器swtmrId是否超过OS_SWTMR_MAX_TIMERID,如果超过则返回错误码。如果定时器编号没有问题,获取定时器控制块LosSwtmrCB *swtmr。⑵处判断要删除的定时器swtmrId是否匹配,不匹配则返回错误码。⑶处判断定时器的状态,如果定时器定时器没有创建,不能删除。如果定时器计时中,需要先停止OsSwtmrStop(swtmr),然后再删除OsSwtmrDelete(swtmr)。

    LITE_OS_SEC_TEXT UINT32 LOS_SwtmrDelete(UINT32 swtmrId)
    {
        SWTMR_CTRL_S *swtmr = NULL;
        UINT32 intSave;
        UINT32 ret = LOS_OK;
        UINT16 swtmrCbId;
    
    ⑴  if (swtmrId >= OS_SWTMR_MAX_TIMERID) {
            return LOS_ERRNO_SWTMR_ID_INVALID;
        }
        intSave = LOS_IntLock();
        swtmrCbId = swtmrId % LOSCFG_BASE_CORE_SWTMR_LIMIT;
        swtmr = g_swtmrCBArray + swtmrCbId;
    ⑵  if (swtmr->usTimerID != swtmrId) {
            LOS_IntRestore(intSave);
            return LOS_ERRNO_SWTMR_ID_INVALID;
        }
    
    ⑶  switch (swtmr->ucState) {
            case OS_SWTMR_STATUS_UNUSED:
                ret = LOS_ERRNO_SWTMR_NOT_CREATED;
                break;
            case OS_SWTMR_STATUS_TICKING:
                OsSwtmrStop(swtmr);
                /* fall through */
            case OS_SWTMR_STATUS_CREATED:
                OsSwtmrDelete(swtmr);
                break;
            default:
                ret = LOS_ERRNO_SWTMR_STATUS_INVALID;
                break;
        }
    
        LOS_IntRestore(intSave);
        return ret;
    }

    接下来,我们继续看看如何调用函数OsSwtmrDelete(swtmr)删除定时器。函数特别简单,把定时器放入空闲定时器链表g_swtmrFreeList头部,然后把定时器状态改为未使用状态就完成了删除。

    STATIC_INLINE VOID OsSwtmrDelete(SWTMR_CTRL_S *swtmr)
    {
        /* insert to free list */
        swtmr->pstNext = g_swtmrFreeList;
        g_swtmrFreeList = swtmr;
        swtmr->ucState = OS_SWTMR_STATUS_UNUSED;
    
    #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1)
        (VOID)memset_s((VOID *)&g_swtmrAlignID[swtmr->usTimerID % LOSCFG_BASE_CORE_SWTMR_LIMIT],
                       sizeof(SwtmrAlignData), 0, sizeof(SwtmrAlignData));
    #endif
    }

    3.3 定时器启动

    创建完毕定时器后,我们可以使用函数LOS_SwtmrStart(UINT32 swtmrId)来启动定时器,下面通过分析源码看看如何启动定时器的。

    ⑴处判断定时器swtmrId是否超过OS_SWTMR_MAX_TIMERID,如果超过则返回错误码。如果定时器编号没有问题,获取定时器控制块LosSwtmrCB *swtmr。⑵处判断要启动的定时器swtmrId是否匹配,不匹配则返回错误码。⑶处判断定时器的状态,如果定时器定时器没有创建,不能启动。如果定时器计时中,需要先停止OsSwtmrStop(swtmr),然后再启动OsSwtmrStart(swtmr)。

    LITE_OS_SEC_TEXT UINT32 LOS_SwtmrStart(UINT32 swtmrId)
    {
        UINT32 intSave;
        UINT32 ret = LOS_OK;
    
    ⑴  if (swtmrId >= OS_SWTMR_MAX_TIMERID) {
            return LOS_ERRNO_SWTMR_ID_INVALID;
        }
    
        intSave = LOS_IntLock();
        SWTMR_CTRL_S *swtmr = g_swtmrCBArray + swtmrId % LOSCFG_BASE_CORE_SWTMR_LIMIT;
    ⑵  if (swtmr->usTimerID != swtmrId) {
            LOS_IntRestore(intSave);
            return LOS_ERRNO_SWTMR_ID_INVALID;
        }
    
    #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1)
        if ((swtmr->ucSensitive == OS_SWTMR_ALIGN_INSENSITIVE) && (swtmr->ucMode == LOS_SWTMR_MODE_PERIOD)) {
            UINT32 swtmrAlignIdIndex = swtmr->usTimerID % LOSCFG_BASE_CORE_SWTMR_LIMIT;
            g_swtmrAlignID[swtmrAlignIdIndex].canAlign = 1;
            if ((swtmr->uwInterval % LOS_COMMON_DIVISOR) == 0) {
                g_swtmrAlignID[swtmrAlignIdIndex].canMultiple = 1;
                g_swtmrAlignID[swtmrAlignIdIndex].times = swtmr->uwInterval / LOS_COMMON_DIVISOR;
            }
        }
    #endifswitch (swtmr->ucState) {
            case OS_SWTMR_STATUS_UNUSED:
                ret = LOS_ERRNO_SWTMR_NOT_CREATED;
                break;
            case OS_SWTMR_STATUS_TICKING:
                OsSwtmrStop(swtmr);
                /* fall through */
            case OS_SWTMR_STATUS_CREATED:
                OsSwtmrStart(swtmr);
                break;
            default:
                ret = LOS_ERRNO_SWTMR_STATUS_INVALID;
                break;
        }
    
        LOS_IntRestore(intSave);
        return ret;
    }

    接下来,我们继续看看如何调用函数OsSwtmrStart(swtmr)启动定时器。函数特别简单,⑴设置定时器的等待超时时间,并把定时器状态改为计时中。⑵处把该定时器插入超时排序链表中。如果已使能任务调度,则修改过期时间。

    LITE_OS_SEC_TEXT VOID OsSwtmrStart(SWTMR_CTRL_S *swtmr)
    {
        UINT64 currTime = OsGetCurrSchedTimeCycle();
    
    ⑴  swtmr->uwCount = swtmr->uwInterval;
        swtmr->ucState = OS_SWTMR_STATUS_TICKING;
    
    #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1)
        if ((g_swtmrAlignID[swtmr->usTimerID % LOSCFG_BASE_CORE_SWTMR_LIMIT].canAlign == 1) &&
            (g_swtmrAlignID[swtmr->usTimerID % LOSCFG_BASE_CORE_SWTMR_LIMIT].isAligned == 0)) {
            g_swtmrAlignID[swtmr->usTimerID % LOSCFG_BASE_CORE_SWTMR_LIMIT].isAligned = 1;
            OsSwtmrFindAlignPos(currTime, swtmr);
        }
    #endif
    ⑵  OsAdd2SortLink(&swtmr->stSortList, currTime, swtmr->uwCount, OS_SORT_LINK_SWTMR);
        if (LOS_TaskIsRunning()) {
    ⑶      OsSchedUpdateExpireTime(currTime);
        }
    }

    3.4 定时器停止

    我们可以使用函数LOS_SwtmrStop(UINT32 swtmrId)来停止定时器,下面通过分析源码看看如何停止定时器的。

    ⑴处判断定时器swtmrId是否超过OS_SWTMR_MAX_TIMERID,如果超过则返回错误码。如果定时器编号没有问题,获取定时器控制块LosSwtmrCB *swtmr。⑵处判断要启动的定时器swtmrId是否匹配,不匹配则返回错误码。⑶处判断定时器的状态,如果定时器定时器没有创建,没有启动,不能停止。如果定时器计时中,会继续调用OsSwtmrStop(swtmr)停止定时器。

    LITE_OS_SEC_TEXT UINT32 LOS_SwtmrStop(UINT32 swtmrId)
    {
        SWTMR_CTRL_S *swtmr = NULL;
        UINT32 intSave;
        UINT16 swtmrCbId;
        UINT32 ret = LOS_OK;
    
    ⑴  if (swtmrId >= OS_SWTMR_MAX_TIMERID) {
            return LOS_ERRNO_SWTMR_ID_INVALID;
        }
        intSave = LOS_IntLock();
        swtmrCbId = swtmrId % LOSCFG_BASE_CORE_SWTMR_LIMIT;
        swtmr = g_swtmrCBArray + swtmrCbId;
    ⑵  if (swtmr->usTimerID != swtmrId) {
            LOS_IntRestore(intSave);
            return LOS_ERRNO_SWTMR_ID_INVALID;
        }
    
    ⑶  switch (swtmr->ucState) {
            case OS_SWTMR_STATUS_UNUSED:
                ret = LOS_ERRNO_SWTMR_NOT_CREATED;
                break;
            case OS_SWTMR_STATUS_CREATED:
                ret = LOS_ERRNO_SWTMR_NOT_STARTED;
                break;
            case OS_SWTMR_STATUS_TICKING:
                OsSwtmrStop(swtmr);
                break;
            default:
                ret = LOS_ERRNO_SWTMR_STATUS_INVALID;
                break;
        }
    
        LOS_IntRestore(intSave);
        return ret;
    }

    接下来,我们继续看看如何调用函数OsSwtmrStop(swtmr)停止定时器。函数特别简单,⑴处从排序链表中删除该定时器的排序链表节点,更改定时器的状态。⑵如果已使能任务调度,则修改过期时间。

    LITE_OS_SEC_TEXT VOID OsSwtmrStop(SWTMR_CTRL_S *swtmr)
    {
    ⑴  OsDeleteSortLink(&swtmr->stSortList, OS_SORT_LINK_SWTMR);
        swtmr->ucState = OS_SWTMR_STATUS_CREATED;
    
        if (LOS_TaskIsRunning()) {
    ⑵       OsSchedUpdateExpireTime(OsGetCurrSchedTimeCycle());
    #if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1)
            g_swtmrAlignID[swtmr->usTimerID % LOSCFG_BASE_CORE_SWTMR_LIMIT].isAligned = 0;
    #endif
        }
    }

    4、定时器和Tick时间关系

    定时器加入到超时排序链表后,随时时间一个tick一个tick的逝去,需要不断的检查定时器是否超时到期。从之前的文章,已经知道系统每走过一个tick,系统就会调用一次Tick中断的处理函数OsTickHandler(),该函数会调用定时器扫描函数OsSwtmrScan()来扫描、更新定时器时间。我们看下OsSwtmrScan()的代码。

    ⑴处获取超时排序链表的链表节点listObject,⑵判断排序链表是否为空,为空则返回。⑶获取排序链表的下一个链表节点sortList。⑷循环遍历超时排序链表上响应时间小于等于当前时间的链表节点,意味着定时器到期,需要处理定时器的回调函数。⑸从超时排序链表中删除超时的节点,⑹获取定时器控制块SWTMR_CTRL_S *swtmr,调用函数OsSwtmrTimeoutHandle(swtmr)执行定时器回调函数,并设置需要调度的标记needSchedule。⑺如果超时排序链表为空则终止循环。

    STATIC BOOL OsSwtmrScan(VOID)
    {
        BOOL needSchedule = FALSE;
    ⑴  LOS_DL_LIST *listObject = &g_swtmrSortLinkList->sortLink;
    
    ⑵  if (LOS_ListEmpty(listObject)) {
            return needSchedule;
        }
    
    ⑶  SortLinkList *sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);
        UINT64 currTime = OsGetCurrSchedTimeCycle();
    ⑷  while (sortList->responseTime <= currTime) {
    ⑸      OsDeleteNodeSortLink(g_swtmrSortLinkList, sortList);
    
    ⑹      SWTMR_CTRL_S *swtmr = LOS_DL_LIST_ENTRY(sortList, SWTMR_CTRL_S, stSortList);
            OsSwtmrTimeoutHandle(swtmr);
    
            needSchedule = TRUE;
    ⑺      if (LOS_ListEmpty(listObject)) {
                break;
            }
    
            sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);
        }
    
        return needSchedule;
    }

    我们最后看下函数OsSwtmrTimeoutHandle()。⑴处把定时器回调函数写入定时器队列。⑵如果是一次性定时器,会把这个定时器删除,回收到空闲定时器链表,状态设置为未使用状态,然后更新定时器的编号timerId。⑶如果定时器属于周期性定时器,重新启动定时器。⑷如果是一次性定时器但不删除,则把定时器状态设置为创建状态。

    STATIC VOID OsSwtmrTimeoutHandle(SWTMR_CTRL_S *swtmr)
    {
        SwtmrHandlerItem swtmrHandler;
    
        swtmrHandler.handler = swtmr->pfnHandler;
        swtmrHandler.arg = swtmr->uwArg;
    
    ⑴  (VOID)LOS_QueueWriteCopy(g_swtmrHandlerQueue, &swtmrHandler, sizeof(SwtmrHandlerItem), LOS_NO_WAIT);
    ⑵  if (swtmr->ucMode == LOS_SWTMR_MODE_ONCE) {
            OsSwtmrDelete(swtmr);
            if (swtmr->usTimerID < (OS_SWTMR_MAX_TIMERID - LOSCFG_BASE_CORE_SWTMR_LIMIT)) {
                swtmr->usTimerID += LOSCFG_BASE_CORE_SWTMR_LIMIT;
            } else {
                swtmr->usTimerID %= LOSCFG_BASE_CORE_SWTMR_LIMIT;
            }
    ⑶  } else if (swtmr->ucMode == LOS_SWTMR_MODE_PERIOD) {
            OsSwtmrStart(swtmr);
    ⑷  } else if (swtmr->ucMode == LOS_SWTMR_MODE_NO_SELFDELETE) {
            swtmr->ucState = OS_SWTMR_STATUS_CREATED;
        }
    }

    小结

    本文带领大家一起剖析了鸿蒙轻内核的定时器模块的源代码,包含定时器的结构体、定时器池初始化、定时器创建、删除、启动停止等。感谢阅读,如有任何问题、建议,都可以留言给我们: https://gitee.com/openharmony/kernel_liteos_m/issues 。为了更容易找到鸿蒙轻内核代码仓,建议访问 https://gitee.com/openharmony/kernel_liteos_m ,关注Watch、点赞Star、并Fork到自己账户下,谢谢。

     

    点击关注,第一时间了解华为云新鲜技术~

  • 相关阅读:
    Checking Types Against the Real World in TypeScript
    nexus pip proxy config
    go.rice 强大灵活的golang 静态资源嵌入包
    几个golang 静态资源嵌入包
    rpm 子包创建学习
    Rpm Creating Subpackages
    ava 类似jest snapshot 功能试用
    ava js 测试框架基本试用
    The Architectural Principles Behind Vrbo’s GraphQL Implementation
    graphql-compose graphql schema 生成工具集
  • 原文地址:https://www.cnblogs.com/huaweiyun/p/15073881.html
Copyright © 2011-2022 走看看