鸿蒙内核源码分析(时钟任务篇)

时钟概念

  • 时间是非常重要的概念,我们整个学生阶段有个东西很重要,就是校园铃声. 它控制着上课,下课,吃饭,睡觉的节奏.没有它学校的管理就乱套了,老师拖课想拖多久就多久,那可不行,下课铃声一响就是在告诉老师时间到了,该停止了让学生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的第一个节点sortLinkNodeROLLNUM_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;
#endiftaskCB->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;//当前任务不占用CPUnewTask->currCpu = ArchCurrCpuid();//让新任务占用CPU
#endif(VOID)OsTaskSwitchCheck(runTask, newTask);//切换task的检查
#if (LOSCFG_KERNEL_SCHED_STATISTICS == YES)OsSchedStatistics(runTask, newTask);
#endifif ((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
}

写在最后

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙

  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:gitee.com/MNxiaona/733GH

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/404556.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php源码编译与初始化

1 php源码编译 解压 yum install -y bzip2 # 安装解压工具 tar -xf php-7.4.12.tar.bz2 # 解压文件./condigure ./configure --prefix/usr/local/php --with-config-file-path/usr/local/php/etc --enable-fpm --with-fpm-usernginx --with-fpm-groupnginx --with-curl --wi…

线程面试题

1.JDK自带的线程池有哪些&#xff1f; 2.线程池中核心线程数与最大线程数与缓冲任务队列的关系&#xff1f; 先使用核心线程执行任务。 当核心线程不足时&#xff0c;新任务入队列等待。 当队列满且线程数未达最大值时&#xff0c;增加非核心线程执行任务。 当队列满且线程…

Leetcode每日刷题之209.长度最小的子数组(C++)

1.题目解析 根据题目我们知道所给的数组均是正整数&#xff0c;我们需要找到的是该数组的子数组&#xff0c;使其子数组内所有元素之和大于或等于给出的目标数字target&#xff0c;然后返回其长度&#xff0c;最终找出所以满足条件的子数组&#xff0c;并且要返回长度最小的子数…

网络硬盘录像机NVR程序源码海思3520D NVR 安防监控智能升级运用方案

随着安防技术的不断发展&#xff0c;传统的监控系统正逐步向智能化方向转变。海思Hi3520D作为一款高性能的网络视频处理芯片&#xff0c;在NVR&#xff08;网络视频录像机&#xff09;领域有着广泛的应用。本方案旨在探讨如何利用海思Hi3520D芯片的强大功能对现有的NVR系统进行…

vue2使用天地图

需求&#xff1a;用vue2使用天地图展示对应点位数据以及开发中出现的问题等&#xff0c;其实天地图的写法和百度地图差不多 注意&#xff01;&#xff01;&#xff01;天地图的接口不稳定&#xff0c;时常报错418&#xff0c;官网也是一样的情况&#xff0c;推荐还是使用百度或…

C++:C/C++的内存管理

目录 C/C内存分布 C语言中动态内存管理方式 C内存管理方式 new/delete操作内置类型 new/delete操作自定义类型 operator new与operator delete函数 new和delete的实现原理 定位new表达式 常见问题 malloc/free和new/delete的区别 内存泄漏 C/C内存分布 我们先来看以…

【机器学习】(基础篇七) —— 神经网络

神经网络 神经网络是一种模仿人脑神经元结构的计算模型&#xff0c;用于处理复杂的数据模式识别和预测问题。它由大量简单的处理单元&#xff08;称为“神经元”或“节点”&#xff09;组成&#xff0c;这些单元通过连接权重相互连接。神经网络可以学习从输入数据到输出结果之…

GitLab Merge Request流水线

GitLab Merge Request 流程文档 为了提升代码质量&#xff0c;让开发人员参与代码review&#xff0c;现在输出Merge Request的流程文档&#xff1a; 1.项目创建各自开发者的分支&#xff0c;命名规则是dev_名字首字母&#xff0c;比如我是dev_cwq.然后把本地分支推到远端orgin…

2024.8.19 学习记录 —— 作业

一、TCP机械臂测试 #include <myhead.h>#define SER_PORT 8888 // 与服务器保持一致 #define SER_IP "192.168.0.114" // 服务器ip地址int main(int argc, const char *argv[]) {// 创建文件描述符打开键盘文件int fd open("/dev/input/event1…

【数学建模】趣味数模问题——舰艇追击问题

问题描述 某缉私舰位于走私船以东 d 10 km&#xff0c;走私船以匀速 u 8 km/h 向北沿直线行驶。缉私舰立即以速度 v 12 km/h 追赶。缉私舰使用雷达进行跟踪&#xff0c;保持瞬时速度方向始终指向走私船。求解缉私舰的追逐路线和追上走私船所需的时间。 方法 理论求解&…

NIO中的异步—ChannelFuture、CloseFuture以及异步提升在NIO中的应用

ChannelFuture 客户端调用connect后返回值为ChannelFuture对象&#xff0c;我们可以利用ChannelFuture中的channel()方法获取到Channel对象。 由于上述代为为客户端实现&#xff0c;若想启动客户端实现连接操作&#xff0c;必须编写服务端代码&#xff0c;实现如下&#xff1a;…

python中的randint如何使用

python中的randint用来生成随机数&#xff0c;在使用randint之前&#xff0c;需要调用random库。random.randint()是随机生成指定范围内的整数&#xff0c;其有两个参数&#xff0c;一个是范围上限&#xff0c;一个是范围下限。 具体用法如下&#xff1a; import random print…

USB3.2 摘录(四)

系列文章目录 USB3.2 摘录&#xff08;一&#xff09; USB3.2 摘录&#xff08;二&#xff09; USB3.2 摘录&#xff08;三&#xff09; USB3.2 摘录&#xff08;四&#xff09; 文章目录 系列文章目录8 协议层&#xff08;Protocol Layer&#xff09;8.8 三个参数地址信息&…

苍穹外卖项目DAY07

苍穹外卖项目Day07 1、缓存菜品 1.1、问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压力随之增大 1.2、实现思路 通过Redis来缓存菜品的数据&#xff0c;减少数据库查询操作 缓存逻辑分析&#xff…

未来视界,触手可及:揭秘数字透明屏的奇幻之旅

在这个日新月异的科技时代&#xff0c;每一项创新都如同星辰般璀璨&#xff0c;引领着我们向更加智能、更加梦幻的未来迈进。今天&#xff0c;就让我们一起揭开一项颠覆传统视觉体验的前沿科技——数字透明屏的神秘面纱&#xff0c;探索它如何将未来视界&#xff0c;化为触手可…

IDEA:如何在idea中设置自动导包

这里使用的是idea2020版本,但是不同版本操作不会有较大的差别. 在Editer中展开General之后,选中Auto Import,最后勾选中Add unambiguous imports on the fly.

微信视频号评论如何快速采集?三种高效实用的方法

本文将深入探讨如何高效地采集微信视频号评论&#xff0c;通过揭秘三种实用方法&#xff0c;助您迅速掌握这一营销利器。从自动化工具到智能策略&#xff0c;每一步都旨在提升您的市场分析效率&#xff0c;让您在竞争激烈的社交媒体领域中脱颖而出。 一、引言&#xff1a;为何…

探索顶级PDF水印API:PDFBlocks(2024年更新)

引言 在一个敏感信息常常面临风险的时代&#xff0c;能够轻松高效地保护文档的能力至关重要。PDF水印已成为企业和个人寻求保护其知识产权、确保文件保密性的基本工具。 PDFBlocks 文字水印 API是什么&#xff1f; PDFBlocks API 提供了一个强大的解决方案&#xff0c;用于在…

day06——前后端交互

一、计算属性 计算属性就是基于现有的数据推算出来的新属性&#xff0c;只要依赖的数据变化&#xff0c;新属性就会自动更新&#xff0c;而且计算属性多次调用的情况下只会计算一次&#xff0c;效率非常高 简化写法 const app new Vue({ el: #app, data: {}, methods: {//跟da…

记录一次生产jvm问题的排查

记录一次生产问题的排查 第一天晚上 现象 1、前援反馈页面有接口陆续出现请求超时 2、登录后台服务器top命令查看发现java进程发生高cpu占用情况 3、查看对应业务日志&#xff0c;报数据库连接等待超时-数据库连接池连接无空闲 对应处理 1、临时调大数据库连接池最大连接数限…