本文将用一个实际的工程例子来剖析操作系统内核运行原理。在此之前我们先回顾一下之前文章讲述的重点知识点。
《嵌入式实时操作系统3——任务切换》中讲到任务切换5个步骤:保存现场,保存栈指针,找出最高优先级任务,读取栈指针,恢复现场。
《嵌入式实时操作系统4——任务调度》中讲到:操作系统总是运行最高优先级任务,一旦有更高优先级任务就绪,操作系统将立即暂停当前任务运行更高等级任务,相同优先级下的多个任务采取时间片轮询的方式依次运行。
《嵌入式实时操作系统5——就绪表》中讲到:操作系统内核会将就绪的任务存放在就绪表,内核总是从就绪表中找出最高优先级任务,并执行该任务。
《嵌入式实时操作系统8——等待表》中讲到:当多任务系统中有任务需要延时等待时,操作系统内核会将该任务从就绪表中移动到等待表中,等待表的作用就是帮助操作系统内核管理需要延时等待的任务。
《嵌入式实时操作系统10——系统时钟节拍》中讲到:系统时钟节拍服务被定时器中断程序调用,系统时钟节拍服务完成了:更新系统节拍时间,更新等待表和就绪表,处理时间片轮询,切换任务。
操作系统内核运行的本质就是根据需求调度任务,在多个任务中找出最高优先级任务,并运行这个任务。
我们用驾校的例子,驾校只有一辆教学车,多名学生和一个教练。根据报名费的不同将学员分成了VIP等级和普通等级,每个等级有若干个学员。教练然后制定了2张表格:
1、就绪表:记录了正在排队的学员信息
2、等待表:记录了正在休息的学员信息
教练只需要关注就绪表,让就绪表中的学员上车练习。首先教练在就绪表中检查优先等级,判断VIP等级中有没有学员,如果VIP等级有学员,教练优先让VIP的学员上车练习,只有等VIP学员都完成练习放弃练习,才轮到普通学员上车练习。
如果有一个学员需要去厕所20分钟,教练就把这个学员的名字从就绪表中移除,把他的名字放入等待表中,在此期间不会考虑让该学员上车练习。
20分钟后学员上完厕所回来,教练就把这个学员的名字从等待表中移除,把他的名字加入就绪表中。假设此时正在练车的是普通学员,而这位休息的学员是VIP学员(并且希望练车),敬业的教练就要立即让普通学员停止练习,让VIP级学员优先练习。如果这位休息的学员事普通等级,那就让这个他继续等待,相同等级的学员轮流执行。
动态如如下:
通过驾校的例子大家对操作系统内核工作原理有了初步了解,接下来我们根据一个实际工程来深入研究操作系统内核是如何工作的。
该工程是应用于智能家具中的一个多功能空调面板,该面板的主要功能是控制家里的温度和湿度,多功能面板实物如下:
多功能空调面板工程硬件基于国产GD系列芯片,软件基于FreeRTOS操作系统。
多功能空调面板主要有LCD显示,按键输入,温湿度检查3个功能模块。软件工程设计为以下3个任务:
1、温湿度读取任务,优先级为高。任务设计为每间隔4ms读取一个温湿度传感器,读到温湿度数据后,将数据发送给显示任务。
2、按键任务,优先级为低。任务设计为持续扫描4个按键,监测按键是否按下,并能识别按键长按和连击,有按键触发后,将按键数据发给显示任务。
3、显示任务,优先级为低。任务设计为根据接收到的温湿度数据,显示温度和湿度;根据接收到的按键数据,进行显示界面切换。
main函数中设置SysTick定时器为1ms周期定时,创建显示任务,按键任务和温湿度读取任务。main代码如下:
/* github: liyinuo2017 author:liwei */
int32_t main(void)
{
/* 配置系统时钟 */
system_clk_init();
/*显示任务*/
xTaskCreate((TaskFunction_t )display_task,
(const char* )"display",
(uint16_t )SIZE_512,
(void* )NULL,
(UBaseType_t )LOW_PRIO,
(TaskHandle_t* )NULL);
/*按键任务*/
xTaskCreate((TaskFunction_t )button_task,
(const char* )"button",
(uint16_t )SIZE_256,
(void* )NULL,
(UBaseType_t )LOW_PRIO,
(TaskHandle_t* )NULL);
/*温湿度读取任务*/
xTaskCreate((TaskFunction_t )temp_humi_task,
(const char* )"temp_humi",
(uint16_t )SIZE_256,
(void* )NULL,
(UBaseType_t )HIGH_PRIO,
(TaskHandle_t* )NULL);
/* 任务调度 */
vTaskStartScheduler();
while(1);
}
显示任务,按键任务和温湿度读取任务这3个任务的代码如下:
/* github: liyinuo2017 author:liwei */
/*lcd显示任务 */
void display_task(void *pvParameters)
{
/* 初始化 */
display_init();
for(;;)
{
/* 数据更新 ,运行 时间1ms*/
display_data_updata();
/* 显示更新 , 运行 时间1ms*/
display_handle();
}
}
/* 按键任务 */
void button_task(void *pvParameters)
{
/* 初始化 */
button_init();
for(;;)
{
/* 按键扫描 , 运行 时间1ms*/
button_scanf();
/* 按键数据发送给显示任务 , 运行 时间1ms*/
button_data_send();
}
}
/* 温湿度读取任务 */
void temp_humi_task(void *pvParameters)
{
/* 初始化 */
temp_humi_init();
for(;;)
{
/* 读取数据, 运行 时间1ms*/
data_read_calc();
/* 数据发送给显示任务 , 运行 时间1ms*/
temp_humi_data_send();
}
}
执行vTaskStartScheduler函数后,操作系统内核开始任务调度,操作系统的状态如下图:
操作系统启动运行,由于HIGH_PRIO优先级为任务最高优先级,因此操作系统内核让温度读取任务开始运行,当温度读取任务完成读取温湿度数据,并将数据发送给显示任务。
温度读取任务完成相应操作后执行延时等待,操作系统内核会将温度读取任务从就绪表中移除,将温度读取任务添加到等待表中,更新优先级表,切换任务。动态如如下:
当温度读取任务进入等待队列后,LOW_PRIO优先级为任务最高优先级,该优先级下有显示任务和按键任务,显示任务在就绪表前端,显示任务先运行。 动态如如下:
显示任务运行1ms后,定时器周期产生中断,系统时钟节拍服务被执行,更新系统时间,检查等待表,并执行时间片轮询操作,**显示任务被移动到就绪表末端,按键任务来到就绪表前端,**中断返回前执行任务切换,按键任务开始运行。
按键任务运行1ms后,定时器周期产生中断,系统时钟节拍服务被执行,更新系统时间,检查等待表,并执行时间片轮询操作,按**键任务被移动到就绪表末端,显示任务来到就绪表前端,**中断返回前执行任务切换,显示任务开始运行。动态如如下:
系统时钟节拍服务被执行后,更新系统时间,检查等待表,此时温度读取任务完成延时等待,操作系统内核会温度读取任务从等待表中移除,将温度读取任务添加到就绪表中,更新优先级表,由于HIGH_PRIO优先级为任务最高优先级,切换任务,温度读取任务开始运行。动态如如下:
未完待续…
实时操作系统系列将持续更新
创作不易希望朋友们点赞,转发,评论,关注。
您的点赞,转发,评论,关注将是我持续更新的动力
作者:李巍
Github:liyinuoman2017