FreeRTOS 任务调度原理(基于cortexM内核)

2023-05-16

目录

默认FreeRTOS调度策略(单核)

FreeRTOS调度策略的实现

任务创建

任务调度的4种情景:

 1.第一次启动任务调度器 

 2.任务主动触发调度

 3.SystemTick时钟触发调度

 4.因为中断而引起的任务调度


默认FreeRTOS调度策略(单核)

默认情况下,FreeRTOS使用固定优先级抢占式调度策略,并对同等优先级的任务进行循环时间切片:

  • "固定优先级"意味着调度程序不会一直更改任务的优先级,但是由于优先级继承,它可能会暂时提高任务的优先级。

  • "抢占式"意味着调度程序始终运行能够运行的最高优先级的 RTOS 任务,而不管任务何时能够运行。例如,如果中断服务例程 (ISR) 更改了能够运行的最高优先级任务,则计划程序将停止当前运行的较低优先级任务并启动优先级较高的任务 - 即使这发生在某个时间片内。在这种情况下,优先级较低的任务被称为已被优先级较高的任务"抢占"。

  • "轮循机制"是指共享优先级的任务轮流进入"正在运行"状态。

  • "时间切片"意味着调度程序将在每个系统中断时优先级相等的任务之间切换 - 系统中断之间的时间是一个时间片。

FreeRTOS调度策略的实现

list是是FreeRTOS核心的数据结构,它在list.c文件中实现为一个双向循环的链表。而FreeRTOS的抢占式的调度策略就是基于此实现的。

在task.c中定义了一个pxReadyTasksLists[ configMAX_PRIORITIES ] 链表数组,其中

configMAX_PRIORITIES 是定义在FreeRTOSConfig.h中的配置参数表示task的最大优先级。所以有几个优先级就有几个ReadyList,这是一一对应的。如下图:

任务创建

 而当我们创建task时就会在创建时,把创建的task插入对应优先级的pxReadyTasksLists中,如下图:

同时创建任务时也会使pxCurrentTCB指针指向优先级最高的TCB,如下图:

任务调度的4种情景:

1.第一次启动任务调度器 

任务创建完成之后,启动任务调度器,调度器就会使能SVC中断,如下图:(至于为什么要进入SVC中断,因为在只有在特权级模式下,程序才以访问一些内核的寄存器,和特殊功能寄存器,且我们希望在task正常运行时芯片工作在线程模式,而要启动第一个task,需要访问这些寄存器,具体的可以参考这篇文件章:【RTX操作系统教程】第9章 任务运行在特权级或非特权级模式_硬汉Eric2013_新浪博客 (sina.com.cn))

 之后会跳转到SVC中断,而在SVC中会初始化一些通用寄存器,psp指针,打开BASEPRI中断屏蔽寄存器,并切换到线程模式跳转到第一个task的函数入口,之后退出中断就会执行第一个task了,如下图:

 2.任务主动触发调度

当任务执行一些系统提供的API函数时,就可能触发任务调度,例如执行vTaskDelay函数,执行接收或发送队列函数且设置了阻塞时间,执行信号量和锁相关的函数导致阻塞等等。

例如:调用xQueueReceive队列接收函数时,如果队列为空且设置了阻塞时间,则会把当前的task从pxReadyTasksLists插入到delay链表中,然后调用portYIELD_WITHIN_API 使能PendSV中断,然后在PendSV中断里保存当前任务的上下文,并切换到下一个优先级task

 3.SystemTick时钟触发调度

整个os的时间依赖于SystemTick,而SystemTick时间来源于内核硬件的滴答定时器。当某个task运行时,SystemTick时间到了会进入SystemTick的中断,在此中断里会调用xTaskIncrementTick,先判断是否有任务的阻塞时间到了,如果有然后把任务从delayList中放入pxReadyTasksLists中,同时判断新加入的task优先级是否大于当前正在运行的优先级,如果是就置一个pdTRUE的标志位

如果设置了时间片轮转的宏configUSE_TIME_SLICING,就会再判断当前task所在的pxReadyTasksLists中是否还有其他同优先级的任务,有的话就置标志位相当于一个时间片调度一次同优先级的任务。

BaseType_t xTaskIncrementTick( void )
{
    TCB_t * pxTCB;
    TickType_t xItemValue;
    BaseType_t xSwitchRequired = pdFALSE;

    /* Called by the portable layer each time a tick interrupt occurs.
     * Increments the tick then checks to see if the new tick value will cause any
     * tasks to be unblocked. */
    traceTASK_INCREMENT_TICK( xTickCount );

    if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
    {
        /* Minor optimisation.  The tick count cannot change in this
         * block. */
        const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1;

        /* Increment the RTOS tick, switching the delayed and overflowed
         * delayed lists if it wraps to 0. */
        xTickCount = xConstTickCount;
        //判断tick是溢出,是则交换delaylist
        if( xConstTickCount == ( TickType_t ) 0U ) /*lint !e774 'if' does not always evaluate to false as it is looking for an overflow. */
        {
            taskSWITCH_DELAYED_LISTS();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }

        /* See if this tick has made a timeout expire.  Tasks are stored in
         * the  queue in the order of their wake time - meaning once one task
         * has been found whose block time has not expired there is no need to
         * look any further down the list. */
        if( xConstTickCount >= xNextTaskUnblockTime )
        {
            for( ; ; )
            {    //判断延时链表是否为空,为空就是没有要解阻塞的任务,当前任务就一直运行
                if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
                {
                    /* The delayed list is empty.  Set xNextTaskUnblockTime
                     * to the maximum possible value so it is extremely
                     * unlikely that the
                     * if( xTickCount >= xNextTaskUnblockTime ) test will pass
                     * next time through. */
                    xNextTaskUnblockTime = portMAX_DELAY; /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
                    break;
                }
                else
                {
                    /* The delayed list is not empty, get the value of the
                     * item at the head of the delayed list.  This is the time
                     * at which the task at the head of the delayed list must
                     * be removed from the Blocked state. */
                      //找出阻塞优先级最高的task,查看其阻塞时间是否到了
                    pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too.  Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */
                    xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );

                    if( xConstTickCount < xItemValue )
                    {
                        /* It is not time to unblock this item yet, but the
                         * item value is the time at which the task at the head
                         * of the blocked list must be removed from the Blocked
                         * state -  so record the item value in
                         * xNextTaskUnblockTime. */
                        xNextTaskUnblockTime = xItemValue;
                        break; /*lint !e9011 Code structure here is deedmed easier to understand with multiple breaks. */
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                    //时间到了从阻塞链表中移除
                    /* It is time to remove the item from the Blocked state. */
                    ( void ) uxListRemove( &( pxTCB->xStateListItem ) );

                    /* Is the task waiting on an event also?  If so remove
                     * it from the event list. */
                    if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
                    {
                        ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                    //插入reday链表中
                    /* Place the unblocked task into the appropriate ready
                     * list. */
                    prvAddTaskToReadyList( pxTCB );

                    /* A task being unblocked cannot cause an immediate
                     * context switch if preemption is turned off. */
                    //如果支持抢占式调度
                    #if ( configUSE_PREEMPTION == 1 )
                        {
                            /* Preemption is on, but a context switch should
                             * only be performed if the unblocked task has a
                             * priority that is equal to or higher than the
                             * currently executing task. */
                            //判断插入的task优先级是否大于当前的task
                            if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
                            {
                                xSwitchRequired = pdTRUE;
                            }
                            else
                            {
                                mtCOVERAGE_TEST_MARKER();
                            }
                        }
                    #endif /* configUSE_PREEMPTION */
                }
            }
        }

        /* Tasks of equal priority to the currently running task will share
         * processing time (time slice) if preemption is on, and the application
         * writer has not explicitly turned time slicing off. */
        //是否支持时间片调度
        #if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) )
            {
                 //同优先级任务不止一个
                if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 )
                {
                    xSwitchRequired = pdTRUE;
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
        #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */

        #if ( configUSE_TICK_HOOK == 1 )
            {
                /* Guard against the tick hook being called when the pended tick
                 * count is being unwound (when the scheduler is being unlocked). */
                if( xPendedTicks == ( TickType_t ) 0 )
                {
                    vApplicationTickHook();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
        #endif /* configUSE_TICK_HOOK */

        #if ( configUSE_PREEMPTION == 1 )
            {    //是否有挂起请求
                if( xYieldPending != pdFALSE )
                {
                    xSwitchRequired = pdTRUE;
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
        #endif /* configUSE_PREEMPTION */
    }
    else
    {
        ++xPendedTicks;

        /* The tick hook gets called at regular intervals, even if the
         * scheduler is locked. */
        #if ( configUSE_TICK_HOOK == 1 )
            {
                vApplicationTickHook();
            }
        #endif
    }

    return xSwitchRequired;
}

最后会判断返回的标志位,来确定是否进行调度,如果是pdTRUE则会启动pendSV中断在此中断里切换上下文调度到新的task。

4.因为中断而引起的任务调度

中断里调用系统API导致更高优先级的task唤醒,如果不显示的调用调度的函数,则唤醒的任务不会在中断退出后立马调度,它会等到下一个SystemTick中断来时才切换task,这样会造成中断的响应延时,在某些实时性要求高的地方是不能满足需求的。所以可以在中断里直接在条件满足时,先切换task,这样在中断退出时就会直接返回到高优先级的task了。见如下例子:

void ISR(void)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE, tmpHPT=pdFALSE;
    xQueueSendFromISR(rx_handle, &buffer, tmpHPT);
    xHigherPriorityTaskWoken = xHigherPriorityTaskWoken || tmpHPT;

    /* If lHigherPriorityTaskWoken is now equal to pdTRUE, then a context
    switch should be performed before the interrupt exists.  That ensures the
    unblocked (higher priority) task is returned to immediately. */

    portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
}

(如有描述不对烦请指正谢谢!)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS 任务调度原理(基于cortexM内核) 的相关文章

  • ZYNQ中FreeRTOS中使用定时器

    使用普通的Timer中断方式时 Timer中断可以正常运行 但是UDP通信进程无法启动 其中TimerIntrHandler是中断服务程序 打印程序运行时间与从BRAM中读取的数据 void SetupInterruptSystem XSc
  • FreeRTOS记录(九、一个裸机工程转FreeRTOS的实例)

    记录一下一个实际项目由裸机程序改成FreeRTOS 以前产品的平台还是C8051单片机上面的程序 硬件平台改成了STM32L051 同时使用STM32CubeMX生成的工程 使用FreeRTOS系统 EEPROM数据存储读取函数修改更新 2
  • 基于HAL库的FREERTOS-----------三.队列

    一 队列简介 在实际的应用中 常常会遇到一个任务或者中断服务需要和另外一个任务进行 沟通交流 这个 沟通交流 的过程其实就是消息传递的过程 在没有操作系统的时候两个应用程序进行消息传递一般使用全局变量的方式 但是如果在使用操作系统的应用中用
  • FreeRTOS学习(三)开关中断

    声明及感谢 跟随正点原子资料学习 在此作为学习的记录和总结 环境 keil stm32f103 背景知识 Cotex M3的NVIC最多支持240个IRQ 中断请求 1个不可屏蔽 NMI 1个Systick 滴答定时器 Cortex M处理
  • FreeRTOS笔记(一)简介

    这个笔记主要依据韦东山freertos快速入门系列记录 感谢韦东山老师的总结 什么是实时操作系统 操作系统是一个控制程序 负责协调分配计算资源和内存资源给不同的应用程序使用 并防止系统出现故障 操作系统通过一个调度算法和内存管理算法尽可能把
  • FreeRTOS笔记(二)

    FreeRTOS笔记 二 静态任务 文章目录 FreeRTOS笔记 二 静态任务 一 任务定义 二 任务创建 2 1 定义任务栈 2 2 定义任务函数 2 3 定义任务控制块 2 4 实现任务创建函数 三 实现就绪列表 3 1 定义就绪列表
  • FreeRTOS临界段

    1 临界段 在访问共享资源时不希望被其他任务或者中断打断的代码 这段要执行的代码称为临界段代码 2 设置临界段的目的 保护共享资源 例如 全局变量 公共函数 不可重入函数 函数里面使用 了一些静态全局变量 malloc 等 保护外设的实时性
  • 再论FreeRTOS中的configTOTAL_HEAP_SIZE

    关于任务栈和系统栈的基础知识 可以参考之前的随笔 FreeRTOS 任务栈大小确定及其溢出检测 这里再次说明 define configTOTAL HEAP SIZE size t 17 1024 这个宏 官方文档解释 configTOTA
  • 在 Cortex-M3 CPU 上通过 printf 进行输出调试,在 BKPT 指令处停止 + JTAG 和 sw 端口混乱

    我有一个 Keil ULINK2 USB 仿真器盒连接到JTAG我的主板上的连接器 与板载 Cortex M3 CPU TI Stellaris LuminaryMicro LM3S 系列 配合良好 看起来 JTAG 和 SWJ DP 端口
  • 在 core_cm4.h 上为什么有类似 ((uint32_t)(int32_t)IRQn) 的转换?

    在 core cm4 h 的以下代码中 为什么存在双重转换 uint32 t int32 t IRQn 例如在以下函数中 STATIC INLINE void NVIC EnableIRQ IRQn Type IRQn NVIC gt IS
  • FreeRTOS 配置TICK_RATE_HZ

    我使用的是带有 5 4 版 FreeRTOS 的 MSP430f5438 我有一个有趣的问题 我无法弄清楚 基本上 当我将 configTICK RATE HZ 设置为不同的值时 LED 闪烁得更快或更慢 它应该保持相同的速率 我将 con
  • 如何获得可靠的 Cortex M4 短延迟

    我正在将一些代码从 M3 移植到 M4 它使用 3 个 NOP 在串行输出时钟更改之间提供非常短的延迟 M3指令集将NOP的时间定义为1个周期 我注意到 M4 中的 NOP 并不一定会延迟任何时间 我知道我需要禁用编译器优化 但我正在寻找一
  • C++ freeRTOS任务,非静态成员函数的无效使用

    哪里有问题 void MyClass task void pvParameter while 1 this gt update void MyClass startTask xTaskCreate this gt task Task 204
  • 如何创建具有自定义外设和内存映射的 QEMU ARM 机器?

    我正在为 Cortex M3 cpu 编写代码 并且正在使用以下命令执行单元测试qemu arm二进制 现在一切都很好 但我想知道我是否能够使用测试整个系统qemu system arm 我的意思是 我想为 qemu 编写自定义 机器 我将
  • arm gcc工具链为arm-elf或arm-none-eabi,有什么区别?

    当您构建 gcc 工具链时 可以将其构建为arm elf 或arm none eabi 但是有什么区别呢 我今天使用 eabi 但这只是因为其他人似乎都这样做 但由于这是一个非常糟糕的论点 因此理解其中的差异真的很高兴 注意 此工具链将为基
  • 小型 ARM 微控制器的 RTOS 内核之间的可量化差异 [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 有许多不同的 RTOS 可用于微控制器 我专门寻找支持 ARM Cortex M 处理器的 RTOS 另外 我对闭源解决方案不感兴趣 试图从网站
  • 有关 CMake 错误的问题:没有为目标提供源

    我正在尝试使用 cmake 和 eclipse 将 FreeRtos 添加到我的项目中 但出现错误 我运行的是 debian 10 我的 cmake 版本是 3 13 4 cmake 的文件可以在以下位置找到这个 git 仓库 https
  • 当 Cortex-M3 出现硬故障时如何保留堆栈跟踪?

    使用以下设置 基于 Cortex M3 的 C gcc arm 交叉工具链 https launchpad net gcc arm embedded 使用 C 和 C FreeRtos 7 5 3 日食月神 Segger Jlink 与 J
  • CLion 中的 GDB 监视器命令

    我正在尝试使用远程 GDB 调试嵌入式项目 我的系统 目标 ARM Cortex M0 SEGGER J Link GDB Server V6 10 命令行版本 臂 无 eabi gdb 7 10 1 20160616 cvs CLion
  • 在 ARM Cortex-M3 中使用 newlib 的 malloc

    我正在为 ARM Cortex M3 NXP 的 LCP17xx 创建代码 到目前为止我一直在使用静态内存 一切都运行良好 我尝试添加动态内存支持 但是一旦调用 malloc 系统就会卡住 我正在使用 gcc 为 Arm Bare Meta

随机推荐

  • 开启VNC Viewer和windows之间复制粘贴功能

    win10 ubuntu 16 04 VNC 6 18 VNC和windows之间的复制粘贴模板不共享 xff0c 想要开启他们之间的复制粘贴 xff0c 只需要如下命令 xff1a 安装autocutsel sudo apt instal
  • HACK RF学习之旅记录2——简单指标的测试和注意事项

    按照大神ossmann视频lesson5的课程 xff08 https www bilibili com video av7079120 p 61 5 xff09 做了一些测试和学习 一 虚拟机环境下HackRF接电脑USB口速率测试 WI
  • ubuntu直接在当前目录下打开终端terminal的方法

    直接安装一个软件包nautilus open terminal 终端输入 xff1a sudo apt get install nautilus open terminal 重启系统 xff01
  • IP地址与子网划分

    目录 一 IP地址 1 1 IPv4 1 2 IPv6 1 3 IPv4私有网络地址和公有网络地址 1 3 1 公有网络地址 1 3 2 私有网络地址 二 IP地址分类 2 1 IP地址分别分为A B C D E 5类 三 子网划分 3 1
  • 关于OpenCV安装

    1 概述 本文主要讲述关于OpenCV的安装 xff0c 以及安装过程中的问题解决 2 源码安装 2 1 下载 关于opencv一般推荐源码安装 xff0c 可以通过如下方式下载opencv源码 opencv 通过下述命令下载源码 span
  • Windows 11 本地 php 开发环境搭建:PHP + Apache + MySQL +VSCode 安装和环境配置

    目录 前言1 PHP 的下载 安装和配置1 1 下载 php1 2 安装 php1 3 配置 php 系统变量1 4 配置 php ini 2 Apache 的下载 安装和配置2 1 下载 Apache2 2 安装 Apache2 3 修改
  • 【frp配置教程】frp内网穿透服务端frps.ini各配置参数详解

    必须 标识头 common 是不可或缺的部分 必须 服务器IP bind addr 61 0 0 0 0 0 0 0 0为服务器全局所有IP可用 xff0c 假如你的服务器有多个IP则可以这样做 xff0c 或者填写为指定其中的一个服务器I
  • 【Ubuntu】修改Ubuntu的apt-get源为国内镜像源的方法

    转载请注明出处 xff1a http www zgljl2012 com ubuntu xiu gai ubuntude apt getyuan wei a li yun yuan de fang fa 1 原文件备份 sudo cp et
  • 【工程源码】stmdb和ldmia汇编指令

    本文由FPGA爱好者小梅哥编写 xff0c 未经作者许可 xff0c 本文仅允许网络论坛复制转载 xff0c 且转载时请标明原作者 首先一句话说一下stmdb和ldmia指令的作用 xff1a stmdb和ldmia指令一般配对使用 xff
  • 使用51单片机驱动YM12232B型液晶显示屏

    这是一个使用51单片机驱动YM12232B 液晶显示器的例子 xff0c 本人水平有限 xff0c 仅供参考 本实例中将使用51单片机控制YM12232B LCD分别在主窗口和副窗口中显示 科 和 学 字 YM12232B 一共有18个引脚
  • A*,那个传说中的算法

    周日的下午 xff0c 微信 simplemain xff0c 老王又来找大伙儿聊技术了 今天想跟大家聊的 xff0c 是我们经常用到 xff0c 但是却让大家觉得十分神秘的那个算法 xff1a A 想必大家都玩儿过对战类的游戏 xff0c
  • putty无法连接linux虚拟机

    linux安装参考 https linux cn article 5893 1 html 我选择的是Ubuntu 先看window下能不能ping通linux linux ip 地址查看 参考链接 jingyan baidu com art
  • C语言之网络编程(服务器和客户端)

    Linux网络编程 1 套接字 xff1a 源IP地址和目的IP地址以及源端口号和目的端口号的组合称为套接字 其用于标识客户端请求的服务器和服务 常用的TCP IP协议的3种套接字类型如下所示 xff08 1 xff09 流套接字 xff0
  • 无监督学习论文阅读

    无监督学习论文阅读 刚开始接触这方面的内容 xff0c 仅供参考 Diversity Transfer Network for Few Shot Learning xff08 AAAI2020 xff09 1 这篇文章提出了一种新的深度聚类
  • 控制教程 —— 介绍篇:3.PID控制器设计

    承接上一篇 控制教程 介绍篇 xff1a 2 系统分析 介绍完系统建模和基本的系统分析后 xff0c 我们已经了解了被控对象的特性 xff0c 这时 xff0c 就需要用一个合理的控制器 xff0c 让这个被控对象在该控制器下按照指定的给定
  • FreeRTOS —— 4.队列管理

    4 1 本章介绍与适用范围 队列 提供了任务到任务 xff0c 任务到中断以及中断到任务的通信机制 范围 本章旨在使读者更好地理解 xff1a 如何创建队列 队列如何管理其包含的数据 如何将数据发送到队列 如何从队列接收数据 阻塞队列意味着
  • LSTM一般最多堆叠多少层

    一 LSTM一般最多堆叠多少层 在大规模翻译任务的经验中 简单的堆叠LSTM层最多可以工作4层 很少工作6层 超过8层就很差了 Redisual connection有助于梯度的反向传播 xff0c 能够帮助lstm堆叠更多层 xff0c
  • 华为机试在线训练-牛客网(23)判断两个IP是否属于同一子网

    题目描述 子网掩码是用来判断任意两台计算机的IP地址是否属于同一子网络的根据 子网掩码与IP地址结构相同 xff0c 是32位二进制数 xff0c 其中网络号部分全为 1 和主机号部分全为 0 利用子网掩码可以判断两台主机是否中同一子网中
  • APISIX Dashboard中文文档(二)

    2022年7月6日14 31 51 APISIX Dashboard中文文档 一 APISIX Dashboard中文文档 二 APISIX Dashboard中文文档 三 基本部署 在 Linux 上安装 Apache APISIX Da
  • FreeRTOS 任务调度原理(基于cortexM内核)

    目录 默认FreeRTOS调度策略 xff08 单核 xff09 FreeRTOS调度策略的实现 任务创建 任务调度的4种情景 xff1a 1 第一次启动任务调度器 2 任务主动触发调度 3 SystemTick时钟触发调度 4 因为中断而