LiteOS内核源码分析系列十一 软件定时器Swtmr

举报
zhushy 发表于 2021/04/17 16:42:37 2021/04/17
【摘要】 LiteOS内核源码分析系列十一 软件定时器Swtmr软件定时器(Software Timer)是基于系统Tick时钟中断且由软件来模拟的定时器。当经过设定的Tick数后,会触发用户自定义的回调函数。硬件定时器受硬件的限制,数量上不足以满足用户的实际需求。Huawei LiteOS提供了软件定时器功能可以提供更多的定时器,满足用户需求。本文通过分析LiteOS定时器模块的源码,掌握定时器使...

LiteOS内核源码分析系列十一 软件定时器Swtmr

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

本文通过分析LiteOS定时器模块的源码,掌握定时器使用上的差异。LiteOS定时器模块的源代码,均可以在LiteOS开源站点https://gitee.com/LiteOS/LiteOS 获取。定时器源代码、开发文档,示例程序代码如下:


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

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

1.1 定时器结构体定义

在文件kernel\base\include\los_swtmr_pri.h定义的定时器控制块结构体为LosSwtmrCB,结构体源代码如下。定时器状态.state取值OS_SWTMR_STATUS_UNUSEDOS_SWTMR_STATUS_CREATEDOS_SWTMR_STATUS_TICKING,定时器模式.mode取值LOS_SWTMR_MODE_ONCELOS_SWTMR_MODE_PERIODLOS_SWTMR_MODE_NO_SELFDELETE。其他结构体成员的解释见注释部分。

typedef struct {
    SortLinkList sortList;   /**< 定时器排序链表 */
    UINT8 state;             /**< 定时器状态,取值枚举SwtmrState */
    UINT8 mode;              /**< 定时器模式,取值枚举enSwTmrType */
    UINT8 overrun;           /**< 周期定时器重复运行的次数 */
    UINT16 timerId;          /**< 定时器编号Id */
    UINT32 interval;         /**< 周期定时器超时间隔 (单位: tick) */
    UINT32 expiry;           /**< 一次性定时器超时间隔 (单位: tick) */
#ifdef LOSCFG_KERNEL_SMP
    UINT32 cpuid;            /**< 定时器运行的CPU */
#endif
    UINTPTR arg;             /**< 定时器超时回调函数参数*/
    SWTMR_PROC_FUNC handler; /**< 定时器超时回调函数 */
} LosSwtmrCB;

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

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

1.2 定时器常用宏定义

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

#define OS_SWT_FROM_SID(swtmrId) ((LosSwtmrCB *)g_swtmrCBArray + ((swtmrId) % LOSCFG_BASE_CORE_SWTMR_LIMIT))

文件kernel\base\include\los_swtmr_pri.h中定义的定时器状态枚举SwtmrState,如下:

enum SwtmrState {
    OS_SWTMR_STATUS_UNUSED,     /**< 定时器未使用    */
    OS_SWTMR_STATUS_CREATED,    /**< 定时器已创建     */
    OS_SWTMR_STATUS_TICKING     /**< 定时器计时中     */
};

文件kernel\include\los_swtmr.h中定义的定时器类型枚举enSwTmrType,如下:

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进行关闭。开启定时器的情况下,在系统启动时,在kernel\init\los_init.c中调用OsSwtmrInit()进行定时器模块初始化。下面,我们分析下定时器初始化的代码。

⑴定时器只在CPU零核上进行初始化,⑵定时器申请内存,如果申请失败,则返回错误。⑶初始化双向循环链表g_swtmrFreeList,维护未使用的定时器。循环每一个定时器进行初始化,为每一个定时器节点指定索引timerId,并把定时器节点插入未使用定时器双向链表g_swtmrFreeList,链接用的节点是.sortList.sortLinkNode

LOSCFG_BASE_CORE_SWTMR_IN_ISR表示定时器回调函数可以在中断中执行,默认关闭。如果开启的话,定时器回调直接在中断中执行,不需要创建定时器队列和定时器任务。在LOSCFG_BASE_CORE_SWTMR_IN_ISR关闭时,执行⑷处的代码为定时器创建队列,队列的消息大小OS_SWTMR_HANDLE_QUEUE_SIZE等于定时器的数量LOSCFG_BASE_CORE_SWTMR_LIMIT,消息内容的最大大小为sizeof(CHAR *),4字节,说明队列消息是指针。后文分析定时器队列读取写入消息的时候具体来看是什么消息。⑸处调用函数OsSwtmrTaskCreate()创建定时器任务,定时器任务优先级最高,任务的入口函数为OsSwtmrTask(),后文会分析该函数。⑹处初始化排序链表,源码分析系列之前的文章分析过,可以阅读下排序链表数据结构章节。

LITE_OS_SEC_TEXT_INIT UINT32 OsSwtmrInit(VOID)
{
    UINT32 size;
    UINT16 index;
    UINT32 ret;
    LosSwtmrCB *swtmr = NULL;
    UINT32 cpuid = ArchCurrCpuid();if (cpuid == 0) {
        size = sizeof(LosSwtmrCB) * LOSCFG_BASE_CORE_SWTMR_LIMIT;
⑵      swtmr = (LosSwtmrCB *)LOS_MemAlloc(m_aucSysMem0, size);
        if (swtmr == NULL) {
            return LOS_ERRNO_SWTMR_NO_MEMORY;
        }

        (VOID)memset_s(swtmr, size, 0, size);
        g_swtmrCBArray = swtmr;LOS_ListInit(&g_swtmrFreeList);
        for (index = 0; index < LOSCFG_BASE_CORE_SWTMR_LIMIT; index++, swtmr++) {
            swtmr->timerId = index;
            LOS_ListTailInsert(&g_swtmrFreeList, &swtmr->sortList.sortLinkNode);
        }
    }

#ifndef LOSCFG_BASE_CORE_SWTMR_IN_ISR
⑷  ret = LOS_QueueCreate(NULL, OS_SWTMR_HANDLE_QUEUE_SIZE, &g_percpu[cpuid].swtmrHandlerQueue, 0, sizeof(CHAR *));
    if (ret != LOS_OK) {
        return LOS_ERRNO_SWTMR_QUEUE_CREATE_FAILED;
    }

⑸  ret = OsSwtmrTaskCreate();
    if (ret != LOS_OK) {
        return LOS_ERRNO_SWTMR_TASK_CREATE_FAILED;
    }
#endif

⑹  ret = OsSortLinkInit(&g_percpu[cpuid].swtmrSortLink);
    if (ret != LOS_OK) {
        return LOS_ERRNO_SWTMR_SORTLINK_CREATE_FAILED;
    }

    return LOS_OK;
}

我们再看一下定时器任务的入口函数为OsSwtmrTask()。⑴获取定时器队列的编号swtmrHandlerQueue,然后进行for永久循环,队列读取不到数据时会阻塞,因为优先级比较高,定时器队列有数据时该任务就会执行。从⑵处可以了解,从定时器队列中读取的是结构体的指针地址SwtmrHandlerItemPtr swtmrHandler,读取的长度为readSize。成功读取后,获取定时器回调函数及其参数,然后⑷释放定时器消息占用的内存。⑸处执行定时器回调函数。

LITE_OS_SEC_TEXT VOID OsSwtmrTask(VOID)
{
    UINT32 ret, swtmrHandlerQueue;
    SwtmrHandlerItemPtr swtmrHandler = NULL;
    UINT32 readSize;
    readSize = sizeof(CHAR *);

⑴  swtmrHandlerQueue = OsPercpuGet()->swtmrHandlerQueue;
    for (;;) {
⑵      ret = LOS_QueueReadCopy(swtmrHandlerQueue, &swtmrHandler, &readSize, LOS_WAIT_FOREVER);
        if ((ret == LOS_OK) && (readSize == sizeof(CHAR *))) {
⑶          SWTMR_PROC_FUNC handler = swtmrHandler->handler;
            UINTPTR arg = swtmrHandler->arg;(VOID)LOS_MemFree(m_aucSysMem0, swtmrHandler);
            if (handler != NULL) {handler(arg);
            }
        }
    }
}

3、定时器常用操作

3.1 定时器创建

我们分析下创建定时器函数LOS_SwtmrCreate()的代码。⑴处对传入参数定时器超时间隔、定时器模式、回调函数,定时器编号进行校验。⑵判断定时器池是否为空,为空则返回错误,无法创建定时器。⑶处如果定时器不为空,则获取定时器控制块swtmr。⑷处对定时器控制块信息进行初始化。⑸处把该定时器排序链表节点的滚动数初始化为0。

LITE_OS_SEC_TEXT_INIT UINT32 LOS_SwtmrCreate(UINT32 interval,
                                             UINT8 mode,
                                             SWTMR_PROC_FUNC handler,
                                             UINT16 *swtmrId,
                                             UINTPTR arg)
{
    LosSwtmrCB *swtmr = NULL;
    UINT32 intSave;
    SortLinkList *sortList = NULL;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;
    }

    SWTMR_LOCK(intSave);if (LOS_ListEmpty(&g_swtmrFreeList)) {
        SWTMR_UNLOCK(intSave);
        return LOS_ERRNO_SWTMR_MAXSIZE;
    }

⑶  sortList = LOS_DL_LIST_ENTRY(g_swtmrFreeList.pstNext, SortLinkList, sortLinkNode);
    swtmr = LOS_DL_LIST_ENTRY(sortList, LosSwtmrCB, sortList);
    LOS_ListDelete(LOS_DL_LIST_FIRST(&g_swtmrFreeList));
    SWTMR_UNLOCK(intSave);

⑷  swtmr->handler = handler;
    swtmr->mode = mode;
    swtmr->overrun = 0;
    swtmr->interval = interval;
    swtmr->expiry = interval;
    swtmr->arg = arg;
    swtmr->state = OS_SWTMR_STATUS_CREATED;SET_SORTLIST_VALUE(&(swtmr->sortList), 0);
    *swtmrId = swtmr->timerId;
    LOS_TRACE(SWTMR_CREATE, swtmr->timerId);

    return LOS_OK;
}

3.2 定时器删除

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

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

LITE_OS_SEC_TEXT UINT32 LOS_SwtmrDelete(UINT16 swtmrId)
{
    LosSwtmrCB *swtmr = NULL;
    UINT32 intSave;
    UINT32 ret = LOS_OK;
    UINT16 swtmrCBId;if (swtmrId >= OS_SWTMR_MAX_TIMERID) {
        return LOS_ERRNO_SWTMR_ID_INVALID;
    }

    SWTMR_LOCK(intSave);
    swtmrCBId = swtmrId % LOSCFG_BASE_CORE_SWTMR_LIMIT;
    swtmr = g_swtmrCBArray + swtmrCBId;if (swtmr->timerId != swtmrId) {
        SWTMR_UNLOCK(intSave);
        return LOS_ERRNO_SWTMR_ID_INVALID;
    }switch (swtmr->state) {
        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;
    }

    SWTMR_UNLOCK(intSave);
    LOS_TRACE(SWTMR_DELETE, swtmr->timerId);
    return ret;
}

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

STATIC INLINE VOID OsSwtmrDelete(LosSwtmrCB *swtmr)
{
    LOS_ListTailInsert(&g_swtmrFreeList, &swtmr->sortList.sortLinkNode);
    swtmr->state = OS_SWTMR_STATUS_UNUSED;
}

3.3 定时器启动

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

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

LITE_OS_SEC_TEXT UINT32 LOS_SwtmrStart(UINT16 swtmrId)
{
    LosSwtmrCB *swtmr = NULL;
    UINT32 intSave;
    UINT32 ret = LOS_OK;
    UINT16 swtmrCBId;if (swtmrId >= OS_SWTMR_MAX_TIMERID) {
        return LOS_ERRNO_SWTMR_ID_INVALID;
    }

    SWTMR_LOCK(intSave);
    swtmrCBId = swtmrId % LOSCFG_BASE_CORE_SWTMR_LIMIT;
    swtmr = g_swtmrCBArray + swtmrCBId;if (swtmr->timerId != swtmrId) {
        SWTMR_UNLOCK(intSave);
        return LOS_ERRNO_SWTMR_ID_INVALID;
    }switch (swtmr->state) {
        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;
    }

    SWTMR_UNLOCK(intSave);
    LOS_TRACE(SWTMR_START, swtmr->timerId, swtmr->mode, swtmr->overrun, swtmr->interval, swtmr->expiry);
    return ret;
}

接下来,我们继续看看如何调用函数OsSwtmrStart(swtmr)启动定时器。函数特别简单,⑴如果是一次性定时器,把该定时器排序链表节点的滚动数初始化为.expiry。⑵如果是周期定时器,把该定时器排序链表节点的滚动数初始化为.interval。⑶然后把该定时器排序链表节点插入超时排序链表中,并把定时器状态改为计时中。

LITE_OS_SEC_TEXT VOID OsSwtmrStart(LosSwtmrCB *swtmr)
{if ((swtmr->overrun == 0) && ((swtmr->mode == LOS_SWTMR_MODE_ONCE) ||
        (swtmr->mode == LOS_SWTMR_MODE_OPP) ||
        (swtmr->mode == LOS_SWTMR_MODE_NO_SELFDELETE))) {
        SET_SORTLIST_VALUE(&(swtmr->sortList), swtmr->expiry);
    } else {SET_SORTLIST_VALUE(&(swtmr->sortList), swtmr->interval);
    }OsAdd2SortLink(&OsPercpuGet()->swtmrSortLink, &swtmr->sortList);

    swtmr->state = OS_SWTMR_STATUS_TICKING;

#ifdef LOSCFG_KERNEL_SMP
    swtmr->cpuid = ArchCurrCpuid();
#endif
}

3.4 定时器停止

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

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

LITE_OS_SEC_TEXT UINT32 LOS_SwtmrStop(UINT16 swtmrId)
{
    LosSwtmrCB *swtmr = NULL;
    UINT32 intSave;
    UINT32 ret = LOS_OK;
    UINT16 swtmrCBId;if (swtmrId >= OS_SWTMR_MAX_TIMERID) {
        return LOS_ERRNO_SWTMR_ID_INVALID;
    }

    SWTMR_LOCK(intSave);
    swtmrCBId = swtmrId % LOSCFG_BASE_CORE_SWTMR_LIMIT;
    swtmr = g_swtmrCBArray + swtmrCBId;if (swtmr->timerId != swtmrId) {
        SWTMR_UNLOCK(intSave);
        return LOS_ERRNO_SWTMR_ID_INVALID;
    }switch (swtmr->state) {
        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;
    }

    SWTMR_UNLOCK(intSave);
    LOS_TRACE(SWTMR_STOP, swtmr->timerId);
    return ret;
}

接下来,我们继续看看如何调用函数OsSwtmrStop(swtmr)停止定时器。函数特别简单,⑴处首先获取超时排序链表,然后⑵从排序链表中删除该定时器的排序链表节点。最后执行⑶更改定时器的状态。

LITE_OS_SEC_TEXT STATIC VOID OsSwtmrStop(LosSwtmrCB *swtmr)
{
    SortLinkAttribute *sortLinkHeader = NULL;

#ifdef LOSCFG_KERNEL_SMP
    sortLinkHeader = &g_percpu[swtmr->cpuid].swtmrSortLink;
#else
⑴  sortLinkHeader = &g_percpu[0].swtmrSortLink;
#endifOsDeleteSortLink(sortLinkHeader, &swtmr->sortList);

⑶  swtmr->state = OS_SWTMR_STATUS_CREATED;
    swtmr->overrun = 0;
}

4、定时器和Tick时间关系

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

⑴处获取超时排序链表,然后更新排序链表的游标,获取超时排序链表的链表节点listObject。⑵判断排序链表是否为空,为空则返回。⑶获取排序链表的下一个链表节点sortList,然后把链表节点的滚动数减少1。⑷循环遍历超时排序链表上滚动数为0的链表节点,滚动数为0,意味着定时器到期,需要处理定时器的汇回调数。⑸从超时排序链表中删除超时的节点,然后获取定时器控制块LosSwtmrCB *swtmr

当关闭LOSCFG_BASE_CORE_SWTMR_IN_ISR宏时,⑹为定时器回调函数结构体申请内存,申请成功则执行⑺处代码,把定时器回调函数和参数的指针赋值给回调函数结构体变量的swtmrHandler的成员。⑻把定时器回调函数结构体变量swtmrHandler的指针写入定时器队列,如果写入失败会调用LOS_MemFree(),写入成功则不会释放内存。⑼处调用OsSwtmrUpdate(swtmr)更新定时器,稍后分析其代码。当开启LOSCFG_BASE_CORE_SWTMR_IN_ISR宏时,不需要把定时器的回调函数写入定时器队列,直接处理执行。⑽处代码从定时器控制块中获取回调函数及参数,然后⑾执行定时器的回调函数。⑿如果超时排序链表为空,跳出循环。否则,获取排序链表上的下一个节点,继续循环遍历,来处理同时多个定时器到期的情况。

LITE_OS_SEC_TEXT VOID OsSwtmrScan(VOID)
{
    SortLinkList *sortList = NULL;
    LosSwtmrCB *swtmr = NULL;
    LOS_DL_LIST *listObject = NULL;
⑴  SortLinkAttribute* swtmrSortLink = &OsPercpuGet()->swtmrSortLink;

    SORTLINK_CURSOR_UPDATE(swtmrSortLink->cursor);
    SORTLINK_LISTOBJ_GET(listObject, swtmrSortLink);

    LOS_SpinLock(&g_swtmrSpin);if (LOS_ListEmpty(listObject)) {
        LOS_SpinUnlock(&g_swtmrSpin);
        return;
    }
⑶  sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);
    ROLLNUM_DEC(sortList->idxRollNum);while (ROLLNUM(sortList->idxRollNum) == 0) {
        sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);LOS_ListDelete(&sortList->sortLinkNode);
        swtmr = LOS_DL_LIST_ENTRY(sortList, LosSwtmrCB, sortList);

#ifndef LOSCFG_BASE_CORE_SWTMR_IN_ISR
        LOS_TRACE(SWTMR_EXPIRED, swtmr->timerId);
⑹      SwtmrHandlerItemPtr swtmrHandler = (SwtmrHandlerItemPtr)LOS_MemAlloc(m_aucSysMem0, sizeof(SwtmrHandlerItem));
        if (swtmrHandler != NULL) {
⑺          swtmrHandler->handler = swtmr->handler;
            swtmrHandler->arg = swtmr->arg;if (LOS_QueueWriteCopy(OsPercpuGet()->swtmrHandlerQueue, &swtmrHandler, sizeof(CHAR *), LOS_NO_WAIT)) {
                (VOID)LOS_MemFree(m_aucSysMem0, swtmrHandler);
            }
        }OsSwtmrUpdate(swtmr);
#else
⑽      SWTMR_PROC_FUNC handler = swtmr->handler;
        UINTPTR arg = swtmr->arg;
        OsSwtmrUpdate(swtmr);
        if (handler != NULL) {
            LOS_SpinUnlock(&g_swtmrSpin);
            LOS_TRACE(SWTMR_EXPIRED, swtmr->timerId);handler(arg);
            LOS_SpinLock(&g_swtmrSpin);
        }
#endifif (LOS_ListEmpty(listObject)) {
            break;
        }
        sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);
    }

    LOS_SpinUnlock(&g_swtmrSpin);
}

我们最后看下函数OsSwtmrUpdate()。⑴处如果是一次性定时器,会把这个定时器删除,回收到定时器池,状态设置为未使用状态,然后更新定时器的编号timerId。⑵如果是一次性定时器但不删除,则把定时器状态设置为创建状态。否则定时器属于周期性定时器,执行⑶记录定时器执行的次数.overrun,然后重新启动定时器。

STATIC INLINE VOID OsSwtmrUpdate(LosSwtmrCB *swtmr)
{if (swtmr->mode == LOS_SWTMR_MODE_ONCE) {
        OsSwtmrDelete(swtmr);
        if (swtmr->timerId < (OS_SWTMR_MAX_TIMERID - LOSCFG_BASE_CORE_SWTMR_LIMIT)) {
            swtmr->timerId += LOSCFG_BASE_CORE_SWTMR_LIMIT;
        } else {
            swtmr->timerId %= LOSCFG_BASE_CORE_SWTMR_LIMIT;
        }} else if (swtmr->mode == LOS_SWTMR_MODE_NO_SELFDELETE) {
        swtmr->state = OS_SWTMR_STATUS_CREATED;
    } else {
⑶      swtmr->overrun++;
        OsSwtmrStart(swtmr);
    }
}

小结

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

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。