FreeRTOS学习笔记—②RTOS的认识及任务管理篇
由于正在学习韦东山老师的RTOS课程,结合了网上的一些资料,整理记录了下自己的感悟,用于以后自己的回顾。如有不对的地方请各位大佬纠正。
文章目录
- 一、RTOS的优势
- 二、RTOS的核心功能
- 2.1 任务管理
- 2.1.1 任务的创建
- 2.1.2 任务的删除*
- 2.1.3 任务优先级和Tick
- 一、优先级
- 二、Tick(滴答)
- 三、优先级的实验
- 三、优先级设定的实验
- 2.1.4 任务状态
- 一、阻塞状态(Blocked)
- 二、就绪状态(Ready)
- 三、暂停状态(Suspended)
- 四、完整的状态转移图
- 2.1.5 相对延时和绝对延时
- 一、相对延时函数
- 二、绝对延时函数
- 三、延时实验
- 2.1.6 空闲任务及钩子函数
- 一、钩子函数
- 2.1.7 调度算法
- 一、调度算法的配置
一、RTOS的优势
①:确定性和实时性:
RTOS的最大特点是能够在严格的时间约束内完成任务。这种确定性对于时间敏感的应用(如工业控制、医疗设备等)至关重要。
②:优先级调度:
RTOS通常支持优先级调度机制,确保高优先级的任务可以抢占低优先级的任务执行。这种机制保证了关键任务能够在最短时间内得到处理。
③:低延迟和高响应性:
RTOS设计的目标是最小化任务切换时间和中断延迟,从而实现高响应性。这在需要快速反应的嵌入式系统中非常重要。
④:资源管理和内存控制:
RTOS通常提供精细的资源管理工具,允许开发者更好地控制内存和CPU资源的使用。这种控制对于嵌入式系统中的资源有限环境尤其重要。
⑤:模块化和灵活性:
RTOS通常具有模块化设计,允许开发者根据具体需求启用或禁用特定的功能模块。这种灵活性有助于优化系统性能和减少系统开销。
⑥:可靠性和稳定性:
RTOS被广泛应用于需要高可靠性和稳定性的系统中,例如自动驾驶、军事系统等。RTOS通过严格的测试和验证,确保其在各种边界情况下都能稳定运行。
⑦:较小的内存占用:
RTOS通常占用的内存和资源较少,这使得它非常适合嵌入式系统或其他资源受限的环境。
二、RTOS的核心功能
RTOS的核心功能块主要分为任务管理、内核管理、时间管理以及通信管理4部分,框架图如下所示:
(1)任务管理:负责管理和调度任务的执行,确保系统中的任务能够按照预期运行。
(2)内核管理:负责系统核心功能的管理,包括内存、中断、异常处理和系统启动等。
(3)时间管理:负责所有与时间相关的操作,包括系统时钟、定时器、任务延迟和周期性任务的执行。
(4)通信管理:提供任务之间的通信机制,确保任务能够有效地协作和共享资源。
2.1 任务管理
2.1.1 任务的创建
任务就是一个无返回的函数(Void)。由于函数传参的不同,一个函数可以创建多个任务,然后每个任务都有对应自身的栈,也就是说一个函数可以有多个栈(当然一个函数对应一个栈也是可以的)。使用下面的函数用于创建任务:
void TaskAFunction(void *param)
{int* tmp = (int*) param;//首先将void *指针类型的param转为int *类型的指针 int value = *tmp; //然后解引用来获取指针指向的值while(1){printf("%d",value);}
}
尽管是同一个函数,但是创建的多个任务主要不同还是在于传参而不是名字,下面的代码使用了相同的名字(“TaskA”)创建了三个参数不同的任务。
int x1=1;int x2=2;int x3=3;
int main( void )
{TaskHandle_t xHandleTask1;#ifdef DEBUGdebug();
#endifprvSetupHardware();printf("Hello, world!\r\n");xTaskCreate(TaskAFunction,"TaskA",100,&x1,1,NULL);xTaskCreate(TaskAFunction,"TaskA",100,&x2,1,NULL);xTaskCreate(TaskAFunction,"TaskA",100,&x3,1,NULL);/* Start the scheduler. */vTaskStartScheduler();/* Will only get here if there was not enough heap space to create theidle task. */return 0;
}
2.1.1 xTaskCreate
上面使用的xTaskCreate是动态创建任务的,当然还有静态创建任务的函数xTaskCreateStatic,后面再提静态创建。下图为xTaskCreate函数的参数及介绍:
下图摘自韦东山的FreeRTOS完全开发手册3.2.2节
2.1.2 任务的删除*
任务的删除使用如下函数,其中填入的参数如果是NULL表示自杀,如果是自己的句柄则是被杀,别人的句柄就是杀人。
void vTaskDelete( TaskHandle_t xTaskToDelete );
实验是在vTask1任务中嵌套vTask2任务的创建,而vTask2任务中执行删除自身任务的操作,而相对延时函数vTaskDelay( xDelay100ms );
在Task1中的存在与否会有影响么呢?
代码如下所示,肯定先创建并运行Task1,执行完自身的printf后,创建并优先调用Task2,Task2也会printf自身信息并删除自己的任务(内存还未释放)。此时就要注意了vTaskDelay
函数会起了一个很重要的作用。因为vTaskDelay
的存在使得Task1进入了阻塞状态,此时没有其他任务(Task2也被删除啦)需要执行,导致系统会执行优先级最低的IDLE任务,这个任务会把Task2所占用栈的内存给释放。代码如下
TaskHandle_t xTask2Handle = NULL;
int main( void )
{...xTaskCreate(vTask1, "Task 1", 1000, NULL, 1, NULL);...
}
void vTask1( void *pvParameters )
{const TickType_t xDelay100ms = pdMS_TO_TICKS( 100UL );//100ms的延时BaseType_t ret;/* 任务函数的主体一般都是无限循环 */for( ;; ){/* 打印任务1的信息 */printf("Task1 is running\r\n");ret = xTaskCreate( vTask2, "Task 2", 1000, NULL, 2, &xTask2Handle );if (ret != pdPASS)//判断vTask2是否创建成功,一般pdPASS默认为1printf("Create Task2 Failed\r\n");vTaskDelay( xDelay100ms );}
}void vTask2( void *pvParameters )
{/* 打印任务的信息 */printf("Task2 is running and about to delete itself\r\n");// 可以直接传入参数NULL,进行“自杀”vTaskDelete(xTask2Handle);
}
实验结果如下,Task1带有相对延时函数后,能够正常释放被删除的Task2所占用的内存空间,所以能够如下打印:
通过上文我们知道vTaskDelay
函数会起一个很重要的作用。此刻若是删除这个函数的话,Task1自然不会进入阻塞状态而系统更没机会调用IDLE任务,多次被删除的Task2任务所占用的内存一直无法释放而导致最后内存的耗尽,结果如下。
2.1.3 任务优先级和Tick
一、优先级
优先级在上文中提过,优先级的值大的优先执行,相同优先级的则交替执行,这个函数xTaskCreate(vTask1, "Task 1", 1000, NULL, 1, NULL);
的第5个参数则是表示优先级。
如何找到优先级最高的任务,RTOS的调度器会根据configMAX_PRIORITIES
的值来判断采用C函数还是汇编指令的方法来实现调度。
二、Tick(滴答)
函数vTaskDelay
可以用于指定任务休眠的时间,一般有以下两种表示方式:
方式一:vTaskDelay(5)
【存在延时不准的问题】
该方式直接设置5个Tick,根据下面公式可以算出时间T为:
T=(1/configTICK_RATE_HZ)*5=0.05s=50ms
方式二:vTaskDelay(pdMS_TO_TICKS(50UL))
【存在延时不准的问题】
该方式采用pdMS_TO_TICKS宏直接将ms转换为tick,上式表示为等待50ms。
三、优先级的实验
参考韦东山FreeRTOS手册,创建了3个任务,其中Task1和Task2的优先级为1,Task3的优先级为2。我们知道Task3任务优先级明显高于Task1和Task2的,但是如果不对Task3进行进行vTaskDelay的话,高优先级的会一直占用CPU,那么Task1和Task2的则不会有机会执行(就像备胎一样,一直在当女神的备胎,但是在女神眼里就是没正主优先级高,备胎就算等着舔不到女神,说明不要当舔狗,不过这也对应了任务的阻塞状态)。Task1~3的代码和main代码如下:
xTaskCreate(vTask1,"Task1",1000,NULL,1,NULL);
xTaskCreate(vTask2,"Task2",1000,NULL,1,NULL);
xTaskCreate(vTask3,"Task3",1000,NULL,2,NULL);void vTask1( void *pvParameters )
{/* 任务函数的主体一般都是无限循环 */for( ;; ){/* 打印任务1的信息 */printf("T1\r\n");}
}
void vTask2( void *pvParameters )
{/* 任务函数的主体一般都是无限循环 */for( ;; ){/* 打印任务2的信息 */printf("T2\r\n");}
}
void vTask3( void *pvParameters )
{const TickType_t xDelay3000ms=pdMS_TO_TICKS(1000UL);/* 任务函数的主体一般都是无限循环 */for( ;; ){/* 打印任务3的信息 */printf("T3\r\n");
// vTaskDelay(xDelay3000ms);}
结果如下,只执行了Task3
当解开vTask3
函数中vTaskDelay(xDelay3000ms);代码的注释后,结果如下。Task3只执行1次后就不执行了,后面是Task1和Task2两个优先级为1的相互执行。那是因为Task3执行到vTaskDelay这个函数后会进入休眠状态,尽管优先级高于Task1和2,但是休眠状态不占用CPU资源,于是让给了两个优先级相同的Task1和Task2,而Task3休眠结束后,Task1和Task2没有休眠机制于是疯狂不断运行从而导致Task3的打印只出现了一次。
三、优先级设定的实验
本实验主要是通过vTaskPrioritySet
函数实现对任务优先级的设定。该函数具体如下:
void vTaskPrioritySet(TaskHandle_t xTask, UBaseType_t uxNewPriority);
其中第一个参数是也就是对应Task的handle,即每个任务在xTaskCreate
创建任务时所传入的第6个参数xTask2Handle。而第二个参数uxNewPriority是通过函数uxTaskPriorityGet
进行获取。
完整的实验如下,创建Task1和Task2。在Task1中print,并提高Task2的任务优先级来保证高于Task1。在Task2中同样print自己内容,并降低Task2的任务优先级来保证低于Task1。这样很明显两者通过调整任务优先级来实现一个来回执行的效果,代码如下:
void vTask1( void *pvParameters )
{UBaseType_t uxPriority;//获取Task1的优先级,其中NULL表示获取自身的优先级uxPriority = uxTaskPriorityGet(NULL);/* 任务函数的主体一般都是无限循环 */for( ;; ){/* 打印任务1的信息 */printf("Task1 is runing\r\n");printf("About to raise the Task 2 priority\r\n");/*通过使用vTask1的优先级再+1,来保证vTask2具有更高的优先级,*/vTaskPrioritySet( xTask2Handle, ( uxPriority + 1 ) );}
}void vTask2( void *pvParameters )
{UBaseType_t uxPriority;//获取Task2的优先级,其中NULL表示获取自身的优先级uxPriority = uxTaskPriorityGet(NULL);/* 任务函数的主体一般都是无限循环 */for( ;; ){/* 打印任务2的信息 */printf("Task2 is runing\r\n");printf("About to lower the Task 2 priority\r\n");/*通过使用vTask2的优先级再-2,来保证vTask1具有更低的优先级,*/vTaskPrioritySet(NULL,(uxPriority - 2));}
}
经代码验证,Task1与Task2的效果如下:
2.1.4 任务状态
任务一般可以分为运行(Runing)和非运行(不 Runing)两类。但是非运行的状态还能分成:①阻塞状态;②暂停状态;③就绪状态。
一、阻塞状态(Blocked)
阻塞状态,指的是任务因为等待某个事件或条件发生而无法继续执行的状态。如(1)相对/绝对延时函数这类时间等待;(2)队列或信号量等待;(3)事件标志等待。等等。这个状态下任务不会占用CPU资源,一旦满足某个事件的条件,就能转为就绪状态了。
二、就绪状态(Ready)
就绪状态,即随时准备响应调度器的号召,可以由阻塞状态转换而成。就像女神会择优选择好的备胎来处一样,调度器也会选择优先级最高且就绪(Ready)的任务来运行。
优先级最高好理解,就绪状态是怎么由阻塞状态转过来的呢?这个就涉及到了事件的概念,时间一般包含两类:(1)时间相关事件;(2)同步事件。/* 同步事件的具体概念后面学习内容会涉及 */
(1)时间相关事件:即设定一定的时间,这个时间内会处于阻塞状态,时间满足了就会转成就绪状态,就像延时函数vTaskDelay
一样,能够用来实现周期性/超时功能。
(2)同步事件:某个任务在等待别的任务或者中断服务程序发来的信息来唤醒它。这些同步方式包括:①任务通知;②队列;③事件组;④信号量(semaphoe);⑤互斥量(mutex);等
三、暂停状态(Suspended)
&emsp暂停状态一般很少用,唯一使用的方法就是通过void vTaskSuspend( TaskHandle_t xTaskToSuspend );
来使用。
四、完整的状态转移图
2.1.5 相对延时和绝对延时
FreeRTOS中两个延时函数分别是相对延时函数vTaskDelay()
和绝对延时函数vTaskDelayUntil()
,尽管两个函数都能使任务进入堵塞状态,但是由于延时方式的差异也会导致应用也有所不同。
一、相对延时函数
相对延时函数vTaskDelay()
的开始时间是从任务中执行到这个函数开始计算的,上面提到过这个函数的时间并不准确,是因为容易受到其他任务和中断活动的影响导致的。以当前任务遇到更高优先级的任务为例,当前任务执行到这个相对延时函数后会进入阻塞状态,系统会调度其他任务运行。如果有更高的优先级任务处于就绪状态,那么调度器会优先运行高优先级任务。当高优先级任务占用了CPU资源后,当前这个调用了vTaskDelay
函数的低优先级任务则需等待高优先级任务结束或者进入阻塞状态,才能再次运行,这也会导致延迟时间的不准确。此外遇到中断处理时间较长或者频繁发生导致占用过多的CPU时间,也会导致原计划中任务被推迟,在中断结束后调度器才会重新调度任务,因此vTaskDelay
延迟时间可能会比预期要长。
void vTaskDelay(TickType_t xTicksToDelay);
二、绝对延时函数
绝对延时函数vTaskDelayUntil()
的开始时间。如下所示,参数pxPreviousWakeTime用于存储上次任务唤醒的时刻;而参数xTimeIncrement用于表示每次任务被唤醒后所要延时的时间。正是由于有存储上轮任务唤醒时刻的机制,这个绝对延时函数更适合用于实现周期性的延时操作。
void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )
三、延时实验
相对延时的实验结果如下图所示,flag1为1表示Task1任务运行中,flag1为0表示Task2运行中(Task1处于堵塞状态)。相对延时函数的开始时间是从调用vTaskDelay
这个函数开始(即flag1从1跳变到0时)计算的50ms。
绝对延时的实验结果如下图所示,flag1为1表示Task1任务运行中,flag1为0表示Task2运行中(Task1处于堵塞状态)。绝对延时函数的开始时间是从Task1记录的上轮任务调用时间开始计算的的50ms。
具体实验代码如下所示:
void vTask1( void *pvParameters )
{const TickType_t xDelay50ms = pdMS_TO_TICKS(50UL);TickType_t xLastWakeTime;int i;//获取获取当前的Tick CountxLastWakeTime = xTaskGetTickCount();/* 任务函数的主体一般都是无限循环 */for( ;; ){flag=1;for(i=0;i<5;i++){printf("Task 1 is running\r\n");}
#if 1vTaskDelay(xDelay50ms);
#elsevTaskDelayUntil(&xLastWakeTime,xDelay50ms);
#endif}
}void vTask2( void *pvParameters )
{for( ;; ){flag=0;printf("Task 2 is running\r\n");}
}
2.1.6 空闲任务及钩子函数
空闲任务也就是IDLE任务,在本文的 “ 2.1.2 任务的删除 ”这个实验例子中有体现。在任务的删除中一般离不开IDLE任务,可以回看下,我个人感觉还是写的比较清晰的。
IDLE任务的比较特殊,永远不会堵塞,优先级为0。一般在系统没有任务或任务处于堵塞状态下,IDLE任务会被调出来。
一、钩子函数
空闲任务的钩子函数是FreeRTOS提供的一种机制,允许用户在系统进入空闲任务时执行一些特定的操作。可以通过定义一个空闲任务钩子函数(vApplicationIdleHook())来扩展 IDLE 任务的功能,比如在系统空闲时进入低功耗模式、执行后台任务等,具体作用如下:
- 执行一些低优先级的、后台的、需要连续执行的函数
- 测量系统的空闲时间:空闲任务能被执行就意味着所有的高优先级任务都停止了,所以测量空闲任务占据的时间,就可以算出处理器占用率。
- 让系统进入省电模式:空闲任务能被执行就意味着没有重要的事情要做,当然可以进入省电模式了。
2.1.7 调度算法
一、调度算法的配置
调度算法不仅要保证高优先级的任务先运行,还要确保同优先级的就绪态任务以“轮转调度”的策略来轮流执行。当然轮流调度存在的不保证任务运行时间的公平分配,因此可以细化运行时间的分配。
从3个角度理解多种调度算法:
(1)可否抢占?高优先级的任务能否优先执行(配置项: configUSE_PREEMPTION)
√: 可以:被称作"可抢占调度"(Pre-emptive),高优先级的就绪任务马上执行,下面再细化。
×: 不可以:不能抢就只能协商了,被称作"合作调度模式"(Co-operative Scheduling)
①:当前任务执行时,更高优先级的任务就绪了也不能马上运行,只能等待当前任务主动让出CPU资源。
②:其他同优先级的任务也只能等待:更高优先级的任务都不能抢占,平级的更应该老实点
(2)可抢占的前提下,同优先级的任务是否轮流执行(配置项:configUSE_TIME_SLICING)
√: 轮流执行:被称为"时间片轮转"(Time Slicing),同优先级的任务轮流执行,你执行一个时间片、我再执行一个时间片
×: 不轮流执行:英文为"without Time Slicing",当前任务会一直执行,直到主动放弃、或者被高优先级任务抢占
(3)在"可抢占"+"时间片轮转"的前提下,进一步细化:空闲任务是否让步于用户任务(配置项:configIDLE_SHOULD_YIELD)
√: 空闲任务低人一等,每执行一次循环,就看看是否主动让位给用户任务
×: 空闲任务跟用户任务一样,大家轮流执行,没有谁更特殊
下表用于配置调度算法,一共包含三个配置项,分别是(1)用于可抢占调度的配置项configUSE_PREEMPTION;(2)用于时间片轮转的配置项configUSE_TIME_SLICING;(3)用于关闭Tick中断来实现省电的配置项onfigUSE_TICKLESS_IDLE。
(1)配置项configUSE_PREEMPTION的影响
实验共有两个优先级为0的Task1和Task2,一个优先级为2的Task3。每个任务都有自己对应的flag(1表示该任务运行中),若系统所有任务都未执行则将IDLE任务的标志位置1。改变FreeRTOSConfig.c中配置项configUSE_PREEMPTION的值来判断影响:
此时FreeRTOSConfig.c里面配置项USE_PREEMPTION为1表示高优先级抢占
int main(void)
{......xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);xTaskCreate(vTask2, "Task 2", 1000, NULL, 0, NULL);xTaskCreate(vTask3, "Task 3", 1000, NULL, 2, NULL);......
}
void vTask1( void *pvParameters )
{for( ;; ){flagIdleTaskrun = 0;flagTask1run = 1;flagTask2run = 0;flagTask3run = 0;/* 打印任务的信息 */printf("T1\r\n"); }
}
void vTask2( void *pvParameters )
{ for( ;; ){flagIdleTaskrun = 0;flagTask1run = 0;flagTask2run = 1;flagTask3run = 0;/* 打印任务的信息 */printf("T2\r\n"); }
}
void vTask3( void *pvParameters )
{ const TickType_t xDelay5ms = pdMS_TO_TICKS( 5UL ); for( ;; ){flagIdleTaskrun = 0;flagTask1run = 0;flagTask2run = 0;flagTask3run = 1;/* 打印任务的信息 */printf("T3\r\n"); // 如果不休眠的话, 其他任务无法得到执行vTaskDelay( xDelay5ms );}
}
void vApplicationIdleHook(void)//空闲状态下的钩子函数,在task.c里面掉用
{flagIdleTaskrun = 1;flagTask1run = 0;flagTask2run = 0;flagTask3run = 0; /* 故意加入打印让flagIdleTaskrun变为1的时间维持长一点 */printf("Id\r\n");
}#if ( configUSE_IDLE_HOOK == 1 )//钩子函数的调用
{extern void vApplicationIdleHook( void );/* Call the user defined function from within the idle task. This* allows the application designer to add background functionality* without the overhead of a separate task.* NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES,* CALL A FUNCTION THAT MIGHT BLOCK. */vApplicationIdleHook();
}
实验结果如下所示,Task3优先级最高,优先执行了该任务,并在红线1的时刻调用延时函数进入了阻塞状态。此时Task1和Task2两个同优先级的任务开始交叉执行。等到Task3延时结束后由于优先级最高则会立马抢占重新开始Task3任务的执行。而在这三个任务都不执行的时候,系统则会执行IDLE状态(对应红线)。
相应的如果FreeRTOSConfig.c中配置项configUSE_PREEMPTION的值为0表示不抢占的话,结果如下所示。可以看到在红线前半部分正常,当Task3因为延时进入阻塞状态后,开始就混乱了。没有抢占更没有协商好,即使Task3延时超时后,优先级更高的它也没机会执行了。
(2)配置项configUSE_TIME_SLICING的影响
实验代码如上,只不过这里是对时间片是否轮转来判断影响的。因此这里只改变configUSE_TIME_SLICING的值,另外两个配置项都为1。
下图为时间片轮转,即配置项configUSE_TIME_SLICING值为1。
下图为时间片不轮转,即配置项configUSE_TIME_SLICING值为0。不同于时间片轮转会在高优先级任务Task3阻塞的时候(flag3为0的时候)轮流执行相同优先级的Task1和Task2。时间片不轮转的情况下,在高优先级任务阻塞时只引起了一个任务的执行(Task1/Task2)。而只有高优先级任务就绪或者不再运行时才会引起任务的切换。
(3)配置项configIDLE_SHOULD_YIELD的影响
实验代码如上,只不过这里是对空闲任务是否让步来进行。因此这里只改变configIDLE_SHOULD_YIELD的值,另外两个配置项都为1。
下图为空闲任务让步,即配置项configIDLE_SHOULD_YIELD值为1。
下图为空闲任务不让步,即配置项configIDLE_SHOULD_YIELD值为0。可以看到配置为空闲任务为不让步后,三者的优先级是相同的。在高优先级任务阻塞的时候,Task1、Task2以及IDLE任务都是相同优先级,因此他们会采用轮流执行。
相关文章:

FreeRTOS学习笔记—②RTOS的认识及任务管理篇
由于正在学习韦东山老师的RTOS课程,结合了网上的一些资料,整理记录了下自己的感悟,用于以后自己的回顾。如有不对的地方请各位大佬纠正。 文章目录 一、RTOS的优势二、RTOS的核心功能2.1 任务管理2.1.1 任务的创建2.1.2 任务的删除*2.1.3 任…...

【C++从练气到飞升】22---C++中的异常
🎈个人主页:库库的里昂 ✨收录专栏:C从练气到飞升 🎉鸟欲高飞先振翅,人求上进先读书🎉 目录 ⛳️推荐 一、C语言传统的处理错误的方式 二、C异常 三、异常的使用 3.1 异常的抛出和捕获 3.1.1 异常的抛…...

前端:HTML、CSS、JS、Vue
1 前端 内容概要 了解前端三件套(HTML、CSS、JS)在前端所起的作用掌握HTML标签的功能,掌握重要标签(a标签,form标签)了解CSS了解JS的基础语法掌握Vue的基础语法重点掌握Vue项目怎么启动项目掌握前后端分离是什么。前端做什么事情,后端做什么…...

RocksDB简介
一、RocksDB是什么 常见的数据库如 Redis Mysql Mongo 可以单独提供网络服务RocksDB提供存储服务,是一个嵌入式KV存储引擎 Rocksdb没有server code,用户需要自己实现server的部分来得到c-s架构的数据库。二、RocksDB的诞生 基于flash存储和ssd普及,网络latency在query worklo…...
[VC] Visual Studio中读写权限冲突
前置场景: 编译没有报错,但是运行提示 内存异常: 情景1: 如下代码运行异常,提示引发了异常:写入权限冲突。*** 是 0xFFFFF..... char* str (char*)malloc(10);str[0] 0x30; 解决方案:要包含头…...
ChatGPT3.5/4.0新手使用手册,国内中文版使用教程
引言 欢迎使用ChatGPT!无论你是刚开始接触AI聊天机器人,还是已经有了一些使用经验,这篇新手使用手册将帮助你快速上手,并且从ChatGPT中获得最优的体验。本文主要聚焦于提示词(Prompt)的使用教学࿰…...
基于MicroPython的ESP8266与超声波传感器设计方案
基于MicroPython的ESP8266与超声波传感器的设计方案: 一、硬件准备 1. ESP8266 开发板(如NodeMCU) 2. 超声波传感器(如HC-SR04) 3. 杜邦线若干 二、硬件连接 1. 将超声波传感器的VCC引脚和ESP8266 的3.3V引脚,分别连接5V和3.3V电…...

仿华为车机UI--图标从Workspace拖动到Hotseat同时保留图标在原来位置
基于Android13 Launcher3,原生系统如果把图标从Workspace拖动到Hotseat里则Workspace就没有了,需求是执行拖拽动作后,图标同时保留在原位置。 实现效果如下: 实现思路: 1.如果在workspace中拖动,则保留原来“改变图标…...
C++ 中的 override 和 overload的区别
目录 1.Overload(重载) 2.override(重写) 3.override 和 overload 的根本区别 4.override 和 overload 的实际应用 5.override 和 overload 的常见误区 6.总结 1.Overload(重载) 定义:在同一个作用域内,可以声明几个功能类似的函数名相同的函数&am…...

spring boot3框架@Validated失效
项目中使用的springboot3.2.1,在使用Validated校验controller里参数时始终不生效;在网上查了相关资料,添加了spring-boot-starter-validation依赖但还是不行 经过层层调试,终于发现问题; springboot3添加Validated后校验的是 ja…...

UE5引擎工具链知识点
当我们提到“引擎工具链的开发”时,通常指的是为游戏开发或其他类型的软件开发创建一系列工具和技术栈的过程。这包括但不限于游戏引擎本身(如Unity或Unreal Engine),以及围绕这些引擎构建的各种工具和服务,比如用于构…...

Python的图像算术与逻辑运算详解
一.图像加法运算 图像加法运算主要有两种方法。第一种是调用Numpy库实现,目标图像像素为两张图像的像素之和;第二种是通过OpenCV调用add()函数实现。第二种方法的函数原型如下: dst add(src1, src2[, dst[, mask[, dtype]]]) – src1表示第…...

WSL 下的 CentOS 装 Docker
WSL 下的 CentOS 装 Docker 卸载旧版本安装前的准备工作1. 安装 yum-utils2. 添加阿里云的 yum 镜像仓库3. 快速生成 Yum 缓存 安装Docker启动docker运行 hello-world卸载 Docker 引擎参考资料 卸载旧版本 sudo yum remove docker \ docker-client \ docker-client-latest \ d…...

v0.dev快速开发
探索v0.dev:次世代开发者之利器 今之技艺日新月异,开发者之工具亦随之进步不辍。v0.dev者,新兴之开发者利器也,迅速引起众多开发者之瞩目。本文将引汝探究v0.dev之基本功能与优势,助汝速速上手,提升开发之…...
python之字符串
创建字符串 s "Hello, World!"常用字符串操作 获取字符串长度 length len(s) print(length) # 输出: 13字符串拼接 s1 "Hello" s2 "World" s3 s1 ", " s2 "!" print(s3) # 输出: Hello, World!重复字符串 s …...

算法打卡 Day28(回溯算法)-组合总数 + 组合总数 Ⅱ+ 电话号码的字母组合
文章目录 Leetcode 17-电话号码的字母组合题目描述解题思路 Leetcode 39-组合总数题目描述解题思路 Leetcode 216-组合总数 Ⅲ题目描述解题思路 Leetcode 17-电话号码的字母组合 题目描述 https://leetcode.cn/problems/letter-combinations-of-a-phone-number/description/ …...

【Hadoop|MapReduce篇】MapReduce概述
1. MapReduce定义 MapReduce是一个分布式运算程序的编程框架,是用户开发“基于Hadoop的数据分析应用”的核心框架。 MapReduce核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个Hadoop集群上。 2. Map…...

设置Virtualbox虚拟机共享文件夹
由于工作环境的原因,选择Virtualbox的方式安装虚拟操作系统,常用的操作系统为ubuntu,不知道道友是否也曾遇到这样的问题,就是虚拟机和主机进行文件拖拽的时候,会因为手抖造成拖拽失败,虚拟机界面显示大个的…...
从零开始的机器学习之旅
尊敬的读者们,在这个快速发展的数字时代,机器学习无疑已经成为了科技领域的一颗璀璨明星。它如同一把打开未来之门的钥匙,让我们能够窥探到数据背后的无限可能。今天,我将带领大家开启一段从零开始的机器学习之旅,让我…...

开源还是封闭?人工智能的两难选择
这篇文章于 2024 年 7 月 29 日首次出现在 The New Stack 上。人工智能正处于软件行业的完美风暴中,现在马克扎克伯格 (Mark Zuckerberg) 正在呼吁开源 AI。 关于如何控制 AI 的三个强大观点正在发生碰撞: 1 . 所有 AI 都应该是开…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...

(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里 脚本1 #!/bin/bash #定义变量 ip10.1.1 #循环去ping主机的IP for ((i1;i<10;i)) doping -c1 $ip.$i &>/dev/null[ $? -eq 0 ] &&am…...
【大厂机试题解法笔记】矩阵匹配
题目 从一个 N * M(N ≤ M)的矩阵中选出 N 个数,任意两个数字不能在同一行或同一列,求选出来的 N 个数中第 K 大的数字的最小值是多少。 输入描述 输入矩阵要求:1 ≤ K ≤ N ≤ M ≤ 150 输入格式 N M K N*M矩阵 输…...

篇章一 论坛系统——前置知识
目录 1.软件开发 1.1 软件的生命周期 1.2 面向对象 1.3 CS、BS架构 1.CS架构编辑 2.BS架构 1.4 软件需求 1.需求分类 2.需求获取 1.5 需求分析 1. 工作内容 1.6 面向对象分析 1.OOA的任务 2.统一建模语言UML 3. 用例模型 3.1 用例图的元素 3.2 建立用例模型 …...