返回发烧友论坛
帖子
发 帖
提问题
发文章
发资料
发讨论
发活动
草稿箱
注册/登录
[文章]鸿蒙内核源码分析(时钟管理篇):时钟是触发调度最大的源动力
ElecFans小喇叭
2020-11-24 10:01:29
0
鸿蒙系统
内核
源码
扫一扫,分享给好友
复制链接分享
0
0
时钟管理模块很简单,却有内核最重要的代码段OsTickHandler(),这是干嘛的,可以理解为JAVA的定时任务,而是系统内核的定时器。因鸿蒙目前开放的是轻量级的内核lite os(LOS),所以tick的频率不会太高
对应张大爷的故事:很简单就是场馆的那个大钟,每10分响一次,一次就是一个Tick(节拍)
多久Tick一次?看代码
<font style="vertical-align: inherit;"><font style="vertical-align: inherit;">/ ** </font></font>
<font style="vertical-align: inherit;"><font style="vertical-align: inherit;">* @ingroup los_config *一秒内的抽动</font></font>
<font style="vertical-align: inherit;"><font style="vertical-align: inherit;">数</font></font>
<font style="vertical-align: inherit;"><font style="vertical-align: inherit;">* / </font></font>
<font style="vertical-align: inherit;"><font style="vertical-align: inherit;">#ifndef LOSCFG_BASE_CORE_TICK_PER_SECOND </font></font>
<font style="vertical-align: inherit;"><font style="vertical-align: inherit;">#define LOSCFG_BASE_CORE_TICK_PER_SECOND 100 // //每秒间隔100次触发,当然这是可以改的</font></font>
<font style="vertical-align: inherit;"><font style="vertical-align: inherit;">#endif</font></font>
复制代码
每秒100 tick,即每秒100次调用时钟中断处理程序,时间片单位为10ms
一次任务分配多少时间给进展执行呢?答案是2个时间片,即20ms
对应张大爷的故事:节目喊到后这次进来的总表演时间,10分钟一个时间片,共2片,表演20分钟。
/ **
* @ingroup los_config
*具有相同优先级的任务的最长执行时间
* /
#ifndef LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT
#define LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT 2
#endif
/ **
* @ingroup los_process
*保留时间片进程
* /
#define OS_PROCOUT_BASE_CLOCK_RR
复制代码
用完需要可以再分配,但一次就是只给2个时间片,怕你一直占着茅坑,后面还有人在等的。详细代码:OsSchedResched(VOID)
//重新
调试实现VOID OsSchedResched(VOID)
{
LosTaskCB * runTask = NULL;
LosTaskCB * newTask = NULL;
LosProcessCB * runProcess = NULL;
LosProcessCB * newProcess = NULL;
LOS_ASSERT(LOS_SpinHeld(&g_taskSpin)); //必须持有任务自旋锁,自旋锁是不是前进方向去抢锁,而且CPU各个核之间去争夺锁
如果(!OsPreemptableInSched()){//是否置了重新调度标识位
return;
}
runTask = OsCurrTaskGet(); //获取当前任务
newTask = OsGetTopTask(); //获取优先级最最最高的任务
/ *总是能够获得一个任务* /
LOS_ASSERT(newTask!= NULL); //不能没有
需求调试的任务if(runTask == newTask){//当前任务就是最高任务,那还调度个啥的,直接退出。
返回;
}
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多核的情况
/ *屏蔽新运行任务的所有者处理器* /
runTask-> currCpu = OS_TASK_INVALID_CPUID; //当前任务不占用CPU
newTask-> currCpu = ArchCurrCpuid(); //让新任务占用CPU
#endif
(VOID)OsTaskSwitchCheck(runTask,newTask); //切换任务的检查
#if(LOSCFG_KERNEL_SCHED_STATISTICS ==是)
OsSchedStatistics(runTask,newTask);
#endif
if(((newTask-> timeSlice == 0)&&(newTask-> policy == LOS_SCHED_RR)){//没有时间片且是抢占式调度的方式,请注意非抢占式都不需要时间片的。
newTask-> timeSlice = LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT; //给新任务时间片默认20ms
}
OsCurrTaskSet((VOID *)newTask); //设置新的任务为CPU核的当前任务
if(OsProcessIsUserMode(newProcess)){//用户模式下会怎么样?
OsCurrUserTaskSet(newTask-> userArea); //设置任务栈空间
}
PRINT_TRACE(“ cpu%d运行进程名称:(%s)pid:%d状态:%x threadMap:%x任务名称:(%s)tid: %d状态:%x-> n“
”新进程名称:(%s)pid:%d状态:%x threadMap:%x任务名称:(%s)tid:%d状态:%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);
/ *执行任务上下文切换* /
OsTaskSchedule(newTask,runTask); //重新执行调度,主要是切换CPU的模板
}
复制代码
在哪里设置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);
如果(ret!= LOS_OK){
PRINT_ERR(“%s,%d创建滴答irq失败,ret:0x%x n”,__FUNCTION__,__LINE__,ret);
}
}
复制代码
OsTickHandler是tick的中断处理程序,其中完成了时间片的检查和任务的扫描。
/ *
*说明:滴答中断处理程序
* /
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 ==是)
HalClockIrqClear(); / *与每个平台的差异* /
#endif
OsTimesliceCheck();
OsTaskScan(); / *任务超时扫描* /
#if(LOSCFG_BASE_CORE_SWTMR == YES)
OsSwtmrScan();
#万一
}
复制代码
LITE_OS_SEC_TEXT VOID OsTimesliceCheck(VOID)
{
LosTaskCB * runTask = NULL;
LosProcessCB * runProcess = OsCurrProcessGet();
如果(runProcess-> policy!= LOS_SCHED_RR){
转到SCHED_TASK;
}
if(runProcess-> timeSlice!= 0){
runProcess-> timeSlice-; //进程时间片减少一次
if(runProcess-> timeSlice == 0){
LOS_Schedule(); //进程时间片用完,发起调度
}
}
SCHED_TASK:
runTask = OsCurrTaskGet();
如果(runTask-> policy!= LOS_SCHED_RR){
返回;
}
if(runTask-> timeSlice!= 0){
runTask-> timeSlice-; //对应任务时间片也减少一次
如果(runTask-> timeSlice == 0){
LOS_Schedule();
}
}
}
复制代码
OsTaskScan()
OsTaskScan()不断查任务的状态,有任务就去执行,毫不夸张的可以说是进程有序执行的源动力之所在!
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->光标;
/ *
*当任务挂起超时时,任务块位于超时sortlink
*(每个cpu)和ipc(互斥,sem等)的块上,可以
通过任一超时将其唤醒*或相应的IPC正在等待。
*
*现在使用了同步sortlink操作,因此整个任务扫描都需要
得到保护,从而防止另一个内核同时进行sortlink删除。
* /
LOS_SpinLock(&g_taskSpin);
如果(LOS_ListEmpty(listObject)){
LOS_SpinUnlock(&g_taskSpin);
返回;
}
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;
如果(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);
}
如果(LOS_ListEmpty(的ListObject)){
中断;
}
sortList = LOS_DL_LIST_ENTRY(listObject-> pstNext,SortLinkList,sortLinkNode);
}
LOS_SpinUnlock(&g_taskSpin);
如果(needSchedule!= FALSE){
LOS_MpSchedule(OS_MP_CPU_ALL);
LOS_Schedule();
}
}
复制代码
以上代码对应张大爷的故事:钟声一响起张大爷起身去查节目时间用完没有,没用完继续你的表演,用完了去外面重新排队,大爷再从外面选一个优先级最高的节目进来表演,就这么简单!除了钟响大爷去工作之外,还有什么情况启用大爷不要偷懒,起来走两步呢?
除了tick会触发调度,还有一些情况会触发调度?
全部文章进入>>
鸿蒙系统源码分析(总目录)
查看
进入>>【
Gitee仓
】阅读,代码仓库加注同步更新...。
文章来源:图解鸿蒙原始码逐行注释分析
举报
回帖
高级模式
发布
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。
侵权投诉
链接复制成功,分享给好友