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

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

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

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

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

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

1.1 定时器结构体定义

在文件kernel\include\los_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 定时器常用宏定义

定时器头文件kernel\include\los_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进行关闭。开启定时器的情况下,在系统启动时,在kernel\src\los_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)\n",
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;
}
}
#endif ⑶ 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:
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到自己账户下,谢谢。

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

鸿蒙轻内核定时器Swtmr:不受硬件和数量限制,满足用户需求的更多相关文章

  1. 从五大结构体,带你掌握鸿蒙轻内核动态内存Dynamic Memory

    摘要:本文带领大家一起剖析了鸿蒙轻内核的动态内存模块的源代码,包含动态内存的结构体.动态内存池初始化.动态内存申请.释放等. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列九 动态内存Dyna ...

  2. 深层剖析鸿蒙轻内核M核的动态内存如何支持多段非连续性内存

    摘要:鸿蒙轻内核M核新增支持了多段非连续性内存区域,把多个非连续性内存逻辑上合一,用户不感知底层的不同内存块. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列九 动态内存Dynamic Mem ...

  3. 鸿蒙轻内核M核的故障管家:Fault异常处理

    摘要:本文先简单介绍下Fault异常类型,向量表及其代码,异常处理C语言程序,然后详细分析下异常处理汇编函数实现代码. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十八 Fault异常处理& ...

  4. 带你熟悉鸿蒙轻内核Kconfig使用指南

    摘要:本文介绍了Kconfig的基础知识,和鸿蒙轻内核的图形化配置及进阶的使用方法. 本文分享自华为云社区<鸿蒙轻内核Kconfig使用笔记>,作者: zhushy. 1. Kconfig ...

  5. 鸿蒙轻内核M核源码分析:LibC实现之Musl LibC

    摘要:本文学习了LiteOS-M内核Musl LibC的实现,特别是文件系统和内存分配释放部分. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十九 Musl LibC>,作者:zhus ...

  6. 鸿蒙轻内核源码分析:文件系统LittleFS

    摘要:本文先介绍下LFS文件系统结构体的结构体和全局变量,然后分析下LFS文件操作接口. 本文分享自华为云社区<# 鸿蒙轻内核M核源码分析系列二一 02 文件系统LittleFS>,作者: ...

  7. 鸿蒙轻内核源码分析:文件系统FatFS

    摘要:本文为大家介绍FatFS文件系统结构体的结构体和全局变量,并分析FatFS文件操作接口. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列二一 03 文件系统FatFS>,作者:zh ...

  8. 底半部之工作队列和tasklet,内核定时器。

    1.软中断机制  不能以模块形式出现   使用起来不够灵活2.tasklet  核心数据结构       struct tasklet_struct      {          function  ...

  9. Linux内核——定时器和时间管理

    定时器和时间管理 系统定时器是一种可编程硬件芯片.它能以固定频率产生中断.该中断就是所谓的定时器中断.它所相应的中断处理程序负责更新系统时间,还负责执行须要周期性执行的任务. 系统定时器和时钟中断处理 ...

随机推荐

  1. 四、SSL虚拟证书

    沿用练习三,配置基于加密网站的虚拟主机,实现以下目标: 域名为www.c.com 该站点通过https访问 通过私钥.证书对该站点所有数据加密 4.2 方案 源码安装Nginx时必须使用--with- ...

  2. NX二次开发-矩阵乘矩阵的几何意义

    函数:UF_MTX3_multiply() 或者UF_MTX3_multiply_t().推荐使用UF_MTX3_multiply() 函数说明:矩阵相乘,得到新的矩阵,如下图WCS与ABS重合,在暗 ...

  3. asp.net core配合vue实现后端验证码逻辑

    概述 网上的前端验证码逻辑总感觉不安全,验证码建议还是使用后端配合验证. 如果产品确定可以上网的话,就可以使用腾讯,百度等第三方验证,对接方便.但是产品可能内网部署,就必须自己写了. 本文章就是基于这 ...

  4. 基于ABP落地领域驱动设计-02.聚合和聚合根的最佳实践和原则

    目录 前言 聚合 聚合和聚合根原则 包含业务原则 单个单元原则 事务边界原则 可序列化原则 聚合和聚合根最佳实践 只通过ID引用其他聚合 用于 EF Core 和 关系型数据库 保持聚合根足够小 聚合 ...

  5. 玩转STM32MP157- 使用fbtft驱动 lcd ili9341

    之前使用了 fbtft 成功驱动了lcd st7735r,现在尝试下驱动 ili9341, 配置 跟之前用 fbtft 驱动 st7735r 一样,先用 make menuconfig 配置内核,添加 ...

  6. 创建Akamai cdn api授权

    注:通过Akamai Cli purge和通过Akamai API进行刷新之前,都要事先创建类似于如下的刷新的凭据,这两种刷新方式所创建的凭据是相同的. 目的:创建Akamai CDN API授权以便 ...

  7. Redis在linux系统中的优化

    通常来看,Redis开发和运维人员更加关注的是Redis本身的一些配置优化,例如AOF和RDB的配置优化.数据结构的配置优化等,但是对于操作系统是否需要针对Redis做一些配置优化不甚了解或者不太关心 ...

  8. Linux中curl的用法

    一.简介:在Linux中curl是一个利用URL规则在命令行下工作的文件传输工具,是一款强大的http命令行工具.支持文件的上传和下载,是综合传输工具. 二.语法:curl [option] [url ...

  9. @EnableDiscoveryClient与Nacos自动注册

    前一阵看到有篇博客说cloud从Edgware版本开始,可以不加@EnableDiscoveryClient注解,只要配置好注册中心的相关配置即可自动开启服务注册功能,比较好奇其中的原理,研究了一番特 ...

  10. SDLC开发安全流程

    2020年12月9日跟内部小伙伴分享SDLC流程及安全的一些思考,简单画了一个图,时间过得太快,记录一下 参考 https://blog.csdn.net/liqiuman180688/article ...