FreeRTOS 时间管理

news2024/11/17 11:55:27

文章目录

  • 一、FreeRTOS 延时函数
    • 1. 函数 vTaskDelay()
    • 2. 函数 prvAddCurrentTaskToDelayedList()
    • 3. 函数 vTaskDelayUntil()
  • 二、FreeRTOS 系统时钟节拍


一、FreeRTOS 延时函数

1. 函数 vTaskDelay()

在 FreeRTOS 中延时函数也有相对模式和绝对模式,不过在 FreeRTOS 中不同的模式用的函数不同,其中函数 vTaskDelay()是相对模式(相对延时函数),函数 vTaskDelayUntil()是绝对模式(绝对延时函数)。函数 vTaskDelay()在文件 tasks.c 中有定义,要使用此函数的话宏 INCLUDE_vTaskDelay 必须为 1,函数代码如下:

void vTaskDelay( const TickType_t xTicksToDelay )
{
	BaseType_t xAlreadyYielded = pdFALSE;
	//延时时间要大于 0。
	if( xTicksToDelay > ( TickType_t ) 0U ) (1)
	{
		configASSERT( uxSchedulerSuspended == 0 );
		vTaskSuspendAll(); (2)
		{
			traceTASK_DELAY();
			prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE ); (3)
		}
		xAlreadyYielded = xTaskResumeAll(); (4)
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
	if( xAlreadyYielded == pdFALSE ) (5)
	{
		portYIELD_WITHIN_API(); (6)
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
}

(1)、延时时间由参数 xTicksToDelay 来确定,为要延时的时间节拍数,延时时间肯定要大
于 0。否则的话相当于直接调用函数 portYIELD()进行任务切换。

(2)、调用函数 vTaskSuspendAll()挂起任务调度器。

(3) 、 调 用 函 数 prvAddCurrentTaskToDelayedList() 将 要 延 时 的 任 务 添 加 到 延 时 列 表pxDelayedTaskList 或 者 pxOverflowDelayedTaskList() 中 。 后 面 会 具 体 分 析 函 数
prvAddCurrentTaskToDelayedList()。

(4)、调用函数 xTaskResumeAll()恢复任务调度器。

(5)、如果函数 xTaskResumeAll()没有进行任务调度的话那么在这里就得进行任务调度。

(6)、调用函数 portYIELD_WITHIN_API()进行一次任务调度。

2. 函数 prvAddCurrentTaskToDelayedList()

函数 prvAddCurrentTaskToDelayedList()用于将当前任务添加到等待列表中,函数在文件
tasks.c 中有定义,缩减后的函数如下:

static void prvAddCurrentTaskToDelayedList( TickType_t x TicksToWait, const BaseType_t xCanBlockIndefinitely )
{
	TickType_t xTimeToWake;
	const TickType_t xConstTickCount = xTickCount; (1)
	#if( INCLUDE_xTaskAbortDelay == 1 )
	{
		//如果使能函数 xTaskAbortDelay()的话复位任务控制块的 ucDelayAborted 字段为
		//pdFALSE。
		pxCurrentTCB->ucDelayAborted = pdFALSE;
	}
	#endif
	
	if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) (2)
	{
		portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); (3)
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
	
	#if ( INCLUDE_vTaskSuspend == 1 )
	{
		if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) )(4)
		{
			vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) ); (5)
		}
		else
		{
			xTimeToWake = xConstTickCount + xTicksToWait; (6)
			listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), \ (7)
			xTimeToWake );
			if( xTimeToWake < xConstTickCount ) (8)
			{
				vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->\ (9)
				xStateListItem ) );
			}
		   else
			{
				vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) ); (10)
				if( xTimeToWake < xNextTaskUnblockTime ) (11)
				{
					xNextTaskUnblockTime = xTimeToWake; (12)
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
	}
	/***************************************************************************/
	/****************************其他条件编译语句*******************************/
	/***************************************************************************/
}

}
(1)、读取进入函数 prvAddCurrentTaskToDelayedList()的时间点并保存在 xConstTickCount 中,后面计算任务唤醒时间点的时候要用到。xTickCount 是时钟节拍计数器,每个滴答定时器中断xTickCount 都会加一。

(2)、要将当前正在运行的任务添加到延时列表中,肯定要先将当前任务从就绪列表中移除。

(3)、将当前任务从就绪列表中移除以后还要取消任务在 uxTopReadyPriority 中的就绪标记。
也就是将 uxTopReadyPriority 中对应的 bit 清零。

(4) 、 延 时 时 间 为 最 大 值 portMAX_DELAY , 并 且 xCanBlockIndefinitely 不 为pdFALSE(xCanBlockIndefinitely 不为 pdFALSE 的话表示允许阻塞任务)的话直接将当前任务添加到挂起列表中,任务就不用添加到延时列表中。

(5)、将当前任务添加到挂起列表 xSuspendedTaskList 的末尾。

(6)、计算任务唤醒时间点,也就是(1)中获取到的进入函数prvAddCurrentTaskToDelayedList()的时间值 xConstTickCount 加上延时时间值 xTicksToWait。

(7)、将计算到的任务唤醒时间点值 xTimeToWake 写入到任务列表中壮态列表项的相应字
段中。

(8)、计算得到的任务唤醒时间点小于 xConstTickCount,说明发生了溢出。全局变量
xTickCount 是 TickType_t 类型的,这是个 32 位的数据类型,因此在用 xTickCount 计算任务唤醒时间点xTimeToWake的时候的肯定会出现溢出的现象。FreeRTOS针对此现象专门做了处理,在 FreeROTS 中定义了两个延时列表 xDelayedTaskList1 和 xDelayedTaskList2,并且也定义了两个指针 pxDelayedTaskList 和 pxOverflowDelayedTaskList 来访问这两个列表,在初始化列表函数prvInitialiseTaskLists() 中 指 针 pxDelayedTaskList 指 向 了 列 表 xDelayedTaskList1 ,指针pxOverflowDelayedTaskList 指向了列表 xDelayedTaskList2。这样发生溢出的话就将任务添加到pxOverflowDelayedTaskList 所指向的列表中,如果没有溢出的话就添加到 pxDelayedTaskList 所指向的列表中。

(9)、如果发生了溢出的话就将当前任务添加到pxOverflowDelayedTaskList所指向的列表中。

(10)、如果没有发生溢出的话就将当前任务添加到 pxDelayedTaskList 所指向的列表中。

(11)、xNextTaskUnblockTime 是个全局变量,保存着距离下一个要取消阻塞的任务最小时
间点值。 当 xTimeToWake 小于 xNextTaskUnblockTime 的话说明有个更小的时间点来了。

(12)、更新 xNextTaskUnblockTime 为 xTimeToWake。

3. 函数 vTaskDelayUntil()

函数 vTaskDelayUntil()会阻塞任务,阻塞时间是一个绝对时间,那些需要按照一定的频率
运行的任务可以使用函数 vTaskDelayUntil()。此函数再文件 tasks.c 中有如下定义:

void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )
{
	TickType_t xTimeToWake;
	BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;
	configASSERT( pxPreviousWakeTime );
	configASSERT( ( xTimeIncrement > 0U ) );
	configASSERT( uxSchedulerSuspended == 0 );
	vTaskSuspendAll(); (1)
	{
		const TickType_t xConstTickCount = xTickCount; (2)
		xTimeToWake = *pxPreviousWakeTime + xTimeIncrement; (3)
		if( xConstTickCount < *pxPreviousWakeTime ) (4)
		{
			if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake >\ (5)
				xConstTickCount ) )
			{
				xShouldDelay = pdTRUE; (6)
			}
		   else
		   {
				mtCOVERAGE_TEST_MARKER();
		   }
  	   }
	   else
		{
			if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > \ (7)
			xConstTickCount ) )
			{
				xShouldDelay = pdTRUE; (8)
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		*pxPreviousWakeTime = xTimeToWake; (9)
		if( xShouldDelay != pdFALSE ) (10)
		{
			traceTASK_DELAY_UNTIL( xTimeToWake );
			prvAddCurrentTaskToDelayedList( xTimeToWake - xConstTickCount, pdFALSE );(11)
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	xAlreadyYielded = xTaskResumeAll(); (12)
	if( xAlreadyYielded == pdFALSE )
	{
		ortYIELD_WITHIN_API();
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
}

参数:
pxPreviousWakeTime: 上一次任务延时结束被唤醒的时间点,任务中第一次调用函数
vTaskDelayUntil 的话需要将 pxPreviousWakeTime 初始化进入任务的 while()循环体的时间点值。在以后的运行中函数 vTaskDelayUntil()会自动更新 pxPreviousWakeTime。
xTimeIncrement: 任务需要延时的时间节拍数(相对于 pxPreviousWakeTime 本次延时的节拍数)。

(1)、挂起任务调度器。

(2)、记录进入函数 vTaskDelayUntil()的时间点值,并保存在 xConstTickCount 中。

(3)、根据延时时间 xTimeIncrement 来计算任务下一次要唤醒的时间点,并保存在
xTimeToWake 中。可以看出这个延时时间是相对于 pxPreviousWakeTime 的,也就是上一次任务被唤醒的时间点。pxPreviousWakeTime、xTimeToWake、xTimeIncrement 和 xConstTickCount 的关系如下图所示。
在这里插入图片描述如上图中 (1)为任务主体,也就是任务真正要做的工作,(2)是任务函数中调用vTaskDelayUntil()对任务进行延时,(3)为其他任务在运行。任务的延时时间是xTimeIncrement,这个延时时间是相对于 pxPreviousWakeTime 的,可以看出任务总的执行时间一定要小于任务的延时时间 xTimeIncrement!也就是说如果使用 vTaskDelayUntil()的话任务相当于任务的执行周期永远都是 xTimeIncrement,而任务一定要在这个时间内执行完成。这样就保证了任务永远按照一定的频率运行了,这个延时值就是绝对延时时间,因此函数 vTaskDelayUntil()也叫做绝对延时函数。

(4)、根据图 12.3.1 可以看出,理论上 xConstTickCount 要大于 pxPreviousWakeTime 的,但是也有一种情况会导致 xConstTickCount 小于 pxPreviousWakeTime,那就是xConstTickCount 溢出了!

(5)、既然 xConstTickCount 都溢出了,那么计算得到的任务唤醒时间点肯定也是要溢出的,
并且 xTimeToWake 肯定也是要大于 xConstTickCount 的。这种情况如下图所示:
在这里插入图片描述
(6)、如果满足(5)条件的话就将 pdTRUE 赋值给 xShouldDelay,标记允许延时。

(7)、还有其他两种情况,一:只有 xTimeToWake 溢出,二:都没有溢出。只有 xTimeToWake溢出的话如下图所示:
在这里插入图片描述
都不溢出的话就如图 12.3.1 所示,这两种情况都允许进行延时。

(8)、将 pdTRUE 赋值给 xShouldDelay,标记允许延时。

(9)、更新 pxPreviousWakeTime 的值,更新为 xTimeToWake,为本函数的下一次执行做准
备。

(10)、经过前面的判断,允许进行任务延时。

(11)、调用函数 prvAddCurrentTaskToDelayedList()进行延时。函数的第一个参数是设置任务
的阻塞时间,前面我们已经计算出了任务下一次唤醒时间点了,那么任务还需要阻塞的时间就是下一次唤醒时间点 xTimeToWake 减去当前的时间 xConstTickCount。而在函数 vTaskDelay()中只是简单的将这参数设置为 xTicksToDelay。

(12)、调用函数 xTaskResumeAll()恢复任务调度器。
函数 vTaskDelayUntil()的使用方法如下:

void TestTask( void * pvParameters )
{
	TickType_t PreviousWakeTime;
	//延时 50ms,但是函数 vTaskDelayUntil()的参数需要设置的是延时的节拍数,不能直接
	//设置延时时间,因此使用函数 pdMS_TO_TICKS 将时间转换为节拍数。
	const TickType_t TimeIncrement = pdMS_TO_TICKS( 50 );
	PreviousWakeTime = xTaskGetTickCount(); //获取当前的系统节拍值
	for( ;; )
	{
		/******************************************************************/
		/*************************任务主体*********************************/
		/******************************************************************/
		//调用函数 vTaskDelayUntil 进行延时
		vTaskDelayUntil( &PreviousWakeTime, TimeIncrement);
	}
}

其实使用函数 vTaskDelayUntil()延时的任务也不一定就能周期性的运行,使用函数vTaskDelayUntil()只能保证你按照一定的周期取消阻塞,进入就绪态。如果有更高优先级或者中断的话你还是得等待其他的高优先级任务或者中断服务函数运行完成才能轮到你。这个绝对延时只是相对于 vTaskDelay()这个简单的延时函数而言的。

二、FreeRTOS 系统时钟节拍

不管是什么系统,运行都需要有个系统时钟节拍,前面已经提到多次了,xTickCount 就是
FreeRTOS 的系统时钟节拍计数器。每个滴答定时器中断中 xTickCount 就会加一xTickCount 的具体操作过程是在函数 xTaskIncrementTick()中进行的,此函数在文件 tasks.c 中有定义,如下:

BaseType_t xTaskIncrementTick( void )
{
	TCB_t * pxTCB;
	TickType_t xItemValue;
	BaseType_t xSwitchRequired = pdFALSE;
	//每个时钟节拍中断(滴答定时器中断)调用一次本函数,增加时钟节拍计数器 xTickCount 的
	//值,并且检查是否有任务需要取消阻塞。
	traceTASK_INCREMENT_TICK( xTickCount );
	if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) (1)
	{
		const TickType_t xConstTickCount = xTickCount + 1; (2)
		//增加系统节拍计数器 xTickCount 的值,当为 0,也就是溢出的话就交换延时和溢出列
		//表指针值。
		xTickCount = xConstTickCount;
		if( xConstTickCount == ( TickType_t ) 0U ) (3)
		{
			taskSWITCH_DELAYED_LISTS(); (4)
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
		//判断是否有任务延时时间到了,任务都会根据唤醒时间点值按照顺序(由小到大的升
		//序排列)添加到延时列表中,这就意味这如果延时列表中第一个列表项对应的任务的
		//延时时间都没有到的话后面的任务就不用看了,肯定也没有到。
		if( xConstTickCount >= xNextTaskUnblockTime ) (5)
		{
			for( ;; )
			{
				if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) (6)
				{
					//延时列表为空,设置 xNextTaskUnblockTime 为最大值。
					xNextTaskUnblockTime = portMAX_DELAY; (7)
					break;
				}
				else
				{
					//延时列表不为空,获取延时列表的第一个列表项的值,根据判断这个值
					//判断任务延时时间是否到了, 如果到了的话就将任务移除延时列表。
					pxTCB = ( TCB_t * )\ (8)
					 listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
					xItemValue =\ (9)
					listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );
					if( xConstTickCount < xItemValue ) (10)
					{
						//任务延时时间还没到,但是 xItemValue 保存着下一个即将解除
						//阻塞态的任务对应的解除时间点,所以需要用 xItemValue 来更新
						//变量 xNextTaskUnblockTime
						xNextTaskUnblockTime = xItemValue; (11)
						break;
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
					//将任务从延时列表中移除
					( void ) uxListRemove( &( pxTCB->xStateListItem ) ); (12)
					//任务是否还在等待其他事件?如信号量、队列等,如果是的话就将这些
					//任务从相应的事件列表中移除。相当于等待事件超时退出!
					if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) !\ (13)
					= NULL )
					{
						( void ) uxListRemove( &( pxTCB->xEventListItem ) ); (14)
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
					//将任务添加到就绪列表中
					prvAddTaskToReadyList( pxTCB ); (15)
					#if ( configUSE_PREEMPTION == 1 )
					{
						//使用抢占式内核,判断解除阻塞的任务优先级是否高于当前正在
						//运行的任务优先级,如果是的话就需要进行一次任务切换!
						if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) (16)
						{
							xSwitchRequired = pdTRUE;
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					#endif /* configUSE_PREEMPTION */
				}
			}
		}
		//如果使能了时间片的话还需要处理同优先级下任务之间的调度
		#if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) )(17)
		{
			if( listCURRENT_LIST_LENGTH( &( \
				pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 )
			{
				xSwitchRequired = pdTRUE;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif 
		
		//使用时钟节拍钩子函数
		#if ( configUSE_TICK_HOOK == 1 )
		{
			if( uxPendedTicks == ( UBaseType_t ) 0U )
			{
				vApplicationTickHook(); (18)
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* configUSE_TICK_HOOK */
	
	}
	else //任务调度器挂起 (19)
	{
		++uxPendedTicks; (20)
		#if ( configUSE_TICK_HOOK == 1 )
		{
			vApplicationTickHook();
		}
		#endif
	}
	#if ( configUSE_PREEMPTION == 1 )
	{
		if( xYieldPending != pdFALSE ) (21)
		{
			xSwitchRequired = pdTRUE;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	#endif /* configUSE_PREEMPTION */
	return xSwitchRequired; (22)
}

}
(1)、判断任务调度器是否被挂起。

(2)、将时钟节拍计数器 xTickCount 加一,并将结果保存在 xConstTickCount 中,下一行程
序会将 xConstTickCount 赋值给 xTickCount,相当于给 xTickCount 加一。

(3)、xConstTickCount 为 0,说明发生了溢出!

(4)、如果发生了溢出的话使用函数 taskSWITCH_DELAYED_LISTS 将延时列表指针
pxDelayedTaskList 和溢出列表指针 pxOverflowDelayedTaskList 所指向的列表进行交换,函数taskSWITCH_DELAYED_LISTS()本质上是个宏,在文件 tasks.c 中有定义,将这两个指针所指向的列表交换以后还需要更新 xNextTaskUnblockTime 的值。

(5)、变量 xNextTaskUnblockTime 保存着下一个要解除阻塞的任务的时间点值,如果
xConstTickCount 大于 xNextTaskUnblockTime 的话就说明有任务需要解除阻塞了。

(6)、判断延时列表是否为空。

(7)、如果延时列表为空的话就将 xNextTaskUnblockTime 设置为 portMAX_DELAY。

(8)、延时列表不为空,获取延时列表第一个列表项对应的任务控制块。

(9)、获取(8)中获取到的任务控制块中的壮态列表项值。

(10)、任务控制块中的壮态列表项值保存了任务的唤醒时间点,如果这个唤醒时间点值大于
当前的系统时钟(时钟节拍计数器值),说明任务的延时时间还未到。

(11)、任务延时时间还未到,而且 xItemValue 已经保存了下一个要唤醒的任务的唤醒时间
点,所以需要用 xItemValue 来更新 xNextTaskUnblockTime。

(12)、任务延时时间到了,所以将任务先从延时列表中移除。

(13)、检查任务是否还等待某个事件,比如等待信号量、队列等。如果还在等待的话就任务
从相应的事件列表中移除。因为超时时间到了!

(14)、将任务从相应的事件列表中移除。

(15)、任务延时时间到了,并且任务已经从延时列表或者事件列表中已经移除。所以这里需
要将任务添加到就绪列表中。

(16)、延时时间到的任务优先级高于正在运行的任务优先级,所以需要进行任务切换了,标
记 xSwitchRequired 为 pdTRUE,表示需要进行任务切换。

(17)、如果使能了时间片调度的话,还要处理跟时间片调度有关的工作,具体过程参考 9.6
小节。

(18)、如果使能了时间片钩子函数的话就执行时间片钩子函数 vApplicationTickHook(),函
数的具体内容由用户自行编写。

(19)、如果调用函数 vTaskSuspendAll()挂起了任务调度器的话在每个滴答定时器中断就不
不会更新 xTickCount 了。取而代之的是用 uxPendedTicks 来记录调度器挂起过程中的时钟节拍数。这样在调用函数 xTaskResumeAll()恢复任务调度器的时候就会调用 uxPendedTicks 次函数xTaskIncrementTick(),这样 xTickCount 就会恢复,并且那些应该取消阻塞的任务都会取消阻塞。

函数 xTaskResumeAll()中相应的处理代码如下:

BaseType_t xTaskResumeAll( void )
{
	TCB_t *pxTCB = NULL;
	BaseType_t xAlreadyYielded = pdFALSE;
	configASSERT( uxSchedulerSuspended );
	taskENTER_CRITICAL();
	/************************************************************************/
	/****************************省略部分代码********************************/
	/************************************************************************/
	UBaseType_t uxPendedCounts = uxPendedTicks; 
	if( uxPendedCounts > ( UBaseType_t ) 0U )
	{
		//do-while()循环体,循环次数为 uxPendedTicks
		do
		{
			if( xTaskIncrementTick() != pdFALSE ) //调用函数 xTaskIncrementTick
			{
				xYieldPending = pdTRUE; //标记需要进行任务调度。
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
			--uxPendedCounts; //变量减一
		} while( uxPendedCounts > ( UBaseType_t ) 0U );
		uxPendedTicks = 0; //循环执行完毕,uxPendedTicks 清零
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
	/************************************************************************/
	/****************************省略部分代码********************************/
	/************************************************************************/
	taskEXIT_CRITICAL();
	return xAlreadyYielded;
}

(20)、uxPendedTicks 是个全局变量,在文件 tasks.c 中有定义,任务调度器挂起以后此变量
用来记录时钟节拍数。

(21)、有时候调用其他的 API 函数会使用变量 xYieldPending 来标记是否需要进行上下文切
换,后面具体遇到具体分析。

(22)、返回 xSwitchRequired 的值,xSwitchRequired 保存了是否进行任务切换的信息,如果为 pdTRUE 的话就需要进行任务切换,pdFALSE 的话就不需要进行任务切换。函数
xPortSysTickHandler()中调用 xTaskIncrementTick()的时候就会判断返回值,并且根据返回值决定是否进行任务切换。

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

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

相关文章

epoll进阶

epoll除了提供select/poll那种IO事件的电平触发&#xff08;Level Triggered&#xff09;外&#xff0c;还提供了边沿触发&#xff08;Edge Triggered&#xff09;&#xff0c;这就使得用户空间程序有可能缓存IO状态&#xff0c;减少epoll_wait/epoll_pwait的调用&#xff0c;提…

经典文献阅读之--Orbeez-SLAM(单目稠密点云建图)

0. 简介 对于现在的VSLAM而言&#xff0c;现在越来越多的工作开始聚焦于如何将深度学习结合到VSLAM当中&#xff0c;而最近的这个工作就给出了一个比较合适的方法。《Orbeez-SLAM: A Real-time Monocular Visual SLAM with ORB Features and NeRF-realized Mapping》这篇文章&…

如何将模块加载到linux内核

一 顺利的情况 假设存在一个文件叫mymq.c,下该文件相同目录下的makefile如下语句&#xff1a; obj-y mymq.o 然后编译&#xff1a;编译完成了以后&#xff0c;mymq.c文件中&#xff0c;有个函数叫mymq_open,搜索这个函数在不在System.map文件中&#xff0c;如果在&#xff…

大屏使用echart开发省市地图数据,并点击省获取市地图数据

1. 本文在基础上进行改进&#xff0c;后端使用若依后端 IofTV-Screen: &#x1f525;一个基于 vue、datav、Echart 框架的物联网可视化&#xff08;大屏展示&#xff09;模板&#xff0c;提供数据动态刷新渲染、屏幕适应、数据滚动配置&#xff0c;内部图表自由替换、Mixins注入…

CDN与网络安全

DDoS攻击的影响远不止眼前所见。这些攻击不仅会造成巨大的经济损失&#xff0c;还会对受害公司或组织的声誉和形象产生严重影响。研究表明&#xff0c;受害公司至少需要10个小时才能开始解决攻击&#xff0c;而解除还需要4.5个小时。甚至在检测到攻击之前平均数小时&#xff0c…

一文详解Spring事务传播机制

背景 我们在使用Spring管理数据库事务的时候很方便&#xff0c;只需要在代理对象中引入注解Transactional 就可以开启事务了。在使用Transactional时&#xff0c;一般主要关心两个方面&#xff0c;一个是异常回滚的定义&#xff08;设置rollbackFor&#xff09;&#xff0c;另…

Python统计学:如何理解单样本t检验?

单样本的t检验 指样本的均值是否某个值存在差异。 比如一包薯片标的克重为50g&#xff0c;但每包不一定都是50g&#xff0c;那么我们可以对薯片进行随机抽样&#xff0c;检验它与50g是否有差异。 1 提出假设&#xff1a; 原假设&#xff1a;薯片的平均重量是50g&#xff1b; …

模板方法设计模式解读

目录 豆浆制作问题 模板方法模式基本介绍 基本介绍 模板方法模式的原理类图 模板方法模式解决豆浆制作问题 应用实例要求 思路分析和图解(类图) 模板方法模式的钩子方法 模板方法模式的注意事项和细节 豆浆制作问题 编写制作豆浆的程序&#xff0c;说明如下: 1) 制作豆…

Adobe认证是什么?

Adobe认证又称为Adobe国际认证(英文:Adobe Certified Professional)是Adobe公司CEO签发的权威国际认证体系&#xff0c;旨在为用户提供Adobe软件的专业认证。 Adobe认证包括产品技能认证和职业技能认证多个级别&#xff0c;从初学者到专业人士都可以参加。 Adobe认证覆盖了各…

ORA-27090故障,关于AIO-MAX-NR

在给某银行进行巡检时发现asm中的alert一直报ORA-27090错误。 通过巡检脚本&#xff0c;整理错误发生时间如下&#xff1a; 信息收集&#xff1a; 发生类似的错误&#xff0c;先收集alert日志的信息&#xff0c;操心系统的message日志。 Errors in file /u01/app/grid/diag/…

适合小白学习预处理与程序环境,这篇文章就够了

目录 一. 前言 二. 正文 2.1 “冷知识”&#xff1a;程序环境 2.21 翻译环境——程序从无到有&#xff1a;程序编译 链接 2.22 运行环境——程序开跑 2.3 那些鲜为人知&#xff1a;预定义符号 2.4 预处理指令 #define 2. 41 #和## —— 2.42 宏和函数优劣对比 2.…

阿里全新推出:微服务突击手册,把所有操作都写出来了

今天给大家带来的这份微服务是由阿里巴巴高级技术专家整理&#xff0c;针对Spring Cloud在国内的使用情况&#xff0c;结合国内上百家企业使用Spring Cloud落地微服务架构时遇到的问题和相应的解决方案结合成了这份电子版教程。&#xff08;文末有免费的获取方式&#xff09; …

火伞云APP盾,您身边的DDoS攻击安全保护专家

近年来全球各地区DDoS攻击的比例和次数在逐年增加&#xff0c;严重影响到网络信息安全。 主要有以下原因&#xff1a; 攻击成本低&#xff0c;攻击无壁垒。进行DDoS攻击成本很低&#xff0c;最低甚至只需要几百元就可以轻松发起一次攻击&#xff0c;然而被攻击的对象可能动辄…

3自由度并联绘图机器人实现写字功能(一)

1. 功能说明 本文示例将实现R305样机3自由度并联绘图机器人写字的功能。 2. 电子硬件 在这个示例中&#xff0c;采用了以下硬件&#xff0c;请大家参考&#xff1a; 主控板 Basra主控板&#xff08;兼容Arduino Uno&#xff09; 扩展板Bigfish2.1扩展板电池7.4V锂电池 3. 功能…

九种 OOM 常见原因及解决方案(IT枫斗者)

九种 OOM 常见原因及解决方案(IT枫斗者) 什么是OOM&#xff1f; OOM&#xff0c;全称“Out Of Memory”&#xff0c;翻译成中文就是“内存用完了”&#xff0c;来源于java.lang.OutOfMemoryError。看下关于的官方说明&#xff1a;Thrown when the Java Virtual Machine canno…

使用 Amazon SageMaker 构建文本摘要应用

背景介绍 文本摘要&#xff0c;就是对给定的单个或者多个文档进行梗概&#xff0c;即在保证能够反映原文档的重要内容的情况下&#xff0c;尽可能地保持简明扼要。质量良好的文摘能够在信息检索过程中发挥重要的作用&#xff0c;比如利用文摘代替原文档参与索引&#xff0c;可…

数据结构复习题(包含答案)

第一章 概论 一、选择题 1、研究数据结构就是研究&#xff08; D &#xff09;。 A. 数据的逻辑结构 B. 数据的存储结构 C. 数据的逻辑结构和存储结构 D. 数据的逻辑结构、存储结构及其基本操作 2、算法分析的两个主要方面是&#xff08; A …

Multi-modal Alignment using Representation Codebook

Multi-modal Alignment using Representation Codebook 题目Multi-modal Alignment using Representation Codebook译题使用表示子空间的多模态对齐期刊/会议CVPR 摘要&#xff1a;对齐来自不同模态的信号是视觉语言表征学习&#xff08;representation learning&#xff09;…

SpringMVC文件上传、异常处理、拦截器

SpringMVC文件上传、异常处理、拦截器 基本配置准备&#xff1a;maven项目模块 application.xml <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.…

数据库系统-数据物理存储

文章目录 一、DBMS原理1.1 DB物理存储1.1.1 磁盘的结构&特性1.1.2 DBMS数据存储&查询原理记录&#xff1a;磁盘块 1.2 DB文件组织方法1.2.1 无序文件组织1.2.2 有序记录文件1.2.3 散列文件(Hash File)1.2.4 聚簇文件(Clustering File) 1.3 Oracle 物理存储简介 一、DBM…