时钟节拍由配置为中断触发模式的硬件定时器产生,当中断到来时,将调用一次:void OsTickHandler(void),通知操作系统已经过去一个系统时钟;不同硬件定时器中断实现都不同,
/**
* @ingroup los_config
* Number of Ticks in one second
*/
#ifndef LOSCFG_BASE_CORE_TICK_PER_SECOND
#define LOSCFG_BASE_CORE_TICK_PER_SECOND 100 //默认每秒100次触发,当然这是可以改的
#endif
每秒100个tick,时间单位为10毫秒, 即每秒调用时钟中断处理程序100次.
/*
* Description : Tick interruption handler
*///节拍中断处理函数 ,鸿蒙默认10ms触发一次
LITE_OS_SEC_TEXT VOID OsTickHandler(VOID)
{
//...
OsTimesliceCheck();//进程和任务的时间片检查
OsTaskScan(); /* task timeout scan *///任务扫描
#if (LOSCFG_BASE_CORE_SWTMR == YES)
OsSwtmrScan();//定时器扫描,看是否有超时的定时器
#endif
}
它主要干了三件事情
#ifndef LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT
#define LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT 2 //2个时间片,20ms
#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();//任务时间片用完,发起调度
}
}
}
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;//只处理这个游标上的链表,因为系统对超时任务都已经规链表了.
//当任务因超时而挂起时,任务块处于超时排序链接上,(每个cpu)和ipc(互斥锁、扫描电镜等)的块同时被唤醒
/*不管是超时还是相应的ipc,它都在等待。现在使用synchronize sortlink precedure,因此整个任务扫描需要保护,防止另一个核心同时删除sortlink。
* 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);//拿本次Tick对应链表的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);//核间通讯,给所有CPU发送调度信号
LOS_Schedule();//开始调度
}
}
/*
* Description: Tick interrupt interface module of software timer
* Return : LOS_OK on success or error code on failure
*///OsSwtmrScan 由系统时钟中断处理函数调用
LITE_OS_SEC_TEXT VOID OsSwtmrScan(VOID)//扫描定时器,如果碰到超时的,就放入超时队列
{
SortLinkList *sortList = NULL;
SWTMR_CTRL_S *swtmr = NULL;
SwtmrHandlerItemPtr swtmrHandler = NULL;
LOS_DL_LIST *listObject = NULL;
SortLinkAttribute* swtmrSortLink = &OsPercpuGet()->swtmrSortLink;//拿到当前CPU的定时器链表
swtmrSortLink->cursor = (swtmrSortLink->cursor + 1) & OS_TSK_SORTLINK_MASK;
listObject = swtmrSortLink->sortLink + swtmrSortLink->cursor;
//由于swtmr是在特定的sortlink中,所以需要很小心的处理它,但其他CPU Core仍然有机会处理它,比如停止计时器
/*
* it needs to be carefully coped with, since the swtmr is in specific sortlink
* while other cores still has the chance to process it, like stop the timer.
*/
LOS_SpinLock(&g_swtmrSpin);
if (LOS_ListEmpty(listObject)) {
LOS_SpinUnlock(&g_swtmrSpin);
return;
}
sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);
ROLLNUM_DEC(sortList->idxRollNum);
while (ROLLNUM(sortList->idxRollNum) == 0) {
sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);
LOS_ListDelete(&sortList->sortLinkNode);
swtmr = LOS_DL_LIST_ENTRY(sortList, SWTMR_CTRL_S, stSortList);
swtmrHandler = (SwtmrHandlerItemPtr)LOS_MemboxAlloc(g_swtmrHandlerPool);//取出一个可用的软时钟处理项
if (swtmrHandler != NULL) {
swtmrHandler->handler = swtmr->pfnHandler;
swtmrHandler->arg = swtmr->uwArg;
if (LOS_QueueWrite(OsPercpuGet()->swtmrHandlerQueue, swtmrHandler, sizeof(CHAR *), LOS_NO_WAIT)) {
(VOID)LOS_MemboxFree(g_swtmrHandlerPool, swtmrHandler);
}
}
if (swtmr->ucMode == LOS_SWTMR_MODE_ONCE) {
OsSwtmrDelete(swtmr);
if (swtmr->usTimerID < (OS_SWTMR_MAX_TIMERID - LOSCFG_BASE_CORE_SWTMR_LIMIT)) {
swtmr->usTimerID += LOSCFG_BASE_CORE_SWTMR_LIMIT;
} else {
swtmr->usTimerID %= LOSCFG_BASE_CORE_SWTMR_LIMIT;
}
} else if (swtmr->ucMode == LOS_SWTMR_MODE_NO_SELFDELETE) {
swtmr->ucState = OS_SWTMR_STATUS_CREATED;
} else {
swtmr->ucOverrun++;
OsSwtmrStart(swtmr);
}
if (LOS_ListEmpty(listObject)) {
break;
}
sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode);
}
LOS_SpinUnlock(&g_swtmrSpin);
}
欢迎大家关注公众号<程序猿百晓生>,可以了解到一下知识点。
1.OpenHarmony开发基础
2.OpenHarmony北向开发环境搭建
3.鸿蒙南向开发环境的搭建
4.鸿蒙生态应用开发白皮书V2.0 & V3.0
5.鸿蒙开发面试真题(含参考答案)
6.TypeScript入门学习手册
7.OpenHarmony 经典面试题(含参考答案)
8.OpenHarmony设备开发入门【最新版】
9.沉浸式剖析OpenHarmony源代码
10.系统定制指南
11.【OpenHarmony】Uboot 驱动加载流程
12.OpenHarmony构建系统--GN与子系统、部件、模块详解
13.ohos开机init启动流程
14.鸿蒙版性能优化指南
.......
//调度算法的实现
VOID OsSchedResched(VOID)
{
LosTaskCB *runTask = NULL;
LosTaskCB *newTask = NULL;
LosProcessCB *runProcess = NULL;
LosProcessCB *newProcess = NULL;
LOS_ASSERT(LOS_SpinHeld(&g_taskSpin));//必须持有任务自旋锁,自旋锁是不是进程层面去抢锁,而是CPU各自核之间去争夺锁
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);//通过进程ID索引拿到进程实体
newProcess = OS_PCB_FROM_PID(newTask->processID);//同上
OsSchedSwitchProcess(runProcess, newProcess);//切换进程,里面主要涉及进程空间的切换,也就是MMU的上下文切换.
#if (LOSCFG_KERNEL_SMP == YES)//CPU多核的情况
/* mask new running task's owner processor */
runTask->currCpu = OS_TASK_INVALID_CPUID;//当前任务不占用CPU
newTask->currCpu = ArchCurrCpuid();//让新任务占用CPU
#endif
(VOID)OsTaskSwitchCheck(runTask, newTask);//切换task的检查
#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;//给新任务时间片 默认 20ms
}
OsCurrTaskSet((VOID*)newTask);//设置新的task为CPU核的当前任务
if (OsProcessIsUserMode(newProcess)) {//用户模式下会怎么样?
OsCurrUserTaskSet(newTask->userArea);//设置task栈空间
}
/* do the task context switch */
OsTaskSchedule(newTask, runTask);//切换任务上下文,注意OsTaskSchedule是一个汇编函数 见于 los_dispatch.s
}
如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。