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内核) 的相关文章

  • 【FreeRTOS开发问题】FreeRTOS内存溢出

    FreeRTOS内存溢出 如下图所示 FreeRTOS编译完成后可以看到 系统提示无法分配内存到堆 Objects Template axf Error L6406E No space in execution regions with A
  • freertos---软定时器

    一 软件定时器介绍 freeRTOS软件定时器的时基是基于系统时钟节拍实现的 可以创建很多个 在硬件定时器资源不充足的情况下非常有用 软件定时器一般用作周期性地执行函数 在创建软件定时器时指定软件定时器的回调函数 在回调函数中实现相应的功能
  • FreeRTOS学习笔记<中断>

    中断概念 Cortex M的NVIC最多支持240个IRQ 中断请求 1个不可屏蔽中断 NMI 1个Systick 滴答定时器 定时器中断和多个系统异常 Cortex M处理器有多个用于管中断和异常的可编程寄存器 这些寄存器大多数都在 NV
  • FreeRTOS临界区

    FreeRTOS临界区是指那些必须完整运行 不能被打断的代码段 比如有的外设的初始化需要严格的时序 初始化过程中不能被打断 FreeRTOS 在进入临界区代码的时候需要关闭中断 当处理完临界区代码以后再打开中断 FreeRTOS 系统本身就
  • 【FreeRTOS】任务通知的使用

    作者主页 凉开水白菜 作者简介 共同学习 互相监督 热于分享 多加讨论 一起进步 专栏资料 https pan baidu com s 1nc1rfyLiMyw6ZhxiZ1Cumg pwd free 点赞 收藏 再看 养成习惯 订阅的粉丝
  • freertos————互斥锁

    线程安全 多线程程序处于一个多变的环境 可访问的全局变量和堆数据随时可能被其他的线程改变 多个线程同时访问一个共享数据 可能造成严重的后果 出现问题的是之前移植了一个freemodbus的从站 多个任务访问全局变量保持寄存器区 导致最后读出
  • Arduino IDE将FreeRTOS用于STM32

    介绍 适用于STM32F103C8的FreeRTOS STM32F103C是一种能够使用FreeRTOS的ARM Cortex M3处理器 我们直接在Arduino IDE中开始使用STM32F103C8的FreeRTOS 我们也可以使用K
  • [FreeRTOS入门学习笔记]定时器

    定时器的使用步骤 1 定义一个handle xTimerCreate创建 2 启动定时器 在Task1中调用 通过队列通知守护任务来执行定时器任务 要再config头文件中定义守护任务相关配置 虽然定时器是在task1中启动 但是定时器的任
  • 13-FreeRTOS任务创建与删除

    任务创建和删除API函数位于文件task c中 需要包含task h头文件 task h里面包函数任务的类型函数 例如 对xTaskCreate的调用 通过指针方式 返回一个TaskHandle t 变量 然后可将该变量用vTaskDele
  • FreeRTOS实时操作系统(三)任务挂起与恢复

    系列文章 FreeRTOS实时操作系统 一 RTOS的基本概念 FreeRTOS实时操作系统 二 任务创建与任务删除 HAL库 FreeRTOS实时操作系统 三 任务挂起与恢复 FreeRTOS实时操作系统 四 中断任务管理 FreeRTO
  • FreeRTOS之系统配置

    1 FreeRTOS的系统配置文件为FreeRTOSConfig h 在此配置文件中可以完成FreeRTOS的裁剪和配置 在官方的demo中 每个工程都有一个该文件 2 先说一下 INCLUDE 开始的宏 使用 INCLUDE 开头的宏用来
  • FreeRTOSConfig.h 配置优化及深入

    本篇目标 基于上一篇的移植freertos stm32f4 freertos 上 修改 FreeRTOSConfig h 文件的相关配置来优化辅助 FreeRtos 的使用 并且建立一些基本功能 信号量 消息地列等 的简单应用位于 stm3
  • 手臂睡眠模式进入和退出差异WFE、WFI

    我对 ARM 架构相当陌生 我正在尝试了解唤醒机制 首先 我发现很难找到这方面的好信息 ARM 的文档似乎对该主题非常简洁 我想了解的是皮质 特别是我正在使用的 M0 何时会醒来 作为参考 我还查阅了以下资料 WFI 和 WFE 指令以及事
  • 我可以将哪些 Cortex-M3 中断用于通用工作?

    我有一些代码需要在特定中断发生时运行 我不想在中断本身的上下文中执行它 但我也不希望它在线程模式下执行 我想以低于促使其运行的高级中断的优先级运行它 但也以高于线程级别 以及其他一些中断 的优先级运行它 我想我需要使用其他中断处理程序之一
  • 如何将 void* 转换为函数指针?

    我在 FreeRTOS 中使用 xTaskCreate 其第四个参数 void const 是传递给新线程调用的函数的参数 void connect to foo void const task params void on connect
  • 为什么 Cortex-A 的复位处理程序位于 0x0 而 Cortex-M3 则不然

    Cortex M3 的初始堆栈指针值位于 0x0 复位处理程序位于 0x4 原因是什么 这样做的设计理由是什么 为什么 ARM 人员不能像 Cortex A 那样将 0x0 留给复位处理程序 然后在复位处理程序内初始化 SP 我认为这属于
  • 使用硬浮点时使用 gcc 交叉编译简单示例时出现问题

    当我尝试编译一个简单的例子时test c int main return 0 对于具有硬浮点 ABI 的 cortex m7 目标 使用以下调用 arm none eabi gcc mcpu cortex m7 mfpu fpv5 d16
  • ARM Cortex-M4 中不同情况的条件汇编分支指令持续时间是多少?

    我试图确定下面简单延迟循环的时钟周期的确切持续时间 loop subs r2 r2 1 bne loop The Cortex M4 技术参考手册 https developer arm com documentation ddi0439
  • NTP请求包

    我试图弄清楚我需要在 NTP 请求包中发送 客户端 什么才能从服务器检索 NTP 包 我正在 Cortex M3 Stellaris LM3S6965 上使用 LWIP 据我了解 我将收到 UDP 标头 然后收到具有不同时间戳的 NTP 协
  • DSP 库 - RFFT - 奇怪的结果

    最近我一直在尝试在我的STM32F4 Discovery评估板上进行FFT计算 然后将其发送到PC 我已经调查了我的问题 我认为我对制造商提供的 FFT 函数做错了 我正在使用 CMSIS DSP 库 现在我一直在用代码生成样本 如果工作正

随机推荐

  • 开启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 因为中断而