FreeRTOS记录(四、FreeRTOS任务堆栈溢出问题和临界区)

article/2025/8/27 4:39:25
本来计划是消息队列、信号量、任务通知、事件集、邮件的文章
但是因为自己调试的时候遇到了一个问题,还是把堆栈溢出问题放到前面来说
..增加临界区的使用说明     											2021/11/7
..临界区的使用部分增加任务挂起与临界区说明    							2021/11/26

目录

  • 任务堆栈问题的出现
  • FreeRTOS任务栈溢出检测
    • vApplicationStackOverflowHook
  • FreeRTOS任务运行情况查询
    • vTaskList
  • 临界区的使用
    • 临界区API介绍
      • taskENTER_CRITICAL
      • taskEXIT_CRITICAL
      • taskENTER_CRITICAL_FROM_ISR
      • taskEXIT_CRITICAL_FROM_ISR( x )
    • 任务挂起与临界区区别

说明:FreeRTOS 专栏与我的 RT-Thread 专栏不同,我的 RT-Thread 专栏是从理论学习一步一步循序渐进,从 0 起步的 完整教学,而 FreeRTOS 更偏向于 我直接拿来使用,需要用到什么,然后引出知识点,在使用中发现问题,解然后再解决问题。

本 FreeRTOS 专栏记录的开发环境:

  1. FreeRTOS记录(一、熟悉开发环境以及CubeMX下FreeRTOS配置)
  2. FreeRTOS记录(二、FreeRTOS任务API认识和源码简析)
  3. FreeRTOS记录(三、FreeRTOS任务调度原理解析_Systick、PendSV、SVC)

任务堆栈问题的出现

为了写记录,自己先建立好了几个任务,其中就有 I2C读取温湿度传感器数据的任务,最初每个任务是使用的系统默认128字(512字节)作为默认大小,但是我感觉有些任务512字节有些浪费,比如提示系统运行的跑马灯,于是我把一些任务改成了64字大小,当然设置成为64,需要先把系统的最小任务大小设置为64。

在这里插入图片描述

刚开始我把这些简单的任务都设置成为64了,然后发现运行的时候,就会死掉,死掉的原因当然是堆栈溢出了,当然,很容易想到是我的 I2C 读取温湿度的任务导致的溢出,其他任务实在没干什么事情,
I2C任务和其他任务的大小如下:

osThreadDef(Led_toggle, Start_Led_toggle, osPriorityLow, 0,64);Led_toggleHandle = osThreadCreate(osThread(Led_toggle), NULL);/* definition and creation of printfTask */osThreadDef(printfTask, StartprintfTask, osPriorityLow, 0, 128);printfTaskHandle = osThreadCreate(osThread(printfTask), NULL);/* definition and creation of KeyTask */osThreadDef(KeyTask, StartKeyTask, osPriorityIdle, 0, 64);KeyTaskHandle = osThreadCreate(osThread(KeyTask), NULL);/* definition and creation of THread */osThreadDef(THread, StartTHread, osPriorityNormal, 0, 64);THreadHandle = osThreadCreate(osThread(THread), NULL);/* definition and creation of spiflash */osThreadDef(spiflash, Startspiflash, osPriorityIdle, 0, 64);spiflashHandle = osThreadCreate(osThread(spiflash), NULL);...
/* USER CODE END Header_StartTHread */
void StartTHread(void const * argument)
{/* USER CODE BEGIN StartTHread */float T=0,H=0;/*64会溢出字的内存空间不够SHT21 协议读取*//* Infinite loop */for(;;){SHT2X_THMeasure();T=(getTemperature()/100.0);H=(getHumidity()/100.0); osThreadSuspendAll();printf("\r\n%4.2f C\r\n%4.2f%%\r\n",T,H);osThreadResumeAll();osDelay(3000);}/* USER CODE END StartTHread */
}

后面还是把THread任务大小改成128,看上去每隔一定时间采集一定的次数,一切正常
(实际上有个bug就存在了,只是其他任务比较简单,看不出任何问题)。

本来准备把任务通知、消息队列的使用说明一下,然后开启了一个硬件定时器准备周期采集温湿度数据,从中断时发送任务通知,和从任务中发送任务通知。

开启定时器以后,定时器中断优先级改为6,定时器时间为1S一次中断,受FreeRTOS管理的中断优先级我是按照默认的设置为5:

在这里插入图片描述

通过我们前面的知识就知道,FreeRTOS进入临界区以后,硬件定时器的中断是会被屏蔽的。所以在检查问题的时候这个问题不用考虑。

在定时器中断中做了个简单的计数:

/*** @brief This function handles TIM3 global interrupt.*/
void TIM3_IRQHandler(void)
{/* USER CODE BEGIN TIM3_IRQn 0 */time3_count++;if(time3_count >= 10){time3_count = 0;}/* USER CODE END TIM3_IRQn 0 */HAL_TIM_IRQHandler(&htim3);/* USER CODE BEGIN TIM3_IRQn 1 *//* USER CODE END TIM3_IRQn 1 */
}

通过按键任务查看一下计数,这么做只是为了测试一切正常:

/* USER CODE END Header_StartKeyTask */
void StartKeyTask(void const * argument)
{/* USER CODE BEGIN StartKeyTask *//* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(K2_GPIO_Port,K2_Pin) == 0){osDelay(10);if(HAL_GPIO_ReadPin(K2_GPIO_Port,K2_Pin) == 0){osThreadSuspendAll();printf("K2 pushed!!,time3_count is :%d\r\n",time3_count);osThreadResumeAll();while(HAL_GPIO_ReadPin(K2_GPIO_Port,K2_Pin) == 0){osDelay(10);}}}osDelay(1);   }/* USER CODE END StartKeyTask */
}void Start_Led_toggle(void const * argument)
{/* USER CODE BEGIN Start_Led_toggle *//* Infinite loop */for(;;){  osDelay(500);HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);  osDelay(500);HAL_GPIO_TogglePin(LED2_GPIO_Port,LED2_Pin);osDelay(500);}/* USER CODE END Start_Led_toggle */
}

于是问题就出来了。

现象是:我只要按了一次按键,I2C读取任务 和 LED灯任务就永远不会运行了,但是按键任务,包括硬件定时器数据都是正常的,就是程序没有死机,只是有2个任务死掉了。

能够想到应该是堆栈的问题,因为确实没有什么复杂的程序运行,优先级的问题我也考虑过了,每个任务是否会释放CPU控制权也都检测过。

还将FreeRTOS能够用的总堆大小改成了10K:

在这里插入图片描述

依然不行,最后还是将所有的任务大小还是改回了128字,任务看上去解决了,按键按下,能够获取数值,而且所有任务能够周期运行。

FreeRTOS任务栈溢出检测

然后想着FreeRTOS是有检测任务堆栈溢出功能的,于是找到相关的内容,参考了一些网上的资料。

在CubeMX中,选择使用堆栈溢出钩子函数,启动栈溢出检测方案为方案二,如下图:

在这里插入图片描述

vApplicationStackOverflowHook

选中以后生成的代码,多了一个vApplicationStackOverflowHook函数,直接在里面打印溢出的任务名,如下图:

/* Hook prototypes */
void vApplicationStackOverflowHook(xTaskHandle xTask, signed char *pcTaskName);/* USER CODE BEGIN 4 */
__weak void vApplicationStackOverflowHook(xTaskHandle xTask, signed char *pcTaskName)
{/* Run time stack overflow checking is performed ifconfigCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook function iscalled if a stack overflow is detected. */printf("任务:%s 溢出\r\n",pcTaskName);
}

终于,问题的根本原因终于找到了:

在这里插入图片描述
顿时心中压抑一阵的疑问终于打开,温湿度读取使用128字的大小还是不够,那么解决办法当然还是增加这个任务的空间大小,改成192字,终于所有任务都正常了。

FreeRTOS任务运行情况查询

问题虽然已经解决,但是我们以后要怎样才能确定自己的任务大小呢,这时候,我们可以使用到可视化追踪功能,查看所有任务的运行情况和堆栈使用大小,在CubeMX中使能相应的功能,如下图:

在这里插入图片描述

然后还需要在Include definetions部分使能eTaskGetState:

在这里插入图片描述

vTaskList

然后在程序中,我们使用的是osThreadList 函数,其实也就是调用了vTaskList函数:

/**
* @brief   Lists all the current threads, along with their current state 
*          and stack usage high water mark.
* @param   buffer   A buffer into which the above mentioned details
*          will be written
* @retval  status code that indicates the execution status of the function.
*/
osStatus osThreadList (uint8_t *buffer)
{
#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS == 1 ) )vTaskList((char *)buffer);
#endifreturn osOK;
}

打印任务查看任务状态:

我们为了说明问题,下面的代码中THread任务的大小还是128字,因为还没有讲任务通知,信号量之类的知识,我这里使用自己定义的一个标志位printfstate_on 来判断是否需要打印任务状态:

  /* definition and creation of Led_toggle */osThreadDef(Led_toggle, Start_Led_toggle, osPriorityLow, 0, 128);Led_toggleHandle = osThreadCreate(osThread(Led_toggle), NULL);/* definition and creation of printfTask */osThreadDef(printfTask, StartprintfTask, osPriorityLow, 0, 256);printfTaskHandle = osThreadCreate(osThread(printfTask), NULL);/* definition and creation of KeyTask */osThreadDef(KeyTask, StartKeyTask, osPriorityIdle, 0, 128);KeyTaskHandle = osThreadCreate(osThread(KeyTask), NULL);/* definition and creation of THread */osThreadDef(THread, StartTHread, osPriorityNormal, 0, 128);THreadHandle = osThreadCreate(osThread(THread), NULL);/* definition and creation of spiflash */osThreadDef(spiflash, Startspiflash, osPriorityIdle, 0, 128);spiflashHandle = osThreadCreate(osThread(spiflash), NULL);/* USER CODE END Header_StartprintfTask */
void StartprintfTask(void const * argument)
{/* USER CODE BEGIN StartprintfTask *//* Infinite loop */for(;;){if(printfstate_on){printfstate_on =0;uint8_t mytaskstatebuffer[500];printf("==================================\r\n");printf("任务名          任务状态    优先级     剩余栈   任务序号\r\n");osThreadList((uint8_t *)&mytaskstatebuffer);printf("%s\r\n",mytaskstatebuffer);}osDelay(10);//释放CPU占用权不要忘了延时}/* USER CODE END StartprintfTask */
}/* USER CODE END Header_StartKeyTask */
void StartKeyTask(void const * argument)
{/* USER CODE BEGIN StartKeyTask *//* Infinite loop */for(;;){if(HAL_GPIO_ReadPin(K2_GPIO_Port,K2_Pin) == 0){osDelay(10);if(HAL_GPIO_ReadPin(K2_GPIO_Port,K2_Pin) == 0){taskENTER_CRITICAL();printf("K2 pushed!!,time3_count is :%d\r\n",time3_count);taskEXIT_CRITICAL();printfstate_on = 1;while(HAL_GPIO_ReadPin(K2_GPIO_Port,K2_Pin) == 0){osDelay(10);}}}osDelay(1);}/* USER CODE END StartKeyTask */
}

结果如下图:

在这里插入图片描述
图中有2个地方有问题,第一个是温湿度中的剩余栈为0 ,溢出了,第二个是KeyTask的任务序号异常。

这个基本上可以确定是,THread任务和 KeyTask任务 在内存空间上是使用的连续的内存空间,一前一后,THread任务溢出导致改写了属于 KeyTask任务所在内存的数据,导致他的任务序号异常。这是内存空间相关的知识。

然后还需要说一下任务状态的意思:


/* Task states returned by eTaskGetState. */
typedef enum
{eRunning = 0,	/* X  A task is querying the state of itself, so must be running. */eReady,			/* R The task being queried is in a read or pending ready list. */eBlocked,		/* B The task being queried is in the Blocked state. */eSuspended,		/* S The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */eDeleted,		/* D The task being queried has been deleted, but its TCB has not yet been freed. */eInvalid			/* Used as an 'invalid state' value. */
} eTaskState;

我们把THread任务大小改成 192 字,运行结果如下:

在这里插入图片描述

为了更加说明任务堆栈大小的问题,根据推荐视频里面的教程我也做了测试,我们可以看到 printfTask 的剩余栈只剩下12个字,所以我们在 printfTask 中把剩余的空间占用测试一下:

在这里插入图片描述

测试结果如下,printfTask 任务栈大小多用了2个字:

在这里插入图片描述

这个剩余栈的大小在视频中定义了一个为0的数组,直接会占用任务使用的栈,我测试并没有出现这种小现象,可能是因为gcc编译器的优化处理。

在这里插入图片描述

任务状态的查看是需要占用一定的的内存空间的,尤其是当任务多了以后,我们这里的使用只是方便调试阶段查找确定问题。

临界区的使用

在我们上面的例子中,在调试中使用到printf的时候都加了任务挂起和任务恢复osThreadSuspendAll()osThreadResumeAll(),除了这种操作,更加建议的操作是使用临界区。合理使用了临界区也会使得程序减少很多不必要的bug。

临界区在前面文章我们已经提到过很多FreeRTOS的临界区屏蔽中断使用的是basepri寄存器,那么什么情况下使用临界区呢:

  • 用户不想被打断的代码
  • 调用公共函数的代码(不可重入函数)
  • 读取或者修改变量(全局变量)
  • 对时序有精准要求的操作
  • 使用硬件资源(比如I2C通讯,但是得注意在通讯中不能使用利用了systick的延时函数)

临界区API介绍

临界区的相关API如下:

API名称API说明
taskENTER_CRITICAL进入临界段,内部调用taskDISABLE_INTERRUPTS
taskEXIT_CRITICAL退出临界段,内部调用taskENABLE_INTERRUPTS
taskENTER_CRITICAL_FROM_ISR进入临界段(在中断中使用)
taskEXIT_CRITICAL_FROM_ISR(x)退出临界段(在中断中使用)
taskENABLE_INTERRUPTS开启中断
taskDISABLE_INTERRUPTS关闭受FreeRTOS管理的中断

x:上次中断屏蔽寄存器操作值

#define taskENTER_CRITICAL()		portENTER_CRITICAL()
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()/*** task. h** Macro to mark the end of a critical code region.  Preemptive context* switches cannot occur when in a critical region.** NOTE: This may alter the stack (depending on the portable implementation)* so must be used with care!** \defgroup taskEXIT_CRITICAL taskEXIT_CRITICAL* \ingroup SchedulerControl*/
#define taskEXIT_CRITICAL()			portEXIT_CRITICAL()
#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
#define portSET_INTERRUPT_MASK_FROM_ISR()		ulPortRaiseBASEPRI()
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)	vPortSetBASEPRI(x)
#define portDISABLE_INTERRUPTS()				vPortRaiseBASEPRI()
#define portENABLE_INTERRUPTS()					vPortSetBASEPRI(0)
#define portENTER_CRITICAL()					vPortEnterCritical()
#define portEXIT_CRITICAL()						vPortExitCritical()

taskENTER_CRITICAL

taskENTER_CRITICAL 最终还是调用了vPortRaiseBASEPRI函数实现屏蔽中断的操作:

/*----------------------------------------------*/
...
#define portENTER_CRITICAL()					vPortEnterCritical()
/*----------------------------------------------*/
...
/*----------------------------------------------*/
void vPortEnterCritical( void )
{portDISABLE_INTERRUPTS();uxCriticalNesting++;/* This is not the interrupt safe version of the enter critical function soassert() if it is being called from an interrupt context.  Only APIfunctions that end in "FromISR" can be used in an interrupt.  Only assert ifthe critical nesting count is 1 to protect against recursive calls if theassert function also uses a critical section. */if( uxCriticalNesting == 1 ){configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );}
}
/*----------------------------------------------*/
...
/*----------------------------------------------*/
#define portDISABLE_INTERRUPTS()				vPortRaiseBASEPRI()
/*----------------------------------------------*/
...
/*----------------------------------------------*/portFORCE_INLINE static void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI;__asm volatile("	mov %0, %1												\n" \"	msr basepri, %0											\n" \"	isb														\n" \"	dsb														\n" \:"=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory");
}

taskEXIT_CRITICAL

taskEXIT_CRITICAL最终还是调用了vPortSetBASEPRI函数实现使能中断的操作:

/*----------------------------------------------*/
...
#define portEXIT_CRITICAL()						vPortExitCritical()
/*----------------------------------------------*/
...
/*----------------------------------------------*/
void vPortExitCritical( void )
{configASSERT( uxCriticalNesting );uxCriticalNesting--;if( uxCriticalNesting == 0 ){portENABLE_INTERRUPTS();}
}
/*----------------------------------------------*/
...
/*----------------------------------------------*/
#define portENABLE_INTERRUPTS()					vPortSetBASEPRI(0)
/*----------------------------------------------*/
...
/*----------------------------------------------*/portFORCE_INLINE static void vPortSetBASEPRI( uint32_t ulNewMaskValue )
{__asm volatile("	msr basepri, %0	" :: "r" ( ulNewMaskValue ) : "memory");
}

taskENTER_CRITICAL_FROM_ISR

taskENTER_CRITICAL_FROM_ISR最终调用了ulPortRaiseBASEPRI函数实现屏蔽中断的操作:

/*----------------------------------------------*/
...
#define portSET_INTERRUPT_MASK_FROM_ISR()		ulPortRaiseBASEPRI()
/*----------------------------------------------*/
...
/*----------------------------------------------*/
portFORCE_INLINE static uint32_t ulPortRaiseBASEPRI( void )
{
uint32_t ulOriginalBASEPRI, ulNewBASEPRI;__asm volatile("	mrs %0, basepri											\n" \"	mov %1, %2												\n" \"	msr basepri, %1											\n" \"	isb														\n" \"	dsb														\n" \:"=r" (ulOriginalBASEPRI), "=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory");/* This return will not be reached but is necessary to prevent compilerwarnings. */return ulOriginalBASEPRI;
}
/*-----------------------------------------------------------*/

taskEXIT_CRITICAL_FROM_ISR( x )

taskENTER_CRITICAL_FROM_ISRtaskEXIT_CRITICAL一样,调用了vPortSetBASEPRI函数实现使能中断的操作,只不过他多了一个参数,参数为 上次中断屏蔽寄存器操作值:

/*----------------------------------------------*/
...
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)	vPortSetBASEPRI(x)
/*----------------------------------------------*/
...
/*----------------------------------------------*/
portFORCE_INLINE static void vPortSetBASEPRI( uint32_t ulNewMaskValue )
{__asm volatile("	msr basepri, %0	" :: "r" ( ulNewMaskValue ) : "memory");
}
/*-----------------------------------------------------------*/

知道这些以后,所以在任务使用printf的前后改成(中断中是不建议使用printf的):

	taskENTER_CRITICAL();printf("\r\n%4.2f C\r\n%4.2f%%\r\n",T,H);taskEXIT_CRITICAL();

任务挂起与临界区区别

在我们前面使用过vTaskSuspendAll() xTaskResumeAll()函数。

taskENTER_CRITICALvTaskSuspendAll()的区别在于:

taskENTER_CRITICALtaskENABLE_INTERRUPTS不仅不能切换任务,还不能响应中断(不能响应可屏蔽中断),而
vTaskSuspendAll() 他们只是不能进行任务切换,但是没有屏蔽中断。

taskENTER_CRITICALtaskENABLE_INTERRUPTS的区别在于:

使用taskDISABLE_INTERRUPTS()taskENABLE_INTERRUPTS()不支持嵌套(比如程序中前面调用了2个taskDISABLE_INTERRUPTS() ,后面使用一个taskENABLE也可以打开中断)。
使用taskENTER_CRITICAL() taskEXIT_CRITICAL()支持嵌套,调用了几个taskENTER_CRITICAL() 就得调用几个 taskEXIT_CRITICAL()才能使能中断。

在使用中发现问题,解决问题,思考问题!


http://chatgpt.dhexx.cn/article/EwdkFgVp.shtml

相关文章

1-FreeRTOS入门指南

本专栏是根据官方提供的文档进行FreeRTOS的各个功能函数的说明,以及函数的使用 本专栏不涉及动手操作,只是对原理进行说明,FreeRTOS基础知识篇更新完成会对如何在开发板上进行上手实战操作。 这里不会对比其他RTOS的优缺点,因为每…

freeRTOS调度

freeRTOS调度策略 背景 嵌入式的设备开发通常有两种模式:裸机开发和OS开发。像功能较为强大的SOC通常都会先一直linux或其他OS然后进行业务逻辑的开发;而单片机则有很多简单的应用场景直接使用裸机开发的模式,稍微复杂些的场景,会…

FreeRTOS 解析

目录 Task Task State Task Priority Idle Task Run Time Statistics Task Scheduling Single-core 单核处理器 AMP 非对称多核处理器 SMP 对称多核处理器 Context Switch Inter-task Communication and Synchronization Queue 队列 Binary Semaphore 二值信号量 …

FreeRTOS队列

在实际的应用中,常常会遇到一个任务或者中断服务需要和另外一个任务进行“沟通交流”,这个“沟通交流”的过程其实就是消息传递的过程。在没有操作系统的时候两个应用程序进行消息传递一般使用全局变量的方式,但是如果在使用操作系统的应用中…

FreeRTOS任务状态

1. 任务状态介绍 FreeRTOS中的任务状态,可以简单的分为运行态(running)和非运行态(not running)。 但是对于非运行态我们还可以继续细分: 阻塞状态(Blocked)暂停(挂起…

FreeRTOS延时

1、相对延时函数 将当前任务添加到阻塞列表pxDelayedTaskList,任务进入阻塞态。 vTaskDelay → prvAddCurrentTaskToDelayedList → vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) )。任务的阻塞时间更新到状态列表项xStateListItem x…

FreeRTOS多任务管理

文章目录 1、任务1.1 任务简介1.2 任务调度1.3 任务的状态 (就绪态 / 运行态 / 阻塞态 / 挂起态)1.4 空闲任务 2、动态创建两个任务2.1 定义动态内存空间的堆2.2 定义任务函数2.3 定义 任务控制块 指针2.4 动态创建任务 xTaskCreate()2.5 启动任务 vTaskStartScheduler() 3、常…

FreeRTOS 移植

源码 FreeRTOS源码 选择 FreeRTOS 的最新版本 V9.0.0(2016 年)比较稳定 1)解压后的文件 提取的文件, FreeRTOS—>Source->portable目录下的MemMang,RVDS MemMang 存放内存相关的源文件,heap_1.c,heap_2.c,he…

初识FreeRTos

初识FreeRTos Preface一、FreeRTos简介二、 为什么选择FreeRTos三、FreeRTos资料和源码的下载四、介绍我自己的开发环境4.1FreeRTos4.2AlienTek Summary Preface 距离我上一次写博客已经差不多有5个月了,最近想给自己挖个新坑,讲一讲关于FreeRTos 相关的…

FreeRTOS移植

1. 前言 本文是基于FreeRTOSv9.0.0版本的实时系统,移植到STM32F103芯片平台上。移植环境使用的是MDK5.32版本,我在移植之前就已经构建好了一个裸机工程源码了,而且已经确保了这份裸机代码是没有问题的。开始移植之前我强烈建议一定要确认自己…

FreeRTOS快速入门-初探FreeRTOS

首发,公众号【一起学嵌入式】 对于 RTOS 入门系列文章,已经更新完一款(RT-Thread): 助你快速入门 RT-Thread 这个系列的文章结合 RT-Thread,介绍过 RTOS 相关的核心知识。 接下来,开始另外一…

FreeRTOS系列|FreeRTOS简介

1. RTOS简介 RTOS全称为 Real Time Operation System,即实时操作系统。RTOS强调的是实时性,又分为硬实时和软实时。硬实时要求在规定的时间内必须完成操作,不允许超时;而软实时里对处理过程超时的要求则没有很严格。RTOS的核心就…

freertos任务基础知识(freertos篇)

多任务 对于单任务系统就是常说裸机大while循环,有的时候也需要加一些中断服务函数完成一些处理,相比于多任务而言上面的单任务系统也叫做前后台系统,即(中断服务函数叫前台程序,大while叫后台程序) 前后台…

FreeRTOS学习,适用于FreeRTOS初学者,FreeRTOS整体知识框架

目录 一、为什么要学习FreeRTOS 二、前言------从0到1认识FreeRTOS 1、逻辑系统与多任务系统 1.1、裸机系统:裸机系统通常分成轮询系统和前后台系统 1.2、多任务系统 2、数据结构-列表与列表项 三、任务 3.1基本属性 3.1.1任务栈 3.1.2任务控制块 3.1.3任…

一、初识FreeRTOS之FreeRTOS简介

目录 一、什么是FreeRTOS? 二、为什么选择FreeRTOS? 三、FreeRTOS的特点 四、FreeRTOS资料与源码下载 五、FreeRTOS源码文件介绍 一、什么是FreeRTOS? Free即免费的,RTOS的全称是Real time operating system,中文就是实时操作…

Docker技术入门与实战-第3版.pdf OCR 高清 可复制

Docker技术入门与实战-第3版.pdf OCR 高清 可复制 Docker技术入门与实战-第3版.pdf OCR 高清 可复制简介预览下载地址 Docker技术入门与实战-第3版.pdf OCR 高清 可复制 简介 本书从Docker基本原理开始,深入浅出地讲解Docker的构建与操作,内容系统全面…

从docker到kubernetes(k8s)入门与实战--docker教程

安装docker 装个依赖 yum -y install gcc gcc-c 卸载旧版本 yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-selinux \ docker-engine-selinux \ docker-engine 安装依…

Docker 入门到实战教程(二)安装Docker

一.卸载旧版本 较旧的 Docker 版本称为 docker 或 docker-engine 。如果已安装这些程序,请卸载它们以及相关的依赖项。 $ sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate…

Docker入门及进阶教程

阅读声明: 该教程笔记来源于dockers——菜鸟教程 一、Docker介绍 1. Docker教程 Docker是一个开源的容器引擎,基于Go语言并遵从Apache2.0协议开源。 Docker可以让开发者打包他们的应用以及依赖包到一个轻量级,可移植的容器,然后发…

Docker 实战

目录 一、常用操作 1、镜像创建 2、镜像查询 3、镜像删除 4、镜像运行 5、查询容器进程 6、查询容器执行日志 7、删除容器 8、进入容器 二、导入和导出 1、save 和 load 2、export 和 import 3、区别 三、案例 1、检查是否安装依赖 2、安装依赖 3、docker打包镜…