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

FreeRTOS 知识点

一、配置过程

二、基本知识点

2.1 抢占优先级和响应优先级

在 FreeRTOS 中,任务的调度方式主要有 ​​抢占式(Preemptive)​​ 和 ​​协作式(Cooperative)​​ 两种模式,它们的核心区别在于 ​​任务如何释放 CPU 控制权​​,以及 ​​调度器如何决定任务切换​​。以下是详细对比:

1.抢占式调度(Preemptive Scheduling)​

高优先级任务可立即抢占低优先级任务​​,无需等待当前任务主动让出 CPU。

依赖配置​​:需在 FreeRTOSConfig.h中启用:

#define configUSE_PREEMPTION    1  // 启用抢占式调度
#define configUSE_TIME_SLICING  1  // 可选:同优先级任务时间片轮转

工作流程​

  • 任务就绪​​:当一个高优先级任务进入就绪状态(如被创建、延迟结束、收到信号量等),调度器会​​立即检查​​是否需要切换。
  • ​抢占发生​​:如果新就绪的任务优先级​​高于当前任务​​,CPU 会​​立即切换​​到高优先级任务。
  • ​无需主动让步​​:即使当前任务未调用 taskYIELD()或 vTaskDelay(),也会被强制打断。

优点​​

  • ​​实时性强​​:高优先级任务能快速响应(适合中断服务、紧急事件处理)。
  • ​​自动化调度​​:开发者无需手动管理任务切换。

​​缺点​​

  • ​​资源竞争风险​​:需注意优先级反转(Priority Inversion)问题(可通过互斥量优先级继承解决)。
  • ​​上下文切换开销​​:频繁抢占会增加 CPU 负载。

​2. 协作式调度(Cooperative Scheduling)​

特点​​

  • ​​任务必须主动释放 CPU​​,否则会一直运行直到完成。
  • ​​依赖配置​​:需在 FreeRTOSConfig.h中关闭抢占:
#define configUSE_PREEMPTION    0  // 关闭抢占式调度

工作流程​​

  • ​​任务运行​​:当前任务会一直占用 CPU,直到:
  • 调用 taskYIELD()主动让出 CPU。
  • 调用阻塞 API(如 vTaskDelay(), xQueueReceive())。
  • ​​调度器介入​​:只有当任务主动放弃 CPU 时,调度器才会选择​​下一个最高优先级就绪任务​​运行。

优点​​

  • ​​确定性高​​:任务切换完全由代码控制,避免不可预知的抢占。
  • ​​资源竞争少​​:无需频繁处理共享数据的互斥问题(适合简单系统)。
  • ​​低开销​​:减少上下文切换次数。

​​缺点​​

  • ​​实时性差​​:高优先级任务可能因低优先级任务不释放 CPU 而无法及时响应。
  • ​​开发者负担​​:需手动插入 taskYIELD(),否则可能导致低优先级任务“饿死”。

3. 如何选择?​​

​​选抢占式​​:

  • 需要快速响应中断或高优先级事件(如传感器数据处理)。
  • 系统中有多个不同优先级的任务。

​​选协作式​​:

  • 资源受限的裸机升级项目(减少调度复杂性)。
  • 任务执行时间短且可预测(如串口协议解析)。

2.2 响应优先级和抢占优先级

1. 优先级的基本机制​​

FreeRTOS 使用 ​​单一的优先级数值​​(通常为 0到 configMAX_PRIORITIES-1,默认最高优先级为 configMAX_PRIORITIES-1)来管理任务调度。

​​数值越大,优先级越高​​(例如优先级 3> 2)。

高优先级任务可​​抢占​​(Preempt)低优先级任务,无需等待当前任务主动释放 CPU。

​​2. 抢占优先级(Preemptive Priority)​​

​​定义​​:高优先级任务​​立即抢占​​低优先级任务的 CPU 使用权。

​​表现​​:

         如果任务 A(优先级 3)就绪,而当前运行的是任务 B(优先级 2),FreeRTOS 会​​立即切换​​到任务 A。

        这是 FreeRTOS 默认的调度行为(需配置 configUSE_PREEMPTION=1)。

​​关键点​​:

       抢占是​​自动的​​,无需任务主动让步(除非使用 taskYIELD())。确保高优先级任务能​​实时响应​​。

​​3. 响应优先级(Response Priority)​​

​​定义​​:任务在​​就绪状态​​下被调度的顺序,完全由优先级决定。

​​表现​​:

当多个任务同时就绪时,调度器会选择​​优先级最高​​的任务运行(即响应最快)。

例如:任务 A(优先级 3)和任务 B(优先级 2)同时就绪,任务 A 会优先被调度。

​​关键点​​:

“响应优先级”是抢占式调度的​​结果​​,而非独立配置。

与“抢占优先级”是同一机制的两个视角:

  • ​​抢占​​:强调中断当前任务的行为。
  • ​​响应​​:强调任务被选中的顺序。

FreeRTOS 中 “实时性” 的体现是什么:   抢占式:高优先级任务可打断低优先级任务(只要高优先级任务就绪,立即执行),实时性强;

2.3 FreeRTOS 的任务状态有哪些?状态之间如何切换?

  1. 考察点:任务生命周期的理解(高频基础题)。
  2. 核心答点:5 种状态 ——就绪(Ready)运行(Running)阻塞(Blocked)挂起(Suspended)删除(Deleted)
  3. 切换逻辑:运行→就绪(被高优先级任务抢占)、运行→阻塞(调用 vTaskDelay()/ 等待信号量)、阻塞→就绪(延时到 / 信号量触发)、就绪→运行(调度器选择最高优先级就绪任务)、任意状态→挂起(vTaskSuspend())、挂起→就绪(xTaskResume())。

2.4 FreeRTOS 的内核组成有哪些核心模块

考察点:内核架构的整体认知。
核心答点:任务管理(创建 / 删除 / 切换)、时间管理(定时器 / 延时)、同步与通信(信号量 / 队列 / 事件组 / 互斥锁)、内存管理(堆 / 栈分配)、中断管理(临界区 / 中断安全 API)。

2.5 FreeRTOS 的 “临界区” 是什么?如何保护临界区?

考察点:中断与任务的资源冲突解决逻辑。
核心答点:临界区是 “不能被中断打断的代码段”(如操作共享变量);
保护方式:

  • 任务级:taskENTER_CRITICAL() / taskEXIT_CRITICAL()(关闭任务调度,不关闭中断);
  • 中断级:taskENTER_CRITICAL_FROM_ISR() / taskEXIT_CRITICAL_FROM_ISR()(关闭中断,需在中断服务函数中使用)。

2.6 详细说明 FreeRTOS 的 “临界区” 是什么?如何保护临界区。

在 FreeRTOS 中,临界区(Critical Section) 是指一段 “不允许被中断或其他任务打断” 的代码段,通常用于操作共享资源(如全局变量、硬件寄存器、外设等),以防止多任务并发或中断触发导致的数据竞争和不一致问题。

1. 任务级临界区保护(在任务中使用)

适用于任务代码中需要保护的临界区,核心是 “禁止任务调度器切换”(但不禁止中断,仅禁止因任务调度导致的打断)。

// 进入临界区:禁止任务调度
taskENTER_CRITICAL();// 临界区代码(操作共享资源)
// ...// 退出临界区:恢复任务调度
taskEXIT_CRITICAL();

工作原理:

  • taskENTER_CRITICAL() 会关闭 FreeRTOS 调度器(通过禁止 PendSV 中断,PendSV 是任务切换的触发源),但不影响其他硬件中断(如定时器、串口中断);
  • 临界区代码执行期间,高优先级任务即使就绪也无法抢占当前任务;
  • taskEXIT_CRITICAL() 会恢复调度器,若有高优先级任务就绪,会触发任务切换。

注意事项:

  • 临界区代码要尽可能短,避免影响系统实时性(高优先级任务会被阻塞等待);
  • 不可嵌套调用(多次调用 taskENTER_CRITICAL() 需对应相同次数的 taskEXIT_CRITICAL(),但不建议嵌套)。

2. 中断级临界区保护(在中断服务程序中使用)

适用于中断服务程序(ISR)中需要保护的临界区,核心是 “暂时关闭中断”(防止被更高优先级中断打断)。

// 进入临界区:保存当前中断状态并关闭中断
UBaseType_t uxSavedInterruptStatus;
uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();// 临界区代码(操作共享资源)
// ...// 退出临界区:恢复中断状态
taskEXIT_CRITICAL_FROM_ISR(uxSavedInterruptStatus);

工作原理:

  • taskENTER_CRITICAL_FROM_ISR() 会先保存当前的中断使能状态,再关闭全局中断(通过修改 CPU 状态寄存器,如 ARM 的 PRIMASK);
  • 临界区代码执行期间,所有中断(无论优先级)都被禁止,确保代码不被任何中断打断;
  • taskEXIT_CRITICAL_FROM_ISR() 会恢复进入临界区前的中断状态(避免误关闭其他中断)。

注意事项:

  • 中断中的临界区必须极致简短(微秒级),否则会严重影响系统对外部事件的响应(如丢失中断);
  • 必须使用返回值 uxSavedInterruptStatus 作为参数传递给退出函数,否则可能导致中断无法恢复。

2.7  FreeRTOS 静态创建和动态创建

在 FreeRTOS 中,任务创建主要有两种方式:静态创建和动态创建。这两种方式各有特点,适用于不同的应用场景。

考察点:任务创建的两种内存分配方式。
核心答点:两种核心创建函数;
区别:

  • xTaskCreate():动态分配任务栈和控制块(依赖 FreeRTOS 堆管理,无需用户手动分配内存);
  • xTaskCreateStatic():静态分配(需用户手动指定任务栈数组、控制块变量,不依赖堆,适合内存受限场景)。

2.7 什么是 “空闲任务(Idle Task)”?它的作用是什么?

考察点:FreeRTOS 内核的基础任务。
核心答点:空闲任务是 FreeRTOS 启动调度器(vTaskStartScheduler())时自动创建的最低优先级(优先级 0)任务;
作用:

  • 当无其他就绪任务时,CPU 执行空闲任务(避免 CPU 空转);
  • 回收 “动态创建且被删除” 的任务的内存(需开启 configUSE_IDLE_HOOK 配置空闲钩子函数)。

2.8 FreeRTOS 中的信号量(Semaphore)有哪几种类型?分别用在什么场景?

特性二进制信号量(Binary Semaphore)计数信号量(Counting Semaphore)互斥信号量(Mutex)
值范围只能为 0 或 10 到最大计数(用户定义)只能为 0 或 1(类似二进制)
核心用途任务间 / 中断 - 任务同步;简单互斥有限资源的并发访问控制(如连接池)共享资源的互斥访问(解决优先级反转)
所有权无(任何任务可释放)无(任何任务可释放)有(只有持有者可释放)
优先级继承有(避免优先级反转)
创建函数xSemaphoreCreateBinary()xSemaphoreCreateCounting()xSemaphoreCreateMutex()
典型初始值同步场景为 0;互斥场景为 1资源初始数量(如 5 表示 5 个资源)1(资源空闲)

1. 二进制信号量(Binary Semaphore)

适用场景:任务间同步、中断与任务同步,或简单互斥(无优先级继承需求)。

示例:中断与任务同步

#include "FreeRTOS.h"
#include "semphr.h"SemaphoreHandle_t xBinarySemaphore;// 初始化:创建二进制信号量(初始值为0)
void vSetup() {xBinarySemaphore = xSemaphoreCreateBinary();if (xBinarySemaphore != NULL) {// 创建任务xTaskCreate(vTaskHandleEvent, "HandleEvent", 128, NULL, 1, NULL);vTaskStartScheduler();}
}// 中断服务程序:触发时释放信号量
void EXTI_IRQHandler() {BaseType_t xHigherPriorityTaskWoken = pdFALSE;// 清除中断标志(硬件相关)EXTI_ClearFlag();// 从ISR中释放信号量(必须用FromISR版本)xSemaphoreGiveFromISR(xBinarySemaphore, &xHigherPriorityTaskWoken);// 若需切换任务,请求调度portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}// 任务:等待中断信号并处理
void vTaskHandleEvent(void *pvParam) {for (;;) {// 等待信号量(永久阻塞,直到中断触发)if (xSemaphoreTake(xBinarySemaphore, portMAX_DELAY) == pdPASS) {printf("处理中断事件\n");}}
}

2. 计数信号量(Counting Semaphore)

适用场景:控制对有限数量资源的并发访问(如允许 5 个任务同时使用某个资源)。

示例:控制 3 个串口资源的并发访问

#include "FreeRTOS.h"
#include "semphr.h"SemaphoreHandle_t xCountingSemaphore;
#define MAX_SERIAL_PORT 3  // 最大串口数量// 初始化:创建计数信号量(初始值为3,表示3个可用资源)
void vSetup() {xCountingSemaphore = xSemaphoreCreateCounting(MAX_SERIAL_PORT, MAX_SERIAL_PORT);if (xCountingSemaphore != NULL) {// 创建5个任务竞争串口资源for (int i = 0; i < 5; i++) {xTaskCreate(vTaskUseSerial, "SerialTask", 128, (void*)i, 1, NULL);}vTaskStartScheduler();}
}// 任务:使用串口资源
void vTaskUseSerial(void *pvParam) {int taskId = (int)pvParam;for (;;) {// 尝试获取串口资源(最多等待100ms)if (xSemaphoreTake(xCountingSemaphore, pdMS_TO_TICKS(100)) == pdPASS) {printf("任务%d:获取串口成功,开始传输数据\n", taskId);vTaskDelay(pdMS_TO_TICKS(500));  // 模拟数据传输printf("任务%d:释放串口\n", taskId);xSemaphoreGive(xCountingSemaphore);  // 释放资源} else {printf("任务%d:获取串口失败,重试\n", taskId);}vTaskDelay(pdMS_TO_TICKS(100));}
}

3. 互斥信号量(Mutex)

适用场景:共享资源的互斥访问,尤其适用于存在优先级差异的任务(解决优先级反转问题)。

示例:保护共享内存的访问

#include "FreeRTOS.h"
#include "semphr.h"SemaphoreHandle_t xMutex;
int g_sharedMemory = 0;  // 共享资源// 初始化:创建互斥信号量(初始值为1,资源空闲)
void vSetup() {xMutex = xSemaphoreCreateMutex();if (xMutex != NULL) {// 创建3个不同优先级的任务xTaskCreate(vHighPriorityTask, "HighTask", 128, NULL, 3, NULL);xTaskCreate(vMediumPriorityTask, "MediumTask", 128, NULL, 2, NULL);xTaskCreate(vLowPriorityTask, "LowTask", 128, NULL, 1, NULL);vTaskStartScheduler();}
}// 低优先级任务:持有共享资源
void vLowPriorityTask(void *pvParam) {for (;;) {xSemaphoreTake(xMutex, portMAX_DELAY);printf("低优先级任务:持有共享资源\n");vTaskDelay(pdMS_TO_TICKS(2000));  // 长时间占用资源printf("低优先级任务:释放共享资源\n");xSemaphoreGive(xMutex);vTaskDelay(pdMS_TO_TICKS(1000));}
}// 中优先级任务:频繁运行(可能抢占低优先级任务)
void vMediumPriorityTask(void *pvParam) {for (;;) {printf("中优先级任务:运行中\n");vTaskDelay(pdMS_TO_TICKS(100));}
}// 高优先级任务:需要访问共享资源
void vHighPriorityTask(void *pvParam) {for (;;) {printf("高优先级任务:等待共享资源\n");xSemaphoreTake(xMutex, portMAX_DELAY);printf("高优先级任务:访问共享资源\n");xSemaphoreGive(xMutex);vTaskDelay(pdMS_TO_TICKS(1000));}
}

关键特性:当低优先级任务持有互斥锁时,高优先级任务等待期间会触发优先级继承(低优先级任务临时提升至与高优先级任务相同的优先级),避免中优先级任务抢占 CPU 导致的优先级反转。

2.9 队列(Queue)的作用是什么?它的核心特性有哪些?

1 队列的核心特性

  • 数据传递方式:采用拷贝传递(数据被复制到队列缓冲区),而非指针传递,避免内存访问冲突。
  • 先进先出(FIFO):默认按入队顺序出队,也可配置为优先级出队(高优先级数据优先)。
  • 多对多通信:多个任务 / 中断可向同一队列发送数据,多个任务可从同一队列接收数据。
  • 阻塞机制:发送 / 接收数据时可指定超时时间,无数据 / 空间时阻塞等待,提高 CPU 效率。
  • 中断安全:提供 FromISR 系列 API,支持从中断服务程序(ISR)中操作队列。

2. 队列的关键概念

  • 队列长度:可存储的最大数据项数量(创建时指定)。
  • 数据项大小:每个数据项的字节数(创建时指定,所有数据项大小相同)。
  • 队头 / 队尾:数据入队从队尾添加,出队从队头移除(FIFO 模式)。
  • 阻塞超时
    • 发送时:队列满时,任务阻塞等待空间,超时后返回失败。
    • 接收时:队列空时,任务阻塞等待数据,超时后返回失败。

3. 代码实例

任务间通过队列传递数据

场景TaskSender 周期性产生数据并发送到队列,TaskReceiver 从队列接收并处理数据。

#include "FreeRTOS.h"
#include "queue.h"
#include <stdio.h>// 定义队列句柄
QueueHandle_t xDataQueue;// 发送任务:产生数据并发送到队列
void vTaskSender(void *pvParameters) {int32_t lDataToSend = 0;BaseType_t xStatus;for (;;) {// 发送数据到队列(队尾),超时时间0(不阻塞)xStatus = xQueueSend(xDataQueue, &lDataToSend, 0);if (xStatus != pdPASS) {printf("队列满,发送失败!\n");} else {printf("发送数据: %d\n", lDataToSend);lDataToSend++;}vTaskDelay(pdMS_TO_TICKS(500)); // 每500ms发送一次}
}// 接收任务:从队列接收数据并处理
void vTaskReceiver(void *pvParameters) {int32_t lReceivedData;BaseType_t xStatus;for (;;) {// 从队列接收数据,超时时间1000ms(等待1秒)xStatus = xQueueReceive(xDataQueue, &lReceivedData, pdMS_TO_TICKS(1000));if (xStatus == pdPASS) {printf("接收数据: %d\n", lReceivedData);} else {printf("1秒内未收到数据!\n");}}
}int main(void) {// 创建队列:长度为5,每个数据项为int32_t(4字节)xDataQueue = xQueueCreate(5, sizeof(int32_t));if (xDataQueue != NULL) {// 创建发送和接收任务xTaskCreate(vTaskSender, "Sender", 128, NULL, 1, NULL);xTaskCreate(vTaskReceiver, "Receiver", 128, NULL, 2, NULL);// 启动调度器vTaskStartScheduler();}// 若调度器启动失败,进入死循环for (;;);return 0;
}
中断与任务通过队列传递数据

场景:外部中断触发时,ISR 向队列发送事件标志,任务从队列接收并处理中断事件。

#include "FreeRTOS.h"
#include "queue.h"
#include "stm32f4xx.h" // 以STM32为例,其他平台需适配QueueHandle_t xInterruptQueue;// 初始化外部中断(硬件相关)
void vInitInterrupt() {// 配置GPIO为输入,使能外部中断(省略具体硬件配置)NVIC_EnableIRQ(EXTI0_IRQn); // 使能EXTI0中断
}// 中断服务程序:向队列发送事件
void EXTI0_IRQHandler(void) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;uint32_t ulEvent = 1; // 事件标志(1表示中断触发)// 清除中断标志EXTI_ClearITPendingBit(EXTI_Line0);// 从ISR发送数据到队列(必须使用FromISR版本)xQueueSendFromISR(xInterruptQueue, &ulEvent, &xHigherPriorityTaskWoken);// 若有更高优先级任务被唤醒,请求任务切换portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}// 处理任务:接收中断事件并处理
void vTaskInterruptHandler(void *pvParameters) {uint32_t ulReceivedEvent;BaseType_t xStatus;for (;;) {// 等待中断事件(永久阻塞)xStatus = xQueueReceive(xInterruptQueue, &ulReceivedEvent, portMAX_DELAY);if (xStatus == pdPASS) {printf("收到中断事件,执行处理逻辑\n");// 此处添加中断事件的具体处理代码}}
}int main(void) {// 创建队列:长度为3,每个数据项为uint32_txInterruptQueue = xQueueCreate(3, sizeof(uint32_t));if (xInterruptQueue != NULL) {vInitInterrupt(); // 初始化中断xTaskCreate(vTaskInterruptHandler, "IntHandler", 128, NULL, 1, NULL);vTaskStartScheduler();}for (;;);return 0;
}
优先级队列(数据插队)

场景:紧急数据通过 xQueueSendToFront() 插入队头,优先被处理。

// 发送紧急数据(插入队头)
void vSendEmergencyData(int32_t lEmergencyData) {BaseType_t xStatus;// 紧急数据插入队头,确保优先处理xStatus = xQueueSendToFront(xDataQueue, &lEmergencyData, pdMS_TO_TICKS(100));if (xStatus == pdPASS) {printf("紧急数据 %d 已插入队头\n", lEmergencyData);}
}

2.10. 事件组(Event Group)的作用是什么?和信号量、队列有什么区别?

核心特性

  • 事件表示:使用一个 32 位无符号整数(EventBits_t)存储事件,每个位代表一个独立事件(bit0~bit31)。
  • 逻辑触发:任务可等待事件的 “逻辑与”(所有指定事件都发生)或 “逻辑或”(任一指定事件发生)。
  • 事件持久性:事件发生后会保持置位状态,直到被显式清除(或任务读取时自动清除)。
  • 多任务等待:多个任务可同时等待同一事件组的不同事件组合。
  • 中断安全:支持从中断服务程序(ISR)中设置事件位。

2.11 什么是 “任务通知(Task Notification)”?它相比队列、信号量有什么优势?

考察点:FreeRTOS 高效同步机制(较新特性)。

核心答点:任务通知是 FreeRTOS v8.2.0 后新增的机制,通过 “直接向任务发送通知” 实现同步 / 通信(每个任务有一个 32 位的通知值);
优势:

  • 更高效:无需创建队列 / 信号量等内核对象,直接操作任务控制块,减少内存开销和 CPU 消耗;
  • 灵活:支持多种通知类型(如设置值、递增、覆盖、脉冲等),可替代二进制信号量、计数信号量、队列(单数据)等场景。

通知类型(Action)

发送通知时可指定对接收任务通知值的操作,共 8 种类型(通过 eAction 参数设置),核心类型包括:

  • eNoAction:仅发送通知,不修改通知值。
  • eSetBits:按位或操作(类似事件组的置位)。
  • eIncrement:通知值递增(类似计数信号量)。
  • eSetValueWithOverwrite:直接覆盖通知值(类似队列发送,覆盖模式)。
功能任务中使用的 API中断中使用的 API(ISR)
发送通知xTaskNotify()xTaskNotifyFromISR()
等待通知ulTaskNotifyTake()(简化版)-
等待通知(高级)xTaskNotifyWait()-
清除通知无需单独 API,xTaskNotifyWait() 可清除-

2.12 FreeRTOS 有哪几种内存分配方案(堆管理方案)?分别有什么特点?

考察点:内存管理的核心方案(高频基础题)。
核心答点:5 种堆管理方案(定义在 heap_1.c ~ heap_5.c 中,用户需选择一个引入工程);

  • 堆 1(heap_1):仅支持动态分配(pvPortMalloc()),不支持释放(vPortFree()),适合无需删除任务 / 信号量的场景(简单、安全);
  • 堆 2(heap_2):支持分配和释放,采用 “最佳适配” 算法,但不合并相邻空闲块,易产生内存碎片;
  • 堆 3(heap_3):封装标准 C 库的 malloc() 和 free(),依赖编译器的内存管理,可重入(适合有操作系统支持的场景);
  • 堆 4(heap_4):支持分配和释放,采用 “最佳适配”+“空闲块合并”,减少内存碎片,支持动态调整堆大小;
  • 堆 5(heap_5):基于堆 4,支持 “非连续内存块”(如将 RAM 分为多个区域,堆 5 可管理这些分散的内存),适合内存布局复杂的场景。

文章转载自:

http://hueP8sGt.bhjtL.cn
http://kcvxEPld.bhjtL.cn
http://JKJ2ee9v.bhjtL.cn
http://zIdZkhSR.bhjtL.cn
http://RwMQehBF.bhjtL.cn
http://7HA9Y6rl.bhjtL.cn
http://rXQUwowS.bhjtL.cn
http://pPHMQlFV.bhjtL.cn
http://iIE9xAtS.bhjtL.cn
http://7pwdwO5F.bhjtL.cn
http://CVCEb4Ro.bhjtL.cn
http://NEpHv2xT.bhjtL.cn
http://NU7pDT3D.bhjtL.cn
http://kP7LdXwJ.bhjtL.cn
http://vrVZHo64.bhjtL.cn
http://mfRfl2qz.bhjtL.cn
http://OALpB7E5.bhjtL.cn
http://wCzrHXW3.bhjtL.cn
http://wuZ4oRxp.bhjtL.cn
http://iYgXlJ10.bhjtL.cn
http://jQE2sUDf.bhjtL.cn
http://TbvJFFIn.bhjtL.cn
http://8f8luTqO.bhjtL.cn
http://PdMn9Okd.bhjtL.cn
http://jjxpzaaA.bhjtL.cn
http://2bHIE6rt.bhjtL.cn
http://IJ2TSA9z.bhjtL.cn
http://sKRmjt4n.bhjtL.cn
http://VL5yxZf2.bhjtL.cn
http://8CkJPjEY.bhjtL.cn
http://www.dtcms.com/a/382680.html

相关文章:

  • Mac电脑上如何打印出字体图标
  • 2.2顺序表
  • 如何打造高效AI智能体工具
  • 2025智能制造研发效率提升指南:从“项目-流程-数据”闭环看工具选型
  • 【leetcode】5. 最长回文子串
  • 01trie
  • P4342 [IOI 1998] Polygon -普及+/提高
  • 13.ImGui-搭建内部绘制的ImGui项目框架(无消息循环的简单ImGui实例)
  • 工业互联网与数字孪生:解码产业数字化转型的核心支撑
  • 知识库内容冗余重复该怎么办
  • ScreenToGif:一款免费开源的屏幕录制与GIF制作工具
  • XHR与Fetch取消请求的方法及原理深度解析
  • 除了 transformer 还有哪些 新的 神经网络架构
  • 鸿蒙NEXT的Web组件网络安全与隐私保护实践
  • D. Coprime
  • 利用python pandas库清洗病例处方清洗步骤
  • 数据库在并发访问时,不同隔离级别下脏读幻读问题
  • Python核心技术开发指南(065)——with语句
  • Python核心技术开发指南(064)——析构方法
  • 20250913-01: Langchain概念:Runnable可运行接口
  • 记一次谷歌语法获取路径 针对空白页面
  • Java GC:从GC Roots到分代设计的哲学
  • 一款4000℃高温材料设计方案及性能预测
  • 【leetcode】64. 最小路径和
  • 2.10组件间的通信
  • MinerU学习
  • 网络安全学习
  • 如何用 Rust 重写 SQLite 数据库(一):项目探索
  • Qwen3-80B-A3B混合注意力机制
  • OBS使用教程:OBS多路推流插件如何下载?如何安装使用?