[文章]鸿蒙内核源码分析(时钟管理篇):时钟是触发调度最大的源动力

阅读量0
推荐
0
0
时钟管理模块很简单,却有内核最重要的代码段OsTickHandler(),这是干嘛的,可以理解为JAVA的定时任务,而是系统内核的定时器。因鸿蒙目前开放的是轻量级的内核lite os(LOS),所以tick的频率不会太高
对应张大爷的故事:很简单就是场馆的那个大钟,每10分响一次,一次就是一个Tick(节拍)
多久Tick一次?看代码
  1. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">/ ** </font></font>
  2. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">* @ingroup los_config *一秒内的抽动</font></font>
  3. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">数</font></font>
  4. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">* / </font></font>
  5. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">#ifndef LOSCFG_BASE_CORE_TICK_PER_SECOND </font></font>
  6. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">#define LOSCFG_BASE_CORE_TICK_PER_SECOND 100 // //每秒间隔100次触发,当然这是可以改的</font></font>
  7. <font style="vertical-align: inherit;"><font style="vertical-align: inherit;">#endif</font></font>
复制代码

每秒100 tick,即每秒100次调用时钟中断处理程序,时间片单位为10ms
一次任务分配多少时间给进展执行呢?答案是2个时间片,即20ms
对应张大爷的故事:节目喊到后这次进来的总表演时间,10分钟一个时间片,共2片,表演20分钟。
  1. / **
  2. * @ingroup los_config
  3. *具有相同优先级的任务的最长执行时间
  4. * /
  5. #ifndef LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT
  6. #define LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT 2
  7. #endif

  8. / **
  9. * @ingroup los_process
  10. *保留时间片进程
  11. * /
  12. #define OS_PROCOUT_BASE_CLOCK_RR
复制代码
用完需要可以再分配,但一次就是只给2个时间片,怕你一直占着茅坑,后面还有人在等的。详细代码:OsSchedResched(VOID)
  1. //重新
  2. 调试实现VOID OsSchedResched(VOID)
  3. {
  4.     LosTaskCB * runTask = NULL;
  5.     LosTaskCB * newTask = NULL;
  6.     LosProcessCB * runProcess = NULL;
  7.     LosProcessCB * newProcess = NULL;

  8.     LOS_ASSERT(LOS_SpinHeld(&g_taskSpin)); //必须持有任务自旋锁,自旋锁是不是前进方向去抢锁,而且CPU各个核之间去争夺锁

  9.     如果(!OsPreemptableInSched()){//是否置了重新调度标识位
  10.         return;
  11.     }

  12.     runTask = OsCurrTaskGet(); //获取当前任务
  13.     newTask = OsGetTopTask(); //获取优先级最最最高的任务

  14.     / *总是能够获得一个任务* /
  15.     LOS_ASSERT(newTask!= NULL); //不能没有

  16.     需求调试的任务if(runTask == newTask){//当前任务就是最高任务,那还调度个啥的,直接退出。
  17.         返回;
  18.     }

  19.     runTask-> taskStatus&=〜OS_TASK_STATUS_RUNNING; //当前任务状态位置成没有运行状态
  20.     newTask-> taskStatus | = OS_TASK_STATUS_RUNNING; //最高任务状态位置成运行状态

  21.     runProcess = OS_PCB_FROM_PID(runTask-> processID); //通过进程ID索引获取到进程实体
  22.     newProcess = OS_PCB_FROM_PID(newTask-> processID); //同上

  23.     OsSchedSwitchProcess(runProcess,newProcess); //切换进程,里面主要涉及进程空间的切换,也就是MMU的切换。

  24. #if(LOSCFG_KERNEL_SMP == YES)// CPU多核的情况
  25.     / *屏蔽新运行任务的所有者处理器* /
  26.     runTask-> currCpu = OS_TASK_INVALID_CPUID; //当前任务不占用CPU
  27.     newTask-> currCpu = ArchCurrCpuid(); //让新任务占用CPU
  28. #endif

  29.     (VOID)OsTaskSwitchCheck(runTask,newTask); //切换任务的检查

  30. #if(LOSCFG_KERNEL_SCHED_STATISTICS ==是)
  31.     OsSchedStatistics(runTask,newTask);
  32. #endif

  33.     if(((newTask-> timeSlice == 0)&&(newTask-> policy == LOS_SCHED_RR)){//没有时间片且是抢占式调度的方式,请注意非抢占式都不需要时间片的。
  34.         newTask-> timeSlice = LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT; //给新任务时间片默认20ms
  35.     }

  36.     OsCurrTaskSet((VOID *)newTask); //设置新的任务为CPU核的当前任务

  37.     if(OsProcessIsUserMode(newProcess)){//用户模式下会怎么样?
  38.         OsCurrUserTaskSet(newTask-> userArea); //设置任务栈空间
  39.     }

  40.     PRINT_TRACE(“ cpu%d运行进程名称:(%s)pid:%d状态:%x threadMap:%x任务名称:(%s)tid: %d状态:%x->  n“
  41.                 ”新进程名称:(%s)pid:%d状态:%x threadMap:%x任务名称:(%s)tid:%d状态:%x! n “,
  42.                 ArchCurrCpuid(),
  43.                 runProcess-> processName,runProcess-> processID,runProcess-> processStatus,
  44.                 runProcess-> threadScheduleMap,runTask-> taskName,runTask-> taskID,runTask-> taskStatus,
  45.                 newProcess-> processName,newProcess-> processID,newProcess-> processStatus,
  46.                 newProcess-> threadScheduleMap,newTask-> taskName,newTask-> taskID,newTask-> taskStatus);

  47.     / *执行任务上下文切换* /
  48.     OsTaskSchedule(newTask,runTask); //重新执行调度,主要是切换CPU的模板
  49. }
复制代码

在哪里设置tick的某些函数?
从main中可以看到tick的初始化和中断服务程序的注册
  1. //中断处理函数
  2. VOID OsTickEntry(VOID)
  3. {
  4.     OsTickHandler(); ///最最关键函数

  5.     / * clear private timer * /
  6.     g_privateTimer-> intStatus = 0x01;
  7. }

  8. //由main函数调用,注册中断处理函数OsTickEntry
  9. VOID HalClockInit(VOID)
  10. {
  11.     UINT32 ret;

  12.     ret = LOS_HwiCreate(PRVTIMER_INT_NUM,0xa0,0,OsTickEntry,NULL);
  13.     如果(ret!= LOS_OK){
  14.         PRINT_ERR(“%s,%d创建滴答irq失败,ret:0x%x  n”,__FUNCTION__,__LINE__,ret);
  15.     }
  16. }
复制代码

OsTickHandler是tick的中断处理程序,其中完成了时间片的检查和任务的扫描。

  1. / *
  2. *说明:滴答中断处理程序
  3. * /
  4. LITE_OS_SEC_TEXT VOID OsTickHandler(VOID)
  5. {
  6.     UINT32 intSave;

  7.     TICK_LOCK(intSave);
  8.     g_tickCount [ArchCurrCpuid()] ++;
  9.     TICK_UNLOCK(intSave);

  10. #ifdef LOSCFG_KERNEL_VDSO
  11.     OsUpdateVdsoTimeval();
  12. #endif

  13. #ifdef LOSCFG_KERNEL_TICKLESS
  14.     OsTickIrqFlagSet(OsTicklessFlagGet());
  15. #endif

  16. #if(LOSCFG_BASE_CORE_TICK_HW_TIME ==是)
  17.     HalClockIrqClear(); / *与每个平台的差异* /
  18. #endif

  19.     OsTimesliceCheck();

  20.     OsTaskScan(); / *任务超时扫描* /

  21. #if(LOSCFG_BASE_CORE_SWTMR == YES)
  22.     OsSwtmrScan();
  23. #万一
  24. }
复制代码
  1. LITE_OS_SEC_TEXT VOID OsTimesliceCheck(VOID)
  2. {
  3.     LosTaskCB * runTask = NULL;
  4.     LosProcessCB * runProcess = OsCurrProcessGet();
  5.     如果(runProcess-> policy!= LOS_SCHED_RR){
  6.         转到SCHED_TASK;
  7.     }

  8.     if(runProcess-> timeSlice!= 0){
  9.         runProcess-> timeSlice-; //进程时间片减少一次
  10.         if(runProcess-> timeSlice == 0){
  11.             LOS_Schedule(); //进程时间片用完,发起调度
  12.         }
  13.     }

  14. SCHED_TASK:
  15.     runTask = OsCurrTaskGet();
  16.     如果(runTask-> policy!= LOS_SCHED_RR){
  17.         返回;
  18.     }

  19.     if(runTask-> timeSlice!= 0){
  20.         runTask-> timeSlice-; //对应任务时间片也减少一次
  21.         如果(runTask-> timeSlice == 0){
  22.             LOS_Schedule();
  23.         }
  24.     }
  25. }
复制代码


OsTaskScan()
OsTaskScan()不断查任务的状态,有任务就去执行,毫不夸张的可以说是进程有序执行的源动力之所在!
  1. LITE_OS_SEC_TEXT VOID OsTaskScan(VOID)
  2. {
  3.     SortLinkList * sortList = NULL;
  4.     LosTaskCB * taskCB = NULL;
  5.     BOOL needSchedule = FALSE;
  6.     UINT16 tempStatus;
  7.     LOS_DL_LIST * listObject = NULL;
  8.     SortLinkAttribute * taskSortLink = NULL;

  9.     taskSortLink =&OsPercpuGet()-> taskSortLink;
  10.     taskSortLink-> cursor =(taskSortLink-> cursor +1)和OS_TSK_SORTLINK_MASK;
  11.     listObject = taskSortLink-> sortLink + taskSortLink->光标;

  12.     / *
  13.      *当任务挂起超时时,任务块位于超时sortlink
  14.      *(每个cpu)和ipc(互斥,sem等)的块上,可以
  15.      通过任一超时将其唤醒*或相应的IPC正在等待。
  16.      *
  17.      *现在使用了同步sortlink操作,因此整个任务扫描都需要
  18.      得到保护,从而防止另一个内核同时进行sortlink删除。
  19.      * /
  20.     LOS_SpinLock(&g_taskSpin);

  21.     如果(LOS_ListEmpty(listObject)){
  22.         LOS_SpinUnlock(&g_taskSpin);
  23.         返回;
  24.     }
  25.     sortList = LOS_DL_LIST_ENTRY(listObject-> pstNext,SortLinkList,sortLinkNode);
  26.     ROLLNUM_DEC(sortList-> idxRollNum);

  27.     while(ROLLNUM(sortList-> idxRollNum)== 0){
  28.         LOS_ListDelete(&sortList-> sortLinkNode);
  29.         taskCB = LOS_DL_LIST_ENTRY(sortList,LosTaskCB,sortList);
  30.         taskCB-> taskStatus&=〜OS_TASK_STATUS_PEND_TIME;
  31.         tempStatus = taskCB-> taskStatus;
  32.         如果(tempStatus&OS_TASK_STATUS_PEND){
  33.             taskCB-> taskStatus&=〜OS_TASK_STATUS_PEND;
  34. #if(LOSCFG_KERNEL_LITEIPC == YES)
  35.             taskCB-> ipcStatus&=〜IPC_THREAD_STATUS_PEND;
  36. #endif
  37.             taskCB-> taskStatus | = OS_TASK_STATUS_TIMEOUT;
  38.             LOS_ListDelete(&taskCB-> pendList);
  39.             taskCB-> taskSem = NULL;
  40.             taskCB-> taskMux = NULL;
  41.         } else {
  42.             taskCB-> taskStatus&=〜OS_TASK_STATUS_DELAY;
  43.         }

  44.         if(!(tempStatus&OS_TASK_STATUS_SUSPEND)){
  45.             OS_TASK_SCHED_QUEUE_ENQUEUE(taskCB,OS_PROCESS_STATUS_PEND);

  46.         }

  47.         如果(LOS_ListEmpty(的ListObject)){
  48.             中断;
  49.         }

  50.         sortList = LOS_DL_LIST_ENTRY(listObject-> pstNext,SortLinkList,sortLinkNode);
  51.     }

  52.     LOS_SpinUnlock(&g_taskSpin);

  53.     如果(needSchedule!= FALSE){
  54.         LOS_MpSchedule(OS_MP_CPU_ALL);
  55.         LOS_Schedule();
  56.     }
  57. }
复制代码
以上代码对应张大爷的故事:钟声一响起张大爷起身去查节目时间用完没有,没用完继续你的表演,用完了去外面重新排队,大爷再从外面选一个优先级最高的节目进来表演,就这么简单!除了钟响大爷去工作之外,还有什么情况启用大爷不要偷懒,起来走两步呢?

除了tick会触发调度,还有一些情况会触发调度?
全部文章进入>>鸿蒙系统源码分析(总目录)查看
进入>>【Gitee仓】阅读,代码仓库加注同步更新...。
文章来源:图解鸿蒙原始码逐行注释分析

回帖

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
链接复制成功,分享给好友