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毫秒就记一次数(在FreeConfig.h)文件中进行配置 #define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) 判断是否需要任务切换在FreeRTOS里面每次间隔1毫秒切换一次(程序…...

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

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

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

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

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

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

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

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

ubuntu 22.04 -- cmake安装
安装方式一:源码安装 1、下载安装包 官网下载:下载链接: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. 反转字符串 题目 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间,你必须**原地修改输入数组**、使用 O(1) 的额外空间解决这一问题。 示例 1: 输入࿱…...

【Linux】应用层协议序列化和反序列化
欢迎来到Cefler的博客😁 🕌博客主页:折纸花满衣 🏠个人专栏:题目解析 🌎推荐文章:C【智能指针】 前言 在正式代码开始前,会有一些前提知识引入 目录 👉🏻序列…...

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

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

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

PCB---Design Entry cis 绘图 导出
修改纸张大小: 画图前准备:导入 画图: 习惯: 电源朝上 地朝下 配置pbc_footprint编号: 都配置好编号就可以导出了 导出:...

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

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

Qt 拖放功能详解:理论与实践并举的深度指南
拖放(Drag and Drop)作为一种直观且高效的用户交互方式,在现代图形用户界面中扮演着重要角色。Qt 框架提供了完善的拖放支持,允许开发者在应用程序中轻松实现这一功能。本篇博文将详细阐述Qt拖放机制的工作原理,结合详…...

Springboot+Vue项目-基于Java+MySQL的企业客户管理系统(附源码+演示视频+LW)
大家好!我是程序猿老A,感谢您阅读本文,欢迎一键三连哦。 💞当前专栏:Java毕业设计 精彩专栏推荐👇🏻👇🏻👇🏻 🎀 Python毕业设计 &…...

【Linux学习】Linux指令(四)
文章标题 🚀zip/unzip指令:🚀tar指令(重要):🚀uname –r指令:🚀关机指令🚀几个常用操作 🚀zip/unzip指令: zip 与 unzip的安装 yum i…...

阿里云服务器 使用Certbot申请免费 HTTPS 证书及自动续期
前言 Certbot是一款免费且开源的自动化安全证书管理工具,由电子前沿基金会(EFF)开发和维护,是在Linux、Apache和Nginx服务器上配置和管理SSL/TLS证书的一种机制。Certbot可以自动完成域名的认证并安装证书。 一、 安装软件 1.1…...

统一SQL-number/decimal/dec/numeric转换
统一SQL介绍 https://www.light-pg.com/docs/LTSQL/current/index.html 源和目标 源数据库:Oracle 目标数据库:Postgresql,TDSQL-MySQL,达梦8,LightDB-Oracle 操作目标 通过统一SQL,将Oracle中的numb…...

软件测试入门学习笔记
系统测试流程规范 一.研发模型 1.瀑布模型 从可行性研究(或系统分析)开始,需求 2.增量迭代模型 3.敏捷开发模型 二.质量模型...

31. 下一个排列
题目描述 整数数组的一个排列 就是将其所有成员以序列或线性顺序排列。 例如,arr [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。 整数数组的下一个排列是指其整数的下一个字典序更大的排列。更正式地&…...

Android笔记: mkdirs不生效失败
Manifest已经配置权限,代码中也动态获取权限,mkdirs一直返回false File.mkdirs()方法创建文件夹失败 1、动态申请读写权限 <!--SDCard写权限--> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!--SDCard读权…...

需要添加的硬币的最小数量(Lc2952)——贪心+构造
给你一个下标从 0 开始的整数数组 coins,表示可用的硬币的面值,以及一个整数 target 。 如果存在某个 coins 的子序列总和为 x,那么整数 x 就是一个 可取得的金额 。 返回需要添加到数组中的 任意面值 硬币的 最小数量 ,使范围 …...

军工保密资质介绍及申请要求
军工保密资质介绍 军工保密资质是指国家对从事军工研发、生产、销售等活动的企事业单位进行的一种资质认证。该资质的核心目标是保护国家军事机密和军事技术秘密,确保国家安全和国防利益。军工保密资质的认证标准非常严格,涉及企业的安全管理、技术保密…...

ES6的编程风格
ES6 提出了两个新的声明变量的命令:let和const。其中,let完全可以取代var,因为两者语义相同,而且let没有副作用。 var命令存在变量提升效用,let命令没有这个问题 if (true) {console.log(x); // ReferenceErrorlet x…...

springboot 载入自定义的yml文件转DTO
json解析的pom引入 <dependency><groupId>cn.hutool</groupId><artifactId>hutool-json</artifactId><version>5.8.20</version></dependency>resources目录下的my-data.yml project:data:- name: service-genbase-package:…...