当前位置: 首页 > article >正文

RTOS:启动调度器的作用(含源码逐行解读)

文章目录

  • 前言
  • 一、启动调度器
  • 二、详细逻辑分析
  • 三、逐行分析
    • 3.1、traceENTER_vTaskStartScheduler
    • 3.2、configASSERT( ( sizeof( UBaseType_t ) * taskBITS_PER_BYTE ) >= configNUMBER_OF_CORES );
    • 3.3、xReturn = prvCreateIdleTasks();
    • 3.4、xTimerCreateTimerTask();
    • 3.5、freertos_tasks_c_additions_init
    • 3.6、portDISABLE_INTERRUPTS
    • 3.7、configSET_TLS_BLOCK
    • 3.8、portCONFIGURE_TIMER_FOR_RUN_TIME_STATS
    • 3.9、traceTASK_SWITCHED_IN 和 traceSTARTING_SCHEDULER
    • 3.10、xPortStartScheduler
    • 3.11、configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
    • 3.12、xIdleTaskHandles
    • 3.13、uxTopUsedPriority
    • 3.14、traceRETURN_vTaskStartScheduler
  • 四、启动调度器的作用


前言

操作系统,在入行嵌入式j接触它之前,感觉那是多么高深、神圣的技术,感觉它是高不可攀的。曾经也幻想过它有多么的复杂,在裸机编程时无数次想去应用这个技术,但无奈,无人指引,以致于每每都会望而却步。如今,也在多种OS的基础上做过各行各业的软件开发,所以想在闲暇之时,将使用过的OS内核软件逐行阅读,以提升自我编程能力、去了解更多软件编程思想。写此专栏文章的目的也仅仅是为了让这个过程留痕。

本文所有代码源于RTOS-Keenel

一、启动调度器

话不多说,直接上源码。
void vTaskStartScheduler( void )
{BaseType_t xReturn;traceENTER_vTaskStartScheduler();#if ( configUSE_CORE_AFFINITY == 1 ) && ( configNUMBER_OF_CORES > 1 ){/* Sanity check that the UBaseType_t must have greater than or equal to* the number of bits as confNUMBER_OF_CORES. */configASSERT( ( sizeof( UBaseType_t ) * taskBITS_PER_BYTE ) >= configNUMBER_OF_CORES );}#endif /* #if ( configUSE_CORE_AFFINITY == 1 ) && ( configNUMBER_OF_CORES > 1 ) */xReturn = prvCreateIdleTasks();#if ( configUSE_TIMERS == 1 ){if( xReturn == pdPASS ){xReturn = xTimerCreateTimerTask();}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_TIMERS */if( xReturn == pdPASS ){/* freertos_tasks_c_additions_init() should only be called if the user* definable macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is* the only macro called by the function. */#ifdef FREERTOS_TASKS_C_ADDITIONS_INIT{freertos_tasks_c_additions_init();}#endif/* Interrupts are turned off here, to ensure a tick does not occur* before or during the call to xPortStartScheduler().  The stacks of* the created tasks contain a status word with interrupts switched on* so interrupts will automatically get re-enabled when the first task* starts to run. */portDISABLE_INTERRUPTS();#if ( configUSE_C_RUNTIME_TLS_SUPPORT == 1 ){/* Switch C-Runtime's TLS Block to point to the TLS* block specific to the task that will run first. */configSET_TLS_BLOCK( pxCurrentTCB->xTLSBlock );}#endifxNextTaskUnblockTime = portMAX_DELAY;xSchedulerRunning = pdTRUE;xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;/* If configGENERATE_RUN_TIME_STATS is defined then the following* macro must be defined to configure the timer/counter used to generate* the run time counter time base.   NOTE:  If configGENERATE_RUN_TIME_STATS* is set to 0 and the following line fails to build then ensure you do not* have portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() defined in your* FreeRTOSConfig.h file. */portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();traceTASK_SWITCHED_IN();traceSTARTING_SCHEDULER( xIdleTaskHandles );/* Setting up the timer tick is hardware specific and thus in the* portable interface. *//* The return value for xPortStartScheduler is not required* hence using a void datatype. */( void ) xPortStartScheduler();/* In most cases, xPortStartScheduler() will not return. If it* returns pdTRUE then there was not enough heap memory available* to create either the Idle or the Timer task. If it returned* pdFALSE, then the application called xTaskEndScheduler().* Most ports don't implement xTaskEndScheduler() as there is* nothing to return to. */}else{/* This line will only be reached if the kernel could not be started,* because there was not enough FreeRTOS heap to create the idle task* or the timer task. */configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );}/* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0,* meaning xIdleTaskHandles are not used anywhere else. */( void ) xIdleTaskHandles;/* OpenOCD makes use of uxTopUsedPriority for thread debugging. Prevent uxTopUsedPriority* from getting optimized out as it is no longer used by the kernel. */( void ) uxTopUsedPriority;traceRETURN_vTaskStartScheduler();
}

二、详细逻辑分析

用于启动调度器,调度器是操作系统的核心部分,负责决定哪个任务应该运行。

主要步骤如下:

  • 1、调用 prvCreateIdleTasks 函数创建空闲任务。空闲任务是系统中优先级最低的任务,当没有其他任务需要运行时,空闲任务才会运行。
  • 2、如果启用了定时器,调用 xTimerCreateTimerTask 函数创建定时器任务。定时器任务是用于处理定时器事件的任务。
  • 3、如果上述步骤都成功了,就会调用函数 freertos_tasks_c_additions_init 进行一些额外的初始化工作。这个函数是可选的,只是当定义了 FREERTOS_TASKS_C_ADDITIONS_INIT 宏才会调用。
  • 4、禁用中断。这是为了确保在调用 xPortStartScheduler 之前不会发生中断。在创建的任务堆栈中,有一个状态,其中就包括了中断开关的状态。因此,当第一个任务开始运行时,中断会自动被重新启用。
  • 5、如果启用了C运行时的TLS支持,函数会调用 configSET_TLS_BLOCK 函数,将C运行时的TLS块切换到第一个运行的任务的TLS块。
  • 6、初始化一些全局变量
  • 7、如果定义了 configGENERATE_RUN_TIME_STATS 宏,函数会调用 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS 宏,配置用于生成运行时间统计的定时器/计数器。
  • 8、调用 xPortStartScheduler 函数启动调度器。
  • 9、如果 xPortStartScheduler 函数返回,那么可能是由于没有足够的堆内存来创建空闲任务或定时器任务,或者应用程序嗲用了 xTaskEndScheduler 函数。在后一种情况下,函数会调用 vPortEndScheduler 函数。

三、逐行分析

3.1、traceENTER_vTaskStartScheduler

用于跟踪和调试的宏。

3.2、configASSERT( ( sizeof( UBaseType_t ) * taskBITS_PER_BYTE ) >= configNUMBER_OF_CORES );

检查 UBaseType_t 类型的位数大于核心数量

3.3、xReturn = prvCreateIdleTasks();

为每个核心创建一个空闲任务。

3.4、xTimerCreateTimerTask();

创建定时器任务。前提 configUSE_TIMERS 是启用了,并且 prvCreateIdleTasks 函数成功创建了空闲任务。

3.5、freertos_tasks_c_additions_init

进行一些额外的初始化工作。

3.6、portDISABLE_INTERRUPTS

禁用中断,是为了确保在调用 xPortStartScheduler 函数之前不会发生中断。在创建的任务的堆栈中,有一个状态字,其中包括中断开关的状态。因此,当第一个任务开始运行时,中断会自动被重新启用。

3.7、configSET_TLS_BLOCK

如果启用了TLS支持(configUSE_C_RUNTIME_TLS_SUPPORT宏打开),那么调用该函数将运行TLS块切换到第一个运行任务的TLS。

3.8、portCONFIGURE_TIMER_FOR_RUN_TIME_STATS

如果定义了 configGENERATE_RUN_TIME_STATS 宏,那么调用 8、portCONFIGURE_TIMER_FOR_RUN_TIME_STATS 宏会配置用于生成运行时间统计的定时器/计数器。

3.9、traceTASK_SWITCHED_IN 和 traceSTARTING_SCHEDULER

用于跟踪和调试。

3.10、xPortStartScheduler

启动调度器。

3.11、configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );

如果 xPortStartScheduler 函数返回异常,那么可能是由于没有足够的堆内存来创建空闲任务或定时器任务。

3.12、xIdleTaskHandles

总是将 INCLUDE_xTaskGetIdleTaskHandle 设置为0,可避免编译器发出警告。意味着 xIdleTaskHandles 未在其他地方调用。

3.13、uxTopUsedPriority

防止被优化掉。

3.14、traceRETURN_vTaskStartScheduler

用于跟踪和调试。

四、启动调度器的作用

  • 1、创建空闲任务,空闲任务是系统中优先级最低的任务,当没有其他任务需要运行时,空闲任务会运行。

  • 2、创建定时器任务,如果启用了定时器,那么就会创建一个定时器任务,用于处理定时器事件。

  • 3、启动调度器,调度器开始运行后,会根据任务的优先级和状态,决定哪个任务应该运行。

  • 4、管理系统资源,如处理器、内存、文件和设备。

    总的来说,该函数主要作用就是启动RTOS的调度器,开始执行任务并管理系统资源。

http://www.lryc.cn/news/2393130.html

相关文章:

  • 【Python 进阶】抽象基类(Abstract Base Class)
  • Armv7l或树莓派32位RPI 4B编译faiss
  • 嵌入式开发STM32 -- 江协科技笔记
  • [网页五子棋][用户模块]客户端开发(登录功能和注册功能)
  • nt!MiInitializeSystemCache函数分析之PointerPte->u.List.NextEntry的由来
  • MQTT协议,EMQX部署,MQTTX安装学习
  • 如何理解UDP 和 TCP 区别 应用场景
  • 60天python训练计划----day40
  • 干泵,干式螺杆真空泵
  • Tailwind CSS 实战:基于 Kooboo 构建 AI 对话框页面(五):语音合成输出与交互增强
  • 职业本科院校无人机专业人才培养解决方案
  • 利用机器学习优化数据中心能效
  • 软件评测机构如何保障质量?检测资质、技术实力缺一不可
  • 微软开源bitnet b1.58大模型,应用效果测评(问答、知识、数学、逻辑、分析)
  • ubuntu 安装上传的 ffmpeg_7.1.1.orig.tar.xz并使用
  • Web3怎么开发类似MetaMask的钱包
  • Linux多线程(六)之线程控制4【线程ID及进程地址空间布局】
  • 1.什么是node.js、npm、vue
  • Xamarin入门笔记(Xamarin已经被MAUI取代)
  • 排查Oracle文件打开数过多
  • 应用层协议http(无代码版)
  • 8.5 Q1|广州医科大学CHARLS发文 甘油三酯葡萄糖指数累积变化与 0-3期心血管-肾脏-代谢综合征人群中风发生率的相关性
  • 交叉编译tcpdump工具
  • 【Python-Day 20】揭秘Python变量作用域:LEGB规则与global/nonlocal关键字详解
  • golang 柯里化(Currying)
  • 无人机停机坪运行技术分析!
  • comfyui 工作流中 视频长度和哪些参数有关? 生成15秒的视频,再加上RTX4060 8G显卡,尝试一下
  • 【Java Web】速通HTML
  • 在线制作幼教早教行业自适应网站教程
  • WSL 开发环境搭建指南:Java 11 + 中间件全家桶安装实战