HarmonyOS内核源码分析(CPU篇)|内核如何描述CPU?很明显CPUcpu负责执行指令。谁能给它指点一下?它是一个线程(也叫任务),任务是内核的调度单位。CPU将执行它被调度到哪个任务的指令。要执行一条指令,必须有一条取指指令的起始地址。起始地址就是众所周知的main函数。一个程序加载解析完成后,内核会找到main函数在ELF中的位置,并自动创建一个线程,指定线程的入口地址为main函数的地址,由此开始抓取之路,翻译和执行。多线程内核是如何处理的呢?同样,以JAVA为例,对于内核来说,new线程中的run()函数和main()函数没有区别。它是一个线程(任务)的执行入口。注意系列中反复说任务就是线程,线程就是任务。它们是对事物不同层次的描述。对应用层是一个线程,对内核层是一个任务。Yes有多少线程就会有多少条目,它们都接受调度算法的调度。调度算法只认优先级,不管你是main()还是run(),都不会区别对待你。定时器的实现也是通过任务实现的,只是一个系统任务OsSwtmrTaskCreate,优先级最高,入口地址OsSwtmrTask由系统指定。因此,要了解CPU,首先要了解任务。任务是了解内核的主线。很容易理解它并分析内核。事半功倍。看似高级的CPU,不过是草木皆兵。不相信吗?然后看看内核是如何描述CPU的。本文将围绕这个结构展开。内核如何描述CPU?typedefstruct{//*tasksortlink*//taskwait/delay排序列表SortLinkAttributeswtmrSortLink;/*swtmrsortlink*///Timer排序列表UINT32idleTaskID;/*idletaskid*///空闲任务ID在OsIdleTaskCreateUINT32taskLockCnt;/*tasklockflag*///任务锁数,当>0时,需要重新调度UINT32swtmrHandlerQueue;/*softwaretimertimeoutqueueid*///软时钟超时队列句柄UINT32swtmrTaskID;/*softwaretimertaskid*///软时钟任务IDUINT32schedFlag;/*pendingschedulerflag*///调度标志cpuhaltorexcflag*///CPU停止或运行的标志#endif}Percpu;结构并不复杂,但是很重要,我们一一分解。●taskSortLink有什么用?主动或被动中断,并进入等待状态。?主动中断,如:主动延迟300毫秒,这是应用层很常见的操作。?被动中断,如:申请互斥量失败,等待事件发生等。当出现这些情况时,任务就会挂在taskSortLink上。UINT32OsTaskWait(LOS_DL_LIST*list,UINT32timeout,BOOLneedSched){LosTaskCB*runTask=NULL;LOS_DL_LIST*pendObj=NULL;runTask=OsCurrTaskGet();//获取当前任务OS_TASK_SCHED_QUEUErun_DEQUEUE(,OS_PROCESS_STATUS_PEND);//从就绪队列中取出任务并变成阻塞状态pendObj=&runTask->pendList;runTask->taskStatus|=OS_TASK_STATUS_PEND;//把阻塞任务标签贴到任务上LOS_ListTailInsert(list,pendObj);//把阻塞任务放到列表中,这个步骤很关键很重要!if(timeout!=LOS_WAIT_FOREVER){//当没有永远等待时加入定时器链表}if(needSched==TRUE){//是否需要调度OsSchedResched();//申请调度,直接切换任务上下文,任务不再执行if(runTask->taskStatus&OS_TASK_STATUS_TIMEOUT){//再次选择schedule时执行该语句,上面的语句可能隔了很久,所以可能超时了runTask->taskStatus&=~OS_TASK_STATUS_TIMEOUT;//如果taskhasatimeoutlabel,那么就去掉那个标签returnLOS_ERRNO_TSK_TIMEOUT;}}returnLOS_OK;}LITE_OS_SEC_TEXTSTATICINLINEVOIDOsAdd2TimerList(LosTaskCB*taskCB,UINT32timeOut){SET_SORTLIST_VALUE(&taskCB->sortList,timeOut);//设置idxRollNum的值为timeOutOsAdd2SortLink(&OsPercpuGet()->taskSortLink,&taskCB->sortList);//把任务放到定时器排序列表中#if(LOSCFG_KERNEL_SMP==YES)//注意:这里的排序不是传统意义上的12345的排序,而是按照12345的值排序timeOut决定放哪个CPUcoretaskCB->timerCpu=ArchCurrCpuid();#endif}`OsAdd2SortLink`在taskSortLink[0:7]链表上,将任务挂在排序后的链表上,因为等待时间不同,所以内核会根据时间长短对这些任务进行排序。与定时器相关的变量有3个。该系列的定时器机制章节中有定时器的详细说明。你可以去看看。响应函数UINT32swtmrTaskID;//理解定时器的机制其实就是OsSwtmrTaskCreate,只要理解:定时器(SWTMR_CTRL_S),定时任务(swtmrTaskID)、定时器响应函数(SwtmrHandlerItem)、定时器处理队列swtmrHandlerQueue。一句话,定时任务swtmrTaskID是优先级最高的系统任务。循环读取队列swtmrHandlerQueue中的过期时间。定时器(SWTMR_CTRL_S),执行定时器对应的响应函数SwtmrHandlerItem。idleTaskID空闲任务,注意这是另外一个任务,每个CPU核心都有自己的空闲任务,CPU没事干的时候就呆在里面。空闲任务是什么样的?看!LITE_OS_SEC_TEXTWEAKVOIDOsIdleTask(VOID){while(1){//只有一个无限循环#ifdefLOSCFG_KERNEL_TICKLESS//低功耗模式切换,关闭idletaskif(OsTickIrqFlagGet()){OsTickIrqFlagSet(0);OsTicklessStart();}#endifWfi();//WFI指令:armcore立即进入低功耗待机状态,等待中断,进入睡眠模式}}无限循环,只有一条汇编指令`Wfi`。什么意思?`WFI`(Waitforinterrupt):等待中断到来。`WFI`一般用于cpuidle。在处理器发生中断或类似异常之前,WFI指令不需要做任何事情。具体在【鸿蒙内核源码解析(总目录)】(https://my.oschina.net/u/3751245/blog/4626852)中有详细介绍,大家可以去查看。TaskLockCnt这个很简单,记录等待锁的任务数。任务的优先级在运行过程中会不断变化。比如优先级高的任务A在等待某个锁,但是持有锁的任务B的优先级低。这时,它会调整高B的优先级至少在A的水平,增加B被调度算法命中的概率,从而可以快速释放锁,交给A运行。taskLockCnt记录了CPU运行过的等待锁的任务数。schedFlagscheduledLabel.typedefenum{INT_NO_RESCH=0,/*noneedstoschedule*///不需要调度INT_PEND_RESCH,/*pendingscheduleflag*///阻塞调度}SchedFlag;调度并不总是成功的,在某些情况下内核会阻塞调度。例如:当OS_INT_ACTIVE硬中断发生时。STATICINLINEVOIDLOS_Schedule(VOID){if(OS_INT_ACTIVE){//硬件中断发生,调度被阻塞OsPercpuGet()->schedFlag=INT_PEND_RESCH;//return;}OsSchedPreempt();//抢占式调度}●excFlag标识运行状态CPU,仅在多核CPU下可见。#if(LOSCFG_KERNEL_SMP==YES)typedefenum{CPU_RUNNING=0,/*cpuisrunning*///CPUisrunningCPU_HALT,/*cpuinthehalt*///CPU处于挂起状态CPU_EXC/*cpuintheexc*///CPU处于异常状态}ExcFlag;#endif以上是内核对CPU的描述的全貌,不是很复杂。后续章节将介绍多CPU协同工作。参与贡献●访问注解仓库地址●fork本仓库>>新建一个Feat_xxx分支>>提交代码注解>>新建一个PullRequest●新建一个Issue了解更多请访问:鸿蒙科技社区共建华为官方https://harmonyos.51cto.com
