提示:本文基于开源鸿蒙内核分析,官方源码【kernel_liteos_a】官方文档【docs】参考文档【Huawei LiteOS】
本文作者:鸿蒙内核发烧友,将持续研究鸿蒙内核,更新博文,敬请关注。内容仅代表个人观点,错误之处,欢迎大家指正完善。本系列全部文章进入 查看 鸿蒙系统源码分析(总目录)
目录
时钟管理模块很简单,但却有内核最重要的代码段 OsTickHandler(),这是干嘛的,可以理解为 JAVA的定时任务,但这是系统内核的定时器。因鸿蒙目前开放的是 轻量级的内核 lite os (LOS),所以tick的频率不会太高
详见代码: los_tick.c los_timeslice.c
多久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
/**
* @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 函数调用,注册中断处理函数 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%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 != 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 会触发调度,还有哪些情况会触发调度?
想好了请在评论区里留言 ,本系列更多文章进入 鸿蒙系统源码分析(总目录) 查看