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

FreeRTOS任务管理

 1. 任务状态理论讲解

 定时器职中断周期此处的1000Hz表示的是没次间隔1毫秒就记一次数(在FreeConfig.h)文件中进行配置

#define configTICK_RATE_HZ			( ( TickType_t ) 1000 )

判断是否需要任务切换在FreeRTOS里面每次间隔1毫秒切换一次(程序辅助理解图如下所示)

 FreeRTOS中三种状态的切换(分别是就绪态,阻塞态,暂停态),三种状态的理解图如下说所示

2. 任务状态实验

对应程序:08_freertos_example_task_status

  • 任务切换的基础:tick中断

  • 有哪些任务状态?状态切换图

  • 怎么管理不同状态的任务:放在不同链表里

  • 阻塞状态(Blocked)举例:vTaskDelay函数

  • 暂停状态(Suspended)举例:vTaskSuspend/vTaskResume

任务状态理解图展示

程序测试过程中遇到的问题

Load "F:\\STM32Study\\FreeRTOS_WDS\\1-6freertos_example_createtaskstatic\\FreeRTOS\\Demo\\CORTEX_STM32F103_Keil\\RTOSDemo.axf" 
BS \\RTOSDemo\main.c\200, 1
WS 1, `ch
LA `task1flagrun
___^
*** error 34: undefined identifier
LA `task2flagrun
___^
*** error 34: undefined identifier
LA `task3flagrun
___^
*** error 34: undefined identifier
*** error 65: access violation at 0x4002100C : no 'write' permission
*** error 65: access violation at 0x40021010 : no 'write' permission

解决问题参考文章

Keil Debug——*** error 65: access violation at 0x20005000 : no ‘write‘ permission_*** error 65: access violation at 0x4002100c : no -CSDN博客文章浏览阅读840次,点赞2次,收藏5次。当遇到"*** error 65: access violation at 0x20005000 : no 'write' permission"类似错误时,这通常表示程序试图写入一个没有写入权限的内存地址。这个错误可能有多种原因,以下是一些常见的解决方法:1. 检查内存保护设置:请确保您的程序没有试图写入受保护的内存区域。某些嵌入式系统可能使用内存保护机制,如存储器保护单元(MPU)或写保护寄存器。请检查您的系统文档或参考手册,了解如何正确配置内存保护设置。_*** error 65: access violation at 0x4002100c : no 'write' permissionhttps://blog.csdn.net/weixin_44406127/article/details/134159125

具体修改方式如下所示

测试代码

// 定义一个全局标记为用于表示任务是否在运行
static int task1flagrun = 0;
static int task2flagrun = 0;
static int task3flagrun = 0;TaskHandle_t xHandleTask1;   // 任务1的句柄
TaskHandle_t xHandleTask2;   // 任务2的句柄
TaskHandle_t xHandleTask3;   // 任务3的句柄void Task1Function(void * pram){// 获取TickCount的函数TickType_t tStart = xTaskGetTickCount();TickType_t t;// 添加标记位int flag  = 0;while(1){t = xTaskGetTickCount();task1flagrun = 1;task2flagrun = 0;task3flagrun = 0;printf("1"); if(!flag && (t > tStart + 10)){vTaskSuspend(xHandleTask3);   // 命令任务3进入暂停状态flag = 1;}if(t > tStart + 20){vTaskResume(xHandleTask3);    // 调用Resume进入就绪状态}}
}void Task2Function(void * pram){int i = 0;while(1){task1flagrun = 0;task2flagrun = 1;task3flagrun = 0;printf("2");// 让任务2进入到阻塞状态vTaskDelay(10);  // 这个是系统的延迟函数,主动进入阻塞状态,// 等待某个时刻的到来,这个时刻就是当前时间加上10个tick}
}void Task3Function(void * pram){while(1){task1flagrun = 0;task2flagrun = 0;task3flagrun = 1;printf("3");}
}void TaskGenericFunction(void * pram){int val = (int) pram;while(1){task1flagrun = 0;task2flagrun = 0;task3flagrun = 1;printf("%d",val);}
}StackType_t xTask3Stack[100];
StaticTask_t xTask3TCB;
// 创建空闲的任务(创建静态任务的时候要编写一个函数让CPU在空闲的状态下也不能闲着)
StackType_t xIdleTaskStack[100];
StaticTask_t xIdleTaskTCB;// 提供空闲栈的大小
void vApplicationGetIdleTaskMemory(StaticTask_t ** ppxIdleTaskTCBBuffer,StackType_t ** ppxIdleTaskStackBuffer,uint32_t * pulIdleTaskStackSize
){*ppxIdleTaskTCBBuffer = &xIdleTaskTCB;*ppxIdleTaskStackBuffer = xIdleTaskStack;*pulIdleTaskStackSize = 100;
}int main( void )
{#ifdef DEBUGdebug();
#endifprvSetupHardware();printf("Hello, world!\r\n");xTaskCreate(Task1Function,"Task1",100,NULL,1,&xHandleTask1);xTaskCreate(Task2Function,"Task2",100,NULL,1,NULL);xHandleTask3 = xTaskCreateStatic(Task3Function,"task3",100,NULL,1,xTask3Stack,&xTask3TCB);vTaskStartScheduler();/* Will only get here if there was not enough heap space to create theidle task. */return 0;
}

 测试程序PWM波形图

3. vTaskDelay和vTaskDelayUntil原理用法

对应程序:09_freertos_example_delay

有两个Delay函数:

  • vTaskDelay:至少等待指定个数的Tick Interrupt才能变为就绪状态

  • vTaskDelayUntil

    • 老版本,没有返回值

    • 等待到指定的绝对时刻,才能变为就绪态。

  • xTaskDelayUntil

    • 新版本,返回pdTRUE表示确实延迟了,返回pdFALSE表示没有发生延迟(因为延迟的时间点早就过了)

    • 等待到指定的绝对时刻,才能变为就绪态。

遇到这个问题:遇到不恰当的争论

 这个问题的原因是在keil结束断点调试的时候没有吧断点取消掉,然后就会一直报这个错误,并且KEIL无法关闭,调试完程序后一定要吧keil关闭掉

代码测试过程中遇到的问题

解决问题的博客:解决调试时候出现的“Encountered an improper argument”错误-CSDN博客文章浏览阅读2.6w次,点赞23次,收藏30次。今天分享一个我们在调试时候出现的一个错误,同时这个错误让我的工程也崩了好几十次。_encountered an improper argumenthttps://blog.csdn.net/OMGMac/article/details/126614887

测试程序的代码

 

// 定义一个全局标记为用于表示任务是否在运行
static int task1flagrun = 0;
static int task2flagrun = 0;
static int task3flagrun = 0;static int rands[] = {10,3,6,8,9,99,66};TaskHandle_t xHandleTask1;   // 任务1的句柄
TaskHandle_t xHandleTask2;   // 任务2的句柄
TaskHandle_t xHandleTask3;   // 任务3的句柄void Task1Function(void * pram){// 获取TickCount的函数TickType_t tStart = xTaskGetTickCount();int i = 0;int j = 0;while(1){task1flagrun = 1;task2flagrun = 0;task3flagrun = 0;// 延时函数执行的时间是不固定的for(i = 0; i <rands[j]; i++){printf("1"); 	}j++;if(j == 7){j = 0;}vTaskDelay(20);	}
}void Task2Function(void * pram){int i = 0;while(1){task1flagrun = 0;task2flagrun = 1;task3flagrun = 0;printf("2");}
}void Task3Function(void * pram){while(1){task1flagrun = 0;task2flagrun = 0;task3flagrun = 1;printf("3");}
}void TaskGenericFunction(void * pram){int val = (int) pram;while(1){task1flagrun = 0;task2flagrun = 0;task3flagrun = 1;printf("%d",val);}
}StackType_t xTask3Stack[100];
StaticTask_t xTask3TCB;
// 创建空闲的任务(创建静态任务的时候要编写一个函数让CPU在空闲的状态下也不能闲着)
StackType_t xIdleTaskStack[100];
StaticTask_t xIdleTaskTCB;// 提供空闲栈的大小
void vApplicationGetIdleTaskMemory(StaticTask_t ** ppxIdleTaskTCBBuffer,StackType_t ** ppxIdleTaskStackBuffer,uint32_t * pulIdleTaskStackSize
){*ppxIdleTaskTCBBuffer = &xIdleTaskTCB;*ppxIdleTaskStackBuffer = xIdleTaskStack;*pulIdleTaskStackSize = 100;
}int main( void )
{#ifdef DEBUGdebug();
#endifprvSetupHardware();printf("Hello, world!\r\n");xTaskCreate(Task1Function,"Task1",100,NULL,2,&xHandleTask1);xTaskCreate(Task2Function,"Task2",100,NULL,1,NULL);xHandleTask3 = xTaskCreateStatic(Task3Function,"task3",100,NULL,1,xTask3Stack,&xTask3TCB);vTaskStartScheduler();/* Will only get here if there was not enough heap space to create theidle task. */return 0;
}

编写代码后程序的执行结果展示:PWM波形结果展示vTaskDelay()这个函数可以保证休眠的时间是一样的但是无法保证程序执行的时刻间隔是一样的

vTaskDelayUntil函数的含义

测试代码

// 定义一个全局标记为用于表示任务是否在运行
static int task1flagrun = 0;
static int task2flagrun = 0;
static int task3flagrun = 0;static int rands[] = {10,3,6,8,9,99,66};TaskHandle_t xHandleTask1;   // 任务1的句柄
TaskHandle_t xHandleTask2;   // 任务2的句柄
TaskHandle_t xHandleTask3;   // 任务3的句柄void Task1Function(void * pram){// 获取TickCount的函数,表示将启动的时间记录下来TickType_t tStart = xTaskGetTickCount();int i = 0;int j = 0;while(1){task1flagrun = 1;task2flagrun = 0;task3flagrun = 0;// 延时函数执行的时间是不固定的for(i = 0; i <rands[j]; i++){printf("1"); 	}j++;if(j == 7){j = 0;}
#if 0 vTaskDelay(20);	
#elsevTaskDelayUntil(&tStart,20);
#endif}
}void Task2Function(void * pram){int i = 0;while(1){task1flagrun = 0;task2flagrun = 1;task3flagrun = 0;printf("2");}
}void Task3Function(void * pram){while(1){task1flagrun = 0;task2flagrun = 0;task3flagrun = 1;printf("3");}
}void TaskGenericFunction(void * pram){int val = (int) pram;while(1){task1flagrun = 0;task2flagrun = 0;task3flagrun = 1;printf("%d",val);}
}StackType_t xTask3Stack[100];
StaticTask_t xTask3TCB;
// 创建空闲的任务(创建静态任务的时候要编写一个函数让CPU在空闲的状态下也不能闲着)
StackType_t xIdleTaskStack[100];
StaticTask_t xIdleTaskTCB;// 提供空闲栈的大小
void vApplicationGetIdleTaskMemory(StaticTask_t ** ppxIdleTaskTCBBuffer,StackType_t ** ppxIdleTaskStackBuffer,uint32_t * pulIdleTaskStackSize
){*ppxIdleTaskTCBBuffer = &xIdleTaskTCB;*ppxIdleTaskStackBuffer = xIdleTaskStack;*pulIdleTaskStackSize = 100;
}int main( void )
{#ifdef DEBUGdebug();
#endifprvSetupHardware();printf("Hello, world!\r\n");xTaskCreate(Task1Function,"Task1",100,NULL,2,&xHandleTask1);xTaskCreate(Task2Function,"Task2",100,NULL,1,NULL);xHandleTask3 = xTaskCreateStatic(Task3Function,"task3",100,NULL,1,xTask3Stack,&xTask3TCB);vTaskStartScheduler();/* Will only get here if there was not enough heap space to create theidle task. */return 0;
}

PWM波形展示

4. 空闲任务及其钩子函数

对应程序:10_freertos_example_idletask,在05_freertos_example_createtask基础上修改

  • 任务后的清理工作在哪执行?分两类:

    • 自杀的任务:在空闲任务中完成清理工作,比如释放内存(都自杀了,怎么清理自己的尸体? 由别人来做)

    • 非自杀的任务:在vTaskDelete内部完成清理工作(凶手执行清理工作)

  • 空闲任务何时才能执行?

  • 空闲任务只能处于这2个状态之一:Running、Ready

  • 空闲任务钩子函数

    • 执行一些低优先级的、后台的、需要连续执行的函数

    • 测量系统的空闲时间:空闲任务能被执行就意味着所有的高优先级任务都停止了,所以测量空闲任 务占据的时间,就可以算出处理器占用率。

    • 让系统进入省电模式:空闲任务能被执行就意味着没有重要的事情要做,当然可以进入省电模式 了。

    • 绝对不能导致任务进入Blocked、Suspended状态

    • 如果你会使用 vTaskDelete() 来删除任务,那么钩子函数要非常高效地执行。如果空闲任务移植 卡在钩子函数里的话,它就无法释放内存。

空闲任务和钩子函数

定时宏:实现函数(这个钩子函数有一定的限制就是不能让空闲的任务进入空闲的状态或者是阻塞的状态要让空闲的任务执行一些清理的状态,因此空闲的任务要么处于运行状态,要么处于就绪状态永远不能处于阻塞状态,钩子函数执行的速度要越快越好)

实现代码

void Task2Function(void * param);// 这个是任务的标志位
static int task1flagrun = 0;
static int task2flagrun = 0;
static int taskidleflagrun = 0;// 在主函数中创建任务1,在任务1里面创建任务2,并在任务1中删除任务2
void Task1Function(void * param)
{TaskHandle_t xHandleTask2;BaseType_t xReturn;while (1){// 在任务1里面设置对应的变量task1flagrun = 1;task2flagrun = 0;taskidleflagrun = 0;printf("1");// 保存这个函数的返回值xReturn = xTaskCreate(Task2Function, "Task2", 1024, NULL, 2, &xHandleTask2);// 判断如果等于pass的话表示返回成功if (xReturn != pdPASS)printf("xTaskCreate err\r\n");//vTaskDelete(xHandleTask2);}
}void Task2Function(void * param)
{while (1){// 在任务2里面设置自己对应的变量task1flagrun = 0;task2flagrun = 1;taskidleflagrun = 0;printf("2");   // 观察变量的高低电平//vTaskDelay(2);vTaskDelete(NULL);}
}// 这个是任务调度函数:也就是钩子函数
void vApplicationIdleHook( void )
{task1flagrun = 0;task2flagrun = 0;taskidleflagrun = 1;	printf("0");
}/*-----------------------------------------------------------*/int main( void )
{TaskHandle_t xHandleTask1;#ifdef DEBUGdebug();
#endifprvSetupHardware();printf("Hello, world!\r\n");// 设置任务1的优先级将任务1的优先级设置为0xTaskCreate(Task1Function, "Task1", 100, NULL, 0, &xHandleTask1);/* Start the scheduler. */vTaskStartScheduler();/* Will only get here if there was not enough heap space to create theidle task. */return 0;
}

实现函数(钩子函数不能放在死循环中不然空闲任务就不能执行其他的操作比如清理工作)

5. 任务调度算法

正在运行的任务,被称为"正在使用处理器",它处于运行状态。在单处理器系统中,任何时间里只能有一个任务处于运行状态。

非运行状态的任务,它处于这3种状态之一:

  • 阻塞(Blocked)

  • 暂停(Suspended)

  • 就绪(Ready)

就绪态的任务,可以被调度器挑选出来切换为运行状态,调度器永远都是挑选最高优先级的就绪态任务并让它进入运行状态。

阻塞状态的任务,它在等待"事件",当事件发生时任务就会进入就绪状态。

事件分为两类:

  • 时间相关的事件

    • 所谓时间相关的事件,就是设置超时时间:在指定时间内阻塞,时间到了就进入就绪状态。

    • 使用时间相关的事件,可以实现周期性的功能、可以实现超时功能。

  • 同步事件

    • 同步事件就是:某个任务在等待某些信息,别的任务或者中断服务程序会给它发送信息。

    • 怎么"发送信息"?方法很多

      • 任务通知(task notification)

      • 队列(queue)

      • 事件组(event group)

      • 信号量(semaphoe)

      • 互斥量(mutex)等

      • 这些方法用来发送同步信息,比如表示某个外设得到了数据。

static void prvSetupHardware( void );static volatile int flagIdleTaskrun = 0;  // 空闲任务运行时flagIdleTaskrun=1
static volatile int flagTask1run = 0;     // 任务1运行时flagTask1run=1
static volatile int flagTask2run = 0;     // 任务2运行时flagTask2run=1
static volatile int flagTask3run = 0;     // 任务3运行时flagTask3run=1/*-----------------------------------------------------------*/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)
{flagIdleTaskrun = 1;flagTask1run = 0;flagTask2run = 0;flagTask3run = 0;	/* 故意加入打印让flagIdleTaskrun变为1的时间维持长一点 *///printf("Id\r\n");				
}int main( void )
{prvSetupHardware();//动态创建任务1优先级是0xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);//动态创建任务2优先级是0xTaskCreate(vTask2, "Task 2", 1000, NULL, 0, NULL);//动态创建任务3优先级是2xTaskCreate(vTask3, "Task 3", 1000, NULL, 2, NULL);/* 启动调度器 */vTaskStartScheduler();/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */return 0;
}

此处的高优先级的先执行

 

以下是任务调度相关部分的代码

/* Demo app includes. */
static void prvSetupHardware( void );static volatile int flagIdleTaskrun = 0;  // 空闲任务运行时flagIdleTaskrun=1
static volatile int flagTask1run = 0;     // 任务1运行时flagTask1run=1
static volatile int flagTask2run = 0;     // 任务2运行时flagTask2run=1
static volatile int flagTask3run = 0;     // 任务3运行时flagTask3run=1/*-----------------------------------------------------------*/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)
{flagIdleTaskrun = 1;flagTask1run = 0;flagTask2run = 0;flagTask3run = 0;	/* 故意加入打印让flagIdleTaskrun变为1的时间维持长一点 *///printf("Id\r\n");				
}int main( void )
{prvSetupHardware();//动态创建任务1优先级是0xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);//动态创建任务2优先级是0xTaskCreate(vTask2, "Task 2", 1000, NULL, 0, NULL);//动态创建任务3优先级是2xTaskCreate(vTask3, "Task 3", 1000, NULL, 2, NULL);/* 启动调度器 */vTaskStartScheduler();/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */return 0;
}

这种调度策略表示的是不允许抢占,(使用这种方式编写代码最好是在做完一些事情之后放弃占用CPU的资源)

 

#define configUSE_PREEMPTION 1  是否支持抢占式调度 #define configUSE_TIME_SLICING 1              时间片轮转(默认的情况下应该是支持时间片轮转的)

这个是是否支持时间片轮转的的任务函数(PWM)波形

#define configIDLE_SHOULD_YIELD 1 这个表示的是空闲的任务应该空闲礼让他人,将GPU资源让给用户任务

这个是是否空闲任务礼让其他的CPU任务的PWM波形

这个是礼让(空闲的任务礼让其他的任务)

这个是不礼让(空闲的任务不礼让,也其他的任务一样抢占CPU的资源)

5.2 调度策略

  • 是否抢占?

  • #define configUSE_PREEMPTION 1 是否支持抢占式调度

  • 允许抢占时,是否允许时间片轮转?

  • #define configUSE_TIME_SLICING 1 时间片轮转(默认的情况下应该是支持时间片轮转的)

  • 允许抢占、允许时间片轮转时,空闲任务是否让步?

  • #define configIDLE_SHOULD_YIELD 1 这个表示的是空闲的任务应该空闲礼让他人,将GPU资源让给用户任务

相关文章:

FreeRTOS任务管理

1. 任务状态理论讲解 定时器职中断周期此处的1000Hz表示的是没次间隔1毫秒就记一次数&#xff08;在FreeConfig.h&#xff09;文件中进行配置 #define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) 判断是否需要任务切换在FreeRTOS里面每次间隔1毫秒切换一次&#xff08;程序…...

Qwen1.5大语言模型微调实践

在人工智能领域&#xff0c;大语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;的兴起和广泛应用&#xff0c;为自然语言处理&#xff08;NLP&#xff09;带来了前所未有的变革。Qwen1.5大语言模型作为其中的佼佼者&#xff0c;不仅拥有强大的语言生成和理…...

购物车实现

目录 1.购物车常见的实现方式 2.购物车数据结构介绍 3.实例分析 1.controller层 2.service层 1.购物车常见的实现方式 方式一&#xff1a;存储到数据库 性能存在瓶颈方式二&#xff1a;前端本地存储 localstorage在浏览器中存储 key/value 对&#xff0c;没有过期时间。s…...

HTML5漫画风格个人介绍源码

源码介绍 HTML5漫画风格个人介绍源码&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面&#xff0c;重定向这个界面 效果截图 源码下载 HTML5漫画风格…...

工业数学模型——高炉煤气发生量预测(三)

1、工业场景 冶金过程中生产的各种煤气&#xff0c;例如高炉煤气、焦炉煤气、转炉煤气等。作为重要的副产品和二次能源&#xff0c;保证它们的梯级利用和减少放散是煤气能源平衡调控的一项紧迫任务&#xff0c;准确的预测煤气的发生量是实现煤气系统在线最优调控的前提。 2、…...

pnpm - Failed to resolve loader: cache-loader. You may need to install it.

起因 工作原因需要研究 vue-grid-layout 的源码&#xff0c;于是下载到本地。因为我习惯使用 pnpm&#xff0c;所以直接用 pnpm i 安装依赖&#xff0c;npm run serve 启动失败。折腾了一番没成功。 看到源码里有 yarn.lock&#xff0c;于是重新用 yarn install 安装依赖&…...

CSS transition和animation的用法和区别

Transition和Animation在CSS中都是用于实现元素状态变化的效果&#xff0c;但它们在用法和特性上存在明显的区别。 Transition transition是过度属性&#xff0c;主要强调的是元素状态的过渡效果。 它通常用于在元素的状态发生变化时&#xff0c;平滑地过渡到一个新的状态。…...

书籍推荐(附上每本书的看点)

1、《FPGA深度解析》&#xff0c;这本书的FIFO部分我觉得讲得很好; 2、《verilog数字系统设计教程》&#xff0c;夏宇闻老师的蓝皮书&#xff0c;这本书里包含很多考试知识点; 3、《SOC设计方法和实现》郭炜老师写的&#xff0c;我觉得他的低功耗设计讲得很好; 《高级FPGA设计结…...

LLM理解v1

答疑 什么是知识库&#xff1f; LLM&#xff08;Large Language Models&#xff0c;大型语言模型&#xff09;如GPT系列&#xff0c;通常是基于海量的文本数据进行训练的。它们通过分析和理解这些数据来生成回答、撰写文章、解决问题等。当我们提到LLM的“本地知识库”时&…...

ubuntu 22.04 -- cmake安装

安装方式一&#xff1a;源码安装 1、下载安装包 官网下载&#xff1a;下载链接&#xff1a;Download CMake 也可以使用命令行下载 wget https://github.com/Kitware/CMake/releases/download/v3.26.5/cmake-3.26.5.tar.gz2、解压并安装 # 1、解压 tar -zxvf cmake-3.26.5.…...

字符串算法题(第二十四天)

344. 反转字符串 题目 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间&#xff0c;你必须**原地修改输入数组**、使用 O(1) 的额外空间解决这一问题。 示例 1&#xff1a; 输入&#xff1…...

【Linux】应用层协议序列化和反序列化

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;题目解析 &#x1f30e;推荐文章&#xff1a;C【智能指针】 前言 在正式代码开始前&#xff0c;会有一些前提知识引入 目录 &#x1f449;&#x1f3fb;序列…...

使用Canal同步MySQL 8到ES中小白配置教程

&#x1f680; 使用Canal同步MySQL 8到ES中小白配置教程 &#x1f680; 文章目录 &#x1f680; 使用Canal同步MySQL 8到ES中小白配置教程 &#x1f680;**摘要****引言****正文**&#x1f4d8; 第1章&#xff1a;初识Canal1.1 Canal概述1.2 工作原理解析 &#x1f4d8; 第2章&…...

关于部署ELK和EFLK的相关知识

文章目录 一、ELK日志分析系统1、ELK简介1.2 ElasticSearch1.3 Logstash1.4 Kibana&#xff08;展示数据可视化界面&#xff09;1.5 Filebeat 2、使用ELK的原因3、完整日志系统的基本特征4、ELK的工作原理 二、部署ELK日志分析系统1、服务器配置2、关闭防火墙3、ELK ElasticSea…...

实验室信息系统源码 saas模式java+.Net Core版开发的云LIS系统全套源码可二次开发有演示

实验室信息系统源码 saas模式java.Net Core版开发的云LIS系统全套源码可二次开发有演示 一、技术框架 技术架构&#xff1a;Asp.NET CORE 3.1 MVC SQLserver Redis等 开发语言&#xff1a;C# 6.0、JavaScript 前端框架&#xff1a;JQuery、EasyUI、Bootstrap 后端框架&am…...

PCB---Design Entry cis 绘图 导出

修改纸张大小&#xff1a; 画图前准备&#xff1a;导入 画图&#xff1a; 习惯&#xff1a; 电源朝上 地朝下 配置pbc_footprint编号&#xff1a; 都配置好编号就可以导出了 导出&#xff1a;...

vue 一键更换主题颜色

这里提供简单的实现步骤&#xff0c;具体看自己怎么加到项目中 我展示的是vue2 vue3同理 在 App.vue 添加 入口处直接修改 #app { // 定义的全局修改颜色变量--themeColor:#008cff; } // 组件某些背景颜色需要跟着一起改变&#xff0c;其他也是同理 /deep/ .ant-btn-primar…...

WebKit内核游览器

WebKit内核游览器 基础概念游览器引擎Chromium 浏览器架构Webkit 资源加载这里就不得不提到http超文本传输协议这个概念了&#xff1a; 游览器多线程HTML 解析总结 基础概念 百度百科介绍 WebKit 是一个开源的浏览器引擎&#xff0c;与之相对应的引擎有Gecko&#xff08;Mozil…...

Qt 拖放功能详解:理论与实践并举的深度指南

拖放&#xff08;Drag and Drop&#xff09;作为一种直观且高效的用户交互方式&#xff0c;在现代图形用户界面中扮演着重要角色。Qt 框架提供了完善的拖放支持&#xff0c;允许开发者在应用程序中轻松实现这一功能。本篇博文将详细阐述Qt拖放机制的工作原理&#xff0c;结合详…...

Springboot+Vue项目-基于Java+MySQL的企业客户管理系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目&#xff0c;所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

Element Plus 表单(el-form)中关于正整数输入的校验规则

目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入&#xff08;联动&#xff09;2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...