鸿蒙内核源码分析:时钟管理模块很简单,却有内核最重要的代码段!

来自:CSDN 2020-10-04

# 鸿蒙

4 个

作者 | 深入研究鸿蒙,鸿蒙内核发烧友

出品 | CSDN(ID:CSDNnews)

头图 | CSDN 下载自东方 IC

时钟管理模块很简单,但却有内核最重要的代码段 OsTickHandler ( ) ,这是干嘛的,可以理解为 JAVA 的定时任务,但这是系统内核的定时器。因鸿蒙目前开放的是轻量级的内核 lite os ( LOS ) ,所以 tick 的频率不会太高。

对应张大爷的故事:很简单就是场馆的那个大钟,每 10 分响一次,一次就是一个 Tick ( 节拍 )

多久 Tick 一次?

/** * @ingroup los_config * Number of Ticks in one second */#ifndef LOSCFG_BASE_CORE_TICK_PER_SECOND#define LOSCFG_BASE_CORE_TICK_PER_SECOND 100 //*kfy 默认每秒 100 次触发,当然这是可以改的 #endif

每秒 100 tick ,即每秒 100 次调用时钟中断处理程序 , 时间片单位为 10ms

一次任务分配多少时间给进程执行呢?答案是 2 个时间片,即 20ms

对应张大爷的故事:节目喊到后这次进来的总表演时间,10 分钟一个时间片,共 2 片,表演 20 分钟。

/** * @ingroup los_config * Longest execution time of tasks with the same priorities */#ifndef LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT#define LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT 2#endif /** * @ingroup los_process * Hold the time slice process */#define OS_PROCESS_SCHED_RR_INTERVAL LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT

用完需要可以再分配,但一次就是只给 2 个时间片,怕你一直占着茅坑,后面还有人在等的 . 详细代码:OsSchedResched ( VOID )

VOID OsSchedResched ( VOID ) { LosTaskCB *runTask = NULL; LosTaskCB *newTask = NULL; LosProcessCB *runProcess = NULL; LosProcessCB *newProcess = NULL; LOS_ASSERT ( LOS_SpinHeld ( &g_taskSpin ) ) ; if ( !OsPreemptableInSched ( ) ) { return; } runTask = OsCurrTaskGet ( ) ; newTask = OsGetTopTask ( ) ; /* always be able to get one task */ LOS_ASSERT ( newTask != NULL ) ; if ( runTask == newTask ) { return; } runTask->taskStatus &= ~OS_TASK_STATUS_RUNNING; newTask->taskStatus |= OS_TASK_STATUS_RUNNING; runProcess = OS_PCB_FROM_PID ( runTask->processID ) ; newProcess = OS_PCB_FROM_PID ( newTask->processID ) ; OsSchedSwitchProcess ( runProcess, newProcess ) ; #if ( LOSCFG_KERNEL_SMP == YES ) /* mask new running task's owner processor */ runTask->currCpu = OS_TASK_INVALID_CPUID; newTask->currCpu = ArchCurrCpuid ( ) ;#endif ( VOID ) OsTaskSwitchCheck ( runTask, newTask ) ; #if ( LOSCFG_KERNEL_SCHED_STATISTICS == YES ) OsSchedStatistics ( runTask, newTask ) ;#endif if ( ( newTask->timeSlice == 0 ) && ( newTask->policy == LOS_SCHED_RR ) ) { newTask->timeSlice = LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT; // 只给两个时间片 } OsCurrTaskSet ( ( VOID* ) newTask ) ; if ( OsProcessIsUserMode ( newProcess ) ) { OsCurrUserTaskSet ( newTask->userArea ) ; } PRINT_TRACE ( "cpu%d run process name: ( %s ) pid: %d status: %x threadMap: %x task name: ( %s ) tid: %d status: %x ->n" " new process name: ( %s ) pid: %d status: %x threadMap: %x task name: ( %s ) tid: %d status: %x!n", ArchCurrCpuid ( ) , runProcess->processName, runProcess->processID, runProcess->processStatus, runProcess->threadScheduleMap, runTask->taskName, runTask->taskID, runTask->taskStatus, newProcess->processName, newProcess->processID, newProcess->processStatus, newProcess->threadScheduleMap, newTask->taskName, newTask->taskID, newTask->taskStatus ) ; /* do the task context switch */ OsTaskSchedule ( newTask, runTask ) ;}

在哪里设置 tick 的回调函数?

从 main 中可以看到 tick 的初始化和中断服务程序的注册

// 中断处理函数 VOID OsTickEntry ( VOID ) { OsTickHandler ( ) ; // 最最关键函数 /* clear private timer */ g_privateTimer->intStatus = 0x01;} // 由 main 函数调用,注册中断处理函数 OsTickEntryVOID HalClockInit ( VOID ) { UINT32 ret; ret = LOS_HwiCreate ( PRVTIMER_INT_NUM, 0xa0, 0, OsTickEntry, NULL ) ; if ( ret != LOS_OK ) { PRINT_ERR ( "%s, %d create tick irq failed, ret:0x%xn", __FUNCTION__, __LINE__, ret ) ; }}

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

/* * Description : Tick interruption handler */LITE_OS_SEC_TEXT VOID OsTickHandler ( VOID ) { UINT32 intSave; TICK_LOCK ( intSave ) ; g_tickCount [ ArchCurrCpuid ( ) ] ++; TICK_UNLOCK ( intSave ) ; #ifdef LOSCFG_KERNEL_VDSO OsUpdateVdsoTimeval ( ) ;#endif #ifdef LOSCFG_KERNEL_TICKLESS OsTickIrqFlagSet ( OsTicklessFlagGet ( ) ) ;#endif #if ( LOSCFG_BASE_CORE_TICK_HW_TIME == YES ) HalClockIrqClear ( ) ; /* diff from every platform */#endif OsTimesliceCheck ( ) ; OsTaskScan ( ) ; /* task timeout scan */ #if ( LOSCFG_BASE_CORE_SWTMR == YES ) OsSwtmrScan ( ) ;#endif}

LITE_OS_SEC_TEXT VOID OsTimesliceCheck ( VOID ) { LosTaskCB *runTask = NULL; LosProcessCB *runProcess = OsCurrProcessGet ( ) ; if ( runProcess->policy != LOS_SCHED_RR ) { goto SCHED_TASK; } if ( runProcess->timeSlice != 0 ) { runProcess->timeSlice--;// 进程时间片减少一次 if ( runProcess->timeSlice == 0 ) { LOS_Schedule ( ) ;// 进程时间片用完 , 发起调度 } } SCHED_TASK: runTask = OsCurrTaskGet ( ) ; if ( runTask->policy != LOS_SCHED_RR ) { return; } if ( runTask->timeSlice != 0 ) { runTask->timeSlice--;// 对应任务时间片也减少一次 if ( runTask->timeSlice == 0 ) { LOS_Schedule ( ) ; } }}

OsTaskScan ( )

OsTaskScan ( ) 不断查 task 的状态,有任务就去执行,毫不夸张的可以说是 进程有序执行的源动力之所在!

LITE_OS_SEC_TEXT VOID OsTaskScan ( VOID ) { SortLinkList *sortList = NULL; LosTaskCB *taskCB = NULL; BOOL needSchedule = FALSE; UINT16 tempStatus; LOS_DL_LIST *listObject = NULL; SortLinkAttribute *taskSortLink = NULL; taskSortLink = &OsPercpuGet ( ) ->taskSortLink; taskSortLink->cursor = ( taskSortLink->cursor + 1 ) & OS_TSK_SORTLINK_MASK; listObject = taskSortLink->sortLink + taskSortLink->cursor; /* * When task is pended with timeout, the task block is on the timeout sortlink * ( per cpu ) and ipc ( mutex,sem and etc. ) 's block at the same time, it can be waken * up by either timeout or corresponding ipc it's waiting. * * Now synchronize sortlink preocedure is used, therefore the whole task scan needs * to be protected, preventing another core from doing sortlink deletion at same time. */ LOS_SpinLock ( &g_taskSpin ) ; if ( LOS_ListEmpty ( listObject ) ) { LOS_SpinUnlock ( &g_taskSpin ) ; return; } sortList = LOS_DL_LIST_ENTRY ( listObject->pstNext, SortLinkList, sortLinkNode ) ; ROLLNUM_DEC ( sortList->idxRollNum ) ; while ( ROLLNUM ( sortList->idxRollNum ) == 0 ) { LOS_ListDelete ( &sortList->sortLinkNode ) ; taskCB = LOS_DL_LIST_ENTRY ( sortList, LosTaskCB, sortList ) ; taskCB->taskStatus &= ~OS_TASK_STATUS_PEND_TIME; tempStatus = taskCB->taskStatus; if ( tempStatus & OS_TASK_STATUS_PEND ) { taskCB->taskStatus &= ~OS_TASK_STATUS_PEND;#if ( LOSCFG_KERNEL_LITEIPC == YES ) taskCB->ipcStatus &= ~IPC_THREAD_STATUS_PEND;#endif taskCB->taskStatus |= OS_TASK_STATUS_TIMEOUT; LOS_ListDelete ( &taskCB->pendList ) ; taskCB->taskSem = NULL; taskCB->taskMux = NULL; } else { taskCB->taskStatus &= ~OS_TASK_STATUS_DELAY; } if ( ! ( tempStatus & OS_TASK_STATUS_SUSPEND ) ) { OS_TASK_SCHED_QUEUE_ENQUEUE ( taskCB, OS_PROCESS_STATUS_PEND ) ; needSchedule = TRUE; } if ( LOS_ListEmpty ( listObject ) ) { break; } sortList = LOS_DL_LIST_ENTRY ( listObject->pstNext, SortLinkList, sortLinkNode ) ; } LOS_SpinUnlock ( &g_taskSpin ) ; if ( needSchedule != FALSE ) { LOS_MpSchedule ( OS_MP_CPU_ALL ) ; LOS_Schedule ( ) ; }}

以上代码对应张大爷的故事 : 钟声一响起张大爷起身去查 节目时间用完没有,没用完继续你的表演,用完了去外面重新排队,大爷再从外面选一个优先级最高的节目进来表演,就这么简单!除了钟响大爷去工作之外,还有什么情况能让大爷不要偷懒,起来走两步呢?

除了 tick 会触发调度,还有哪些情况会触发调度?

想好了请在评论区里留言,点击阅读原文查看本系列更多文章

原文地址://blog.csdn.net/kuangyufei/article/details/108603468

更多精彩推荐

共建智慧云基石,阿里云携手英特尔走向数智未来

国产开源,GitHub 标星 47000+ ,百度飞桨从打响第一枪到战役突围

那个放弃谷歌回老家二本教书的清华姚班生,现在怎么样了?

硅谷 2020 最新大数据学习路线:科学使用这一招,12 周助你成为数据分析师

国产开源,GitHub 标星 47000+ ,百度飞桨从打响第一枪到战役突围

用以太坊承载的比特币,还只是起步阶段

点分享

点点赞

点在看