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

鸿蒙内核源码分析:时钟管理模块很简单,却有内核最重要的代码段!
2020年09月24日 15:01 CSDN

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

出品 | 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分钟。

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

/**

* @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 == ) && (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, , OsTickEntry, NULL);

if (ret != LOS_OK) {

PRINT_ERR("%s, %d create tick irq failed, ret:0x%x\n", __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 != ) {

runProcess->timeSlice--;//进程时间片减少一次

if (runProcess->timeSlice == ) {

LOS_Schedule();//进程时间片用完,发起调度

}

}

SCHED_TASK:

runTask = OsCurrTaskGet();

if (runTask->policy != LOS_SCHED_RR) {

return;

}

if (runTask->timeSlice != ) {

runTask->timeSlice--;//对应任务时间片也减少一次

if (runTask->timeSlice == ) {

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) == ) {

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会触发调度,还有哪些情况会触发调度?

财经自媒体联盟更多自媒体作者

新浪首页 语音播报 相关新闻 返回顶部