FreeRTOS 信号量(四) ------ 互斥信号量

news2024/11/26 10:24:48

文章目录

  • 一、互斥信号量简介
  • 二、创建互斥信号量
    • 1. xSemaphoreCreateMutex()
    • 2. xSemaphoreCreateMutexStatic()
  • 三、互斥信号量创建过程分析
  • 四、释放互斥信号量
  • 五、获取互斥信号量


一、互斥信号量简介

互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中(任务与任务或中断与任务之间的同步)二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中。在互斥访问中互斥信号量相当于一个钥匙,当任务想要使用资源的时候就必须先获得这个钥匙,当使用完资源以后就必须归还这个钥匙,这样其他的任务就可以拿着这个钥匙去使用资源。

互斥信号量使用和二值信号量相同的 API 操作函数,所以互斥信号量也可以设置阻塞时间,不同于二值信号量的是互斥信号量具有优先级继承的特性。当一个互斥信号量正在被一个低优先级的任务使用,而此时有个高优先级的任务也尝试获取这个互斥信号量的话就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级,这个过程就是优先级继承。优先级继承尽可能的降低了高优先级任务处于阻塞态的时间,并且将已经出现的“优先级翻转”的影响降到最低。

优先级继承并不能完全的消除优先级翻转,它只是尽可能的降低优先级翻转带来的影响。硬实时应用应该在设计之初就要避免优先级翻转的发生。互斥信号量不能用于中断服务函数中,原因如下:
● 互斥信号量有优先级继承的机制,所以只能用在任务中,不能用于中断服务函数。
● 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。

二、创建互斥信号量

FreeRTOS 提供了两个互斥信号量创建函数,如下表所示:
在这里插入图片描述

1. xSemaphoreCreateMutex()

此函数用于创建一个互斥信号量,所需要的内存通过动态内存管理方法分配。此函数本质是一个宏,真正完成信号量创建的是函数 xQueueCreateMutex(),此函数原型如下:

SemaphoreHandle_t xSemaphoreCreateMutex( void )

参数:
无。

返回值:
NULL: 互斥信号量创建失败。
其他值: 创建成功的互斥信号量的句柄。

2. xSemaphoreCreateMutexStatic()

此函数也是创建互斥信号量的,只不过使用此函数创建互斥信号量的话信号量所需要的RAM 需要由用户来分配,此函数是个宏,具体创建过程是通过函数 xQueueCreateMutexStatic ()来完成的,函数原型如下:

SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer )

参数:
pxMutexBuffer: 此参数指向一个 StaticSemaphore_t 类型的变量,用来保存信号量结构体。

返回值:
NULL: 互斥信号量创建失败。
其他值: 创建成功的互斥信号量的句柄。

三、互斥信号量创建过程分析

这里只分析动态创建互斥信号量函数 xSemaphoreCreateMutex (),此函数是个宏,定义如下:

#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )

可以看出,真正干事的是函数 xQueueCreateMutex(),此函数在文件 queue.c 中有如下定义,

QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType )
{
	Queue_t *pxNewQueue;
	const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0;
	pxNewQueue = ( Queue_t * ) xQueueGenericCreate( uxMutexLength, uxMutexSize,\ (1)
	 ucQueueType );
	prvInitialiseMutex( pxNewQueue ); (2)
	return pxNewQueue;
}

(1)、 调用函数 xQueueGenericCreate()创建一个队列,队列长度为 1,队列项长度为 0,队列类型为参数 ucQueueType。由于本函数是创建互斥信号量的,所以参数 ucQueueType 为queueQUEUE_TYPE_MUTEX。

(2)、 调用函数 prvInitialiseMutex()初始化互斥信号量。
函数 prvInitialiseMutex()代码如下:

static void prvInitialiseMutex( Queue_t *pxNewQueue )
{
	if( pxNewQueue != NULL )
	{
		//虽然创建队列的时候会初始化队列结构体的成员变量,但是此时创建的是互斥
		//信号量,因此有些成员变量需要重新赋值,尤其是那些用于优先级继承的。
		pxNewQueue->pxMutexHolder = NULL; (1)
		pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX; (2)
		//如果是递归互斥信号量的话。
		pxNewQueue->u.uxRecursiveCallCount = 0; (3)
		traceCREATE_MUTEX( pxNewQueue );
		//释放互斥信号量
		( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U,\
		 queueSEND_TO_BACK );
	}
	else
	{
		traceCREATE_MUTEX_FAILED();
	}
}

(1)和(2)、pxMutexHolder 和 uxQueueType这两个成员变量这两个是宏,专门为互斥信号量准备的,在文件 queue.c 中有如下定义:

#define pxMutexHolder pcTail
#define uxQueueType pcHead
#define queueQUEUE_IS_MUTEX NULL

当 Queue_t 用于表示队列的时候 pcHead 和 pcTail 指向队列的存储区域,当 Queue_t 用于表示互斥信号量的时候就不需要 pcHead 和 pcTail 了。当用于互斥信号量的时候将 pcHead 指向NULL 来表示 pcTail 保存着互斥队列的所有者,pxMutexHolder 指向拥有互斥信号量的那个任务的任务控制块。重命名 pcTail 和 pcHead 就是为了增强代码的可读性。

(3)、 如果创建的互斥信号量是互斥信号量的话,还需要初始化队列结构体中的成员变量 u.uxRecursiveCallCount。

互斥信号量创建成功以后会调用函数 xQueueGenericSend()释放一次信号量,说明互斥信号量默认就是有效的!互斥信号量创建完成以后如下图所示:
在这里插入图片描述

四、释放互斥信号量

释放互斥信号量的时候和二值信号量 、计数型信号量一样,都是 用的函数xSemaphoreGive()(实际上完成信号量释放的是函数 xQueueGenericSend())。不过由于互斥信号量涉及到优先级继承的问题,所以具体处理过程会有点区别。使用函数xSemaphoreGive()释放信号量最重要的一步就是将 uxMessagesWaiting加一,而这一步就是通过函数prvCopyDataToQueue() 来完成的,释放信号量的函数xQueueGenericSend() 会调用prvCopyDataToQueue()。互斥信号量的优先级继承也是在函数 prvCopyDataToQueue()中完成的,此函数中有如下一段代码:

static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, 
									  const void * pvItemToQueue, 
									  const BaseType_t xPosition )
{
	BaseType_t xReturn = pdFALSE;
	UBaseType_t uxMessagesWaiting;
	uxMessagesWaiting = pxQueue->uxMessagesWaiting;
	if( pxQueue->uxItemSize == ( UBaseType_t ) 0 )
	{
		#if ( configUSE_MUTEXES == 1 ) //互斥信号量
		{
			if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) (1)
			{
				xReturn = xTaskPriorityDisinherit( ( void * ) pxQueue->pxMutexHolder );(2)
				pxQueue->pxMutexHolder = NULL; (3)
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* configUSE_MUTEXES */
	}
	/*********************************************************************/
	/*************************省略掉其他处理代码**************************/
	/*********************************************************************/
	pxQueue->uxMessagesWaiting = uxMessagesWaiting + 1;
	return xReturn;
}

(1)、当前操作的是互斥信号量。

(2)、调用函数 xTaskPriorityDisinherit()处理互斥信号量的优先级继承问题。

(3)、互斥信号量释放以后,互斥信号量就不属于任何任务了,所以 pxMutexHolder 要指向NULL。

在 来 看 一 下 函 数 xTaskPriorityDisinherit() 是怎么具体的处理优先级继承的,函数xTaskPriorityDisinherit()代码如下:

BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder )
{
	TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;
	BaseType_t xReturn = pdFALSE;
	if( pxMutexHolder != NULL ) (1)
	{
		//当一个任务获取到互斥信号量以后就会涉及到优先级继承的问题,正在释放互斥
		//信号量的任务肯定是当前正在运行的任务 pxCurrentTCB。
		configASSERT( pxTCB == pxCurrentTCB );
		configASSERT( pxTCB->uxMutexesHeld );
		( pxTCB->uxMutexesHeld )--; (2)
		//是否存在优先级继承?如果存在的话任务当前优先级肯定和任务基优先级不同。
		if( pxTCB->uxPriority != pxTCB->uxBasePriority ) (3)
		{
			//当前任务只获取到了一个互斥信号量
			if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 ) (4)
			{
				if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) (5)
				{
					taskRESET_READY_PRIORITY( pxTCB->uxPriority ); (6)
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
				//使用新的优先级将任务重新添加到就绪列表中
				traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority );
				pxTCB->uxPriority = pxTCB->uxBasePriority; (7)
				/* Reset the event list item value. It cannot be in use for
				any other purpose if this task is running, and it must be
				running to give back the mutex. */
				listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), \ (8)
				( TickType_t ) configMAX_PRIORITIES - \
				( TickType_t ) pxTCB->uxPriority );
				prvAddTaskToReadyList( pxTCB ); (9)
				xReturn = pdTRUE; (10)
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	else
	{
	mtCOVERAGE_TEST_MARKER();
	}
	return xReturn;
}

(1)、函数的参数 pxMutexHolder 表示拥有此互斥信号量任务控制块,所以要先判断此互斥信号量是否已经被其他任务获取。

(2)、有的任务可能会获取多个互斥信号量,所以就需要标记任务当前获取到的互斥信号量个数,任务控制块结构体的成员变量uxMutexesHeld 用来保存当前任务获取到的互斥信号量个数。任务每释放一次互斥信号量,变量 uxMutexesHeld 肯定就要减一。

(3)、判断是否存在优先级继承,如果存在的话任务的当前优先级肯定不等于任务的基优先级。

(4)、判断当前释放的是不是任务所获取到的最后一个互斥信号量,因为如果任务还获取了其他互斥信号量的话就不能处理优先级继承。优先级继承的处理必须是在释放最后一个互斥信号量的时候。

(5)、优先级继承的处理说白了就是将任务的当前优先级降低到任务的基优先级,所以要把当前任务先从任务就绪表中移除。当任务优先级恢复为原来的优先级以后再重新加入到就绪表中。

(6)、如果任务继承来的这个优先级对应的就绪表中没有其他任务的话就将取消这个优先级的就绪态。

(7)、重新设置任务的优先级为任务的基优先级 uxBasePriority。

(8)、复位任务的事件列表项。

(9)、将优先级恢复后的任务重新添加到任务就绪表中。

(10)、返回 pdTRUE,表示需要进行任务调度。

五、获取互斥信号量

获取互斥信号量的函数同获取二值信号量和计数型信号量的函数相同,都是xSemaphoreTake()(实际执行信号量获取的函数是xQueueGenericReceive()),获取互斥信号量的过程也需要处理优先级继承的问题,函数 xQueueGenericReceive()在文件 queue.c 中有定义,在前面队列的时候没有分析这个函数,这里就来简单的分析一下这个函数,缩减后的函数代码如下:

BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t 
xTicksToWait, const BaseType_t xJustPeeking )
{
	BaseType_t xEntryTimeSet = pdFALSE;
	TimeOut_t xTimeOut;
	int8_t *pcOriginalReadPosition;
	Queue_t * const pxQueue = ( Queue_t * ) xQueue;
	for( ;; )
	{
		taskENTER_CRITICAL();
	{
	const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;
	//判断队列是否有消息
	if( uxMessagesWaiting > ( UBaseType_t ) 0 ) (1)
	{
		pcOriginalReadPosition = pxQueue->u.pcReadFrom;
		prvCopyDataFromQueue( pxQueue, pvBuffer ); (2)
		if( xJustPeeking == pdFALSE ) (3)
		{
			traceQUEUE_RECEIVE( pxQueue );
			//移除消息
			pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1; (4)
			#if ( configUSE_MUTEXES == 1 ) (5)
			{
				if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
				{
					pxQueue->pxMutexHolder = (6)
					 ( int8_t * ) pvTaskIncrementMutexHeldCount(); 
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			#endif /* configUSE_MUTEXES */
			//查看是否有任务因为入队而阻塞,如果有的话就需要解除阻塞态。
			if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == (7)
			pdFALSE )
			{
				if( xTaskRemoveFromEventList( &
				( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
				{
					//如果解除阻塞的任务优先级比当前任务优先级高的话就需要
					//进行一次任务切换
					queueYIELD_IF_USING_PREEMPTION();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		else (8)
		{
			traceQUEUE_PEEK( pxQueue );
			//读取队列中的消息以后需要删除消息
			pxQueue->u.pcReadFrom = pcOriginalReadPosition;
			//如果有任务因为出队而阻塞的话就解除任务的阻塞态。
			if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == (9)
			pdFALSE )
			{
				if( xTaskRemoveFromEventList( &
				( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
				{
					//如果解除阻塞的任务优先级比当前任务优先级高的话就需要
					//进行一次任务切换
					queueYIELD_IF_USING_PREEMPTION();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		taskEXIT_CRITICAL();
		return pdPASS;
	}
	else //队列为空 (10)
	{
		if( xTicksToWait == ( TickType_t ) 0 )
		{
			//队列为空,如果阻塞时间为 0 的话就直接返回 errQUEUE_EMPTY
			taskEXIT_CRITICAL();
			traceQUEUE_RECEIVE_FAILED( pxQueue );
			return errQUEUE_EMPTY;
		}
		else if( xEntryTimeSet == pdFALSE )
		{
			//队列为空并且设置了阻塞时间,需要初始化时间状态结构体。
			vTaskSetTimeOutState( &xTimeOut );
			xEntryTimeSet = pdTRUE;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
}
		taskEXIT_CRITICAL();
		vTaskSuspendAll();
		prvLockQueue( pxQueue );
		//更新时间状态结构体,并且检查超时是否发生
		if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) (11)
		{
			if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) (12)
			{
				traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );
				#if ( configUSE_MUTEXES == 1 )
				{
					if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) (13)
					{
						taskENTER_CRITICAL();
						{
							vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder );(14)
						}
						taskEXIT_CRITICAL();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				#endif
				vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), (15)
				xTicksToWait );
				prvUnlockQueue( pxQueue );
				if( xTaskResumeAll() == pdFALSE )
				{
					portYIELD_WITHIN_API();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				//重试一次
				prvUnlockQueue( pxQueue );
				( void ) xTaskResumeAll();
			}
		}
		else
		{
			prvUnlockQueue( pxQueue );
			( void ) xTaskResumeAll();
			if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
			{
				traceQUEUE_RECEIVE_FAILED( pxQueue );
				return errQUEUE_EMPTY;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
	}
}

(1)、队列不为空,可以从队列中提取数据。

(2)、调用函数 prvCopyDataFromQueue()使用数据拷贝的方式从队列中提取数据。

(3)、数据读取以后需要将数据删除掉。

(4)、队列的消息数量计数器 uxMessagesWaiting 减一,通过这一步就将数据删除掉了。

(5)、表示此函数是用于获取互斥信号量的。

(6)、获取互斥信号量成功,需要标记互斥信号量的所有者,也就是给 pxMutexHolder 赋值,pxMutexHolder 应 该 是 当 前 任 务 的 任 务 控 制 块 。 但 是 这 里 是 通 过 函 数pvTaskIncrementMutexHeldCount()来赋值的,此函数很简单,只是将任务控制块中的成员变量uxMutexesHeld 加一,表示任务获取到了一个互斥信号量,最后此函数返回当前任务的任务控制块。

(7)、出队成功以后判断是否有任务因为入队而阻塞的,如果有的话就需要解除任务的阻塞态,如果解除阻塞的任务优先级比当前任务的优先级高还需要进行一次任务切换。

(8)、出队的时候不需要删除消息。

(9)、如果出队的时候不需要删除消息的话那就相当于刚刚出队的那条消息接着有效!既然还有有效的消息存在队列中,那么就判断是否有任务因为出队而阻塞,如果有的话就解除任务的阻塞态。同样的,如果解除阻塞的任务优先级比当前任务的优先级高的话还需要进行一次任务切换。

(10)、上面分析的都是队列不为空的时候,那当队列为空的时候该如何处理呢?处理过程和队列的任务级通用入队函数xQueueGenericSend()类似。如果阻塞时间为 0 的话就就直接返回errQUEUE_EMPTY,表示队列空,如果设置了阻塞时间的话就进行相关的处理。

(11)、检查超时是否发生,如果没有的话就需要将任务添加到队列的 xTasksWaitingToReceive列表中。

(12)、检查队列是否继续为空?如果不为空的话就会在重试一次出队。

(13)、表示此函数是用于获取互斥信号量的。

(14)、调用函数 vTaskPriorityInherit()处理互斥信号量中的优先级继承问题,如果函数xQueueGenericReceive()用于获取互斥信号量的话,此函数执行到这里说明互斥信号量正在被其他的任务占用。此函数和 14.8.4 小节中的函数 xTaskPriorityDisinherit()过程相反。此函数会判断当前任务的任务优先级是否比正在拥有互斥信号量的那个任务的任务优先级高,如果是的话就会把拥有互斥信号量的那个低优先级任务的优先级调整为与当前任务相同的优先级!

(15)、经过(12)步判断,队列依旧为空,那么就将任务添加到列表 xTasksWaitingToReceive中。

在上面的分析中,红色部分就是当函数 xQueueGenericReceive()用于互斥信号量的时候的处理过程,其中(13)和(14)条详细的分析了互斥信号量优先级继承的过程。我们举个例子来简单的演示一下这个过程,假设现在有两个任务 HighTask 和 LowTask,HighTask 的任务优先级为 4,LowTask 的任务优先级为 2。这两个任务都会操同一个互斥信号量 Mutex,LowTask 先获取到互斥信号量 Mutex。此时任务 HighTask 也要获取互斥信号量 Mutex,任务HighTask 调用函数xSemaphoreTake()尝试获取互斥信号量 Mutex,发现此互斥信号量正在被任务 LowTask 使用,并且 LowTask 的任务优先级为 2,比自己的任务优先级小,因为任务 HighTask 就会将 LowTask的任务优先级调整为与自己相同的优先级,即 4,然后任务 HighTask 进入阻塞态等待互斥信号量有效。

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

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

相关文章

【报错解决】错误代码18456,SQL Server 登录失败

【报错解决】错误代码18456,SQL Server 登录失败 一、故障原因二、解决办法2.1 使用Windows身份认证登录2.2 windows身份登录后,依次选择:安全性->登录名->sa,然后右击选择属性2.3 在常规选项中重新设置密码2.4 在设置中选择…

创新案例|语言教育App头牌Duolingo如何重新点燃用户增长350%

Duolingo是全球最大的语言教育APP,拥有数亿用户,然而用户增长正在放缓,本案例以Duolingo增长 通过数据建模洞察关键指标,并围绕指标用增长实验驱动,设计植根于创新的增长模式,包括启动排行榜,重…

基于MBD的控制系统建模与仿真软件工具集

随着新能源汽车和自动驾驶技术的快速发展,汽车电子电气架构的发展已成为汽车行业推陈出新的主要动力:车内电控系统变得越来越复杂、软件迭代周期越来越短,汽车电子软件开发和测试的质量与效率要求也越来越高。汽车电控系统的设计开发已然成为…

定时器+中断 闪烁led

文章目录 运行环境:1.1 定时器和中断1)定时器2)轮询和中断 2.1配置1)定时器配置2)中断配置3)RCC和SYS 3.1代码分析3.2添加代码1)中断处理函数IRQ中添加代码2)launch设置 4.1定时器启动和定时器中断启动函数5.1实验效果 运行环境: ubuntu18.0…

VSCode 上的 swift 开发配置

安装Xcode和VsCode 在下列网址下载安装即可 VsCode: https://code.visualstudio.com/ Xcode:https://developer.apple.com/xcode/resources/ 或者apptore 打开xcode要求安装的东西都允许安装一下 启用 Swift 语言支持 确保你已经安装了 Xcode 和 VSCode。这是开始运行的最简…

【ITSS】信息技术服务标准(ITSS)的介绍以及发展历程

信息技术服务标准(ITSS)介绍 ITSS是Information TechnologyService Standards的缩写,中文意思是信息技术服务标准,是在工业和信息化部、国家标准化委的领导和支持下,由ITSS工作组研制的一套IT服务领域的标准库和一套提供IT服务的方…

AD9208的4通道 14-bit、2.4GSPS采样率之中文版资料

板卡概述 FMC137 是一款基于 VITA57.4 标准规范的 JESD204B 接口FMC 子 卡 模 块 , 该 模 块 可 以 实 现 4 路 14-bit 、 2GSPS/2.6GSPS/3GSPSADC 采集功能。该板卡 ADC 器件采用 ADI 公司的 AD9208 芯片,,与 ADI 公司的 AD9689 可以实现…

python 零基础入门难度如何?

在入门前先来了解一下Python是什么。 Python,他其实是一种受众非常广的语言,简单易学,在网上有大把大把的入门教程,学习曲线平滑。除了“简单”“万能”之外,还有众多库,Python的标准库非常强大&#xff0…

TEMPUS FUGIT: 1

环境准备 靶机链接:百度网盘 请输入提取码 提取码:d3du 虚拟机网络链接模式:NET模式 攻击机系统:kali linux 2022.03 信息收集 探测目标靶机开放端口和服务情况。 nmap -p- -A -sV 192.168.255.132 nmap --scriptvuln -p …

lvs作业

文章目录 NAT模式DR模式 基于 CentOS 7 构建 LVS-DR 群集。 对比 LVS 负载均衡群集的 NAT 模式和 DR 模式,比较其各自的优势 。基于 CentOS 7 构建 LVS-DR 群集。 NAT模式 在 LVS 的 NAT 模式中,LVS 将客户端请求的 IP 地址和端口号修改为 LVS 的 NAT …

golang/goland memo

文章目录 golanggolang开发工具goland Build constraints exclude all the Go files in xxxxxxgoland 解决 Unresolved reference xxx问题goland 解决 cannot resolve directory xxxx问题 golang GOROOT:Go的安装目录。 GOPATH 是一个环境变量,用于指定…

软件设计师笔记--计算机系统知识

文章目录 前言学习资料计算机系统CPU运算器控制器进制原码反码补码移码浮点数寻址奇偶校验码海明码循环冗余校验码RISC和CISC流水线存储器Cache中断输入输出控制方式总线加密技术与认证技术加密算法可靠性公式 前言 博主是非科班出身的,但从大一开始自学编程&#…

【JAVA程序设计】(C00132)基于SSM的固定资产管理系统

基于SSM的固定资产管理系统 项目简介项目获取开发环境项目技术运行截图 项目简介 本系统为基于SSM的固定资产管理系统,本系统分为二种用户:超级管理员和普通管理员; 超级管理员功能: 首页查看、设备管理、平台账户管理、设备台账…

JavaScript经典教程(五)-- JavaScript基础 -- for、while、forEach、递归、字符串

186&#xff1a;JavaScript基础 - for、while、forEach、递归、字符串 1、循环 &#xff08;1&#xff09;for循环 1、标准语句 for(初始条件;判断条件;迭代语句){操作内容; }也可以这样写&#xff1a;把初始条件和迭代语句拆出 var a 0; for(;a < 5;){alert(a);a; }其…

【软件测试】项目测试—MySQL数据库操作应用场景?必会知识详全(超详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 数据库在软件测试…

ASEMI代理亚德诺AD8130ARZ-REEL7芯片应用与参数分析

编辑-Z 本文将对AD8130ARZ-REEL7芯片进行详细的应用与参数分析&#xff0c;包括其主要特征、接口定义、电气特性以及使用注意事项等方面&#xff0c;旨在为广大读者提供对该芯片更全面的了解。 1、主要特征 AD8130ARZ-REEL7芯片是一种用于高速、低功耗差分信号放大的电路&…

使用CXF调用WSDL

简介 时隔多年&#xff0c;再次遇到需要调用WebService的业务&#xff0c;对方给予的wsdl说明文档还是内网的链接&#xff0c;并且设有基础访问权限&#xff0c;即在浏览器打开wsdl链接时需要输入【用户名密码】登录后方可查看wsdl文档&#xff0c;这需要设置代理&#xff08;我…

( 字符串) 242. 有效的字母异位词 ——【Leetcode每日一题】

❓242. 有效的字母异位词 难度&#xff1a;简单 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 注意&#xff1a;若 s 和 t 中每个字符出现的次数都相同&#xff0c;则称 s 和 t 互为字母异位词。 示例 1: 输入: s “anagram”, t “…

【5天打卡】学习Lodash的第五天——总结篇

Lodash 含有 Array, Collection, Date, Function, Lang, Math, Number, Object, String 等多个功能模块&#xff0c;总共几百个功能函数。官方文档上以字典顺序排序&#xff0c;不容易总结记忆。通过这5天的学习&#xff0c;我们对这一框架进行总结。主要从lodash的使用优势和迷…

电磁波、射频通信基础知识科普,超通俗解释!

一、电磁波 电磁波是能量的一种&#xff0c;凡是高于绝对零度的物体&#xff0c;都会释出电磁波。电与磁可说是一体两面&#xff0c;电流会产生磁场&#xff0c;变动的磁场则会产生电流。变化的电场和变化的磁场构成了一个不可分离的统一的场。 在低频的电振荡中&#xff0c;…