原标题:鸿蒙内核源码分析:时钟管理模块很简单,却有内核最重要的代码段!
作者 | 深入研究鸿蒙,鸿蒙内核发烧友
出品 | CSDN(ID:CSDNnews)
头图 | CSDN 下载自东方 IC
时钟管理模块很简单,但却有内核最重要的代码段 OsTickHandler,这是干嘛的,可以理解为 JAVA的定时任务,但这是系统内核的定时器。因鸿蒙目前开放的是轻量级的内核 lite os (LOS),所以tick的频率不会太高。
对应张大爷的故事:很简单就是场馆的那个大钟,每10分响一次,一次就是一个Tick(节拍)
多久Tick一次?
/**
* @ingroup los_config
* Number of Ticks in one second
*/
# ifndefLOSCFG_BASE_CORE_TICK_PER_SECOND
# defineLOSCFG_BASE_CORE_TICK_PER_SECOND 100 //*kfy 默认每秒100次触发,当然这是可以改的
# endif
每秒100 tick ,即每秒100次调用时钟中断处理程序, 时间片单位为10ms
一次任务分配多少时间给进程执行呢?答案是 2个时间片,即 20ms
对应张大爷的故事:节目喊到后这次进来的总表演时间,10分钟一个时间片,共2片,表演20分钟。
对应张大爷的故事:很简单就是场馆的那个大钟,每10分响一次,一次就是一个Tick(节拍)
/**
* @ingroup los_config
* Longest execution time of tasks with the same priorities
*/
# ifndefLOSCFG_BASE_CORE_TIMESLICE_TIMEOUT
# defineLOSCFG_BASE_CORE_TIMESLICE_TIMEOUT 2
# endif
/**
* @ingroup los_process
* Hold the time slice process
*/
# defineOS_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 函数调用,注册中断处理函数 OsTickEntry
VOID 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 的中断处理程序,其中完成了时间片的检查和任务的扫描。
/*
* Deion : Tick interruption handler
*/
LITE_OS_SEC_TEXT VOID OsTickHandler(VOID)
{
UINT32 intSave;
TICK_LOCK(intSave);
g_tickCount[ArchCurrCpuid]++;
TICK_UNLOCK(intSave);
# ifdefLOSCFG_KERNEL_VDSO
OsUpdateVdsoTimeval;
# endif
# ifdefLOSCFG_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) {
gotoSCHED_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会触发调度,还有哪些情况会触发调度?
想好了请在评论区里留言