百篇博客系列篇.本篇为:

任务管理相关篇为:

时钟概念

  • 时间是非常重要的概念,我们整个学生阶段有个东西很重要,就是校园铃声. 它控制着上课,下课,吃饭,睡觉的节奏.没有它学校的管理就乱套了,老师拖课想拖多久就多久,那可不行,下课铃声一响就是在告诉老师时间到了,该停止了让学生HAPPY去了.

  • 操作系统也一样,需要通过时间来规范其任务的执行,操作系统中最小的时间单位是时钟节拍 (OS Tick)。任何操作系统都需要提供一个时钟节拍,以供系统处理所有和时间有关的事件,如线程的延时、线程的时间片轮转调度以及定时器超时等。时钟节拍是特定的周期性中断,这个中断可以看做是系统心跳,中断之间的时间间隔取决于不同的应用,一般是 1ms–100ms,时钟节拍率越快,系统的实时响应越快,但是系统的额外开销就越大,从系统启动开始计数的时钟节拍数称为系统时间。

  • 在鸿蒙内核中,时钟节拍的长度可以根据 LOSCFG_BASE_CORE_TICK_PER_SECOND 的定义来调整,等于 1/LOSCFG_BASE_CORE_TICK_PER_SECOND 秒。

时钟节拍的实现方式

时钟节拍由配置为中断触发模式的硬件定时器产生,当中断到来时,将调用一次: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
}

它主要干了三件事情

第一:检查当前任务的时间片,任务执行一次分配多少时间呢?答案是2个时间片,即 20ms.

#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);
}

最后看调度算法的实现

//调度算法的实现
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
}

鸿蒙内核源码分析.总目录

v08.xx 鸿蒙内核源码分析(总目录) | 百万汉字注解 百篇博客分析 | 51.c.h .o

百万汉字注解.百篇博客分析

百万汉字注解 >> 精读鸿蒙源码,中文注解分析, 深挖地基工程,大脑永久记忆,四大码仓每日同步更新< gitee| github| csdn| coding >

百篇博客分析 >> 故事说内核,问答式导读,生活式比喻,表格化说明,图形化展示,主流站点定期更新中< 51cto| csdn| harmony| osc >

关注不迷路.代码即人生

QQ群:790015635 | 入群密码: 666

原创不易,欢迎转载,但请注明出处.

鸿蒙内核源码分析(时钟任务篇) | 触发调度谁的贡献最大 | 百篇博客分析OpenHarmony源码 | v3.05的更多相关文章

  1. 鸿蒙内核源码分析(任务切换篇) | 看汇编如何切换任务 | 百篇博客分析OpenHarmony源码 | v41.03

    百篇博客系列篇.本篇为: v41.xx 鸿蒙内核源码分析(任务切换篇) | 看汇编如何切换任务 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度谁 ...

  2. 鸿蒙内核源码分析(系统调用篇) | 开发者永远的口头禅 | 百篇博客分析OpenHarmony源码 | v37.03

    百篇博客系列篇.本篇为: v37.xx 鸿蒙内核源码分析(系统调用篇) | 开发者永远的口头禅 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度谁 ...

  3. 鸿蒙内核源码分析(并发并行篇) | 听过无数遍的两个概念 | 百篇博客分析OpenHarmony源码 | v25.01

    百篇博客系列篇.本篇为: v25.xx 鸿蒙内核源码分析(并发并行篇) | 听过无数遍的两个概念 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度 ...

  4. 鸿蒙内核源码分析(线程概念篇) | 是谁在不停的折腾CPU? | 百篇博客分析OpenHarmony源码 | v21.06

    百篇博客系列篇.本篇为: v21.xx 鸿蒙内核源码分析(线程概念篇) | 是谁在不断的折腾CPU | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调 ...

  5. 鸿蒙内核源码分析(调度机制篇) | 任务是如何被调度执行的 | 百篇博客分析OpenHarmony源码 | v7.07

    百篇博客系列篇.本篇为: v07.xx 鸿蒙内核源码分析(调度机制篇) | 任务是如何被调度执行的 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调 ...

  6. 鸿蒙内核源码分析(调度队列篇) | 内核有多少个调度队列 | 百篇博客分析OpenHarmony源码 | v6.05

    百篇博客系列篇.本篇为: v06.xx 鸿蒙内核源码分析(调度队列篇) | 内核有多少个调度队列 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度 ...

  7. 鸿蒙内核源码分析(任务管理篇) | 任务池是如何管理的 | 百篇博客分析OpenHarmony源码 | v5.05

    百篇博客系列篇.本篇为: v05.xx 鸿蒙内核源码分析(任务管理篇) | 任务池是如何管理的 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度谁 ...

  8. 鸿蒙内核源码分析(任务调度篇) | 任务是内核调度的单元 | 百篇博客分析OpenHarmony源码 | v4.05

    百篇博客系列篇.本篇为: v04.xx 鸿蒙内核源码分析(任务调度篇) | 任务是内核调度的单元 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度 ...

  9. 鸿蒙源码分析系列(总目录) | 百万汉字注解 百篇博客分析 | 深入挖透OpenHarmony源码 | v8.23

    百篇博客系列篇.本篇为: v08.xx 鸿蒙内核源码分析(总目录) | 百万汉字注解 百篇博客分析 | 51.c.h .o 百篇博客.往期回顾 在给OpenHarmony内核源码加注过程中,整理出以下 ...

随机推荐

  1. 题解 big

    传送门 题面里那个式子 考场上我推了半天那个式子代表什么意思,但就是没想到位运算 \(\lfloor \frac{2x}{2x^n} \rfloor \iff x\gg(n-1)\), 即将x的第n位 ...

  2. 使用JS获取SessionStorage的值

    参考:https://www.jb51.net/article/132729.htm 获取sessionStorage的意义 首先获取它是为了将获得的信息输出或者alert():让人容易看到, 其次, ...

  3. jQuery中的基本选择器(四、一):* 、 . 、element(直接标签名)、 或者用逗号隔开跟多个

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  4. JDK1.5新特性之注解

    时间:2017-1-2 20:14 --注解的概述    注释是给人看的,而注解是给程序(框架)看的.    在Servlet3.0中可以使用注解来替代配置文件,开发者就不用再写配置文件了,而是写注解 ...

  5. Linux centos 安装 maven 3.5.4

    一.maven下载 1.官方下载 打开网址:http://maven.apache.org/download.cgi 下拉滚动条,找到标记处并点击 选择自己想要的版本,我这里选择的是 3.5.4,然后 ...

  6. 【JavasScript】折腾一个基础到不能再基础的顺滑抽奖页面

    前言 事情是这样的,作为一个意志力极低的人,最近一直在找寻提高意志力的方法. 然后决定试一试所谓的"建立奖励机制",也就是说,完成一项意志力挑战后给自己一些奖励(具体操作方法不在这 ...

  7. MySQL——MySQL用户管理

    1.MySQL用户定义:用户名+主机域/ip/.. 2.用户作用: 1.登录 2.管理数据库及数据 3.权限: 读和写操作 4.角色: 数据库定义好的一组权限的定义 5.权限范围: 全库:*.* 单库 ...

  8. NOIP模拟16:「Star Way To Heaven·God Knows·Loost My Music」

    T1:Star Way To Heaven 基本思路:   最小生成树.   假如我们将上边界与下边界看作一个点,然后从上边界经过星星向下边界连边,会发现,他会形成一条线将整个矩形分为左右两个部分. ...

  9. Windos下通过Wpcap抓包实现两个网卡桥接

    目录 1. 背景: 2. 需要的技术手段: 3. 实现逻辑: 4. 应用实例: 1. 背景: 一台电脑允许接多个网口,当然大部分只有一个网口其余都是USB扩展而来,而每个网口之间需要配置不同的网段IP ...

  10. C语言实现任务调度与定时器

    代码实现是在xl2tpd的源码中get到的,感觉很有意思的一段代码.基本功能就是实现定时器,时间到后从定时队列中取出,然后完成指定的任务. 1. schedule.c代码(自己添加了main函数,用来 ...