FreeRTOS学习笔记—②RTOS的认识及任务管理篇

news2024/11/13 22:06:49

  由于正在学习韦东山老师的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 DEBUG
  debug();
#endif
	prvSetupHardware();
	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 the
	idle task. */
	return 0;
}

在这里插入图片描述
2.1.1 xTaskCreate
  上面使用的xTaskCreate是动态创建任务的,当然还有静态创建任务的函数xTaskCreateStatic,后面再提静态创建。下图为xTaskCreate函数的参数及介绍:
在这里插入图片描述
  下图摘自韦东山的FreeRTOS完全开发手册3.2.2节
摘自韦东山的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默认为1
			printf("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 Count
	xLastWakeTime = xTaskGetTickCount();
	/* 任务函数的主体一般都是无限循环 */
	for( ;; )
	{
		flag=1;
		for(i=0;i<5;i++){
			printf("Task 1 is running\r\n");
		}
#if 1
		vTaskDelay(xDelay50ms);
#else
		vTaskDelayUntil(&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任务都是相同优先级,因此他们会采用轮流执行。
在这里插入图片描述

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

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

相关文章

Windows系统安装node.js环境并创建本地服务使用内网穿透发布至公网

目录 前言 1.安装Node.js环境 2.创建node.js服务 3. 访问node.js 服务 4.内网穿透 4.1 安装配置cpolar内网穿透 4.2 创建隧道映射本地端口 5.固定公网地址 前言 作者简介&#xff1a; 懒大王敲代码&#xff0c;计算机专业应届生 今天给大家聊聊Windows系统安装node.js环…

Arch - 架构安全性_认证(Authentication)的标准和实现

文章目录 OverView认证的标准认证的基础认证的范围认证的标准与实践HTTP认证框架Web认证&#xff08;表单认证&#xff09;WebAuthn标准认证流程示例&#xff1a;WebAuthn 小结 认证的实现JAASSpring Security 和 Shiro小结 OverView 即使只限定在“软件架构设计”这个语境下&…

MonoHuman: Animatable Human Neural Field from Monocular Video 精读

一、共享双向变形模块 1. 模块的核心思想 共享双向变形模块的核心目标是解决从单目视频中生成不同姿态下的3D人体形状问题。因为视频中的人物可能处于各种动态姿态下&#xff0c;模型需要能够将这些不同姿态的几何形状进行变形处理&#xff0c;以适应标准的姿态表示并生成新的…

# VMware 共享文件

VMware tools快速安装 VMware 提供了 open-vm-tools&#xff0c;这是 VMware 官方推荐的开源工具包&#xff0c;通常不需要手动安装 VMware Tools&#xff0c;因为大多数 Linux 发行版&#xff08;包括 Ubuntu、CentOS 等&#xff09;都包含了 open-vm-tools&#xff0c;并且已…

FreeRTOS内部机制学习02(消息队列深度学习)

文章目录 队列的核心以及好处队列的核心队列的好处 深入源码了解队列机制深入队列读取操作深入队列写入操作读写队列出超时时间 信号量深入信号量获取以及释放操作 互斥量互斥量和信号量的不同深入源码看优先级继承是怎么操作到的 队列的核心以及好处 队列的核心 队列的核心就…

如何打造高效办公楼物业管理系统?Java SpringBoot+Vue架构详解,实现智能化管理,提升工作效率

&#x1f34a;作者&#xff1a;计算机毕设匠心工作室 &#x1f34a;简介&#xff1a;毕业后就一直专业从事计算机软件程序开发&#xff0c;至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长&#xff1a;按照需求定制化开发项目…

GraphPad Prism 10 for Mac/Win:高效统计分析与精美绘图的科学利器

GraphPad Prism 10 是一款专为科研工作者设计的强大统计分析与绘图软件&#xff0c;无论是Mac还是Windows用户&#xff0c;都能享受到其带来的便捷与高效。该软件广泛应用于生物医学研究、实验设计和数据分析领域&#xff0c;以其直观的操作界面、丰富的统计方法和多样化的图表…

Redis中使用布隆过滤器解决缓存穿透问题

一、缓存穿透(失效)问题 缓存穿透是指查询一个一定不存在的数据&#xff0c;由于缓存中没有命中&#xff0c;会去数据库中查询&#xff0c;而数据库中也没有该数据&#xff0c;并且每次查询都不会命中缓存&#xff0c;从而每次请求都直接打到了数据库上&#xff0c;这会给数据…

认知杂谈53

今天分享 有人说的一段争议性的话 I I 1.自助者天助 首先呢&#xff0c;咱得好好琢磨琢磨“自助者天助”这句话。这话说起来好像有点高深莫测的感觉&#xff0c;其实啊&#xff0c;道理特别简单。 就是说要是你自己都不乐意努力&#xff0c;那老天爷也不会平白无故地来帮你…

[环境配置]ubuntu20.04安装后wifi有图标但是搜不到热点解决方法

最近刚入手一台主机&#xff0c;暗影精灵8plus电竞主机&#xff0c;安装ubuntu后wifi怎么都搜不到热点&#xff0c;前后重装系统6次才算解决问题。这个心酸历程只有搞技术人才明白。下面介绍我解决过程。 首先主机到手后是个windows10系统&#xff0c;我用无线网连接了一下&am…

【每日刷题】Day113

【每日刷题】Day113 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 91. 解码方法 - 力扣&#xff08;LeetCode&#xff09; 2. LCR 098. 不同路径 - 力扣&#xff08;…

服务器流量监控工具vnStat的简单使用以及关于Linux的软中断信号(signal)的一点内容

一、服务器流量监控工具vnStat的简单使用 vnStat是为Linux和BSD设计的基于控制台的网络流量监控工具&#xff0c;通过它可以非常方便在命令行查看流量统计情况。它可以保留某个或多个所选择的网络接口的网络流量日志。为了生成日志&#xff0c;vnStat使用内核提供的信息。换句话…

2024年必看的4款录屏新星,谁才是你的菜?

嘿&#xff0c;小伙伴们&#xff0c;你们的职场好帮手来啦。今天我们要说说办公室里经常被忽略但实际上超有用的东西——录屏软件。现在大家都用数字化工具办公了&#xff0c;不管是做教学视频、记录会议&#xff0c;还是直播玩游戏&#xff0c;录屏软件都是必不可少的。可是市…

FRP内网穿透使用常见问题

本文解答一些关于FRP内网穿透的常见问题 FRP简介 FRP是一款开源的高性能反向代理应用&#xff0c;支持多种协议的内网穿透。它允许用户在外网环境中访问位于内网中的服务器和服务&#xff0c;如Web服务器、MySQL数据库、以及其他基于TCP/UDP的应用程序。FRP以其灵活的配置选项…

C++实现俄罗斯方块(Windows控制台版)

C实现俄罗斯方块&#xff08;Windows控制台版&#xff09; 在油管上看到一个使用C控制台编写的俄罗斯方块小游戏&#xff0c;源代码200多行&#xff0c;B站上也有相关的讲解视频&#xff0c;非常不错&#xff0c;值得学习。 B站讲解视频地址为&#xff1a;【百万好评】国外技术…

Unet改进27:添加DGCST|Vision Transformer与DGSM模块集成在一起的创新结构

本文内容:在不同位置添加DGCST 目录 论文简介 1.步骤一 2.步骤二 3.步骤三 4.步骤四 论文简介 随着移动计算技术的快速发展,在移动设备上部署高效的目标检测算法成为计算机视觉的一个关键研究领域。本研究的重点是优化YOLOv7算法,以提高其在移动平台上的运行效率和速度…

【Linux】传输层协议——UDP

零、传输层的作用是负责数据能够从发送端传输到接收端 一、再来认识一下端口号 端口号&#xff08;Port&#xff09;标识了一个主机进行通信的不同的应用程序。在TCP/IP协议中&#xff0c;用“源IP”&#xff0c;“源端口号”&#xff0c;“目的IP”&#xff0c;“目的端口号”…

Request Response

1 前言 1.1 内容概要 理解Request、Response和HTTP报文之间的关系掌握通过Request能够获得的信息 请求URL、URI、请求协议请求头、客户机和主机请求参数 掌握通过Response能够完成的设置 响应中文乱码问题响应&#xff08;Json&#xff09;字符串、图片&#xff08;文件&a…

【网络】UDP协议的简单使用

目录 服务器 客户端 测试 UDP是基于socket进行网络通信的&#xff0c;那我们这篇博客就来介绍一下基于UDP通信的基本流程&#xff0c;先让服务端和客户端进行简单的跨网络通信。 服务器 首先我们需要创建UDP套接字&#xff0c;用到的接口是 man socket 如果要使用UDP通信&am…

【Python知识宝库】面向对象编程:Python类的深度剖析

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 前言一、类的定义二、类的属性1. 类属性2. 实例属性 三、类的方法1. 实例方法2. 类方法 四、继承五、总结 前言 面向…