嵌入式实时操作系统的设计与开发

时钟管理

在RTOS中,时钟具有非常重要的作用,通过时钟可实现延时任务、周期性触发任务执行、任务有限等待的计时。

大多数嵌入式系统有两种时钟源,分别为实时时钟RTC(Real-Time Clock)和定时器/计数器。
实时时钟一般是靠电池供电,即使系统断电,也可以维持日期和时间。由于实时时钟独立于操作系统,因此也被称为硬件时钟,它为整个系统提供一个时间标准。
嵌入式处理器通常集成了多个定时器或计数器,实时内核需要一个定时器作为系统时钟,并由内核控制系统时钟工作。

一般而言,实时时钟是系统时钟的基准,实时内核通过读取实时时钟来初始化系统时钟,此后,二者保持同步运行,共同维持系统时钟。
因此,系统时钟只有当系统运行起来以后才有效,并且由实时内核完全控制。

定时器一般由晶体振荡器提供周期信号源,并通过程序对其计数寄存器进行设置,让其产生固定周期的脉冲,每次脉冲的产生都触发一个时钟中断,时钟中断的频率既是系统的心跳,也称为时基或Tick,Tick的大小决定了整个系统的时间粒度。

在这里插入图片描述
晶体振荡器提供周期信号源,它通过Bus总线连接到CPU核上,开发人员设定计数寄存器的初始值,随后,每一个晶体振荡器输入信号都会导致该值增加,当计数器溢出时,就产生一个输出脉冲(Pulse),输出脉冲可以用来触发CPU核上的一个中断。
输出脉冲是RTOS的硬件基础,因为它将送到中断控制器上,产生中断信号,触发时钟中断,由时钟中断服务程序维持系统时钟的正常工作。

实时内核的时间管理以系统时钟为基础,通过Tick处理程序来实现。
定时器产生中断后,RTOS将响应并执行器中断服务程序,并在中断服务程序中调用Tick处理函数,它作为实时内核的一部分,与具体的定时器/计数器无关,由系统时钟中断服务程序调用,使内核具有对不同定时器/计数器的适应性。

在内核层初始化中,重要的一步就是通过acoral_intr_attach()将时钟中断服务程序与Ticks处理程序进行绑定。
这样,每当定时器产生一个输出脉冲(Pulse),输出脉冲就向CPU发出一个时钟中断,找到内核层对应的时钟中断号,最终将执行该中断号对应的服务程序,即Ticks处理函数aCoral_Ticks_entry()。

内核时钟管理的绝大部分工作都在aCoral_Ticks_entry()进行的,如线程延迟操作time_delay_deal()、超时处理timeout_delay_deal()、与调度策略相关的操作(如时间片轮转调度)等。

如果任务采用时间片轮转调度,则需要再Ticks处理程序中对当前正在运行的任务已执行的时间进行“+1”操作。执行完该操作后,如果任务的已执行时间同任务时间片相等,则表示任务使用完一个时间片的执行时间,需要通过acoral_sched()触发重调度。

如果开发人员在线程中调用acoral_delay_thread()对线程进行延迟操作,则会让当前运行线程从运行(Running)状态切换到挂起(Suspend)状态,并将其挂载到一个等待队列“acoral_list_t waiting”,这里的等待队列也称为时间等待链,用来存放需要延迟处理的任务。
接下来每当定时器产生一个Tick,Tick处理函数aCoral_Ticks_entry()的time_delay_deal()需要对时间等待链中线程的剩余等待时间进行“减1”操作,如果某个线程的剩余等待时间被减到了0,则将该线程从等待队列中移出,挂载到就绪队列,通过acoral_sched()触发重调度。

如,开发人员用acoral_delay_thread()将线程A、B、C、D分别延迟。
通常情况下,每当定时器产生一个Tick,time_delay_deal()会对时间等待链中的每一个结点进行“减1”操作。
若时间等待链的结点数越多,时钟中断的Tick处理函数的计算开销就比较大,从而降低系统性能。

为了提高系统性能,减小计算开销,可采用差分时间等待链来描述延迟队列。
在这里插入图片描述
队列中某个结点的值是相对于前一个结点的时间差。
当采用查分时间等待链后,每当时钟中断产生一个Tick,只需对队列头部结点进行“减1”操作,当减到0时,就将其从等待链中取下,后续结点将成为新的头部,并且被激活。
等待链其它结点的值保持不变,无须对每一个结点进行“减1”操作,这样可减小计算开销。

如果有新线程要进行延迟操作,需要往差分时间等待链中插入新的结点,如线程E要延迟7Ticks。
只需要在线程B(3+2)和C(3+2+5)中插入线程E即可。再修改结点C的值即可(10-7)。

acoral_list_t time_delay_queue; //线程延时队列,调用线程delay相关函数的线程都会被加到这个队列上,等待一段具体时间后重新被唤醒。acoral_list_t timeout_queue; //aCoral获取资源(互斥量)超时等待队列,即在timeout时间内获取即成功,否则超时失败。
void time_delay_deal()
{acoral_list_t *tmp,*tmp1,*head;acoral_thread_t *thread;head = &time_delay_queue; //获取时间等待链的头部if(acoral_list_empty(head))return;thread = list_entry(head->next,acoral_thread_t,waiting); //获取时间等待链头结点对应的TCB地址thread->delay--; //对时间等待链头结点对应线程delay成员的剩余等待时间for(tmp=head->next;tmp!=head;){if(thread->delay > 0) //如果头结点线程delay成员大于0,则退出break;//delay=0,线程延迟结束tmp1 = tmp>next;acoral_list_del(&thread->waiting); //将该线程从时间等待链中删除tmp = tmp1;thread->state &= ~ACORAL_THREAD_STATE_DELAY; //将线程切换为就绪态	acoral_rdy_thread(thread); //将其挂到就绪队列上}
}

waiting成员主要用来将线程结构挂到相应链表队列,是一个双向链表结构。

struct acoral_list{struct acoral_list *next,*prev;
};

以就绪队列ready为例,当用户调用了acoral_rdy_thread或acoral_resume_thread接口时,就会将线程挂到就绪队列acoral_ready_queue上。
在这里插入图片描述

acoral_list_t policy_list;
void acoral_policy_delay_deal()
{acoral_list_t *tmp,*head;acoral_sched_policy_t *policy_ctrl;head = &policy_list;tmp = head;for(tmp=head->next;tmp!=head;tmp=tmp->next){policy_ctrl = list_entry(tmp,acoral_sched_policy_list,list);if(policy_ctrl->delay_deal!=NULL)policy_ctrl->delay_deal();}
}
void period_policy_init(void)
{acoral_init_list(&period_delay_queue);period_policy.type=ACORAL_SCHED_POLICY_PERIOD;period_policy.policy_thread_init=period_policy_thread_init;period_policy.policy_thread_release=period_policy_thread_release;period_policy.delay_deal=period_delay_deal;period_policy.name="period";acoral_register_sched_policy(&period_policy);
}
typedef struct{unsigned int time; //线程周期,单位为msvoid (*route)(void *args); //线程函数void *args; //线程函数的参数
}period_private_data_t; //周期线程私有数据块,用于保存自己的周期、函数体,以便在新周期重新挂载线程时使用。
acoral_list_t period_delay_queue; //周期线程专用延时队列,只要是周期线程,就会被挂载到这个队列上,
void period_delay_deal()
{acoral_list_t *tmp,*tmp1,*head;acoral_thread_t *thread;period_private_data_t * private_data;head = &period_delay_queue;if(acoral_list_empty(head))return;thread = list_entry(head->next,acoral_thread_t,waiting);thread->delay--;for(tmp=head->next;tmp!=head;){thread = list_entry(tmp,acoral_thread_t,waiting);if(thread->delay > 0)break;private->data = thread->private_data;tmp1 = tmp->next; //保存下一个周期延时队列上的结点acoral_list_del(&thread->waiting);tmp = tmp1;if(thread->state & ACORAL_THREAD_STATE_SUSPEND){thread->stack = (unsigned int *)((char *)thread->stack_buttom+thread->stack_size - 4);HAL_STACK_INIT(&thread->stack,private_data->route,period_thread_exit,private_data->args);acoral_rdy_thread(thread);}period_thread_delay(thread,private_data->time);}
}

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

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

相关文章

React Antd form.getFieldsValue() 和 form.getFieldsValue(true) 有区别吗?

背景 突然发现 antd 的 getFieldsValue()是可以传一个 true 参数的,如题,React Antd form.getFieldsValue() 和 form.getFieldsValue(true) 有区别吗? 验证 确实不一样 结论 getFieldsValue 提供了多种重载方法: getFieldsValue(name…

开学季,“护眼教室”上线,守护孩子光明未来

在关于教室的记忆里,你是否有着这样的滤镜? “电影”滤镜:模糊又遥远,夜间自习像褪色的胶片。 “眩光”滤镜:灯光亮到出现“光环”,看不了多久眼睛就酸胀。 “频闪”滤镜:头顶的灯仿佛飞速眨眼…

【Go Web 篇】从零开始:构建最简单的 Go 语言 Web 服务器

随着互联网的迅速发展,Web 服务器成为了连接世界的关键组件之一。而在现代编程语言中,Go 语言因其卓越的性能和并发能力而备受青睐。本篇博客将带你从零开始,一步步构建最简单的 Go 语言 Web 服务器,让你对 Go 语言的 Web 开发能力…

接口测试与功能测试的区别~

今天为大家分享的是我们在日常测试工作中, 一定会接触并且目前在企业中是主要测试内容的 功能测试与接口测试 一.功能测试与接口测试的基本概念。 1.1 什么是功能测试呢? 功能测试: 是黑盒测试的一方面, 检查实际软件的功能是否符合用户的需求 功能测试测试的内容包括以下…

linux离线安装rdbtools,需先安装python

离线安装python3 下载python包,下载地址:https://www.python.org/ftp/python/ 我选的是https://www.python.org/ftp/python/3.9.0/Python-3.9.0.tgz 将文件上传至linux服务器,解压 tar -xf Python-3.9.0.tgz cd Python-3.9.0 mkdir /usr/l…

c++ qt--信号与槽(二) (第四部分)

c qt–信号与槽(二) (第四部分) 信号与槽的关系 1.一对一 2.一对多 3.多对一 4.多对多 还可以进行传递 信号->信号->槽 一个信号控制多个槽的例子(通过水平滑块控制两个组件) 1.应用的组件 注意这里最下面的组件进行…

pandas读取excel,再写入excel

需求是这样的,从一个表读取数据,然后每次执行创建一个新表将值写入 读取这个表 写入到这个表 分别对应的是e、h列数据,代码如下: import pandas as pd import openpyxl import datetime dfpd.read_excel(rC:\Users\admin\Deskt…

【IMX6ULL驱动开发学习】12.Linux SPI驱动实战:DAC驱动设计流程

基础回顾: 【IMX6ULL驱动开发学习】10.Linux I2C驱动实战:AT24C02驱动设计流程_阿龙还在写代码的博客-CSDN博客 【IMX6ULL驱动开发学习】11.Linux之SPI驱动_阿龙还在写代码的博客-CSDN博客 一、编写驱动 查看芯片手册,有两种DAC数据格式&a…

一文讲透 JavaScript 应用的演进历程

在不断发展的软件开发领域中,很少有编程语言像JavaScript一样产生深远的影响。它起初只是一种简单的脚本语言,但如今已成为现代Web的驱动力量,改变了应用构建和体验的方式。本文将带你沿着时间线,穿越JavaScript的演进历程&#x…

Spring Boot(Vue3+ElementPlus+Axios+MyBatisPlus+Spring Boot 前后端分离)【六】

😀前言 本篇博文是关于Spring Boot(Vue3ElementPlusAxiosMyBatisPlusSpring Boot 前后端分离)【六】,希望你能够喜欢 🏠个人主页:晨犀主页 🧑个人简介:大家好,我是晨犀,希望我的文章…

lnmp架构-nginx

6.nginx基础配置 证书 重定向(80重定向到443) 当访问http时 直接到 https 自动索引: 下载方便 Nginx缓存配置 :缓存可以降低网站带宽,加速用户访问 日志轮询 禁用不必要的日志记录 以节省磁盘IO的消耗 监控的信息 监…

8、Vue 核心技术与实战 智慧商城项目 DAY8~10

1、 项目演示 2、 项目收获 3、 创建项目 4、调整初始化目录 1、删掉(1)assets中的文件(2)components中的文件(3)views中的文件2、(1)修改路由配置,默认的路由是个空数组即可,把路由…