热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入浅出RTOS:第一课——任务调度机制解析

在实时操作系统(RTOS)中,任务切换是核心功能之一,涉及从当前任务A平滑过渡到下一个任务B的过程。这一过程主要关注两个关键问题:一是确定下一个执行的任务(which),这取决于系统的调度策略;二是决定何时进行任务切换(when),通常由外部事件触发或定时器中断驱动,实现高效的资源管理和响应性。此外,任务切换还涉及到上下文保存与恢复等技术细节,确保系统稳定运行。

1 RTOS任务切换

任务切换: 就是从任务A切换到任务B,涉及以下几个问题:



  1. 选择切换到哪个任务?(which) ----> 调度策略问题

  2. 什么时候调度? (when) ----> 被动调度(如时间片)还是主动调度

  3. 调度时候要做什么? (how)----> 现场的保留与恢复

本篇主要关注现场的保留与恢复。

现场 = PC指针(包括寄存器组) + 独立栈

如下图所示,是RTOS多任务切换示意图。图中是内存中有3个任务块:taskA,taskB,taskC 以及CPU寄存器组。

PC指针指向哪个代码段,哪个任务就开始执行。

如:当PC指针指向taskA时,则taskA运行,当PC指针指向taskB时taskB任务开始运行。

但是为了保证task切换出去又能切换回来,则需要保留当前运行环境。

当前运行环境保留在SP指向的任务栈中。

所以:任务的切换指的是CPU指针的切换,需要考虑上下文的保留与恢复问题,上下文保留在任务栈中。

多任务切换图


2 ARM Cotrex-M3系列任务切换

ARM Cotrex-M3平台上,任务切换都是基于触发PendSV异常进入中断,然后在中断处理函数中实现任务切换。这部分许多文章都提及到了。详细情况可参考《UCOS-II在CORTEXT-M3(STM32)上的任务切换示意》一文。

使用PendSV异常的优点:



  1. 设置PendSV异常,通过进入中断自动保存一部分寄存器,可以加快上下文切换速度;

  2. 将PendSV中断优先级置为最低,可以优先执行优先级较高的中断处理。

但是,从学习的角度看,可以绕过PendSV异常,直接蛮干的切换任务。

如下一个150行左右的代码,构造了5个任务组成循环链表结构,通过主动释放CPU,依次轮询调度。

(裁剪了许多东西,如任务只保留ip与sp,保留现场只保留ip与sp,其中ip保存到堆栈里,sp保留到全局变量里,这样做是不完备的,最好保存整个寄存器组)。

#include
#include
//================ debug uart cOnfig=====================//
#define ITM_PORT8(n) (*(volatile unsigned char *)(0xe0000000 + 4*(n)))
#define ITM_PORT16(n) (*(volatile unsigned short *)(0xe0000000 + 4*(n)))
#define ITM_PORT32(n) (*(volatile unsigned long *)(0xe0000000 + 4*(n)))
#define DEMCR (*(volatile unsigned long *)(0xE000EDFC))
#define TRCENA 0X01000000
int fputc(int ch, FILE *f)
{
if(DEMCR & TRCENA) {
while(ITM_PORT32(0) == 0);
ITM_PORT8(0) = ch;
}
return ch;
}
//========================================================//
#define STACK_SIZE 1024
#define MAX_TASK_NUM 5
struct Thread {
unsigned long ip;
unsigned int *sp;
};
typedef struct PCB {
struct Thread thread;
int pid;
volatile long state; /* -1 idle, 0 runnable */
unsigned int stack[STACK_SIZE];
struct PCB *next;
} tPCB;
tPCB task[MAX_TASK_NUM];
tPCB *current_task = NULL;
tPCB *next_task = NULL;
void tTaskRunFirst(void);
void tTaskSwitch(void);
void tTask_schedule(void);
void my_process(void)
{
int i = 0;
while (1) {
i++;
if (i % 100 == 0) {
printf("this is process %d - \r\n", current_task->pid);
tTask_schedule();
printf("this is process %d + \r\n", current_task->pid);
}
}
}
void StackInit (tPCB * task, void (*entry)(void), unsigned int ** stack)
{
(*stack)--;
**stack = (unsigned long)entry; // the entry
}
void tTaskInit(int task_num)
{
int i = 0;
task[i].pid = i;
task[i].state = 0;
task[i].thread.ip = (unsigned long)my_process;
task[i].thread.sp = &task[i].stack[STACK_SIZE - 1];
task[i].next = &task[i];
StackInit(&task[i], my_process, &(task[i].thread.sp));
for (i = 1; i task[i].pid = i;
task[i].state = 0;
task[i].thread.ip = (unsigned long)my_process;
task[i].thread.sp = &task[i].stack[STACK_SIZE - 1];
task[i].next = task[i - 1].next;
task[i - 1].next = &task[i];
StackInit(&task[i], my_process, &(task[i].thread.sp));
}
}
int main(void)
{
printf("Init tasks\r\n");
tTaskInit(MAX_TASK_NUM);
/* run the first task */
current_task = &task[0];
tTaskRunFirst();
}
void tTask_schedule(void)
{
if (current_task == NULL ||
current_task->next == NULL) {
return;
}
printf("enter task schedule ->\r\n");
next_task = current_task->next;
if (next_task->state == 0) {
/* switch to next process */
tTaskSwitch();
}
}
__asm void tTaskRunFirst(void)
{
IMPORT current_task
/* R0 = current_task->thread.ip */
LDR R0, =current_task
LDR R0, [R0]
LDR R0, [R0, #4]

/* POP */
LDMIA R0!,{R14};

/* refresh current_task->thread.ip */
LDR R1, =current_task
LDR R1, [R1]
STR R0, [R1, #4]
BX R14
}
__asm void tTaskSwitch(void)
{
IMPORT current_task
IMPORT next_task
/* R0 = current_task->thread.ip */
LDR R0, =current_task
LDR R0, [R0]
LDR R0, [R0, #4]
/* PUSH */
STMDB R0!, {R14};
/* refresh current_task->thread.ip */
LDR R1, =current_task
LDR R1, [R1]
STR R0, [R1, #4]

/* current_task = next_task; */
LDR R0, =current_task
LDR R1, =next_task
LDR R1, [R1]
STR R1, [R0]
/* R0 = next_task->thread.ip */
LDR R0, =next_task
LDR R0, [R0]
LDR R0, [R0, #4]

/* POP */
LDMIA R0!,{R14};

/* refresh next_task->thread.ip */
LDR R1, =next_task
LDR R1, [R1]
STR R0, [R1, #4]
BX R14
}

运行环境:MDK5 Sim环境

运行结果:

多任务运行串口输出

上述代码能正常运行,表示我们对任务切换的理解方大体不差(这么做当然还有其它问题,比如怎么写成时间片轮询的模式?需要考虑更多问题)。


3 参考:



  1. freertos任务切换xPortPendSVHandler

  2. FreeRTOS任务切换

  3. GCC内联汇编基础

  4. UCOS-II在CORTEXT-M3(STM32)上的任务切换示意



推荐阅读
  • 深入解析 Android IPC 中的 Messenger 机制
    本文详细介绍了 Android 中基于消息传递的进程间通信(IPC)机制——Messenger。通过实例和源码分析,帮助开发者更好地理解和使用这一高效的通信工具。 ... [详细]
  • 本文将探讨2015年RCTF竞赛中的一道PWN题目——shaxian,重点分析其利用Fastbin和堆溢出的技巧。通过详细解析代码流程和漏洞利用过程,帮助读者理解此类题目的破解方法。 ... [详细]
  • 探讨ChatGPT在法律和版权方面的潜在风险及影响,分析其作为内容创造工具的合法性和合规性。 ... [详细]
  • 采用IKE方式建立IPsec安全隧道
    一、【组网和实验环境】按如上的接口ip先作配置,再作ipsec的相关配置,配置文本见文章最后本文实验采用的交换机是H3C模拟器,下载地址如 ... [详细]
  • C#设计模式学习笔记:观察者模式解析
    本文将探讨观察者模式的基本概念、应用场景及其在C#中的实现方法。通过借鉴《Head First Design Patterns》和维基百科等资源,详细介绍该模式的工作原理,并提供具体代码示例。 ... [详细]
  • 本文介绍如何使用 Angular 6 的 HttpClient 模块来获取 HTTP 响应头,包括代码示例和常见问题的解决方案。 ... [详细]
  • 主板IO用W83627THG,用VC如何取得CPU温度,系统温度,CPU风扇转速,VBat的电压. ... [详细]
  • 深入理解Vue.js:从入门到精通
    本文详细介绍了Vue.js的基础知识、安装方法、核心概念及实战案例,帮助开发者全面掌握这一流行的前端框架。 ... [详细]
  • Redux入门指南
    本文介绍Redux的基本概念和工作原理,帮助初学者理解如何使用Redux管理应用程序的状态。Redux是一个用于JavaScript应用的状态管理库,特别适用于React项目。 ... [详细]
  • 如何使用Ping命令来测试网络连接?当网卡安装和有关参数配置完成后,可以使用ping命令来测试一下网络是否连接成功。以winXP为例1、打开XP下DOS窗口具体操作是点击“开始”菜 ... [详细]
  • 本文探讨了为何相同的HTTP请求在两台不同操作系统(Windows与Ubuntu)的机器上会分别返回200 OK和429 Too Many Requests的状态码。我们将分析代码、环境差异及可能的影响因素。 ... [详细]
  • 黑马头条项目:Vue 文章详情模块与交互功能实现
    本文详细介绍了如何在黑马头条项目中配置文章详情模块的路由、获取和展示文章详情数据,以及实现关注、点赞、不喜欢和评论功能。通过这些步骤,您可以全面了解如何开发一个完整的前端文章详情页面。 ... [详细]
  • 深入解析SpringMVC核心组件:DispatcherServlet的工作原理
    本文详细探讨了SpringMVC的核心组件——DispatcherServlet的运作机制,旨在帮助有一定Java和Spring基础的开发人员理解HTTP请求是如何被映射到Controller并执行的。文章将解答以下问题:1. HTTP请求如何映射到Controller;2. Controller是如何被执行的。 ... [详细]
  • yikesnews第11期:微软Office两个0day和一个提权0day
    点击阅读原文可点击链接根据法国大选被黑客干扰,发送了带漏洞的文档Trumps_Attack_on_Syria_English.docx而此漏洞与ESET&FireEy ... [详细]
  • This post discusses an issue encountered while using the @name annotation in documentation generation, specifically regarding nested class processing and unexpected output. ... [详细]
author-avatar
我是80初
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有