基于STM32与FreeRTOS的实时多任务调度实践

news2026/3/13 23:12:34
1. 从裸机到操作系统为什么你的STM32需要FreeRTOS很多刚开始玩STM32的朋友都是从点灯、串口打印这些基础实验入手的。写一个while(1)大循环里面轮询处理各种事件这种“裸机”编程方式简单直接应付简单项目完全没问题。但不知道你有没有遇到过这样的烦恼当你想让LED灯以500ms的频率闪烁同时又要每隔2秒通过串口发送数据还得时不时去读一下传感器的温度——这几个事情如果都塞进一个循环里代码很快就会变得又长又乱而且一个地方的延时比如等待传感器响应会卡住整个系统导致LED闪烁都不流畅了。这就是实时操作系统RTOS要解决的问题。你可以把它想象成一个超级高效的项目经理。在没有项目经理裸机的时候你一个人单片机内核得自己记着所有待办事项并决定先做哪件、后做哪件很容易手忙脚乱。而有了FreeRTOS这位项目经理它允许你把不同的工作拆分成独立的“任务”Task比如让张三专门负责闪灯李四专门负责发串口数据王五专门负责读传感器。项目经理FreeRTOS内核的核心工作就是多任务调度它根据任务的紧急程度优先级决定下一刻该让哪个任务使用CPU。这样从宏观上看多个任务就像在同时运行一样系统响应更及时代码结构也更清晰、更易维护。FreeRTOS就是这样一个为微控制器量身定制的、免费开源的实时操作系统内核。它非常轻量最小内核编译后可能只占6-10KB的ROM和几百字节的RAM对STM32这类资源有限的芯片特别友好。它提供了任务管理、消息队列、信号量、软件定时器等核心机制让我们能用一种更现代、更模块化的方式来构建复杂的嵌入式应用。这次我就带你手把手在STM32上跑起一个真正的多任务程序让你亲身体会一下“任务并行”的爽快感。2. 实战准备搭建你的第一个FreeRTOS多任务工程理论说再多不如动手跑一遍。我强烈建议你跟着我一起操作用代码感受最直接。这里我以市面上非常流行的野火STM32开发板为例它的资料包里通常就有现成的FreeRTOS模板工程这对新手来说能避开很多移植的坑。2.1 找到工程模板并理解核心文件首先在你下载的野火资料包里找到类似“FreeRTOS例程”或“SRAM动态创建多任务”的文件夹。里面会有一个已经配置好的Keil MDK工程。打开它你会看到工程目录里除了熟悉的main.c、stm32fxxx_it.c中断文件外多了一个FreeRTOS的组里面包含了FreeRTOS的核心源码文件比如tasks.c任务调度、queue.c队列、list.c内核列表等。对于初学者我们最需要关注两个配置文件FreeRTOSConfig.h这是FreeRTOS的“总控开关”文件。所有配置项都在这里比如系统时钟频率configTICK_RATE_HZ它决定了系统心跳的节奏通常设为1000即1ms一个tick、可用的最大任务优先级configMAX_PRIORITIES、是否启用队列或信号量等。第一次使用我们可以先保持默认配置。main.c这是我们编写应用代码的主战场。打开模板工程里的main.c你会发现它已经包含了一个多任务的框架。我们的工作就是在理解这个框架的基础上修改它来实现我们自己的三个任务。2.2 剖析多任务程序的骨架模板的main.c虽然看起来代码不少但结构非常清晰。我帮你拆解一下第一部分头文件和句柄声明#include FreeRTOS.h #include task.h #include bsp_led.h #include bsp_usart.h // 任务句柄类似于任务的身份证号用于后续操作任务 static TaskHandle_t LED_Task_Handle NULL; static TaskHandle_t Print_Task_Handle NULL;这里除了引入FreeRTOS的核心头文件还引入了板级支持包BSP的头文件用于操作具体的硬件LED和串口。任务句柄是TaskHandle_t类型的指针任务创建后我们就通过这个句柄来引用它。第二部分任务函数原型和main函数static void LED_Task(void *pvParameters); static void Print_Task(void *pvParameters); static void BSP_Init(void); int main(void) { // 1. 硬件初始化时钟、GPIO、串口等 BSP_Init(); // 2. 创建启动任务通常用于创建其他应用任务 xTaskCreate(AppTaskCreate, AppTaskCreate, 512, NULL, 1, AppTaskCreate_Handle); // 3. 启动FreeRTOS调度器从此CPU控制权交给内核 vTaskStartScheduler(); // 4. 正常情况下永远不会执行到这里 while(1); }main函数的逻辑是RTOS程序的固定套路初始化硬件 - 创建至少一个初始任务 - 启动调度器。一旦vTaskStartScheduler()被调用FreeRTOS内核就开始接管系统根据优先级和状态在多个任务间进行切换。第三部分应用任务创建函数static void AppTaskCreate(void) { taskENTER_CRITICAL(); // 进入临界区保护创建过程不被中断打断 // 创建LED闪烁任务 xTaskCreate(LED_Task, LED_Task, 128, NULL, 2, LED_Task_Handle); // 创建串口打印任务 xTaskCreate(Print_Task, Print_Task, 128, NULL, 1, Print_Task_Handle); taskEXIT_CRITICAL(); // 退出临界区 vTaskDelete(NULL); // 删除自身启动任务使命完成 }xTaskCreate是创建任务的核心函数参数非常多我们逐一拆解LED_Task任务函数的指针即这个任务具体要执行的代码。LED_Task任务的名字字符串形式调试时非常有用。128分配给这个任务的栈大小单位是字Word。栈用于存放函数调用时的局部变量、返回地址等。任务越复杂调用层级越深需要的栈就越大。这里128是个起始值实际开发中需要根据情况调整甚至可以通过FreeRTOS提供的工具来检测栈使用的高水位线。NULL传递给任务函数的参数。可以是一个指向任何数据的指针任务函数内通过pvParameters访问。2任务的优先级。数字越大优先级越高。FreeRTOS调度器永远让就绪态中优先级最高的任务运行。优先级相同的任务则轮流执行时间片调度。LED_Task_Handle用来保存创建成功后返回的任务句柄。第四部分真正的任务函数static void LED_Task(void *pvParameters) { while (1) // 任务函数通常是一个无限循环 { LED1_ON; vTaskDelay(500); // 延时500个系统tick LED1_OFF; vTaskDelay(500); } }任务函数的格式是固定的void vATaskFunction( void *pvParameters )。它内部几乎总是一个while(1)循环因为任务一旦创建就应该持续工作。特别注意在任务中想实现延时必须使用vTaskDelay()或vTaskDelayUntil()而不能用裸机编程里的HAL_Delay()。vTaskDelay()是协作式延时它会让出CPU控制权给其他就绪的任务让系统得以调度。如果你用了HAL_Delay()它是忙等待这个任务就会独占CPU其他任务都无法运行多任务就失去了意义。3. 手把手实现三个周期性任务的创建与调度现在我们来完成文章开头提出的具体需求创建三个独立运行的任务。为了更贴近实际我假设你手头有一块STM32F103或其他型号开发板一个LED连接在某个GPIO上一个USB转串口模块用于调试以及一个AHT20温湿度传感器通过I2C连接。3.1 任务一精准闪烁的LED心跳灯LED任务最简单但却是验证多任务是否正常工作的“指示灯”。我们要求它每500ms变化一次状态亮/灭。static void LED1_Task(void* pvParameters) { const TickType_t xDelay500ms pdMS_TO_TICKS(500); // 将毫秒转换为系统tick数 while (1) { // 翻转LED状态假设LED1_ON和LED1_OFF是已定义的宏 GPIO_WriteBit(GPIOB, GPIO_Pin_0, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_0))); // 或者使用你板子提供的函数LED1_TOGGLE(); // 打印当前状态到串口方便观察 printf([LED Task] Toggled. Tick: %lu\r\n, xTaskGetTickCount()); // 延时500ms。注意vTaskDelay的参数是tick数使用pdMS_TO_TICKS确保时间准确 vTaskDelay(xDelay500ms); } }关键点pdMS_TO_TICKS()是一个宏用于将毫秒时间转换为系统心跳节拍数。如果你的configTICK_RATE_HZ是10001ms一个tick那么pdMS_TO_TICKS(500)就等于500。这样做的好处是即使你以后修改了系统时钟频率代码也不需要重算延时值。在任务里使用printf输出调试信息是个好习惯但要注意串口输出本身比较慢可能会轻微影响任务的时间精度。在实际产品中调试信息可能需要通过其他方式处理。3.2 任务二定时发送数据的串口通信这个任务每隔2000ms向串口发送一次“hello world!”字符串。static void Hello_Task(void* pvParameters) { const TickType_t xDelay2000ms pdMS_TO_TICKS(2000); const char *pcMessage hello world!\r\n; while (1) { // 调用串口发送函数。这里假设USART_SendString是你实现的一个阻塞式发送函数。 // 在实际应用中更推荐使用非阻塞DMA的方式避免任务长时间阻塞。 USART_SendString(USART1, (uint8_t*)pcMessage, strlen(pcMessage)); printf([Hello Task] Message sent at tick: %lu\r\n, xTaskGetTickCount()); vTaskDelay(xDelay2000ms); } }踩坑提醒串口发送如果采用阻塞式即调用一个函数直到所有字节发送完成才返回那么在发送大量数据时这个任务会长时间占用CPU可能导致其他需要快速响应的任务比如按键扫描被“饿死”。更优的做法是使用“中断队列”或“DMA”的方式。例如任务只需将待发送的数据放入一个队列然后由一个专门的“串口发送服务任务”或中断服务程序从队列中取出并发送。这样Hello_Task在放入数据后就可以立刻vTaskDelay不会阻塞。3.3 任务三模拟传感器数据采集第三个任务要求每5000ms采集一次AHT20温湿度数据。由于我们重点是多任务框架这里先模拟数据采集过程。假设我们已经写好了AHT20的驱动函数AHT20_ReadTempHumidity(float *temp, float *hum)。static void AHT20_Task(void* pvParameters) { const TickType_t xDelay5000ms pdMS_TO_TICKS(5000); float temperature 0.0f; float humidity 0.0f; BaseType_t xSensorStatus; while (1) { printf([AHT20 Task] Start reading sensor...\r\n); // 模拟调用传感器驱动函数 xSensorStatus AHT20_ReadTempHumidity(temperature, humidity); if (xSensorStatus pdPASS) // 假设pdPASS表示读取成功 { printf([AHT20 Task] Temp: %.2f C, Humi: %.2f%%\r\n, temperature, humidity); // 在实际项目中这里可以将数据存入全局变量、队列或通过其他方式发送出去 } else { printf([AHT20 Task] Read failed!\r\n); } vTaskDelay(xDelay5000ms); } }深入思考传感器I2C通信也是相对较慢的操作。如果AHT20_ReadTempHumidity是阻塞式的等待I2C应答和转换完成那么这个任务在读取期间也会阻塞。对于更复杂的系统可以考虑将I2C操作也封装成基于事件或回调的非阻塞模式或者为这个任务设置一个合适的优先级确保它不会影响对实时性要求更高的任务。3.4 整合与创建在AppTaskCreate中组装所有任务最后我们需要在AppTaskCreate函数中创建这三个任务并为它们分配合适的优先级和栈大小。static void AppTaskCreate(void) { BaseType_t xReturn; taskENTER_CRITICAL(); // 进入临界区防止创建任务过程被打断 // 创建LED任务优先级设为2较低栈128字 xReturn xTaskCreate(LED1_Task, LED, 128, NULL, 2, LED_Task_Handle); configASSERT(xReturn pdPASS); // 使用断言确保创建成功 // 创建串口打印任务优先级设为2与LED任务同级 xReturn xTaskCreate(Hello_Task, Hello, 256, NULL, 2, Hello_Task_Handle); // 栈给大一点因为printf可能较耗栈 configASSERT(xReturn pdPASS); // 创建传感器任务优先级设为1最低栈256字 xReturn xTaskCreate(AHT20_Task, AHT20, 256, NULL, 1, AHT20_Task_Handle); configASSERT(xReturn pdPASS); printf(All tasks created successfully! Scheduler starting...\r\n); taskEXIT_CRITICAL(); // 退出临界区 vTaskDelete(NULL); // 删除启动任务自身 }优先级设置心得在这个例子里我把LED和Hello任务的优先级设为2AHT20任务设为1。这意味着LED和Hello任务具有相同的较高优先级它们会以时间片轮转的方式共享CPU。而AHT20任务优先级最低只有当LED和Hello任务都在延时vTaskDelay时它才有机会运行。这符合我们的需求LED闪烁和串口打印需要相对及时的响应而传感器采集可以慢一点。当然如果传感器数据非常关键你也可以提高它的优先级。4. 进阶技巧任务间通信与资源管理实战三个任务独立运行已经实现了基本的多任务调度。但真实的嵌入式系统任务之间往往需要协作比如传感器任务采集到的数据需要发送给串口任务去打印或者当某个按键按下时需要通知LED任务改变闪烁模式。这就涉及到任务间通信IPC和共享资源管理。4.1 使用队列传递温湿度数据队列Queue是FreeRTOS中最常用、最安全的数据传递机制。它像一个管道一端写入另一端读出数据是先进先出FIFO。我们改造一下之前的程序让AHT20任务把采集到的数据通过队列发送给一个新建的“数据打印任务”。首先在文件开头定义一个队列句柄并创建队列// 定义队列句柄和队列项结构体 typedef struct { float temperature; float humidity; } SensorData_t; QueueHandle_t xSensorDataQueue NULL; int main(void) { // ... 硬件初始化 ... // 创建队列最多能存放5个SensorData_t结构体每个项的大小是sizeof(SensorData_t) xSensorDataQueue xQueueCreate(5, sizeof(SensorData_t)); if (xSensorDataQueue NULL) { printf(Failed to create queue!\r\n); while(1); } // ... 创建任务启动调度器 ... }然后修改AHT20任务将数据发送到队列而不是直接打印static void AHT20_Task(void* pvParameters) { const TickType_t xDelay5000ms pdMS_TO_TICKS(5000); SensorData_t xData; BaseType_t xStatus; while (1) { if (AHT20_ReadTempHumidity(xData.temperature, xData.humidity) pdPASS) { // 将数据发送到队列尾部等待10个tick10ms如果队列满 xStatus xQueueSendToBack(xSensorDataQueue, xData, pdMS_TO_TICKS(10)); if (xStatus ! pdPASS) { printf([AHT20 Task] Queue full, data lost!\r\n); } } vTaskDelay(xDelay5000ms); } }接着创建一个新的“数据打印任务”专门从队列中取出数据并打印static void DataPrint_Task(void* pvParameters) { SensorData_t xReceivedData; BaseType_t xStatus; while (1) { // 从队列头部接收数据无限期等待直到有数据到来 xStatus xQueueReceive(xSensorDataQueue, xReceivedData, portMAX_DELAY); if (xStatus pdPASS) { printf([DataPrint] Temp: %.2fC, Humi: %.2f%%\r\n, xReceivedData.temperature, xReceivedData.humidity); } } }这样做的好处解耦采集任务和打印任务完全独立互不干扰。采集任务只负责生产数据打印任务只负责消费数据。缓冲队列起到了缓冲作用。如果打印任务因为某种原因暂时较慢比如串口堵塞采集到的数据可以在队列中暂存不会丢失只要队列没满。同步xQueueReceive的portMAX_DELAY参数使得打印任务在没有数据时会自动进入阻塞态让出CPU不浪费资源。4.2 使用二值信号量保护共享资源假设我们的printf函数不是线程安全的很多裸机移植的printf确实不是如果多个任务同时调用printf输出信息可能会交错混乱。这时就需要用互斥信号量Mutex来保护这个共享资源串口。SemaphoreHandle_t xPrintMutex NULL; // 在main函数中创建互斥信号量 xPrintMutex xSemaphoreCreateMutex(); // 封装一个安全的打印函数 void safe_printf(const char *format, ...) { va_list args; char buffer[128]; // 获取互斥锁如果锁被其他任务持有则本任务阻塞等待 if (xSemaphoreTake(xPrintMutex, portMAX_DELAY) pdTRUE) { va_start(args, format); vsnprintf(buffer, sizeof(buffer), format, args); USART_SendString(USART1, (uint8_t*)buffer, strlen(buffer)); va_end(args); // 释放互斥锁 xSemaphoreGive(xPrintMutex); } } // 在各个任务中用 safe_printf 替代 printf通过互斥信号量我们确保了同一时刻只有一个任务能执行USART_SendString从而保证了输出信息的完整性。这是一个非常经典且必要的资源共享保护案例。5. 调试与优化让多任务系统运行得更稳健代码写完了烧录进去看到LED在闪串口有输出是不是就万事大吉了别急多任务系统的调试比裸机复杂这里有几个我踩过坑后总结的实用技巧。1. 栈溢出检测这是新手最容易出问题的地方。每个任务都有自己的栈如果栈空间分配不足任务运行中可能会覆盖掉其他内存区域导致各种诡异崩溃。FreeRTOS提供了两种栈溢出检测机制在FreeRTOSConfig.h中配置configCHECK_FOR_STACK_OVERFLOW方法1在任务切换时检查栈指针是否超出了任务栈范围。这种方法快但只能在溢出发生后检测到。方法2在任务创建时用特定值如0xa5填充栈空间然后定期检查栈末尾的这部分值是否被修改。这种方法能检测到栈的“高水位线”即任务历史上最多用了多少栈。我强烈建议在开发阶段启用方法2。创建任务时栈大小可以先给一个较大的值比如256或512字运行一段时间后通过uxTaskGetStackHighWaterMark()函数查询任务的剩余栈空间最小值然后根据这个值来精确调整栈大小既能保证安全又不浪费内存。2. 合理使用系统视图和调试工具像STM32CubeIDE、SEGGER SystemView等工具可以图形化地展示各个任务的运行状态运行、就绪、阻塞、挂起、切换顺序、CPU占用率等。这对于分析任务调度是否合理、有没有优先级反转、哪个任务长期占用CPU等问题有极大的帮助。光看串口打印是看不清全貌的。3. 优先级设置的艺术优先级不是随便设的。基本原则是对实时性要求越高、需要越快响应的任务优先级应该越高。但也要避免“饥饿”现象高优先级任务如果一直就绪低优先级任务将永远得不到运行。通常我会把由外部事件如中断触发的、需要快速处理的任务设为高优先级把周期性的、计算型的任务设为中优先级把后台的、非紧急的任务如日志上传设为低优先级。并且要慎用vTaskDelay(0)或taskYIELD()这种主动让出CPU的调用它们在某些场景下有用但滥用会破坏调度器的节奏。4. 中断服务程序ISR与任务记住一个黄金法则ISR要短平快。在FreeRTOS中中断服务程序里不能调用可能导致阻塞的API如vTaskDelay,xQueueReceive等。如果中断需要通知任务做进一步处理应该使用FromISR结尾的API如xQueueSendFromISR,xSemaphoreGiveFromISR来发送信号或数据给任务让任务在非中断上下文去处理繁重的工作。这确保了系统的实时性不被中断服务程序拖累。纸上得来终觉浅绝知此事要躬行。多任务编程的很多“感觉”比如对临界区的理解、对优先级调度的把握、对死锁的警惕都是在实际项目中一次次调试和优化中积累起来的。从这三个简单的周期性任务出发你可以尝试加入按键任务去改变LED的闪烁频率或者用消息队列构建一个更复杂的数据处理流水线。遇到问题别怕用好调试工具多看FreeRTOS的官方文档和源码注释社区的讨论也非常活跃。嵌入式开发的世界里让多个任务和谐有序地跑在一颗小小的MCU里看着它们各司其职本身就是一件很有成就感的事情。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2409145.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…