鸿蒙内核阅读笔记-定时器

2023-11-07

简介

近期在阅读鸿蒙liteOS_a,由于是初次探索内核的奥秘。将一些阅读的心得进行分享。希望能在作为笔记的同时,也能帮助更多人学习。
感谢图灵大佬的注释项目,使我能够更加快速的理解。
https://weharmony.github.io/

核心模块

核心模块位于:kernel -> base -> core
其中包括:
los_bitmap.c 用于位操作,改变标志位。
los_process.c 用于控制并发、并行、单核多进程、多核多线程的管理
los_sortlik.c 用于排序
los_swtmr.c 用于定时器
los_sys.c 用于时间管理,转换秒与毫秒,了解当前系统运行时间
los_task.c 用于任务状态管理,一个任务代表一个线程,管理其运行状态。
los_tick.c 用于系统时钟、节拍器。

定时器(los_swtmr.c)

介绍

系统定时器个人理解为用来控制系统中断或者定时触发回调函数。

定时器分为三种状态:
1、 OS_SWTMR_STATUS_UNUSED(定时器未使用)
系统在定时器模块初始化时,会将系统中所有定时器资源初始化成该状态。

2、 OS_SWTMR_STATUS_TICKING(定时器处于计数状态)
在定时器创建后调用LOS_SwtmrStart接口启动,定时器将变成该状态,是定时器运行时的状态。

3、 OS_SWTMR_STATUS_CREATED(定时器创建后未启动,或已停止)
定时器创建后,不处于计数状态时,定时器将变成该状态。

软件定时器提供三种模式:
1、单次触发定时器,这类定时器在启动后只会触发一次定时器事件,然后定时器自动删除。

2、周期触发定时器,这类定时器会周期性的触发定时器事件,直到用户手动停止定时器,否则将永远持续执行下去。

3、单次触发定时器,但这类定时器超时触发后不会自动删除,需要调用定时器删除接口删除定时器。

阅读代码

LITE_OS_SEC_BSS SWTMR_CTRL_S    *g_swtmrCBArray = NULL; 
/*
    swtmr = software timer 软件定时器。
    CBArray是阵列。
    合起来叫做:定时器池。
*/
LITE_OS_SEC_BSS UINT8           *g_swtmrHandlerPool = NULL; 
/*
    赋值上软时钟的回调函数,软时钟结束会调用它。
*/
LITE_OS_SEC_BSS LOS_DL_LIST     g_swtmrFreeList; 

/*
    LOS_DL_LIST 这是双向链表,主要使用承上启下。
    定时器通常至少要包含两个成员:一个超时时间(相对时间或者绝对时间)和一个任务回调函数。
    有的时候还可能包含回调函数被执行时需要传入的参数,以及是否重启定时器等信息。
    链表是双向的,则每个定时器包含指向前一个定时器的指针成员。(有点像某区块链)
*/

LITE_OS_SEC_BSS  SPIN_LOCK_INIT(g_swtmrSpin); 
/*
    初始化软时钟自旋锁,只有SMP情况才需要,只要是自旋锁都是用于CPU多核的同步。
    自旋锁是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。
    我简单理解自旋锁为控制多处理器并发,控制竞争。
*/
#define SWTMR_LOCK(state)       LOS_SpinLockSave(&g_swtmrSpin, &(state)) 
/*
    持有软时钟自旋锁
    这里定义个SWTMR_LOCK常量,用于锁定软时钟的自旋锁。
*/
#define SWTMR_UNLOCK(state)     LOS_SpinUnlockRestore(&g_swtmrSpin, (state)) 
/*
    释放软时钟自旋锁
    这里定义个SWTMR_LOCK常量,用于解除锁定软时钟的自旋锁。
*/

函数部分

LITE_OS_SEC_TEXT VOID OsSwtmrTask(VOID)
/*
    函数作为软时钟的入口。
*/
{
    SwtmrHandlerItemPtr swtmrHandlePtr = NULL;
    /*
        在kernel/base/include/los_swtmr_pri.h
        typedef SwtmrHandlerItem *SwtmrHandlerItemPtr;
        指向处理软件计时器超时的回调函数结构的指针的类型。
        个人理解用于定位。
    */
    SwtmrHandlerItem swtmrHandle;
    /*
        SwtmrHandlerItem是定时器响应函数。
    */
    UINT32 ret, swtmrHandlerQueue;
    /*
        swtmrHandlerQueue是定时器处理队列。
    */
    swtmrHandlerQueue = OsPercpuGet()->swtmrHandlerQueue;
    /*
        OsPercpuGet 获取CPU GET之中的定时器超时队列。
    */
    for (;;) {
        // 死循环获取队列item,一直读干净为止 遍历队列!
        ret = LOS_QueueRead(swtmrHandlerQueue, &swtmrHandlePtr, sizeof(CHAR *), LOS_WAIT_FOREVER);
        // 一个一个读队列
        if ((ret == LOS_OK) && (swtmrHandlePtr != NULL)) {
            /*
                判断队列未 LOS_OK,以及定位指针被移动不为初始值NULL。
                主要判断队列是否为空和是否完成。
            */
            swtmrHandle.handler = swtmrHandlePtr->handler;
            //超时中断处理函数,也称回调函数
            swtmrHandle.arg = swtmrHandlePtr->arg;
            //回调函数的参数
            (VOID)LOS_MemboxFree(g_swtmrHandlerPool, swtmrHandlePtr);
            //静态释放内存,注意在鸿蒙内核只有软时钟注册用到了静态内存
            if (swtmrHandle.handler != NULL) {
                // 确认回调函数存在,然后执行回调函数。
                swtmrHandle.handler(swtmrHandle.arg);
                // 回调函数处理函数
            }
        }
    }
}
//创建软时钟任务,每个cpu core都可以拥有自己的软时钟任务
LITE_OS_SEC_TEXT_INIT UINT32 OsSwtmrTaskCreate(VOID)
{
    UINT32 ret, swtmrTaskID;
    TSK_INIT_PARAM_S swtmrTask;
    UINT32 cpuid = ArchCurrCpuid();//获取当前CPU id
    /*
        创建变量 ret 和 swtmrTaskID 以及swtmrTask,之后获取当前CPU ID。
        操作和当前执行的CPU有关系。
    */

    (VOID)memset_s(&swtmrTask, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));//清0
    /*
        memset_s为初始化函数。
    */
    swtmrTask.pfnTaskEntry = (TSK_ENTRY_FUNC)OsSwtmrTask;//入口函数
    swtmrTask.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;//16K默认内核任务栈
    swtmrTask.pcName = "Swt_Task";//任务名称
    swtmrTask.usTaskPrio = 0;//哇塞! 逮到一个最高优先级的任务 @note_thinking 这里应该用 OS_TASK_PRIORITY_HIGHEST 表示
    swtmrTask.uwResved = LOS_TASK_STATUS_DETACHED;//分离模式
    /*
        首先获取入口函数,然后LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE获取默认任务栈的大小,紧接着指定任务名称,设置任务优先级为0(最高优先级),LOS_TASK_STATUS_DETACHED设置任务为自删除状态。
    */
#if (LOSCFG_KERNEL_SMP == YES)
    swtmrTask.usCpuAffiMask   = CPUID_TO_AFFI_MASK(cpuid);//交给当前CPU执行这个任务
#endif
    ret = LOS_TaskCreate(&swtmrTaskID, &swtmrTask);//创建任务并申请调度
    if (ret == LOS_OK) {
        g_percpu[cpuid].swtmrTaskID = swtmrTaskID;//全局变量记录 软时钟任务ID
        OS_TCB_FROM_TID(swtmrTaskID)->taskStatus |= OS_TASK_FLAG_SYSTEM_TASK;//告知这是一个系统任务
    }
    /*
        调度软时钟任务,执行、申请、记录等几个方面。
    */
    return ret;
}
//回收指定进程的软时钟
LITE_OS_SEC_TEXT_INIT VOID OsSwtmrRecycle(UINT32 processID)
{
    for (UINT16 index = 0; index < LOSCFG_BASE_CORE_SWTMR_LIMIT; index++) {//一个进程往往会有多个定时器
        if (g_swtmrCBArray[index].uwOwnerPid == processID) {//找到一个
            LOS_SwtmrDelete(index);//删除定时器
        }
    }
    /*
        删除某进程的软时钟,传入进程ID,然后利用进程ID遍历数组。
    */
}
//软时钟初始化 ,注意函数在多CPU情况下会执行多次
LITE_OS_SEC_TEXT_INIT UINT32 OsSwtmrInit(VOID)
{
    UINT32 size;
    UINT16 index;
    UINT32 ret;
    SWTMR_CTRL_S *swtmr = NULL;
    UINT32 swtmrHandlePoolSize;
    UINT32 cpuid = ArchCurrCpuid();
    /*
        软时钟初始化变量
        size 大小
        index 索引
        ret 中转变量(应该是临时变量)
        *swtmr 用于定位
        swtmrHandlePoolSize 时钟句柄池大小
        ArchCurrCpuid 当前CUPID

    */
    if (cpuid == 0) {//确保以下代码块由一个CPU执行,g_swtmrCBArray和g_swtmrHandlerPool 是所有CPU共用的
        size = sizeof(SWTMR_CTRL_S) * LOSCFG_BASE_CORE_SWTMR_LIMIT;//申请软时钟内存大小 
        swtmr = (SWTMR_CTRL_S *)LOS_MemAlloc(m_aucSysMem0, size); /* system resident resource */ //常驻内存

        if (swtmr == NULL) {
            ret = LOS_ERRNO_SWTMR_NO_MEMORY;
            goto ERROR;
        }
        /*
            在申请软时钟之后判断时钟是否结束,如果结束则抛出异常。
        */

        (VOID)memset_s(swtmr, size, 0, size);//清0
        g_swtmrCBArray = swtmr;//软时钟
        LOS_ListInit(&g_swtmrFreeList);//初始化空闲链表
        /*
            上篇文章有笔记,时钟是链表。
        */
        for (index = 0; index < LOSCFG_BASE_CORE_SWTMR_LIMIT; index++, swtmr++) {
            swtmr->usTimerID = index;//按顺序赋值
            LOS_ListTailInsert(&g_swtmrFreeList, &swtmr->stSortList.sortLinkNode);//通过sortLinkNode将节点挂到空闲链表 
        }
        /*
            将所有的软时钟挂在链表之上,可能是方便执行吧。
        */
		//想要用静态内存池管理,就必须要使用LOS_MEMBOX_SIZE来计算申请的内存大小,因为需要点前缀内存承载头部信息.
        swtmrHandlePoolSize = LOS_MEMBOX_SIZE(sizeof(SwtmrHandlerItem), OS_SWTMR_HANDLE_QUEUE_SIZE);//计算所有注册函数内存大小
		//规划一片内存区域作为软时钟处理函数的静态内存池。
        g_swtmrHandlerPool = (UINT8 *)LOS_MemAlloc(m_aucSysMem1, swtmrHandlePoolSize); /* system resident resource *///常驻内存
        if (g_swtmrHandlerPool == NULL) {
            ret = LOS_ERRNO_SWTMR_NO_MEMORY;
            goto ERROR;
        }
        /*
            通过计算所需内存大小,然后申请内存大小,最后常驻内存。紧接着判断g_swtmrHandlerPool是否存在如果不存在则抛出异常。
        */

        ret = LOS_MemboxInit(g_swtmrHandlerPool, swtmrHandlePoolSize, sizeof(SwtmrHandlerItem));//初始化软时钟注册池
        if (ret != LOS_OK) {
            ret = LOS_ERRNO_SWTMR_HANDLER_POOL_NO_MEM;
            goto ERROR;
        }
        ret = OsSchedSwtmrScanRegister((SchedScan)OsSwtmrScan);
        if (ret != LOS_OK) {
            goto ERROR;
        }
        /*
            LOS_MemboxInit进行静态内存初始化。返回值ret,先判断初始化是否成功,然后再执行注册扫描并且判断是否成功。
        */
    }
	//每个CPU都会创建一个属于自己的 OS_SWTMR_HANDLE_QUEUE_SIZE 的队列
    ret = LOS_QueueCreate(NULL, OS_SWTMR_HANDLE_QUEUE_SIZE, &g_percpu[cpuid].swtmrHandlerQueue, 0, sizeof(CHAR *));//为当前CPU core 创建软时钟队列 maxMsgSize:sizeof(CHAR *)
    if (ret != LOS_OK) {
        ret = LOS_ERRNO_SWTMR_QUEUE_CREATE_FAILED;
        goto ERROR;
    }
    /*
        创建当前CPU的时钟队列,以及异常判断。
    */

    ret = OsSwtmrTaskCreate();//每个CPU独自创建属于自己的软时钟任务,统一处理队列
    if (ret != LOS_OK) {
        ret = LOS_ERRNO_SWTMR_TASK_CREATE_FAILED;
        goto ERROR;
    }

    ret = OsSortLinkInit(&g_percpu[cpuid].swtmrSortLink);//每个CPU独自对自己软时钟链表排序初始化,为啥要排序因为每个定时器的时间不一样,鸿蒙把用时短的排在前面
    if (ret != LOS_OK) {
        ret = LOS_ERRNO_SWTMR_SORTLINK_CREATE_FAILED;
        goto ERROR;
    }

    return LOS_OK;
ERROR:
    PRINT_ERR("OsSwtmrInit error! ret = %u\n", ret);
    return ret;
}

/*
 * Description: Start Software Timer
 * Input      : swtmr --- Need to start software timer
 */ //开始定时器
LITE_OS_SEC_TEXT VOID OsSwtmrStart(SWTMR_CTRL_S *swtmr)
{
    UINT32 ticks;
    UINT64 currTime = OsGerCurrSchedTimeCycle();
    /*
        ticks 用于计时器存储
        OsGerCurrSchedTimeCycle 获取调度经过多少周期

    */
    if ((swtmr->uwOverrun == 0) && ((swtmr->ucMode == LOS_SWTMR_MODE_ONCE) ||
        (swtmr->ucMode == LOS_SWTMR_MODE_OPP) ||
        (swtmr->ucMode == LOS_SWTMR_MODE_NO_SELFDELETE))) {
        ticks = swtmr->uwExpiry;
    } else {
        ticks = swtmr->uwInterval;
    }
    /*
        在if里面条件情况下 计时器进行存储。
    */
    swtmr->ucState = OS_SWTMR_STATUS_TICKING;

    OsAdd2SortLink(&swtmr->stSortList, currTime, ticks, OS_SORT_LINK_SWTMR);
    if (OS_SCHEDULER_ACTIVE) {
        OsSchedUpdateExpireTime(currTime);
    }
    /*
        OsAdd2SortLink排序链表插入
    */
    return;
}

/*
 * Description: Delete Software Timer
 * Input      : swtmr --- Need to delete software timer, When using, Ensure that it can't be NULL.
 */
STATIC INLINE VOID OsSwtmrDelete(SWTMR_CTRL_S *swtmr)
{
    /* insert to free list */
    LOS_ListTailInsert(&g_swtmrFreeList, &swtmr->stSortList.sortLinkNode);//直接插入空闲链表中,回收再利用
    swtmr->ucState = OS_SWTMR_STATUS_UNUSED;//又干净着呢
    swtmr->uwOwnerPid = 0;//谁拥有这个定时器? 是 0号进程, 0号进程出来了,竟然是虚拟的一个进程.用于这类缓冲使用.
}
STATIC INLINE VOID OsWakePendTimeSwtmr(Percpu *cpu, SWTMR_CTRL_S *swtmr)
{
    LOS_SpinLock(&g_swtmrSpin);
    SwtmrHandlerItemPtr swtmrHandler = (SwtmrHandlerItemPtr)LOS_MemboxAlloc(g_swtmrHandlerPool);
    if (swtmrHandler != NULL) {
        swtmrHandler->handler = swtmr->pfnHandler;
        swtmrHandler->arg = swtmr->uwArg;

        if (LOS_QueueWrite(cpu->swtmrHandlerQueue, swtmrHandler, sizeof(CHAR *), LOS_NO_WAIT)) {
            (VOID)LOS_MemboxFree(g_swtmrHandlerPool, swtmrHandler);
        }
    }
    /*
        自旋锁控制
    */
    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_NO_SELFDELETE) {
        swtmr->ucState = OS_SWTMR_STATUS_CREATED;
    } else {
        swtmr->uwOverrun++;
        OsSwtmrStart(swtmr);
    }

    LOS_SpinUnlock(&g_swtmrSpin);
}
/*
 * Description: Tick interrupt interface module of software timer
 * Return     : LOS_OK on success or error code on failure
 *///OsSwtmrScan 由系统时钟中断处理函数调用
LITE_OS_SEC_TEXT VOID OsSwtmrScan(VOID)//扫描定时器,如果碰到超时的,就放入超时队

{
    Percpu *cpu = OsPercpuGet();
    SortLinkAttribute* swtmrSortLink = &OsPercpuGet()->swtmrSortLink;
    LOS_DL_LIST *listObject = &swtmrSortLink->sortLink;
    /*
        OsPercpuGet 用来获取当前CPU信息
        swtmrSortLink CPU需要处理的定时器链表
        *listObject 指向其链表位置
    */

    /*
     * it needs to be carefully coped with, since the swtmr is in specific sortlink
     * while other cores still has the chance to process it, like stop the timer.
     */
    LOS_SpinLock(&cpu->swtmrSortLinkSpin);

    if (LOS_ListEmpty(listObject)) {
        LOS_SpinUnlock(&cpu->swtmrSortLinkSpin);
        return;
    }
    /*
        LOS_SpinLock和LOS_SpinUnlock是自旋锁控制
    */
    SortLinkList *sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);
    /*
        LOS_DL_LIST_ENTRY获取双向链表节点所在的业务结构体的内存地址。
    */
    
    UINT64 currTime = OsGerCurrSchedTimeCycle();
    while (sortList->responseTime <= currTime) {
        sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);
        OsDeleteNodeSortLink(swtmrSortLink, sortList);

        SWTMR_CTRL_S *swtmr = LOS_DL_LIST_ENTRY(sortList, SWTMR_CTRL_S, stSortList);
        LOS_SpinUnlock(&cpu->swtmrSortLinkSpin);
        /*
            自旋锁控制。
        */

        OsWakePendTimeSwtmr(cpu, swtmr);

        LOS_SpinLock(&cpu->swtmrSortLinkSpin);
        if (LOS_ListEmpty(listObject)) {
            break;
        }

        sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode); 
        /*
            创建链表
        */
    }

    LOS_SpinUnlock(&cpu->swtmrSortLinkSpin);
    /*
        currTime 获得当前的时间周期
        循环中判断如果响应时间小于当前的时间周期,然后获取全部双向链表LOS_DL_LIST_ENTRY,删除节点OsDeleteNodeSortLink。
    */
}

/*
 * Description: Get next timeout
 * Return     : Count of the Timer list
 */
LITE_OS_SEC_TEXT UINT32 OsSwtmrGetNextTimeout(VOID)//获取下一个timeout
{
    return OsSortLinkGetNextExpireTime(&OsPercpuGet()->swtmrSortLink);
}

/*
 * Description: Stop of Software Timer interface
 * Input      : swtmr --- the software timer contrl handler
 */
LITE_OS_SEC_TEXT STATIC VOID OsSwtmrStop(SWTMR_CTRL_S *swtmr)
{
    OsDeleteSortLink(&swtmr->stSortList, OS_SORT_LINK_SWTMR);
    /*
        删除节点的链接。
    */

    swtmr->ucState = OS_SWTMR_STATUS_CREATED;
    swtmr->uwOverrun = 0;

    if (OS_SCHEDULER_ACTIVE) {
        OsSchedUpdateExpireTime(OsGerCurrSchedTimeCycle());
        /*
            更新过期时间。
        */
    }
}

/*
 * Description: Get next software timer expiretime
 * Input      : swtmr --- the software timer contrl handler
 */
LITE_OS_SEC_TEXT STATIC UINT32 OsSwtmrTimeGet(const SWTMR_CTRL_S *swtmr)
{
    return OsSortLinkGetTargetExpireTime(&swtmr->stSortList);
}
//创建定时器,设置定时器的定时时长、定时器模式、回调函数,并返回定时器ID
LITE_OS_SEC_TEXT_INIT UINT32 LOS_SwtmrCreate(UINT32 interval,
                                             UINT8 mode,
                                             SWTMR_PROC_FUNC handler,
                                             UINT16 *swtmrID,
                                             UINTPTR arg)
{
    SWTMR_CTRL_S *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;
        /*
            入参的软件定时器ID指针为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, SWTMR_CTRL_S, stSortList);
    LOS_ListDelete(LOS_DL_LIST_FIRST(&g_swtmrFreeList)); 
    /*删除指定节点*/
    SWTMR_UNLOCK(intSave);

    swtmr->uwOwnerPid = OsCurrProcessGet()->processID;//定时器进程归属设定
    swtmr->pfnHandler = handler;//时间到了的回调函数
    swtmr->ucMode = mode;	//定时器模式
    swtmr->uwOverrun = 0;
    swtmr->uwInterval = interval;	//周期性超时间隔
    swtmr->uwExpiry = interval;		//一次性超时间隔
    swtmr->uwArg = arg;				//回调函数的参数
    swtmr->ucState = OS_SWTMR_STATUS_CREATED;	//已创建状态
    SET_SORTLIST_VALUE(&swtmr->stSortList, OS_SORT_LINK_INVALID_TIME);
    *swtmrID = swtmr->usTimerID;

    return LOS_OK;
}
//接口函数 启动定时器       参数定时任务ID
LITE_OS_SEC_TEXT UINT32 LOS_SwtmrStart(UINT16 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; /*入参的软件定时器ID不正确*/
    }

    swtmrCBID = swtmrID % LOSCFG_BASE_CORE_SWTMR_LIMIT;//取模
    swtmr = g_swtmrCBArray + swtmrCBID;//获取定时器控制结构体

    SWTMR_LOCK(intSave);
    if (swtmr->usTimerID != swtmrID) {//ID必须一样
        SWTMR_UNLOCK(intSave);
        return LOS_ERRNO_SWTMR_ID_INVALID;
    }

    switch (swtmr->ucState) {//判断定时器状态
        case OS_SWTMR_STATUS_UNUSED:
            ret = LOS_ERRNO_SWTMR_NOT_CREATED;
            break;
        /* 如果定时器的状态为启动中,应先停止定时器再重新启动
         * If the status of swtmr is timing, it should stop the swtmr first,
         * then start the swtmr again.
         */
        case OS_SWTMR_STATUS_TICKING://正在计数的定时器
            OsSwtmrStop(swtmr);//先停止定时器,注意这里没有break;,在OsSwtmrStop中状态将会回到了OS_SWTMR_STATUS_CREATED 接下来就是执行启动了
            /* fall-through */
        case OS_SWTMR_STATUS_CREATED://已经创建好了
            OsSwtmrStart(swtmr);//启动定时器
            break;
        default:
            ret = LOS_ERRNO_SWTMR_STATUS_INVALID;
            break;
    }

    SWTMR_UNLOCK(intSave);
    return ret;
}
//接口函数 停止定时器          参数定时任务ID
LITE_OS_SEC_TEXT UINT32 LOS_SwtmrStop(UINT16 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;
        /*
            判断编号如果到达或者超多最大值,然后返回传入参数失败。 后面的方式和前面的相差不大。
        */
    }

    swtmrCBID = swtmrID % LOSCFG_BASE_CORE_SWTMR_LIMIT;//取模
    swtmr = g_swtmrCBArray + swtmrCBID;//获取定时器控制结构体
    SWTMR_LOCK(intSave);

    if (swtmr->usTimerID != swtmrID) {//ID必须一样
        SWTMR_UNLOCK(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;
    }

    SWTMR_UNLOCK(intSave);
    return ret;
}
//接口函数 获得软件定时器剩余Tick数 通过 *tick 带走 
LITE_OS_SEC_TEXT UINT32 LOS_SwtmrTimeGet(UINT16 swtmrID, UINT32 *tick)
{
    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;
    }

    if (tick == NULL) {
        return LOS_ERRNO_SWTMR_TICK_PTR_NULL;
    }

    swtmrCBID = swtmrID % LOSCFG_BASE_CORE_SWTMR_LIMIT;//取模
    swtmr = g_swtmrCBArray + swtmrCBID;//获取定时器控制结构体
    SWTMR_LOCK(intSave);

    if (swtmr->usTimerID != swtmrID) {//ID必须一样
        SWTMR_UNLOCK(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://正在计数的定时器
            *tick = OsSwtmrTimeGet(swtmr);//获取
            break;
        default:
            ret = LOS_ERRNO_SWTMR_STATUS_INVALID;
            break;
    }
    SWTMR_UNLOCK(intSave);
    return ret;
}
//接口函数 删除定时器
LITE_OS_SEC_TEXT UINT32 LOS_SwtmrDelete(UINT16 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;
    }

    swtmrCBID = swtmrID % LOSCFG_BASE_CORE_SWTMR_LIMIT;//取模
    swtmr = g_swtmrCBArray + swtmrCBID;//获取定时器控制结构体
    SWTMR_LOCK(intSave);

    if (swtmr->usTimerID != swtmrID) {//ID必须一样
        SWTMR_UNLOCK(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://正在计数就先停止再删除,这里没有break;
            OsSwtmrStop(swtmr);
            /* fall-through */
        case OS_SWTMR_STATUS_CREATED://再删除定时器
            OsSwtmrDelete(swtmr);
            break;
        default:
            ret = LOS_ERRNO_SWTMR_STATUS_INVALID;
            break;
    }

    SWTMR_UNLOCK(intSave);
    return ret;
}

#endif /* (LOSCFG_BASE_CORE_SWTMR == YES) */
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

鸿蒙内核阅读笔记-定时器 的相关文章

  • 程序员工作压力大,为什么还这么多人想做程序员?是因为喜欢吗?

    最近过年遇到很多亲戚和朋友 不知道大家的身边是怎样的 阿粉身边的亲戚朋友从事计算机相关的还是挺少的 很多还是从事一些传统行业 最近跟一个亲戚聊天的时候就聊到网上对程序员的一些刻板影响 什么格子衫呀 拖鞋短裤呀 脱发以及 35 岁劝退等这些东
  • 黑幕!阿里P8爆出学透这份算法面试文档,不再怕任何大厂算法题

    为什么要学习数据结构和算法 随着应用程序变得越来越复杂和数据越来越丰富 几百万 几十亿甚至几百亿的数据就会出现 而对这么大对数据进行搜索 插入或者排序等的操作就越来越慢 数据结构就是用来解决这些问题的 阅读本教程前 您需要了解的知识 在您开
  • FastJSON、Jackson、Gson性能测试

    起因是公司原先用的是阿里开源的FastJSON 大家用的也比较顺手 但是在出现了两次严重的漏洞后 公司决定放弃FastJSON 使用其他序列化 反序列化工具 考虑大家常用的无非就是FastJSON Jackson和Gson这三种 因此领导让
  • Unittest单元测试框架之unittest构建测试套件

    构建测试套件 在实际项目中 随着项目进度的开展 测试类会越来越多 可是直到现在我 们还只会一个一个的单独运行测试类 这在实际项目实践中肯定是不可行的 在 中可以通过测试套件来解决该问题 测试套件 Test Suite 是由多个测试用例 Te
  • 软件测试-测试用例案例及思维导图展示

    的测试用例 一个杯子的测试用例 一支笔的测试用例 朋友圈点赞的测试用例 功能测试 1点赞后是否显示结果 2 点赞后是否可以取消 3 点赞取消后是否可以重复点赞 4 共同好友点赞后 是否有消息提醒 5 非共同好友点赞后 是否有消息提醒 6 点
  • APP测试设计要点

    app的安装与升级 升级中用户数据 设置 状态的保留 特别注意新版本已去掉的状态或设置 是否可以隔开版本覆盖安装 是否可以覆盖安装更低版本 卸载安装 安装目录清理 SD卡存储数据不被清理 在没有更新或网络时 需要给予用户正确的信息表达 如果
  • 【HttpRunner】接口自动化测试框架

    简介 2018年python开发者大会上 了解到HttpRuuner开源自动化测试框架 采用YAML JSON格式管理用例 能录制和转换生成用例功能 充分做到用例与测试代码分离 相比excel维护测试场景数据更加简洁 在此 利用业余时间研究
  • 程序员养生指南

    目录 前言 调整工作习惯 保持合理饮食 积极参与活动 保持良好睡眠 精神调适与放松 结语 前言 不用多说 想必都知道程序员是一份高强度 高压力 高危 的职业 长期坐姿 熬夜加班等不良生活习惯会对人的身体健康造成负面影响 长时间的工作 高强度
  • 低代码是行业毒瘤?我不这么认为

    低代码是行业毒瘤 我不这么认为 1 什么是低代码 2 低代码的优缺点 3 你认为低代码会替代传统编程吗 4 如何入门低代码 5 常见的低代码平台 1 什么是低代码 低代码是一种可视化的应用开发方法 它允许用户通过较少的代码 以较快的速度来交
  • 程序员的养生之道

    程序员的养生之道 1 对程序员的初次印象 2 我的养生之道 2 1 规律作息 2 2 合理饮食 2 3 健康饮食 2 4 增强锻炼 2 5 心态平和 2 6 生活习惯
  • GoLong的学习之路,进阶,Viper(yaml等配置文件的管理)

    本来有今天是继续接着上一章写微服务的 但是这几天有朋友说 再写Web框架的时候 遇到一个问题 就是很多的中间件 redis 微信 mysql mq 的配置信息写的太杂了 很不好管理 希望我能写一篇有管理配置文件的 所以这篇就放到今天写吧 微
  • 网络安全之等保 2.0 测评

    一 身份鉴别 a 应对登录的用户进行身份标识和鉴别 身份标识具有唯一性 身份鉴别信息具有复杂度要求并定期更换 1 登录 mysql 查看是否使用了口令和密码的组合鉴别身份 mysql h 192 168 100 16 u root p 2
  • 地牢边缘 DUNGEON LIMBUS中文免安装版

    地牢边缘 是一款点阵图形式的像素风经典迷宫探索类游戏 玩家需要在游戏中收集多种装备 随机生成的无限地下城 在生死之际遇见的迷之铁匠和管理复活之村的年轻女性 为了找回遗失的记忆 进入更深的地下城 玩家还可以发展村落以及进化武器的多样化收集要素
  • 外包干了2个月,技术退步明显了...

    先说一下自己的情况 大专生 19年通过校招进入湖南某软件公司 干了接近4年的功能测试 今年8月份 感觉自己不能够在这样下去了 长时间呆在一个舒适的环境会让一个人堕落 而我已经在一个企业干了四年的功能测试 已经让我变得不思进取 谈了2年的女朋
  • MySQL忘记密码了怎么办

    MySQL忘记密码 今天在写jdbc时很悲催的发现自己的MySQL密码忘记了 没有办法了 重新设置一下密码吧 先关闭mysql服务mysql server stop 跳过权限检验 在使用该命令的时候要确保mysql服务是关闭的 不然是无法正
  • [大厂实践] 零配置服务网格与按需集群发现

    本文介绍了Netflix通过扩展Envoy支持按需集群发现机制帮助团队无缝迁移服务网格的实践 原文 Zero Configuration Service Mesh with On Demand Cluster Discovery 在这篇文章
  • 校招失败后,在小公司熬了 2 年终于进了字节跳动,竭尽全力....

    其实两年前校招的时候就往字节投了一次简历 结果很明显凉了 随后这个理想就被暂时放下了 但是这个种子一直埋在心里这两年除了工作以外 也会坚持写博客 也因此结识了很多优秀的小伙伴 从他们身上学到了特别多东西 把这次面试分享出来 也是希望可以帮助
  • 外包干了2个月,技术退步明显...

    先说一下自己的情况 大专生 18年通过校招进入武汉某软件公司 干了接近4年的功能测试 今年年初 感觉自己不能够在这样下去了 长时间呆在一个舒适的环境会让一个人堕落 而我已经在一个企业干了四年的功能测试 已经让我变得不思进取 谈了2年的女朋友
  • 深入了解 Python MongoDB 操作:排序、删除、更新、结果限制全面解析

    Python MongoDB 排序 对结果进行排序 使用 sort 方法对结果进行升序或降序排序 sort 方法接受一个参数用于 字段名 一个参数用于 方向 升序是默认方向 示例 按名称按字母顺序对结果进行排序 import pymongo
  • 掌握 C# 变量:在代码中声明、初始化和使用不同类型的综合指南

    C 变量 变量是用于存储数据值的容器 在 C 中 有不同类型的变量 用不同的关键字定义 例如 int 存储整数 没有小数点的整数 如 123 或 123 double 存储浮点数 有小数点 如 19 99 或 19 99 char 存储单个

随机推荐

  • 数字货币即将面世 蹭“数字货币”热度套路频现

    随着央行数字人民币逐步在北京 上海等地进入测试阶段 数字货币在我国呼之欲出 与此同时 相关谣言或虚假信息也层出不穷 蹭 数字货币 热度的常见套路都有哪些 一起来看看 在网上签到学习就能提现 近日 某非法平台宣称 该平台系国家为大力发展数字货
  • 循序渐进,学会用pyecharts绘制玫瑰图

    循序渐进 学会用pyecharts绘制玫瑰图 玫瑰图简介 玫瑰图全称南丁格尔玫瑰图 是英国护士和统计学家弗罗伦斯 南丁格尔发明的 又名为极区图 南丁格尔自己常昵称这类图为鸡冠花图 coxcomb 用以表达军医院季节性的死亡率 提供给那些不太
  • adb install 多个设备时指定设备

    在emulator 5554模拟器上安装ebook apk adb s emulator 5554 install ebook apk 在真机上安装ebook apk adb s HT9BYL904399 install ebook apk
  • 可孚医疗:「最懂互联网」的医疗器械企业是如何炼成的?

    如果说钉钉在过去的标签是软件 是低代码 那么在医疗这个赛道里 这些标签已经不足以成为钉钉价值的侧写 除了固有标签之外 在可孚医疗的场景里 钉钉可以连接 可以成为智能BI 也更可以做到内外部协同等 作者 皮爷 出品 产业家 1000分 打开可
  • GetX项目级实战

    在使用了 Provider 一年后 遇到了很多阻力 期间尝试过 BLoC MobX 均不如意 一个样本代码太多 使用复杂 一个生产代码要等很久 难道 Flutter 就没有诸如原生 Android 的 jetpack 套装一样方便的套件吗
  • Cyclic Components CodeForces - 977E(找简单环)

    先求连通块 再看是不是所有连通块的点的度数为2 如果是那就是简单环 只不过我觉得我这个代码时间复杂度还是挺高的 虽然这题没啥问题 不过我看有他人是一遍用dfs找环 一遍判断找到环时的那个点的度数是不是2 AC代码 include
  • ES:一次分片设计问题导致的故障

    现象 1 单节点CPU持续高 2 写入骤降 3 线程池队列积压 但没有reject 4 使用方没有记录日志 排查 1 ES监控 只能看到相应的结果指标 无法反应出原因 2 ES日志 大量日志打印相关异常 routate等调用栈 core a
  • Java是解释型还是编译型语言?

    Java是解释型还是编译型语言 首先JVM是什么 JVM虚拟机也是java的运行环境 因为所有系统平台都支持JVM 所以实现了Java的跨平台 我们可以把JVM虚拟机比作人 有食物供我们食用 当我们需要吃哪种食物的时候就吃哪个实物 在JVM
  • 深度学习笔记(五) 代价函数的梯度求解过程和方法

    作为自己的笔记系列 方便自己查阅和理解 1 什么是梯度 梯度 本意是一个向量 矢量 当某一函数在某点处沿着该方向的方向导数取得该点处的最大值 即函数在该点处沿方向变化最快 变化率最大 为该梯度的模 在二元函数的情形 设函数z f x y 在
  • Linux C中对json格式数组数据的生成与解析

    在网络通信中 数据经常被做成json格式的来进行传输 那么我们怎么在linux系统中去做json格式的数据呢 怎么将接收到的json格式的数据解析出来呢 1 linux json库的安装 1 下载json c源码包 2 解压json c的源
  • Android Studio NDK开发注意

    1 如果JNILibs armeabi中有相应的库文件 编绎重新生成的 so文件不会打包到新的apk中
  • 干掉 “重复代码” 的技巧有哪些?

    软件工程师和码农最大的区别就是平时写代码时习惯问题 码农很喜欢写重复代码而软件工程师会利用各种技巧去干掉重复的冗余代码 业务同学抱怨业务开发没有技术含量 用不到设计模式 Java 高级特性 OOP 平时写代码都在堆 CRUD 个人成长无从谈
  • UDP包传送字符串实现方法以及方格乱码的出现原因和解决办法

    在使用socket发送udp包传输文本时 由于包中的char型数组是定长的 且其长度大于消息长度 所以其中必有很多空元素 当接收端接收到udp包时进行转码 空元素就会被转码成方块形状的乱码 解决办法 每条消息发送完毕后添加 作为记号 接收后
  • 浏览器渲染机制 (二)浏览器主进程-浏览器内核-浏览器渲染流程

    文章目录 浏览器主进程和浏览器渲染进程的通信过程 浏览器内核 渲染进程 中线程之间的管理 GUI渲染线程与JS引擎线程互斥 JS阻塞页面加载 WebWorker JS的多线程 WebWorker与SharedWorker 总结浏览器渲染流程
  • adb通过网络连接

    1 使用USB数据线连接设备 2 在命令行输入adb tcpip 5555 5555为端口号 可以自由指定 3 断开 USB数据 此时可以连接你需要连接的 USB设备 4 再计算机命令行输入 adb connect lt 设备的IP地址 g
  • 自动计算30天内的股价最高价源代码

    我可以回答这个问题 您可以使用以下代码来计算30天内股价的最高价 复制 import pandas as pd import yfinance as yf 设置股票代码和日期范围 symbol AAPL start date 2021 01
  • Python绝技:运用Python成为顶级黑客

    Python 是一门常用的编程语言 它不仅上手容易 而且还拥有丰富的支持库 对经常需要针对自己所 处的特定场景 以极少的代码量实现所需的功能 Python绝技 运用Python成为顶级黑客结合具体的场景和真 实的案例 详述了 Python
  • 《软件测试的艺术》第三章 代码检查、走查和评审

    软件测试的艺术 第三章 代码检查 走查和评审 3 1 代码检查与走查 3 2 代码检查 3 2 1 代码检查小组 3 2 2 检查议程与注意事项 3 2 3 对事不对人 和人有关的注意事项 3 2 4 代码检查的衍生功效 3 3 用于代码检
  • 100个python算法超详细讲解:农夫过河

    100个python算法超详细讲解 谷哥技术 1 问题描述 一个农夫在河边带了一匹狼 一只羊和一棵白菜 他需要把这三样东西用 船带到河的对岸 然而 这艘船只能容下农夫本人和另外一样东西 如果农夫 不在场的话 狼会吃掉羊 羊也会吃掉白菜 请编
  • 鸿蒙内核阅读笔记-定时器

    鸿蒙内核阅读笔记 定时器 简介 核心模块 定时器 los swtmr c 介绍 阅读代码 函数部分 简介 近期在阅读鸿蒙liteOS a 由于是初次探索内核的奥秘 将一些阅读的心得进行分享 希望能在作为笔记的同时 也能帮助更多人学习 感谢图