当前位置: 首页 > news >正文

SylixOS 中的软件定时器

1、基本原理

  SylixOS 里的软件定时器是通过 普通定时器等待唤醒链表高速定时器等待唤醒链表 来实现的,所有操作都是围绕这两个链表进行,不同的是普通定时器等待唤醒链表是在线程上下文中判定时间和回调的,而高速定时器等待唤醒链表是在系统 Tick 中断中进行的,他俩的最小有效定时器精度通常为一个 Tick。

struct sigevent;
typedef struct {LW_LIST_MONO               TIMER_monoResrcList;                     /*  定时器资源表                */LW_CLASS_WAKEUP_NODE       TIMER_wunTimer;                          /*  等待唤醒链表                */
#define TIMER_ulCounter        TIMER_wunTimer.WUN_ulCounterUINT8                      TIMER_ucType;                            /*  定时器类型                  */ULONG                      TIMER_ulCounterSave;                     /*  定时器计数值保留值          */ULONG                      TIMER_ulOption;                          /*  定时器操作选项              */UINT8                      TIMER_ucStatus;                          /*  定时器状态                  */PTIMER_CALLBACK_ROUTINE    TIMER_cbRoutine;                         /*  执行函数                    */PVOID                      TIMER_pvArg;                             /*  定时器参数                  */UINT16                     TIMER_usIndex;                           /*  数组中的索引                */#if LW_CFG_PTIMER_AUTO_DEL_EN > 0LW_OBJECT_HANDLE           TIMER_ulTimer;
#endif                                                                  /*  LW_CFG_PTIMER_AUTO_DEL_EN   */LW_OBJECT_HANDLE           TIMER_ulThreadId;                        /*  线程 ID                     */struct sigevent            TIMER_sigevent;                          /*  定时器信号相关属性          *//*  SIGEV_THREAD 必须使能 POSIX */UINT64                     TIMER_u64Overrun;                        /*  timer_getoverrun            */clockid_t                  TIMER_clockid;                           /*  仅对 POSIX 定时器有效       */#if LW_CFG_TIMERFD_EN > 0PVOID                      TIMER_pvTimerfd;                         /*  timerfd 结构                */
#endif                                                                  /*  LW_CFG_TIMERFD_EN > 0       */CHAR                       TIMER_cTmrName[LW_CFG_OBJECT_NAME_SIZE]; /*  定时器名                    */LW_SPINLOCK_DEFINE        (TIMER_slLock);                           /*  自旋锁                      */
} LW_CLASS_TIMER;
typedef LW_CLASS_TIMER        *PLW_CLASS_TIMER;

2、接口详解

LW_API  
LW_OBJECT_HANDLE  API_TimerCreate (CPCHAR             pcName,ULONG              ulOption,LW_OBJECT_ID      *pulId)
{REGISTER PLW_CLASS_TIMER       ptmr;....../* 根据 ulOption 选项控制,创建的是普通定时器还是高速定时器 */if (ulOption & LW_OPTION_ITIMER) {                                  /*  应用级定时器                */ptmr->TIMER_ucType = LW_TYPE_TIMER_ITIMER;                      /*  定时器类型                  */} else {ptmr->TIMER_ucType = LW_TYPE_TIMER_HTIMER;}......ulIdTemp = _MakeObjectId(_OBJECT_TIMER, LW_CFG_PROCESSOR_NUMBER, ptmr->TIMER_usIndex);                      /*  构建对象 id                 */....../* 返回创建好的定时器句柄 */return  (ulIdTemp);
}

启动一个定时器

LW_API  
ULONG  API_TimerStartEx (LW_OBJECT_HANDLE         ulId,ULONG                    ulInitCounter,ULONG                    ulCounter,ULONG                    ulOption,PTIMER_CALLBACK_ROUTINE  cbTimerRoutine,PVOID                    pvArg)
{INTREG                     iregInterLevel;REGISTER UINT16            usIndex;REGISTER PLW_CLASS_TIMER   ptmr;/* 根据入参 ulId 定时器句柄,找到定时器结构 */usIndex = _ObjectGetIndex(ulId);......ptmr = &_K_tmrBuffer[usIndex];....../* 这里就能看出区别,不同类型的定时器,加入的链表是不同的 */ if (ptmr->TIMER_ucType == LW_TYPE_TIMER_ITIMER) {                   /*  加入扫描队列                */_WakeupAdd(&_K_wuITmr, &ptmr->TIMER_wunTimer, LW_TRUE);} else {_WakeupAdd(&_K_wuHTmr, &ptmr->TIMER_wunTimer, LW_FALSE);}
......
}

_K_wuITmr_K_wuHTmr 是全局变量

/*********************************************************************************************************定时器
*********************************************************************************************************/
__KERNEL_EXT LW_CLASS_WAKEUP         _K_wuHTmr;                         /*  高速定时器管理表            */
__KERNEL_EXT LW_CLASS_WAKEUP         _K_wuITmr;                         /*  普通定时器管理表            */

同时,要注意到,_WakeupAdd 将节点加入链表的过程中,不是随意加的,是按照时间的长短进行排序:

VOID  _WakeupAdd (PLW_CLASS_WAKEUP  pwu, PLW_CLASS_WAKEUP_NODE  pwun, BOOL  bProcTime)
{
....../* 按照时间顺序进行链表插入,这里的 WUN_ulCounter 是相对等待时间,时间单位为 tick */while (plineTemp) {pwunTemp = _LIST_ENTRY(plineTemp, LW_CLASS_WAKEUP_NODE, WUN_lineManage);if (pwun->WUN_ulCounter >= pwunTemp->WUN_ulCounter) {           /*  需要继续向后找              */pwun->WUN_ulCounter -= pwunTemp->WUN_ulCounter;plineTemp = _list_line_get_next(plineTemp);} else {if (plineTemp == pwu->WU_plineHeader) {                     /*  如果是链表头                */_List_Line_Add_Ahead(&pwun->WUN_lineManage, &pwu->WU_plineHeader);} else {_List_Line_Add_Left(&pwun->WUN_lineManage, plineTemp);  /*  不是表头则插在左边          */}pwunTemp->WUN_ulCounter -= pwun->WUN_ulCounter;             /*  右侧的点重新计算计数器      */break;}}
....../* * 如果是 _K_wuITmr 普通定时器,则这里会调用 WU_pfuncWakeup 函数,唤醒普通定时器线程* 注意,普通定时器线程被唤醒后,会从系统的 _K_wuDelay 链表删除*/if (bProcTime && pwu->WU_pfuncWakeup) {if (bSaveTime) {__KERNEL_TIME_GET(pwu->WU_i64LastTime, INT64);}pwu->WU_pfuncWakeup(pwu->WU_pvWakeupArg);                       /*  唤醒                        */}
}

2.1 普通定时器

普通定时器 _K_wuITmr 是在线程中,去更新、维护、唤醒

PVOID  _ITimerThread (PVOID  pvArg)
{INTREG                     iregInterLevel;REGISTER PLW_CLASS_TIMER            ptmr;PTIMER_CALLBACK_ROUTINE    pfuncRoutine;PVOID                      pvRoutineArg;PLW_CLASS_TCB              ptcbCur;PLW_CLASS_PCB              ppcb;PLW_CLASS_WAKEUP_NODE      pwun;INT64                      i64CurTime;ULONG                      ulCounter;BOOL                       bNoTimer;(VOID)pvArg;LW_TCB_GET_CUR_SAFE(ptcbCur);                                       /*  当前任务控制块              */for (;;) {iregInterLevel = __KERNEL_ENTER_IRQ();                          /*  进入内核同时关闭中断        */__KERNEL_TIME_GET_IGNIRQ(_K_wuITmr.WU_i64LastTime, INT64);      /*  原始时间                    *//* 因为 _K_wuITmr 链表是经过排序的,链表第一个成员,是需要等待时间最短的 */__WAKEUP_GET_FIRST(&_K_wuITmr, pwun);                           /*  获得第一个节点              */if (pwun) {ulCounter = pwun->WUN_ulCounter;                            /*  已第一个节点等待时间 Sleep  */bNoTimer  = LW_FALSE;} else {ulCounter = LW_ITIMER_IDLE_TICK;                            /*  没有任何节点 (会被自动唤醒) */bNoTimer  = LW_TRUE;}/* 将当前线程从就绪表中删除 */ppcb = _GetPcb(ptcbCur);__DEL_FROM_READY_RING(ptcbCur, ppcb);                           /*  从就绪表中删除              *//* * 将当前线程 TCP 的等待唤醒时间设置为 ulCounter,并加入到系统的 _K_wuDelay 超时唤醒链表* 为什么能多次对同一个 tcb 去 ADD 到 _K_wuDelay 链表?因为每一次被唤醒,该 tcb 都会被从 _K_wuDelay 链表中删除* 这里只是更新 TCB_ulDelay 时间,再重新将其加入 _K_wuDelay 链表*/ptcbCur->TCB_ulDelay = ulCounter;__ADD_TO_WAKEUP_LINE(ptcbCur);                                  /*  加入等待扫描链              *//* 退出内核时,会进行调度,因为当前线程已经被从就绪表删除,这里会调度其他线程运行 */__KERNEL_EXIT_IRQ(iregInterLevel);                              /*  退出内核同时打开中断        *//* 当前线程被唤醒时,会从当前开始运行 */iregInterLevel = __KERNEL_ENTER_IRQ();                          /*  进入内核同时关闭中断        */if (bNoTimer) {__KERNEL_EXIT_IRQ(iregInterLevel);                          /*  退出内核同时打开中断        */continue;}/* 计算真正的睡眠时间(因为可能会有抖动、误差) */__KERNEL_TIME_GET_IGNIRQ(i64CurTime, INT64);                    /*  获得 Sleep 后时间           */ulCounter = (ULONG)(i64CurTime - _K_wuITmr.WU_i64LastTime);     /*  真正睡眠时间                */_K_wuITmr.WU_i64LastTime = i64CurTime;/* 针对 _K_wuITmr 链表上所有节点,都减去 ulCounter 值 */__WAKEUP_PASS_FIRST(&_K_wuITmr, pwun, ulCounter);ptmr = _LIST_ENTRY(pwun, LW_CLASS_TIMER, TIMER_wunTimer);/* 对于已经达到时间的节点,进行删除 */_WakeupDel(&_K_wuITmr, pwun, LW_FALSE);/* 这里会根据定时器模式(one shot 还是 continue 模式进行重载) */if (ptmr->TIMER_ulOption & LW_OPTION_AUTO_RESTART) {ptmr->TIMER_ulCounter = ptmr->TIMER_ulCounterSave;_WakeupAdd(&_K_wuITmr, pwun, LW_FALSE);} else {ptmr->TIMER_ucStatus = LW_TIMER_STATUS_STOP;                /*  填写停止标志位              */}pfuncRoutine = ptmr->TIMER_cbRoutine;pvRoutineArg = ptmr->TIMER_pvArg;__KERNEL_EXIT_IRQ(iregInterLevel);                              /*  退出内核同时打开中断        */LW_SOFUNC_PREPARE(pfuncRoutine);pfuncRoutine(pvRoutineArg);iregInterLevel = __KERNEL_ENTER_IRQ();                          /*  进入内核同时关闭中断        */__WAKEUP_PASS_SECOND();KN_INT_ENABLE(iregInterLevel);                                  /*  这里允许响应中断            */iregInterLevel = KN_INT_DISABLE();__WAKEUP_PASS_END();__KERNEL_EXIT_IRQ(iregInterLevel);                              /*  退出内核同时打开中断        */}return  (LW_NULL);
}

2.2 高速定时器

高速定时器 _K_wuHTmr API_TimerHTicks 函数中去更新、维护、唤醒。API_TimerHTicks 函数在 Tick 中断中被调用。

LW_API
VOID  API_TimerHTicks (VOID)
{INTREG                     iregInterLevel;REGISTER PLW_CLASS_TIMER            ptmr;PLW_CLASS_WAKEUP_NODE      pwun;PTIMER_CALLBACK_ROUTINE    pfuncRoutine;PVOID                      pvRoutineArg;ULONG                      ulCounter = 1;iregInterLevel = __KERNEL_ENTER_IRQ();                              /*  进入内核同时关闭中断        *//* 这里是个 for 循环,针对 _K_wuHTmr 链表上的所有节点,进行减一个 counter (一个 counter = 1个 tick)的操作 */__WAKEUP_PASS_FIRST(&_K_wuHTmr, pwun, ulCounter);ptmr = _LIST_ENTRY(pwun, LW_CLASS_TIMER, TIMER_wunTimer);/* 针对已经到达定时时间的节点,将其从链表中删除 */_WakeupDel(&_K_wuHTmr, pwun, LW_FALSE);if (ptmr->TIMER_ulOption & LW_OPTION_AUTO_RESTART) {ptmr->TIMER_ulCounter = ptmr->TIMER_ulCounterSave;_WakeupAdd(&_K_wuHTmr, pwun, LW_FALSE);} else {ptmr->TIMER_ucStatus = LW_TIMER_STATUS_STOP;                    /*  填写停止标志位              */}pfuncRoutine = ptmr->TIMER_cbRoutine;pvRoutineArg = ptmr->TIMER_pvArg;__KERNEL_EXIT_IRQ(iregInterLevel);                                  /*  退出内核同时打开中断        *//* 执行定时器的执行函数 */LW_SOFUNC_PREPARE(pfuncRoutine);pfuncRoutine(pvRoutineArg);iregInterLevel = __KERNEL_ENTER_IRQ();                              /*  进入内核同时关闭中断        */__WAKEUP_PASS_SECOND();KN_INT_ENABLE(iregInterLevel);                                      /*  这里允许响应中断            */iregInterLevel = KN_INT_DISABLE();__WAKEUP_PASS_END();__KERNEL_EXIT_IRQ(iregInterLevel);                                  /*  退出内核同时打开中断        */
}
http://www.dtcms.com/a/416121.html

相关文章:

  • 关于接口JSON格式(DataTable转换成JSON数据)
  • 加减放大电路与仿真
  • 网站建设公司推荐乐云seo小程序商城系统
  • 网站运营岗位职责描述精品课程网站建设毕业设计论文
  • 实战指南:RVC 语音转换框架
  • 卡片式设计 网站网站站长英语
  • 网站开发如何设置视频教程注册一家公司都需要什么费用
  • 奉贤网站制作网站开发面试
  • 网站要怎么做开发公司工程部绩效考核管理办法
  • Linux:gdb的使用
  • 230板子相关接口总结
  • 怎么制作免费建网站做红包网站
  • 网站建设与管理试卷怎么建立织梦网站
  • wordpress 添加视频无锡网站排名优化公司
  • 2025年最新acw_sc__v2加密cookie算法
  • 《普通逻辑》学习记录——类比推理
  • 韩国家具网站模板黄江镇网站建设
  • seo查询站长太原制作网站的公司哪家好
  • 怀来县网站建设自建网站如何上传视频
  • 潮州市建设工程交易中心网站做淘宝客找商品网站有哪些
  • 结构化方法和面向对象方法对比
  • 凡科建站官网免费注册手机app软件开发机构
  • 网站建设功能需求文档网站开发后期做什么
  • 福州如何做百度的网站php做网页
  • 青建设厅官方网站开发小程序需要多少钱难吗
  • 兼职做诚信网站认证那个公司可以做网站
  • 百度为什么不收录网站的某个版块免费网页搭建
  • leetcode 438 找到字符串中所有的字母异位词
  • 田园综合体建设网站seo技术
  • 动态ip可以做网站吗南宁网络广播电视台