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

本文分享自华为云社区《鸿蒙轻内核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. 基本FTP服务: 文件传输协议

    1.服务端虚拟机Server,安装可以提供FTP服务软件 [root@server0 ~]# yum -y install vsftpd 2.虚拟机Server操作,启动 vsftpd服务,设置为开机 ...

  2. 04-拉格朗日对偶问题和KKT条件

    04-拉格朗日对偶问题和KKT条件 目录 一.拉格朗日对偶函数 二.拉格朗日对偶问题 三.强弱对偶的几何解释 四.鞍点解释 4.1 鞍点的基础定义 4.2 极大极小不等式和鞍点性质 五.最优性条件与 ...

  3. 强烈推荐!15 个Github 顶级Java教程类开源项目

    大家好,我是 Guide 哥!今天给大家推荐 15 个新手也能看懂的 Java 教程方向的开源项目.这些项目无论是对于你学习 Java 还是准备 Java 方向的面试都非常有帮助. 正如我第一个要推荐 ...

  4. 题解 P2257 YY的GCD

    P2257 YY的GCD 解题思路 果然数论的题是真心不好搞. 第一个莫比乌斯反演的题,好好推一下式子吧..(借鉴了blog) 我们要求的答案就是\(Ans=\sum\limits_{i=1}^{n} ...

  5. 【题解】10-19秀秀的森林(forest)

    我恨秀秀倍增LCA+离线 (时光倒流) 题目 秀秀有一棵带n个顶点的树T,每个节点有一个点权ai-.有一天,她想拥有两棵树,于是她从T中删去了一条边.第二天,她认为三棵树或许会更好一些.因此,她又从她 ...

  6. Docker笔记--操作容器命令

    Docker笔记--操作容器命令 创建容器 docker [container] create-- 创建容器,使用docker [container] create命令新建的容器处于停止状态,可以使用 ...

  7. Centos7搭建内网DNS服务器

      一.配置阿里云yum源 执行脚本配置阿里云的yum源,已配置yum源的可以忽略 #!/bin/bash # ******************************************** ...

  8. IDEA搭建一个SpringBoot项目——十分详细(web+mysql)

    ​前排提示: IDEA版本:IntelliJ IDEA 2021.1.1 专业版(是否为专业版影响不大) 搭建目的:前端web页面能够获取到MySQL数据库中的数据 详细步骤: 1. 创建一个新项目 ...

  9. 浅谈HttpDNS

    今天了解了腾讯云的HTTPDNS,这里使用腾讯的资料以及网上查阅的资料做个记录. 对互联网高度依赖的企业,不可避免的需要通过域名来提供互联网服务,而在复杂的互联网环境下,域名被缓存,被劫持导致的业务影 ...

  10. 乘风破浪,Windows11预览版升级和安装,积极准备中的大跃进

    安装Windows11 暂时官方还没出可靠的ISO 升级到Windows11 预览版 关于一些限制 目前DEV预览通道对从老系统升级到Windows11暂时没有什么限制,只是会提示你可能不太好,但是安 ...