【11】FreeRTOS的延时函数

news2024/10/7 10:23:32

目录

  • 1.延时函数-介绍
  • 2.相对延时函数-解析
    • 2.1函数`prvAddCurrentTaskToDelayedList`-解析
    • 2.3滴答定时器中断服务函数`xPortSysTickHandler()`-解析
    • 2.4函数`taskSWITCH_DELAYED_LISTS() `-解析
  • 3.延时函数-实验
  • 4.总结

1.延时函数-介绍

函数描述
vTaskDelay()相对延时
xTaskDelayUntil()绝对延时

相对延时:指每次延时都是从执行函数vTaskDelay()开始,直到延时指定的时间结束(任务被阻塞的时间,到调用此函数开始的时间)
绝对延时:指将整个任务的运行周期看成一个整体,适用于需要按照一定频率运行的任务(整个任务执行的时间,从头到尾的时间)

在这里插入图片描述
上图中的xTimeIncrement为绝对延时时间(假如绝对延时时间为100ms,那么以下三部分之和为100ms),包括以下三部分:
(1)为任务主体,也就是任务真正要做的工作;
(2)是任务函数中调用vTaskDelayUntil()对任务进行延时;
(3)为其他任务在运行(高优先级的任务进行抢占)

2.相对延时函数-解析

首先入口参数必须大于0,延时时间有效。

void vTaskDelay( const TickType_t xTicksToDelay )
    {
        BaseType_t xAlreadyYielded = pdFALSE;

        /* A delay time of zero just forces a reschedule. */
        if( xTicksToDelay > ( TickType_t ) 0U )

vTaskSuspendAll()挂起任务调度器,traceTASK_DELAY()函数并没有被实现。 prvAddCurrentTaskToDelayedList(点击函数名可跳转至解析)
将当前正在执行的任务移到阻塞列表。

            vTaskSuspendAll();
            {
                traceTASK_DELAY();

                /* A task that is removed from the event list while the
                 * scheduler is suspended will not get placed in the ready
                 * list or removed from the blocked list until the scheduler
                 * is resumed.
                 *
                 * This task cannot be in an event list as it is the currently
                 * executing task. */
                prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );
            }

恢复任务调度器。

            xAlreadyYielded = xTaskResumeAll();

判断xAlreadyYielded 是否需要进行任务切换。

        else
        {
            mtCOVERAGE_TEST_MARKER();
        }

        /* Force a reschedule if xTaskResumeAll has not already done so, we may
         * have put ourselves to sleep. */
        if( xAlreadyYielded == pdFALSE )
        {
            portYIELD_WITHIN_API();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }

#endif /* INCLUDE_vTaskDelay */

此函数是将任务挂载到阻塞列表,解除是在滴答定时器的中断服务函数 xPortSysTickHandler()(点击函数名可跳转至解析)中。

2.1函数prvAddCurrentTaskToDelayedList-解析

函数prvAddCurrentTaskToDelayedList()有两个入口参数一个是延时时间xTicksToWait,另一个是xCanBlockIndefinitely 等于pdFALSE。

static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait,
                                            const BaseType_t xCanBlockIndefinitely )

xConstTickCount 存储时钟节拍,滴答定时器中断一次,变量xTickCount加1。宏INCLUDE_xTaskAbortDelay 判断是否是中断延时,这里并没有使用,所以不用管。

{
    TickType_t xTimeToWake;
    const TickType_t xConstTickCount = xTickCount;

    #if ( INCLUDE_xTaskAbortDelay == 1 )
        {
            /* About to enter a delayed list, so ensure the ucDelayAborted flag is
             * reset to pdFALSE so it can be detected as having been set to pdTRUE
             * when the task leaves the Blocked state. */
            pxCurrentTCB->ucDelayAborted = pdFALSE;
        }
    #endif

将当前正在执行的任务的状态列表项使用函数uxListRemove()从就绪列表中移除,移除完判断是否有同等优先级的任务,没有就代表只有这一个任务,被移除掉后就绪列表中剩余任务为0,那么将此优先级的任务优先级复位。

    /* Remove the task from the ready list before adding it to the blocked list
     * as the same list item is used for both lists. */
    if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
    {
        /* The current task must be in a ready list, so there is no need to
         * check, and the port reset macro can be called directly. */
        portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); /*lint !e931 pxCurrentTCB cannot change as it is the calling task.  pxCurrentTCB->uxPriority and uxTopReadyPriority cannot change as called with scheduler suspended or in a critical section. */
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

INCLUDE_vTaskSuspend 判断是否使能挂起,判断延时时间xTicksToWait等于最大延时时间并且xCanBlockIndefinitely 不等于pdFALSE,此时将任务挂载到挂起列表中,由于传入参数为pdFALSE,所以不会挂载到挂起列表中,则执行else内容。

   #if ( INCLUDE_vTaskSuspend == 1 )
        {
            if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) )
            {
                /* Add the task to the suspended task list instead of a delayed task
                 * list to ensure it is not woken by a timing event.  It will block
                 * indefinitely. */
                listINSERT_END( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) );
            }       

else中首先记录时间,xConstTickCount 为进入函数prvAddCurrentTaskToDelayedList()时记录的时间,加上延时时间xTicksToWait就是任务到截止阻塞时间该被恢复的时间;通过函数listSET_LIST_ITEM_VALUE将延时时间写入到列表项值里,此值将用作挂载到阻塞列表时根据此值进行升序排列;

          else
            {
                /* Calculate the time at which the task should be woken if the event
                 * does not occur.  This may overflow but this doesn't matter, the
                 * kernel will manage it correctly. */
                xTimeToWake = xConstTickCount + xTicksToWait;

                /* The list item will be inserted in wake time order. */
                listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );

判断需要等待截止的时间是否小于进入函数prvAddCurrentTaskToDelayedList()时记录的时间,这里判断是否数值溢出,如果溢出就将任务挂载到溢出阻塞列表中,否则挂载到阻塞列表中。

if( xTimeToWake < xConstTickCount )
                {
                    /* Wake time has overflowed.  Place this item in the overflow
                     * list. */
                    vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );
                }
                else
                {
                    /* The wake time has not overflowed, so the current block list
                     * is used. */
                    vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );

判断下一个阻塞超时时间如果大于新的阻塞时间,那么将新的阻塞时间更新为下一个阻塞超时时间。例如下一个xNextTaskUnblockTime 超时时间为30ms,新的阻塞时间xTimeToWake为20ms,肯定是20ms的先来到,所以将下一个阻塞超时时间更新为20ms。

                    /* If the task entering the blocked state was placed at the
                     * head of the list of blocked tasks then xNextTaskUnblockTime
                     * needs to be updated too. */
                    if( xTimeToWake < xNextTaskUnblockTime )
                    {
                        xNextTaskUnblockTime = xTimeToWake;
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }
            }
        }

2.3滴答定时器中断服务函数xPortSysTickHandler()-解析

函数xTaskIncrementTick()的值如果不等于pdFALSE,则进行任务切换,触发PendSV中断。

void xPortSysTickHandler( void )
{
    /* The SysTick runs at the lowest interrupt priority, so when this interrupt
     * executes all interrupts must be unmasked.  There is therefore no need to
     * save and then restore the interrupt mask value as its value is already
     * known - therefore the slightly faster vPortRaiseBASEPRI() function is used
     * in place of portSET_INTERRUPT_MASK_FROM_ISR(). */
    vPortRaiseBASEPRI();
    {
        /* Increment the RTOS tick. */
        if( xTaskIncrementTick() != pdFALSE )
        {
            /* A context switch is required.  Context switching is performed in
             * the PendSV interrupt.  Pend the PendSV interrupt. */
            portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
        }
    }

    vPortClearBASEPRIFromISR();
}

在函数xTaskIncrementTick()中判断任务是否需要被解除。首先判断任务调度器是否被挂起,如果等于pdFALSE 则没有被挂起,进入if内容。

BaseType_t xTaskIncrementTick( void )
{
    TCB_t * pxTCB;
    TickType_t xItemValue;
    BaseType_t xSwitchRequired = pdFALSE;

    /* Called by the portable layer each time a tick interrupt occurs.
     * Increments the tick then checks to see if the new tick value will cause any
     * tasks to be unblocked. */
    traceTASK_INCREMENT_TICK( xTickCount );

    if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )

将系统时钟节拍xTickCount加1,然后再将值赋给自己,没进来一次时钟节拍将自加1。

/* Minor optimisation.  The tick count cannot change in this
         * block. */
        const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1;

        /* Increment the RTOS tick, switching the delayed and overflowed
         * delayed lists if it wraps to 0. */
        xTickCount = xConstTickCount;

判断xConstTickCount 是否为0,为0则值溢出,进入函数 taskSWITCH_DELAYED_LISTS()(点击函数名可跳转至解析)


        if( xConstTickCount == ( TickType_t ) 0U ) /*lint !e774 'if' does not always evaluate to false as it is looking for an overflow. */
        {
            taskSWITCH_DELAYED_LISTS();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }

        /* See if this tick has made a timeout expire.  Tasks are stored in
         * the  queue in the order of their wake time - meaning once one task
         * has been found whose block time has not expired there is no need to
         * look any further down the list. */

判断当前时钟节拍ConstTickCount 是否大于等于下一个阻塞超时时间。

       /* See if this tick has made a timeout expire.  Tasks are stored in
         * the  queue in the order of their wake time - meaning once one task
         * has been found whose block time has not expired there is no need to
         * look any further down the list. */
        if( xConstTickCount >= xNextTaskUnblockTime )
        {
            for( ; ; )
            {

判断阻塞列表中是否有任务,如果没有任务则没有需要被解除的任务,则将下一个阻塞超时时间xNextTaskUnblockTime设置为最大值。

              if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
                {
                    /* The delayed list is empty.  Set xNextTaskUnblockTime
                     * to the maximum possible value so it is extremely
                     * unlikely that the
                     * if( xTickCount >= xNextTaskUnblockTime ) test will pass
                     * next time through. */
                    xNextTaskUnblockTime = portMAX_DELAY; /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
                    break;
                }

else则阻塞列表中有任务,通过函数listGET_OWNER_OF_HEAD_ENTRY()获取阻塞列表的第一个成员的任务控制块;通过函数listGET_LIST_ITEM_VALUE()获取列表项的数值,列表项中一般存放的是阻塞时间,则xItemValue被赋值阻塞时间。

               else
                {
                    /* The delayed list is not empty, get the value of the
                     * item at the head of the delayed list.  This is the time
                     * at which the task at the head of the delayed list must
                     * be removed from the Blocked state. */
                    pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too.  Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */
                    xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );

判断系统时间节拍的数值是否小于阻塞时间,代表此时发生异常。因为在首先进入if时判断了当前的系统时钟节拍比下一个阻塞超时时间大。此时将列表项的值赋值给下一个阻塞超时时间,退出。


                    if( xConstTickCount < xItemValue )
                    {
                        /* It is not time to unblock this item yet, but the
                         * item value is the time at which the task at the head
                         * of the blocked list must be removed from the Blocked
                         * state -  so record the item value in
                         * xNextTaskUnblockTime. */
                        xNextTaskUnblockTime = xItemValue;
                        break; /*lint !e9011 Code structure here is deemed easier to understand with multiple breaks. */
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }

下面的情况为正常执行的情况。使用函数listREMOVE_ITEM()将任务从阻塞列表中移除,同时也从使用函数listREMOVE_ITEM从事件列表中移除。

                    /* It is time to remove the item from the Blocked state. */
                    listREMOVE_ITEM( &( pxTCB->xStateListItem ) );

                    /* Is the task waiting on an event also?  If so remove
                     * it from the event list. */
                    if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
                    {
                        listREMOVE_ITEM( &( pxTCB->xEventListItem ) );
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }

使用函数prvAddTaskToReadyList()将任务添加到就绪列表中,

                    /* Place the unblocked task into the appropriate ready
                     * list. */
                    prvAddTaskToReadyList( pxTCB );

判断宏configUSE_PREEMPTION是否使能抢占式任务调度,是则判断恢复的任务的任务优先级是否比当前正在执行的任务优先级高,是则将任务切换xSwitchRequired变量赋值pdTRUE。

                    /* A task being unblocked cannot cause an immediate
                     * context switch if preemption is turned off. */
                    #if ( configUSE_PREEMPTION == 1 )
                        {
                            /* Preemption is on, but a context switch should
                             * only be performed if the unblocked task has a
                             * priority that is equal to or higher than the
                             * currently executing task. */
                            if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
                            {
                                xSwitchRequired = pdTRUE;
                            }
                            else
                            {
                                mtCOVERAGE_TEST_MARKER();
                            }
                        }
                    #endif /* configUSE_PREEMPTION */
                }
            }
        }

以下程序是时间片调度:

        /* Tasks of equal priority to the currently running task will share
         * processing time (time slice) if preemption is on, and the application
         * writer has not explicitly turned time slicing off. */
        #if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) )
            {
                if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 )
                {
                    xSwitchRequired = pdTRUE;
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
        #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */

        #if ( configUSE_TICK_HOOK == 1 )
            {
                /* Guard against the tick hook being called when the pended tick
                 * count is being unwound (when the scheduler is being unlocked). */
                if( xPendedTicks == ( TickType_t ) 0 )
                {
                    vApplicationTickHook();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
        #endif /* configUSE_TICK_HOOK */

        #if ( configUSE_PREEMPTION == 1 )
            {
                if( xYieldPending != pdFALSE )
                {
                    xSwitchRequired = pdTRUE;
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
        #endif /* configUSE_PREEMPTION */
    }
    else
    {
        ++xPendedTicks;

        /* The tick hook gets called at regular intervals, even if the
         * scheduler is locked. */
        #if ( configUSE_TICK_HOOK == 1 )
            {
                vApplicationTickHook();
            }
        #endif
    }

    return xSwitchRequired;
}

2.4函数taskSWITCH_DELAYED_LISTS() -解析

只溢出之后,将就绪列表pxDelayedTaskList和溢出就绪列表pxOverflowDelayedTaskList进行互换。

/* pxDelayedTaskList and pxOverflowDelayedTaskList are switched when the tick
 * count overflows. */
#define taskSWITCH_DELAYED_LISTS()                                                \
    {                                                                             \
        List_t * pxTemp;                                                          \
                                                                                  \
        /* The delayed tasks list should be empty when the lists are switched. */ \
        configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) );               \
                                                                                  \
        pxTemp = pxDelayedTaskList;                                               \
        pxDelayedTaskList = pxOverflowDelayedTaskList;                            \
        pxOverflowDelayedTaskList = pxTemp;                                       \
        xNumOfOverflows++;                                                        \
        prvResetNextTaskUnblockTime();                                            \
    }

3.延时函数-实验

1、实验目的:学习 FreeRTOS 相对延时和绝对延时API 函数的使用,并了解其区别
2、实验设计:将设计三个任务:start_task、task1,task2 三个任务的功能如下:
start_task:用来创建task1和task2任务 task1用于展示相对延时函数vTaskDelay ( )的使用;
task1:用于展示相对延时函数vTaskDelay ( )的使用;
task2:用于展示绝对延时函数vTaskDelayUntil( )的使用 。
为了直观显示两个延时函数的区别,将使用LED0(PB1) 和LED1(PB0) 的翻转波形来表示

1.首先删除无关的程序内容,task1和task2程序如下,其他程序保持不变。

/* 任务1,用于展示相对延时函数vTaskDelay ( )的使用 */
void task1( void * pvParameters )
{
    while(1)
    {
			LED0=~LED0;
			vTaskDelay(500);
    }
}
/* 任务2,用于展示绝对延时函数vTaskDelayUntil( )的使用 */
void task2( void * pvParameters )
{

    while(1)
    {
    	vTaskDelay(10);
    }
}

2.task1中的本身就是使用的相对于延时,相对于调用的时候起,到延时时间结束,所以不用改变;task2在使用绝对延时函数vTaskDelayUntil()时,可以查看FreeRTOS官网对该函数的使用介绍,根据示例来编写函数使用,绝对延时是整个task2运行的时间。
在这里插入图片描述
由于LED翻转语句执行较快基本看不到差距,所以这里加了死延时。

/* 任务1,用于展示相对延时函数vTaskDelay ( )的使用 */
void task1( void * pvParameters )
{
    while(1)
    {
			LED0=~LED0;
			delay_ms(20);
			vTaskDelay(500);
    }
}
/* 任务2,用于展示绝对延时函数vTaskDelayUntil( )的使用 */
void task2( void * pvParameters )
{

	TickType_t xLastWakeTime;
	xLastWakeTime = xTaskGetTickCount(); /* 获取当前的系统时钟节拍 */
    while(1)	
    {
			LED1=~LED1;
			delay_ms(20);
			vTaskDelayUntil(&xLastWakeTime,500);
    }
}

这里由于手头没有示波器,我并没有做出实验结果,理论结果如下。
理论实验结果
由于task1和task2都加了死延时20ms,LED0翻转周期为520ms左右,而LED1的翻转周期仍为500ms。由于task2优先级比task1要高,task2会抢占task1,所以在执行时,会出现task1处于阻塞延时时结束时,task2处于死延时,此时并不能进行任务切换,task1会等到task2死延时结束进入阻塞延时再运行,LED0的亮灭时间会有所变化,LED0亮灭时间会变长,LED1的亮灭周期为500ms保持不变。将task1的优先级变高,将会影响task2的绝对延时时间。

4.总结

在这里插入图片描述

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

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

相关文章

linux集群技术(三)--七层负载均衡-HAproxy(一)

HAproxy 概述HAproxy 特点案例1案例2-HAproxy动静分离 1.HAproxy 概述 1.1 关于4/7层负载均衡 1. 无负载平衡&#xff1a; 没有负载平衡的简单Web应用程序环境可能如下所示 在此示例中&#xff0c;用户直接连接到您的Web服务器&#xff0c;在yourdomain.com上&#xff0c;…

2023年,如何自学通过PMP?(含pmp资料)

自学需要解决的问题&#xff1a; 自学的难度很大&#xff0c;不管是零基础还是项目管理经验丰富&#xff0c;都要明白两个点&#xff1a; 1、复杂的报考流程&#xff1b; 2、学习毅力&#xff08;通过率&#xff09;。 第一点、复杂的考试流程 考试先英文报名&#xff0c;英文…

Apache DolphinScheduler 助力 Trino 快速实现湖仓一体数据建设

点亮 ⭐️ Star 照亮开源之路https://github.com/apache/dolphinscheduler作者 | 钟嘉杰 Apache DolphinScheduler PMCMember// 在面对联合查询和湖仓一体场景时&#xff0c;Trino 已经是不少开发者的不二之选。Trino 是一个查询引擎&#xff0c;在数仓、即席查询方面非常强大…

MyBatis分页插件

目录 分页插件 Mybatis插件典型适用场景 实现思考 第一个问题 第二个问题 自定义分页插件 分页插件使用 添加pom依赖 插件注册 调用 代理和拦截是怎么实现的 PageHelper 原理 分页插件 MyBatis 通过提供插件机制&#xff0c;让我们可以根据自己的需要去增强MyBati…

idea快捷键大全

idea快捷键大全idea快捷键大全一、速查版1.Idea常用快捷键2.Idea快捷键(全)二、分类版三、其他1.IDEA创建JAVA项目2.IDEA创建web项目3.tomcat安装配置环境4.IDEA中下载源码错误5.maven环境安装6.IDEA创建maven项目7.IDEA一个打开多个项目8.IDEA切换工作目录9.IDEA导入maven项目…

“clashx需要使用管理员权限安装更新一个帮助程序...“问题的解决过程

大家由于各种原因&#xff0c;经常需要出去看看&#xff0c;比如你想玩玩最近大火的人工智能技术chatgpt。但是如果你想玩chatgpt&#xff0c;那需要借助一些工具才能访问&#xff0c;我们都知道。有很多小伙伴会使用clashx这个开源软件&#xff0c;毕竟便宜&#xff0c;而且也…

从0到1一步一步玩转openEuler--21 openEuler 管理服务-改变运行级别

文章目录21 管理服务-改变运行级别21.1 Target和运行级别21.2 查看系统默认启动目标21.3 查看当前系统所有的启动目标21.4 改变默认目标21.5 改变当前目标21.6 切换到救援模式21.7 切换到紧急模式21 管理服务-改变运行级别 21.1 Target和运行级别 systemd用目标&#xff08;t…

数据结构——复杂度讲解(2)

作者&#xff1a;几冬雪来 时间&#xff1a;2023年2月22日 内容&#xff1a;数据结构复杂度讲解 目录 前言&#xff1a; 复杂度讲解&#xff08;2&#xff09;&#xff1a; 1.空间复杂度是什么&#xff1a; 2.空间复杂度讲解&#xff1a; 结尾&#xff1a; 前言&#x…

【深度探讨】哪些领域将迎来区块链爆发性增长

发表时间&#xff1a;2022年12月6日 信息来源&#xff1a;bsvblockchain.org 如果我们不在日常业务和政府流程中采用区块链技术&#xff0c;就会浪费太多的技术价值和资金。这意味着未来几年&#xff0c;我们可能会看到区块链技术的快速落地&#xff0c;而有些国家已经为这种爆…

实验室设计建设方案主要内容

实验室设计建设整体解决方案SICOLAB需要综合考虑实验室的功能需求、空间布局、设备选型、安全防护、节能环保等多方面因素。以下是一个基本的实验室设计建设方案的流程&#xff1a;一、需求分析&#xff1a;了解实验室的使用目的、实验内容、使用人数、设备种类、实验标准等&am…

程序员赚钱指南,兼职社区招募

&#x1f468;‍&#x1f4bb;作者简介&#xff1a;大数据专业硕士在读&#xff0c;CSDN人工智能领域博客专家&#xff0c;阿里云专家博主&#xff0c;专注大数据与人工智能知识分享。 &#x1f389;专栏推荐&#xff1a;目前在写一个CV方向专栏&#xff0c;后期会更新不限于目…

正则表达式(Java)

定义 正则表达式&#xff08;Regular Expression&#xff09;是一种文本模式匹配的工具&#xff0c;可以用来搜索、替换或提取文本中的模式。它可以用来检查文本是否符合某种模式&#xff0c;或者从文本中提取出符合某种模式的部分 一般用法 未分组 public static void mai…

php+vue加油站会员服务系统 java微信小程序

目 录 1绪论 1 1.1项目研究的背景 1 1.2开发意义 1 1.3项目研究现状及内容 5 1.4论文结构 5 2开发技术介绍 7 2.5微信小程序技术 8 3系统分析 9 3.1可行性分析 9 3.1.1技术可行性 9 3.1.2经济可行性 9 3.1.3操作可行性 10 3.2网站性能需求分析 10 3.3网站功能分析 10 3.4系统…

九龙证券|市场化转融资业务试点上线首日平稳运行

2月21日&#xff0c;中国证券金融股份有限公司&#xff08;下称“中证金融”&#xff09;商场化转融资事务试点迎来首个买卖日。全天该事务试点平稳运转&#xff0c;商场化转融资规模合计10亿元。 业内人士以为&#xff0c;商场化转融资事务形式下&#xff0c;证券公司参加转融…

Docker之路(2.Docker详细安装、使用阿里云镜像加速、卸载)

1.安装Docker 1.1 确定Linux环境系统信息 我这里使用的是阿里云的云服务器系统内核是3.10以上的 # 查看系统内核版本 uname -aLinux版本信息 # 查看系统版本信息 cat /etc/os-release1.2 安装 官方安装帮助文档 https://docs.docker.com/engine/install/centos/ 这块看文档…

【Redis】网络模型:Redis的IO多路复用

【Redis】网络模型&#xff1a;Redis的IO多路复用 文章目录【Redis】网络模型&#xff1a;Redis的IO多路复用一、用户空间和内核态空间二、IO模型1、阻塞IO2、非阻塞IO3、IO多路复用4、异步IO5、信号驱动IO六、对比三、IO多路复用1、IO多路复用-select方式2、IO多路复用模型-po…

opencv-StereoBM算法流程(二)

OpenCV BM对于处理非畸变的立体图像, 主要有以下 3 个步骤:1. 预处理滤波: 使图像亮度归一化并加强图像纹理2. 立体匹配: 沿着水平极线用 SAD 窗口进行匹配搜索3. 再滤波: 去除坏的匹配点.匹配之后, 如果左右视差检查使能了 disp12MaxDiff > 0, 还有使用cv::validateDispari…

【服务器数据恢复】raid5阵列2块硬盘先后离线的数据恢复案例

服务器数据恢复环境&#xff1a; 华为s系列服务器&#xff1b; 24块硬盘组成一组raid5磁盘阵列&#xff0c;其中包含1块热备盘。 服务器故障&检测&#xff1a; 服务器工作状态下raid5中有一块硬盘离线&#xff0c;热备盘激活替换离线硬盘并开始进行数据同步&#xff0c;在同…

(二十一)、实现评论功能(1)【uniapp+uinicloud多用户社区博客实战项目(完整开发文档-从零到完整项目)】

1&#xff0c;评论回复模块的样式布局 1.1 在detail页面添加uview中的 Empty 内容为空组件 <!-- 评论区 --><view class"comment"><u-empty mode"comment" icon"http://cdn.uviewui.com/uview/empty/comment.png"></u-emp…

大厂面试官在校招面试中爱问啥?

如果你在简历中写了这句话&#xff0c;保证能拿到大厂面试机会&#xff1a;扎实的计算机基础&#xff0c;良好的数据结构与算法功底。 然后&#xff0c;你就会被问到头皮发麻。 虽然是段子&#xff0c;但也一定程度上说明了大厂非常注重计算机基础&#xff0c;也是真的喜欢问…