FreeRTOS 21:递归互斥信号量

news2024/11/26 8:45:18

递归信号量,见文知义,递归嘛,就是可以重复获取调用的,本来按照信号量的特性,每获取一次可用信号量个数就会减少一个,但是递归则然, 对于已经获取递归互斥量的 任务可以重复获取该递归互斥量, 该任务拥有递归信号量的所有权。 任务成功获取几次递归互斥量, 就要返还几次,在此之前递归互斥量都处于无效状态, 其他任务无法获取, 只有持有递归信号量的任务才能获取与释放。

递归互斥信号量可以看作是特殊的互斥信号量,与互斥信号量不同的是,递归互斥信号量在被获取后,可以被其持有者重复获取,当然递归互斥信号量的持有者需要释放递归互斥信号量与之获取递归互斥信号量相同的次数,递归互斥信号量才算被释放。

递归互斥信号量与互斥信号量一样,也具备优先级继承机制,因此也不能在中断服务函数中使用递归互斥信号量。

FreeRTOS 提供了互斥信号量的一些相关操作函数,其中常用的互斥信号量相关 API 函数, 如下表所示:

创建递归互斥信号量

xSemaphoreCreateRecursiveMutex() 动态方式创建递归互斥信号量

此函数用于使用动态方式创建递归互斥信号量,创建递归互斥信号量所需的内存,由FreeRTOS 从 FreeRTOS 管理的堆中进行分配。该函数实际上是一个宏定义,在 semphr.h 文件中有定义,具体的代码如下所示:


/**
 * semphr.h
 * @code{c}
 * SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void );
 * @endcode
 *
 * 创建一个新的递归互斥锁类型的信号量实例,并返回一个可以引用新递归互斥锁的句柄。
 *
 * 在FreeRTOS内部实现中,递归互斥锁使用一块内存来存储互斥锁结构。如果使用 `xSemaphoreCreateRecursiveMutex()` 创建递归互斥锁,则所需的内存在 `xSemaphoreCreateRecursiveMutex()` 函数内部自动动态分配。(参见 https://www.FreeRTOS.org/a00111.html)。如果使用 `xSemaphoreCreateRecursiveMutexStatic()` 创建递归互斥锁,则应用程序编写者必须提供互斥锁将使用的内存。因此,`xSemaphoreCreateRecursiveMutexStatic()` 允许在不使用任何动态内存分配的情况下创建递归互斥锁。
 *
 * 使用此宏创建的互斥锁可以通过 `xSemaphoreTakeRecursive()` 和 `xSemaphoreGiveRecursive()` 宏访问。不能使用 `xSemaphoreTake()` 和 `xSemaphoreGive()` 宏。
 *
 * 递归使用的互斥锁可以被所有者多次“获取”。互斥锁在所有者对每次成功的“获取”请求都调用 `xSemaphoreGiveRecursive()` 之前不会再次可用。例如,如果一个任务成功“获取”同一个互斥锁5次,那么在它也“释放”互斥锁5次之前,互斥锁将不可用给其他任务。
 *
 * 这种类型的信号量使用优先级继承机制,因此一个任务“获取”互斥锁后必须始终在不再需要互斥锁时“释放”互斥锁。
 *
 * 互斥锁类型的信号量不能在中断服务例程中使用。
 *
 * 请参见 `xSemaphoreCreateBinary()`,它提供了另一种实现,可用于纯同步(其中一个任务或中断总是“释放”信号量,另一个总是“获取”信号量)并在中断服务例程中使用。
 *
 * @return 返回创建的互斥锁信号量的句柄。应为 `SemaphoreHandle_t` 类型。
 *
 * 示例用法:
 * @code{c}
 * SemaphoreHandle_t xSemaphore;
 *
 * void vATask( void * pvParameters )
 * {
 *     // 在调用 xSemaphoreCreateMutex() 之前不能使用信号量。
 *     // 这是一个宏,所以直接传递变量。
 *     xSemaphore = xSemaphoreCreateRecursiveMutex();
 *
 *     if( xSemaphore != NULL )
 *     {
 *         // 互斥锁创建成功。
 *         // 现在可以使用互斥锁。
 *     }
 * }
 * @endcode
 * \defgroup xSemaphoreCreateRecursiveMutex xSemaphoreCreateRecursiveMutex
 * \ingroup Semaphores
 */
#if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) )
    #define xSemaphoreCreateRecursiveMutex()    xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )
#endif

函数 xSemaphoreCreateRecursiveMutex()实际上是调用了函数xQueueCreateMutex()创建了一个递归互斥信号量,这与互斥信号量是大致相同的。

xSemaphoreCreateRecursiveMutexStatic() 静态方式创建递归互斥信号

/**
 * semphr.h
 * @code{c}
 * SemaphoreHandle_t xSemaphoreCreateRecursiveMutexStatic( StaticSemaphore_t *pxMutexBuffer );
 * @endcode
 *
 * 创建一个新的递归互斥锁类型的信号量实例,并返回一个可以引用新递归互斥锁的句柄。
 *
 * 在FreeRTOS内部实现中,递归互斥锁使用一块内存来存储互斥锁结构。如果使用 `xSemaphoreCreateRecursiveMutex()` 创建递归互斥锁,则所需的内存在 `xSemaphoreCreateRecursiveMutex()` 函数内部自动动态分配。(参见 https://www.FreeRTOS.org/a00111.html)。如果使用 `xSemaphoreCreateRecursiveMutexStatic()` 创建递归互斥锁,则应用程序编写者必须提供互斥锁将使用的内存。因此,`xSemaphoreCreateRecursiveMutexStatic()` 允许在不使用任何动态内存分配的情况下创建递归互斥锁。
 *
 * 使用此宏创建的互斥锁可以通过 `xSemaphoreTakeRecursive()` 和 `xSemaphoreGiveRecursive()` 宏访问。不能使用 `xSemaphoreTake()` 和 `xSemaphoreGive()` 宏。
 *
 * 递归使用的互斥锁可以被所有者多次“获取”。互斥锁在所有者对每次成功的“获取”请求都调用 `xSemaphoreGiveRecursive()` 之前不会再次可用。例如,如果一个任务成功“获取”同一个互斥锁5次,那么在它也“释放”互斥锁5次之前,互斥锁将不可用给其他任务。
 *
 * 这种类型的信号量使用优先级继承机制,因此一个任务“获取”互斥锁后必须始终在不再需要互斥锁时“释放”互斥锁。
 *
 * 互斥锁类型的信号量不能在中断服务例程中使用。
 *
 * 请参见 `xSemaphoreCreateBinary()`,它提供了另一种实现,可用于纯同步(其中一个任务或中断总是“释放”信号量,另一个总是“获取”信号量)并在中断服务例程中使用。
 *
 * @param pxMutexBuffer 必须指向一个类型为 `StaticSemaphore_t` 的变量,该变量将用于保存递归互斥锁的数据结构,从而避免动态内存分配。
 *
 * @return 如果递归互斥锁成功创建,则返回一个引用创建的递归互斥锁的句柄。如果 `pxMutexBuffer` 为 `NULL`,则返回 `NULL`。
 *
 * 示例用法:
 * @code{c}
 * SemaphoreHandle_t xSemaphore;
 * StaticSemaphore_t xMutexBuffer;
 *
 * void vATask( void * pvParameters )
 * {
 *     // 递归信号量在创建之前不能使用。这里使用 `xSemaphoreCreateRecursiveMutexStatic()` 创建一个递归互斥锁。
 *     // 将 `xMutexBuffer` 的地址传递给函数,该地址将保存互斥锁的数据结构——因此不会尝试动态内存分配。
 *     xSemaphore = xSemaphoreCreateRecursiveMutexStatic( &xMutexBuffer );
 *
 *     // 由于没有进行动态内存分配,`xSemaphore` 不会为 `NULL`,因此不需要检查它。
 * }
 * @endcode
 * \defgroup xSemaphoreCreateRecursiveMutexStatic xSemaphoreCreateRecursiveMutexStatic
 * \ingroup Semaphores
 */
#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) )
    #define xSemaphoreCreateRecursiveMutexStatic( pxStaticSemaphore )    xQueueCreateMutexStatic( queueQUEUE_TYPE_RECURSIVE_MUTEX, ( pxStaticSemaphore ) )
#endif /* configSUPPORT_STATIC_ALLOCATION */

函数 xSemaphoreCreateRecursiveMutexStatuc()实际上是调用了函数 xQueueCreateMutexStatic()创建了一个递归互斥信号量,这与互斥信号量是大致相同的。

xSemaphoreTakeRecursive() 获取递归互斥信号量


/**
 * semphr.h
 * @code{c}
 * xSemaphoreTakeRecursive(
 *                          SemaphoreHandle_t xMutex,
 *                          TickType_t xBlockTime
 *                        );
 * @endcode
 *
 * 递归获取(或“占有”)一个互斥信号量类型的宏。
 * 该互斥信号量必须通过调用 xSemaphoreCreateRecursiveMutex() 创建。
 *
 * 必须在 FreeRTOSConfig.h 中将 configUSE_RECURSIVE_MUTEXES 设置为 1,才能使用此宏。
 *
 * 此宏不能用于通过 xSemaphoreCreateMutex() 创建的互斥信号量。
 *
 * 递归使用的互斥信号量可以被所有者多次“占有”。互斥信号量在所有者调用 xSemaphoreGiveRecursive() 的次数与成功“占有”请求的次数相同时才会再次可用。例如,如果一个任务成功“占有”同一个互斥信号量 5 次,则该互斥信号量在任务也“释放”该互斥信号量 5 次之前不会对任何其他任务可用。
 *
 * @param xMutex 互斥信号量的句柄。这是通过 xSemaphoreCreateRecursiveMutex() 返回的句柄。
 *
 * @param xBlockTime 等待信号量变为可用的时间(以滴答数为单位)。可以使用宏 portTICK_PERIOD_MS 将其转换为实际时间。阻塞时间为零可用于轮询信号量。如果任务已经拥有该信号量,则无论 xBlockTime 的值如何,xSemaphoreTakeRecursive() 都会立即返回。
 *
 * @return 如果成功获取信号量,则返回 pdTRUE。如果 xBlockTime 到期而信号量仍未可用,则返回 pdFALSE。
 *
 * 使用示例:
 * @code{c}
 * SemaphoreHandle_t xMutex = NULL;
 *
 * // 创建互斥信号量的任务。
 * void vATask( void * pvParameters )
 * {
 *  // 创建一个互斥信号量来保护共享资源。
 *  xMutex = xSemaphoreCreateRecursiveMutex();
 * }
 *
 * // 使用互斥信号量的任务。
 * void vAnotherTask( void * pvParameters )
 * {
 *  // ... 执行其他操作。
 *
 *  if( xMutex != NULL )
 *  {
 *      // 尝试获取互斥信号量。如果互斥信号量不可用,等待 10 个滴答时间看看它是否变为空闲。
 *      if( xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE )
 *      {
 *          // 我们能够获取互斥信号量,现在可以访问共享资源。
 *
 *          // ...
 *          // 由于代码的性质,进一步调用 xSemaphoreTakeRecursive() 占有同一个互斥信号量。在实际代码中,这些调用不会只是连续的调用,而是可能嵌套在更复杂的调用结构中。
 *          xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
 *          xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
 *
 *          // 现在互斥信号量已经被“占有”了三次,因此在也“释放”三次之前不会对其他任务可用。在实际代码中,这些调用也不会只是连续的调用,而是可能嵌套在更复杂的调用结构中。这只是为了说明目的。
 *          xSemaphoreGiveRecursive( xMutex );
 *          xSemaphoreGiveRecursive( xMutex );
 *          xSemaphoreGiveRecursive( xMutex );
 *
 *          // 现在其他任务可以获取互斥信号量。
 *      }
 *      else
 *      {
 *          // 我们无法获取互斥信号量,因此无法安全地访问共享资源。
 *      }
 *  }
 * }
 * @endcode
 * \defgroup xSemaphoreTakeRecursive xSemaphoreTakeRecursive
 * \ingroup Semaphores
 */
#if ( configUSE_RECURSIVE_MUTEXES == 1 )
    #define xSemaphoreTakeRecursive( xMutex, xBlockTime )    xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) )
#endif

函 数 xSemaphoreTakeRecursive() 实际 上是调 用 了函 数xQueueTakeMutexRecursice(),函数 xQueueTakeMutexRecursive()在 queue.c 文件中有定义,具体的代码如下所示:

#if ( configUSE_RECURSIVE_MUTEXES == 1 )

    BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex,
                                         TickType_t xTicksToWait )
    {
        BaseType_t xReturn;
        Queue_t * const pxMutex = ( Queue_t * ) xMutex;

        // 记录进入 xQueueTakeMutexRecursive 函数的事件
        traceENTER_xQueueTakeMutexRecursive( xMutex, xTicksToWait );

        // 断言互斥锁句柄不为空
        configASSERT( pxMutex );

        /* 关于互斥锁的注释,参见 xQueueGiveMutexRecursive() 中的相关注释。 */

        // 记录获取互斥锁的事件
        traceTAKE_MUTEX_RECURSIVE( pxMutex );

        // 检查当前任务是否已经持有该互斥锁
        if( pxMutex->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle() )
        {
            // 增加递归调用计数
            ( pxMutex->u.xSemaphore.uxRecursiveCallCount )++;
            // 返回成功
            xReturn = pdPASS;
        }
        else
        {
            // 尝试获取互斥锁
            xReturn = xQueueSemaphoreTake( pxMutex, xTicksToWait );

            // 如果成功获取互斥锁
            if( xReturn != pdFAIL )
            {
                // 增加递归调用计数
                ( pxMutex->u.xSemaphore.uxRecursiveCallCount )++;
            }
            else
            {
                // 记录获取互斥锁失败的事件
                traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex );
            }
        }

        // 记录返回 xQueueTakeMutexRecursive 函数的事件
        traceRETURN_xQueueTakeMutexRecursive( xReturn );

        // 返回结果
        return xReturn;
    }

#endif /* configUSE_RECURSIVE_MUTEXES */
  1. 1.进入函数

    • 记录进入 xQueueTakeMutexRecursive 函数的事件 traceENTER_xQueueTakeMutexRecursive

  2. 参数检查

    • 通过 configASSERT 确保互斥锁句柄 pxMutex 不为空。

  3. 递归检查

    • 检查当前任务是否已经持有该互斥锁,通过比较 pxMutex->u.xSemaphore.xMutexHolder 和 xTaskGetCurrentTaskHandle()

      • 如果当前任务已经持有互斥锁,增加递归调用计数 pxMutex->u.xSemaphore.uxRecursiveCallCount 并返回 pdPASS

  4. 尝试获取互斥锁

    • 如果当前任务未持有互斥锁,调用 xQueueSemaphoreTake 尝试获取互斥锁。

      • 如果成功获取互斥锁,增加递归调用计数并返回 pdPASS

      • 如果失败,记录失败事件 traceTAKE_MUTEX_RECURSIVE_FAILED 并返回 pdFAIL

  5. 记录返回事件

    • 记录返回 xQueueTakeMutexRecursive 函数的事件 traceRETURN_xQueueTakeMutexRecursive

  6. 返回结果

    • 返回操作结果 xReturn

xSemaphoreGiveRecursive()释放递归互斥信号量

此函数用于释放递归互斥信号量,函数 xSemaphoreGiveRecursive()实际上是一个宏定义,在 semphr.h 文件中有定义,具体的代码如下所示:

/**
 * semphr.h
 * 
 * @code{c}
 * xSemaphoreGiveRecursive( SemaphoreHandle_t xMutex );
 * @endcode
 *
 * 用于递归释放(或“给予”)一个互斥锁类型的信号量的宏。
 * 该互斥锁必须之前通过调用 xSemaphoreCreateRecursiveMutex() 创建。
 *
 * 必须在 FreeRTOSConfig.h 中将 configUSE_RECURSIVE_MUTEXES 设置为 1 才能使用此宏。
 *
 * 此宏不能用于通过 xSemaphoreCreateMutex() 创建的互斥锁。
 *
 * 递归使用的互斥锁可以由所有者多次“获取”。互斥锁在所有者对每次成功的“获取”请求都调用 xSemaphoreGiveRecursive() 之前不会再次可用。例如,如果一个任务成功地“获取”同一个互斥锁 5 次,那么在该任务也“释放”该互斥锁 5 次之前,互斥锁将不可用给其他任务。
 *
 * @param xMutex 要释放或“给予”的互斥锁的句柄。这是 xSemaphoreCreateMutex() 返回的句柄。
 *
 * @return 如果信号量被释放,则返回 pdTRUE。
 *
 * 示例用法:
 * @code{c}
 * SemaphoreHandle_t xMutex = NULL;
 *
 * // 创建互斥锁的任务
 * void vATask( void * pvParameters )
 * {
 *     // 创建互斥锁以保护共享资源
 *     xMutex = xSemaphoreCreateRecursiveMutex();
 * }
 *
 * // 使用互斥锁的任务
 * void vAnotherTask( void * pvParameters )
 * {
 *     // ... 执行其他操作
 *
 *     if( xMutex != NULL )
 *     {
 *         // 尝试获取互斥锁。如果互斥锁不可用,等待 10 个节拍看它是否变得可用。
 *         if( xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE )
 *         {
 *             // 我们能够获取互斥锁,现在可以访问共享资源。
 *
 *             // ...
 *             // 由于代码的性质,进一步调用 xSemaphoreTakeRecursive() 获取同一个互斥锁。在实际代码中这些调用不会是连续的,而是可能嵌套在更复杂的调用结构中。
 *             xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
 *             xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
 *
 *             // 互斥锁已经被“获取”三次,因此在“释放”三次之前将不可用给其他任务。实际代码中这些调用可能是调用栈展开时发生的。这里只是为了演示目的。
 *             xSemaphoreGiveRecursive( xMutex );
 *             xSemaphoreGiveRecursive( xMutex );
 *             xSemaphoreGiveRecursive( xMutex );
 *
 *             // 现在其他任务可以获取互斥锁。
 *         }
 *         else
 *         {
 *             // 我们无法获取互斥锁,因此不能安全地访问共享资源。
 *         }
 *     }
 * }
 * @endcode
 * \defgroup xSemaphoreGiveRecursive xSemaphoreGiveRecursive
 * \ingroup Semaphores
 */
#if ( configUSE_RECURSIVE_MUTEXES == 1 )
    #define xSemaphoreGiveRecursive( xMutex )    xQueueGiveMutexRecursive( ( xMutex ) )
#endif

函 数 xSemaphoreGiveRecursive() 实际 上是调 用 了函 数xQueueGiveMutexRecursice(),函数 xQueueGiveMutexRecursive()在 queue.c 文件中有定义,具体的代码如下所示:

#if ( configUSE_RECURSIVE_MUTEXES == 1 )

    BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex )
    {
        BaseType_t xReturn;
        Queue_t * const pxMutex = ( Queue_t * ) xMutex;

        traceENTER_xQueueGiveMutexRecursive( xMutex ); // 进入函数的跟踪记录

        configASSERT( pxMutex ); // 断言互斥锁句柄不为空

        /* 如果当前任务是持有互斥锁的任务,则 pxMutex->u.xSemaphore.xMutexHolder 不会在此任务之外改变。
         * 如果当前任务不是持有互斥锁的任务,则 pxMutex->u.xSemaphore.xMutexHolder 永远不可能巧合地等于任务的句柄,
         * 因此不需要互斥访问来测试 pxMutex->u.xSemaphore.xMutexHolder 变量。 */
        if( pxMutex->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle() )
        {
            traceGIVE_MUTEX_RECURSIVE( pxMutex ); // 递归释放互斥锁的跟踪记录

            /* 如果 pxMutex->u.xSemaphore.xMutexHolder 等于任务句柄,则 pxMutex->u.xSemaphore.uxRecursiveCallCount 不可能为零,
             * 因此不需要进行下溢检查。此外,pxMutex->u.xSemaphore.uxRecursiveCallCount 只能由互斥锁持有者修改,
             * 而且只能有一个持有者,因此不需要互斥访问来修改 pxMutex->u.xSemaphore.uxRecursiveCallCount 成员。 */
            ( pxMutex->u.xSemaphore.uxRecursiveCallCount )--;

            /* 递归调用计数是否已回退到 0? */
            if( pxMutex->u.xSemaphore.uxRecursiveCallCount == ( UBaseType_t ) 0 )
            {
                /* 释放互斥锁。这将自动解除任何可能正在等待访问互斥锁的任务的阻塞。 */
                ( void ) xQueueGenericSend( pxMutex, NULL, queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK );
            }
            else
            {
                mtCOVERAGE_TEST_MARKER(); // 覆盖率测试标记
            }

            xReturn = pdPASS; // 返回成功
        }
        else
        {
            /* 无法释放互斥锁,因为调用任务不是持有者。 */
            xReturn = pdFAIL; // 返回失败

            traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex ); // 递归释放互斥锁失败的跟踪记录
        }

        traceRETURN_xQueueGiveMutexRecursive( xReturn ); // 返回函数的跟踪记录

        return xReturn;
    }

#endif /* configUSE_RECURSIVE_MUTEXES */
  1. 进入函数并进行跟踪记录

    • 记录函数的进入,便于调试和性能分析。

  2. 断言互斥锁句柄不为空

    • 确保传入的互斥锁句柄 xMutex 不为空。如果为空,程序会在这里中断。

  3. 检查当前任务是否是互斥锁的持有者

    • 获取当前任务的句柄 xTaskGetCurrentTaskHandle() 并与互斥锁的持有者 pxMutex->u.xSemaphore.xMutexHolder 进行比较。

    • 如果当前任务是互斥锁的持有者,继续执行后续操作;否则,跳转到失败处理部分。

  4. 如果是持有者,递减递归调用计数

    • 递减互斥锁的递归调用计数 pxMutex->u.xSemaphore.uxRecursiveCallCount

  5. 如果递归调用计数为0,释放互斥锁

    • 检查递归调用计数是否为0。

    • 如果为0,调用 xQueueGenericSend 函数释放互斥锁。这将解除任何可能正在等待访问互斥锁的任务的阻塞。

    • 如果不为0,调用 mtCOVERAGE_TEST_MARKER 函数进行覆盖率测试标记。

  6. 返回成功并记录

    • 设置返回值 xReturn 为 pdPASS,表示成功。

    • 记录函数的返回。

    • 返回 xReturn

  7. 如果不是持有者,返回失败并记录

    • 设置返回值 xReturn 为 pdFAIL,表示失败。

    • 记录递归释放互斥锁失败。

    • 记录函数的返回。

    • 返回 xReturn

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

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

相关文章

算法|牛客网华为机试41-52C++

牛客网华为机试 上篇:算法|牛客网华为机试21-30C 文章目录 HJ41 称砝码HJ42 学英语HJ43 迷宫问题HJ44 SudokuHJ45 名字的漂亮度HJ46 截取字符串HJ48 从单向链表中删除指定值的节点HJ50 四则运算HJ51 输出单向链表中倒数第k个结点HJ52 计算字符串的编辑距离 HJ41 称砝…

mysql5安全审计

安装插件 插件需要严格与数据库版本适配,不然安装过程中会出现问题 解压插件 cd 插件所在路径unzip audit-plugin-mysql-5.7-1.1.7-921-linux-x86_64.zip#查看mysql默认插件目录 mysql> SHOW GLOBAL VARIABLES LIKE plugin_dir;# 将插件移动到mysql默认插件目…

MySQL 安装与配置

MySQL 安装与配置 MySQL 安装 MySQL 一般分为社区版和商业版,我们使用的是社区版(因为免费)。MySQL 安装的教程在网上有很多,此处就不再进行进行赘述,这里推荐两篇文章:如何在 Windows11 中安装 MySQL 8.…

Flink安装和Flink CDC实现数据同步

一,Flink 和Flink CDC 1, Flink Apache Flink是一个框架和分布式处理引擎,用于对无界和有界数据流进行有状态计算。 中文文档 Apache Flink Documentation | Apache Flink 官方文档 :https://flink.apache.org Flink 中文社区…

车机版 Android Audio 框架笔记

车机版Android Audio 框架涉及的知识点很多,在工作中涉及的功能板块也及其繁杂,后面我会根据工作中的一些实际遇到的实例,逐步拆解 Android Audio的知识点,这里从网上整理了一些思维导图,可以做为未来的一个研究方向&a…

ubuntu 22.04 镜像源更换

双11抢了个云服务器,想要整点东西玩玩,没想到刚上来就不太顺利 使用sudo apt update更新软件,然后发生了如下报错 W: Failed to fetch http://mirrors.jdcloudcs.com/ubuntu/dists/jammy/InRelease 理所当然想到可能是镜像源连接不是很好&…

浅谈Agent

目录 什么是大模型 Agent ? 大模型Agent 有哪些部分组成? 规划(Planning) Planning类型 不依赖反馈的计划 基于反馈的计划 拆解子目标和任务分解方法 COT TOT GOT LLMP 反思和完善 ReAct(融合推理与执行的能力) Reflexion(动态…

NAT网络工作原理和NAT类型

NAT基本工作流程 通常情况下,某个局域网中,只有路由器的ip是公网的,局域网中的设备都是内网ip,内网ip不具备直接与外部应用通信的能力。 处于内网的设备如何借助NAT来实现访问外网的应用? 对于开启了NAT功能的局域网…

Jenkins插件使用问题总结

Git Push插件 插件介绍 主要是用于git推送代码到远程仓库中使用,插件地址 pipeline中使用 官方说明中只有一句代码gitPush(gitScm: scm, targetBranch: env.BRANCH_NAME, targetRepo: origin) 流水线语法中也做的不齐全所以一开始我老是设置错,导致代…

GPT-5 终于来了 —— 人们的预期与现实

高智慧人工智能的两面性,利用AI和被AI利用 前言:人工智能的热度持续升温,似乎已无处不在,但大家对它的感知却并不显著。这种状况有点像美国 2024 年的总统大选,投票前人们彼此不清楚支持谁,直到最终计票才发…

微服务透传日志traceId

问题 在微服务架构中,一次业务执行完可能需要跨多个服务,这个时候,我们想看到业务完整的日志信息,就要从各个服务中获取,即便是使用了ELK把日志收集到一起,但如果不做处理,也是无法完整把一次业…

Matlab实现鲸鱼优化算法优化随机森林算法模型 (WOA-RF)(附源码)

目录 1.内容介绍 2.部分代码 3.实验结果 4.内容获取 1内容介绍 鲸鱼优化算法(Whale Optimization Algorithm, WOA)是受座头鲸捕食行为启发而提出的一种新型元启发式优化算法。该算法通过模拟座头鲸围绕猎物的螺旋游动和缩小包围圈的方式,在…

【学习笔记】网络设备(华为交换机)基础知识 10 —— 信息中心 ① 简介

提示:学习华为交换机信息中心的概述( 包括信息中心的概念、功能、以及信息的分类、分级、和输出 ) ;还包括信息中心常用的命令 ( 使能信息中心、命名信息通道、配置信息过滤、清除统计信息、查看信息中心相关信息的命令…

【unity】unity2021 URP管线下 SceneView没有MipMaps选项了怎么办?扩展Rendering Debugger工具

一、前言 之前项目 Unity打开后 Scene窗口 有一个MipMaps选项模式, 可以查看哪些贴图正常距离下发红 ,说明用不到那么大,可以缩一下尺寸。 但 新的项目在Unity2021上,用了URP, 就没见过这个选项。 查了一篇介绍详细的…

前端代码分析题(选择题、分析题)——JS事件循环分析、await和作用域分析

Promise其实也不难-CSDN博客 Promise 的执行顺序分析 Promise 对象的执行是异步的,但其执行器函数内部的代码是立即执行的,而 then方法注册的回调函数则是在 Promise 状态改变后执行的。 const myPromise new Promise((resolve, reject) > {conso…

DAY24|回溯算法Part03|LeetCode:93.复原IP地址、78.子集、90.子集II

目录 LeetCode:93.复原IP地址 基本思路 C代码 LeetCode:78.子集 基本思路 C代码 LeetCode:90.子集II 基本思路 C代码 通过used实现去重 通过set实现去重 不使用used和set版本 LeetCode:93.复原IP地址 力扣代码链接 文字讲解:LeetCode:93.复原IP地…

ts 将100个元素,每行显示9个元素,然后显示出所有行的元素,由此我们延伸出一个项目需求的简单算法实现。

1、先看一下baidu ai出的结果&#xff1a; 2、我们将上面的代码修改下&#xff0c;定义一个数组&#xff0c;然后记录每行的行号及相应的元素&#xff1a; <template><div>console</div> </template> <script setup lang"ts"> import …

17、论文阅读:VMamba:视觉状态空间模型

前言 设计计算效率高的网络架构在计算机视觉领域仍然是一个持续的需求。在本文中&#xff0c;我们将一种状态空间语言模型 Mamba 移植到 VMamba 中&#xff0c;构建出一个具有线性时间复杂度的视觉主干网络。VMamba 的核心是一组视觉状态空间 (VSS) 块&#xff0c;搭配 2D 选择…

用 Python 从零开始创建神经网络(三):添加层级(Adding Layers)

添加层级&#xff08;Adding Layers&#xff09; 引言1. Training Data2. Dense Layer Class 引言 我们构建的神经网络变得越来越受人尊敬&#xff0c;但目前我们只有一层。当神经网络具有两层或更多隐藏层时&#xff0c;它们变成了“深度”网络。目前我们只有一层&#xff0c…

推荐一款功能强大的视频修复软件:Apeaksoft Video Fixer

Apeaksoft Video Fixer是一款功能强大的视频修复软件&#xff0c;专门用于修复损坏、不可播放、卡顿、画面失真、黑屏等视频问题。只需提供一个准确且有效的样本视频作为参考&#xff0c;该软件就能将受损视频修复到与样本视频相同的质量。该软件目前支持MP4、MOV、3GP等格式的…