FreeRTOS多任务调度原理(基于Cortex-M4)

2023-05-16

目录

1.Cortex-M4中SysTick的重要性

2.Cortex-M4中的中断管理

3.Cortex-M4中影子栈指针

4.Cortex-M4中SVC和PendSV异常

5.多任务启动

6.PendSV业务流程

7.系统时钟节拍详解 

8.SysTick中断服务函数

9.SysTick任务调度


1.Cortex-M4中SysTick的重要性

2.Cortex-M4中的中断管理

        2.1.Cortex-M4 内核支持 256 个中断,其中包含了 16 个内核中断和 240 个外部中断,并且具有256 级的可编程中断优先级设置。但 STM32F4 并没有使用 Cortex-M4 内核的全部东西,而是只用了它的一部分。
        2.2.Cortex-M4处理器中,每一个外部中断都可以被使能或者禁止,并且可以被设置为挂起状态或者清除状态

        2.3.中断和异常向量表

        2.4. Cortex-M4寄存器组——特殊寄存器

                2.4.1.  xPSR:组合程序状态寄存器,该寄存器由三个程序状态寄存器组成:

                           应用PSR(APSR) :  包含前一条指令执行后的条件标志

                           中断PSR(IPSR)  :  包含当前ISR的异常编号

                           执行PSR(EPSR) :  包含Thumb状态位

                2.4.2.  PRIMSK:中断屏蔽特殊寄存器

                2.4.3.  CONTROL:控制寄存器

                           [PRIV]: 为0,处理器处于线程模式的特权级,为1为非特权级
                           [SPSEL] :  为0时,线程模式使用MSP,为1时使用PSP

                           处理器模式时,固定使用MSP

3.Cortex-M4中影子栈指针

4.Cortex-M4中SVC和PendSV异常

5.多任务启动

        5.1.创建空闲任务

        5.2.配置SysTick,PendSV为最低优先级

                优先外部中断触发,保证实时响应外部中断

        5.3.配置SysTick寄存器

        5.4.调用SVC中断

//多任务启动
osKernelStart ();---------->
  vTaskStartScheduler();---------->
    xPortStartScheduler();---------->
        prvStartFirstTask();---------->
            SVC------------------------->
            
//启动任务调度器
void vTaskStartScheduler( void )
{
BaseType_t xReturn;

	/* Add the idle task at the lowest priority. */
	#if( configSUPPORT_STATIC_ALLOCATION == 1 )
	{
	}
	#else
	{
        //动态创建一个空闲任务
		/* The Idle task is being created using dynamically allocated RAM. */
		xReturn = xTaskCreate(	prvIdleTask,
								configIDLE_TASK_NAME,
								configMINIMAL_STACK_SIZE,
								( void * ) NULL,
								portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */
								&xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */
	}
	#endif /* configSUPPORT_STATIC_ALLOCATION */

	if( xReturn == pdPASS )    //如果创建成功
	{
        //关闭中断
		/* Interrupts are turned off here, to ensure a tick does not occur
		before or during the call to xPortStartScheduler().  The stacks of
		the created tasks contain a status word with interrupts switched on
		so interrupts will automatically get re-enabled when the first task
		starts to run. */
		portDISABLE_INTERRUPTS();

        //下一个任务的锁定时间赋值为最大值
        //#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
        //不让时间片进行调度
		xNextTaskUnblockTime = portMAX_DELAY;
        //调度器的运行状态置位,标记开始运行了
		xSchedulerRunning = pdTRUE;
        //初始化  系统的节拍值为0
        //#define configINITIAL_TICK_COUNT 0
		xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;

        //启动调度器
		/* Setting up the timer tick is hardware specific and thus in the
		portable interface. */
		if( xPortStartScheduler() != pdFALSE )
		{
            //因为不会执行到这里,所以没有代码
			/* Should not reach here as if the scheduler is running the
			function will not return. */
		}
		else
		{
			/* Should only reach here if a task calls xTaskEndScheduler(). */
		}
	}
	else
	{
	}

}
//启动调度器
BaseType_t xPortStartScheduler( void )
{
    //配置PendSV和SysTick为最低优先级,为了保证系统的实时性
	/* Make PendSV and SysTick the lowest priority interrupts. */
	portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
	portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;

    /*
        1.初始化systick---->配置为1ms的中断产生时基
        2.开启systick中断
    */
	/* Start the timer that generates the tick ISR.  Interrupts are disabled
	here already. */
	vPortSetupTimerInterrupt();

    //初始化临界段嵌套值
	/* Initialise the critical nesting count ready for the first task. */
	uxCriticalNesting = 0;

    //初始化浮点数寄存器,M4特有,需要初始化
	/* Ensure the VFP is enabled - it should be anyway. */
	prvEnableVFP();

	/* Lazy save always. */
	*( portFPCCR ) |= portASPEN_AND_LSPEN_BITS;

    //启动第一个任务
	/* Start the first task. */
	prvStartFirstTask();

	/* Should not get here! */
	return 0;
}
//启动第一个任务
__asm void prvStartFirstTask( void )
{
	PRESERVE8    //8字节对齐,AAPCS的标准,ARM特有

    /*
        0xE000ED08是中断向量表的一个地址,存储的是MSP的指针
        最终获取到MSP的RAM地址
    */
	/* Use the NVIC offset register to locate the stack. */
	ldr r0, =0xE000ED08
	ldr r0, [r0]
	ldr r0, [r0]

    /*
        重新把MSP的地址,赋值为MSP
        作用:
            1.如果没有在线升级的功能,可以屏蔽这段代码
            2.BSP Bootloader----可以选择程序运行的代码段
            3.这个时候,中断向量表会更新,所以要重新赋值MSP
    */
	/* Set the msp back to the start of the stack. */
	msr msp, r0
	/* Clear the bit that indicates the FPU is in use in case the FPU was used
	before the scheduler was started - which would otherwise result in the
	unnecessary leaving of space in the SVC stack for lazy saving of FPU
	registers. */
	mov r0, #0
	msr control, r0

    //开启全局中断
	/* Globally enable interrupts. */
	cpsie i
	cpsie f
	dsb
	isb
    
    //调用SVC系统
	/* Call SVC to start the first task. */
	svc 0
	nop
	nop
}
//svc异常处理
__asm void vPortSVCHandler( void )
{
	PRESERVE8    //8字节对齐

    /*
        获取当前任务控制块
            任务控制块的第一成员是  任务的栈顶
            获取到栈顶之后,剩下的事就是出栈工作
            出栈--------任务的堆栈
    */
	/* Get the location of the current TCB. */
	ldr	r3, =pxCurrentTCB
	ldr r1, [r3]
	ldr r0, [r1]

    /*
        出栈内核寄存器
            R14就是异常返回值    #define portINITIAL_EXC_RETURN		( 0xfffffffd )
            返回处理模式并在返回后使用进程栈(表示异常退出后,使用PSP)
	*/
    /* Pop the core registers. */
	ldmia r0!, {r4-r11, r14}
    
    //更新栈指针到PSP
	msr psp, r0
	isb
    
    //把basepri赋值为0,即打开屏蔽中断
	mov r0, #0
	msr	basepri, r0

    //异常退出
	bx r14

    //为什么没有恢复其他寄存器?
    //其他寄存器在出栈的时候会自动恢复,由硬件处理
    //最终跳转到任务的执行函数里面
}

6.PendSV业务流程

        6.1.读取当前PSP值获取当前任务栈顶

        6.2.保存s16-s31到栈中保存r4-r11,r14到当前栈中

        6.3.更新栈顶到当前任务控制块中保存r3到栈中关闭中断

        6.4.查找优先级最高的任务,更新当前任务控制块开启中断出栈r3值

                出栈r3,即出栈下一个任务的栈空间

        6.5.出栈r4-r11,r14到当前栈中出栈s16-s31到栈中更新栈顶到PSP调用异常返回指令

__asm void xPortPendSVHandler( void )
{
	extern uxCriticalNesting;
	extern pxCurrentTCB;
	extern vTaskSwitchContext;

	PRESERVE8

	mrs r0, psp
	isb

    //获取当前任务控制块,其实就是获取任务栈顶
	/* Get the location of the current TCB. */
	ldr	r3, =pxCurrentTCB
	ldr	r2, [r3]

    //浮点数处理,如果使能浮点数,就需要入栈
	/* Is the task using the FPU context?  If so, push high vfp registers. */
	tst r14, #0x10
	it eq
	vstmdbeq r0!, {s16-s31}

    //保存内核寄存器----调用者需要做的
	/* Save the core registers. */
	stmdb r0!, {r4-r11, r14}

    //保存当前任务栈顶,把栈顶指针入栈
	/* Save the new top of stack into the first member of the TCB. */
	str r0, [r2]

	stmdb sp!, {r0, r3}

    //使能可屏蔽的中断----临界段
	mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
	msr basepri, r0
	dsb
	isb

    //执行上下文切换
	bl vTaskSwitchContext

    //使能可屏蔽的中断
	mov r0, #0
	msr basepri, r0

    //恢复任务控制块指向的栈顶
    //在上下文切换的时候,当前任务的指针,已经指向了优先级最高的
	ldmia sp!, {r0, r3}

    //获取当前栈顶
	/* The first item in pxCurrentTCB is the task top of stack. */
	ldr r1, [r3]
	ldr r0, [r1]

    //出栈
	/* Pop the core registers. */
	ldmia r0!, {r4-r11, r14}

    //出栈
	/* Is the task using the FPU context?  If so, pop the high vfp registers
	too. */
	tst r14, #0x10
	it eq
	vldmiaeq r0!, {s16-s31}

    //更新PSP指针
	msr psp, r0
	isb

    //异常返回,下面要执行的代码,就是要切换的任务代码了
	bx r14
}
//上下文切换
void vTaskSwitchContext( void )
{
	if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE )
	{
        //标记调度器状态
		/* The scheduler is currently suspended - do not allow a context
		switch. */
		xYieldPending = pdTRUE;
	}
	else
	{
        //标记调度器状态
		xYieldPending = pdFALSE;

        //检查任务栈是否溢出
		/* Check for stack overflow, if configured. */
		taskCHECK_FOR_STACK_OVERFLOW();

        //选择优先级最高的任务,把当前的任务控制块进行赋值
		/* Select a new task to run using either the generic C or port
		optimised asm code. */
		taskSELECT_HIGHEST_PRIORITY_TASK(); /*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. */
		traceTASK_SWITCHED_IN();

	}
}

7.系统时钟节拍详解 

        7.1.配置SysTick装载值

                默认时基为1ms,不要低于1ms;可按10的倍数设置,如10ms,100ms等

        7.2.使能SysTick时钟源;使能SysTick中断;使能SysTick

//系统节拍初始化
#if( configOVERRIDE_DEFAULT_TICK_CONFIGURATION == 0 )

	__weak void vPortSetupTimerInterrupt( void )
	{

		/* Stop and clear the SysTick. */
		portNVIC_SYSTICK_CTRL_REG = 0UL;
		portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;

        /*
            1.操作系统是针对寄存器操作的,因为效率高
            2.首先赋值装载寄存器值 = (CPU频率/频率周期)-1
                2.1.CPU频率在硬件启动时获取
                2.2.configTICK_RATE_HZ是由CubeMX配置而得
            3.配置控制寄存器
                3.1.开启时钟源
                3.2.使能中断
                3.3.使能systick
            4.可以参考M4权威指南,9.5章节----systick定时器
        */
		/* Configure SysTick to interrupt at the requested rate. */
		portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
		portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
	}

#endif /* configOVERRIDE_DEFAULT_TICK_CONFIGURATION */

8.SysTick中断服务函数

        8.1.关闭中断

                由于SysTick中断优先级最低,需要关闭中断,否则会被不停抢断

        8.2.Tick值增加SysTick任务调度启动PendSV

        8.3.开启中断

void xPortSysTickHandler( void )
{
    /*
        1.配置中断屏蔽寄存器
        2.不让中断打断systick中断服务
        3.其实就是进入临界段
    */
	/* 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();
	{
        /*
            操作系统调度接口
            如果调度器返回true,触发pendSV异常
        */
		/* Increment the RTOS tick. */
		if( xTaskIncrementTick() != pdFALSE )
		{
            //触发PendSV
			/* A context switch is required.  Context switching is performed in
			the PendSV interrupt.  Pend the PendSV interrupt. */
			portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
		}
	}
    //清除可屏蔽中断,其实就是打开全部中断---->pendSV就会执行
	vPortClearBASEPRIFromISR();
}

9.SysTick任务调度

        9.1.系统节拍数加1判断是否溢出(uint32)溢出更新任务锁定时间

        9.2.判断是否有任务需要解除阻塞获取延时列表第一个任务控制块(时间排序,第一个时间最短)获取状态列表值,判断时间是否到达,未到达退出

        9.3.任务阻塞事件到达从延时列表中删除从事件列表中删除添加到就绪列表

        9.4.如果使用抢占内核判断任务优先级是否大于当前任务开启任务调度

        9.5.如果使用时间片调度判断当前优先级下是否还有其他任务开启任务调度器

//SysTick任务调度
BaseType_t xTaskIncrementTick( void )
{
TCB_t * pxTCB;
TickType_t xItemValue;
//返回值,表示是否进行上下文切换
BaseType_t xSwitchRequired = pdFALSE;

    //uxSchedulerSuspended 表示内核的调度器是否挂起,pdFALSE表示内核没有挂起
	if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
	{
        //tick计数增加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;

        //判断tick是否溢出
		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();
		}

        /*
            1.当前节拍大于时间片的锁定时间
            2.就说明有任务需要调度了,时间片用完了
        */
		/* 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( ;; )    //一直遍历整个任务延时列表,主要目的是找到时间片最短的任务,进行切换
			{
                //判断任务延时列表中是否为空,即有没有任务在等待调度
				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
				{
                    /*
                        1.从任务延时列表中,获取第一个任务控制块
                            延时列表插入永远是把时间片最短的任务,放在第一个
                        2.获取任务控制块的延时时间
                    */
					/* 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( 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 deedmed easier to understand with multiple breaks. */
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
                
                    //把任务从延时列表中移除
					/* It is time to remove the item from the Blocked state. */
					( void ) uxListRemove( &( 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 )
					{
						( void ) uxListRemove( &( pxTCB->xEventListItem ) );
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}

                    //把任务添加到就绪列表中
					/* Place the unblocked task into the appropriate ready
					list. */
					prvAddTaskToReadyList( pxTCB );

                    //抢占式处理
					/* 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_PREEMPTION == 1 )
		{
			if( xYieldPending != pdFALSE )
			{
				xSwitchRequired = pdTRUE;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* configUSE_PREEMPTION */
	}
	else    //内核调度器挂起了
	{
        //挂起的tick+1
		++xPendedTicks;
	}

	return xSwitchRequired;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS多任务调度原理(基于Cortex-M4) 的相关文章

  • Python3 lambda,map,reduce,filter

    Python3 lambda map reduce filter 转载自 https www cnblogs com hf8051 p 8085424 html https blog csdn net BobYuan888 article
  • Spark+Python函数总结

    Spark 43 Python函数总结 整理自 https www cnblogs com yangzhang home p 6058076 html https blog csdn net nanruoanhao article deta
  • Spark + Python入门

    Spark 43 Python实践入门 整理自 xff1a https www cnblogs com yangzhang home p 6056133 html http spark apache org docs latest quic
  • Numpy函数总结

    Numpy函数总结 整理自 https www jianshu com p 83c8ef18a1e8 基础属性 引入模块 gt gt gt import numpy as np 创建一个list并转化为numpy数组 创建简单的列表 gt
  • pip提速方法

    Author Gary Date 2019 4 12 方法1 在pip参数中添加镜像源地址 豆瓣 xff1a http pypi douban com simple 清华 xff1a https pypi tuna tsinghua edu
  • 使ssh可以以root用户直接登录

    出于安全考虑 ubuntu默认不允许root远程登录 解决方案 安装openssh软件 sudo apt install y openssh server 编辑 SSH 的文件 sudo nano etc ssh sshd config 将
  • 安装Arduino以及ESP8266开发环境

    安装Arduino以及ESP8266开发环境 Author Gary 更新日期 2018 11 20 1 下载安装ArduinoIDE 没什么好说的 xff0c 下载地址 xff1a https www arduino cc en Main
  • 使用Screen来管理终端

    使用Screen来管理终端 转载整理自 xff1a https blog csdn net u013901768 article details 81189348 需要使程序一直运行的情况下 xff0c 可以采用开机自启动的方式 这里为了便
  • 终端关闭后让程序继续运行

    更新 实测此方法有问题 xff0c ctrl 43 z后进程会停止运行 xff0c 即使挂起了也没用了 xff0c 如需挂起后还能继续执行请参考https blog csdn net m0 37340681 article details
  • HiveDDL

    一 数据类型 1 基本数据类型 Hive数据类型 Java数据类型 长度 例子 TINYINT byte 1byte有符号整数 20 SMALINT short 2byte有符号整数 20 INT int 4byte有符号整数 20 BIG
  • Linux解除端口占用-kill进程总结

    Linux解除端口占用 需要解除端口占用时 xff0c 可以通过端口或者进程名查找进程 xff0c 再通过该进程的pid来杀掉该进程 xff1b 也可以通过进程名直接杀死进程 方法1 根据端口查找进程 sudo lsof i lt 端口号
  • Matplot学习总结

    数据可视化库Matplotlib学习总结 更新日期 20181109 安装 需要先安装numpy pip install numpy pip install matplotlib 如果下载速度慢可以参考 https blog csdn ne
  • 使用GDB调试Android Native层代码

    Author Gary Date 2019 2 21 转载整理自 xff1a https wladimir tm4pda github io porting debugging gdb html https www cnblogs com
  • Shell总结

    Author Gary Date 2019 2 22 转载整理自 xff1a http www runoob com linux linux shell variable html bin bash 是一个约定的标记 xff0c 它告诉系统
  • Android I/O截获

    Author Gary Date 2019 3 15 系统版本 Android 6 0 1 r1 Android I O截获 xff0d xff0d 将Android系统中的汇编系统调用封装为C函数 由于项目要求 xff0c 需要拦截And
  • Android添加内核系统调用

    Author Gary Date 2019 4 30 Android版本 Android 6 0 1 r1 内核版本 Linux 3 10 40 手机 Nexus 6 参考资料 http android blogs rice edu 201
  • Ubuntu Linux 安装 .7z 解压和压缩文件

    转载自 https blog csdn net zqlovlg article details 8033456 安装方法 xff1a sudo apt get install p7zip 解压文件 xff1a 7zr x manager 7
  • SSH设置超时时间

    转载自 https blog csdn net cheng830306 article details 21796865 ssh连接超时问题解决方案 xff1a 1 修改server端的etc ssh sshd config ClientA
  • Win10+RTX2060安装TensorFlow+Keras

    Win10 43 RTX2060安装TensorFlow 43 Keras Author Gary Date 2019 6 8 参考资料 https blog csdn net qq 32728345 article details 815
  • Radix Tree总结

    Date 2019 6 19 主要转载自 https www cnblogs com mingziday p 3969269 html https blog csdn net qq 22613757 article details 9104

随机推荐

  • HTTP防劫持方案

    DNS污染检测 被改标题 被挂黑链 被入侵 检测网站是否被劫持 网站打开速度检测 网站是否被黑 域名是否被墙 网站监控 http www iis7 com b wzjk inviteCode 61 496 HTTP劫持是在使用者与其目的网络
  • Linux查看已用空间大小

    查看计算机总容量 df 查看指定文件夹 du h max depth 61 lt 层数 gt lt 需要统计的目录 gt
  • Gcc编译时的-w -W -Wall选项

    今天在看一个makefile时看到了gcc W Wall 这句 xff0c 不明其理 xff0c 专门查看了gcc的使用手册 w的意思是关闭编译时的警告 xff0c 也就是编译后不显示任何warning xff0c 因为有时在编译之后编译器
  • Android8添加arm64系统调用

    Author Gary Date 2019 8 7 Android版本 android 8 1 0 r1 内核版本 Linux 4 4 88 参考文章 https blog csdn net rikeyone article details
  • [RTOS]rtthread,freeRTOS,uCOS等系统简单对比

    在上一篇 嵌入式操作系统浅谈 中 xff0c 大概介绍了我自己再工作中使用到的如rt thread xff0c freeRTOS xff0c uCOS xff0c luatos等系统 xff0c 在这一篇文章中 xff0c 我简单说一下这些
  • Mysql授权允许远程访问解决Navicat for MySQL连接mysql问题 1251错误

    Navicat Premium连接MySQL 1251错误 MySQL Installer 8 0 17 出现上述错误的原因是版本MySQL 8 0 17即8 0开始的MySQL版本 因为采用新的保密方式 而Navicat Premium
  • ubuntu突然进不了图形界面解决方案

    1 环境 Ubuntu 20 04 desktopMacOS Big SurVMware Fusion 2 描述 虚拟机一直开着 xff0c 长时间没有用就自动休眠锁屏了 xff0c 但是发现输密码后进不了桌面 xff0c 只好重启 xff
  • 文档-操作手册与用户手册的区别

    背景 我们都已经了解了操作手册和用户手册都是给用户看的 xff0c 那么两者之间的区别是什么呢 xff1f 为什么要产生这两个文档呢 xff1f 详解 1 操作手册是系统级别的文档 xff0c 而用户手册是需求级别的文档 操作手册讲述的是如
  • msOS使用小结

    在做激关打标机的项目的过程中 xff0c 使用msOS遇到了一些问题 xff0c 和解决方法 xff0c 还有一些小体会 xff0c 现在拿出来分享一下 一 代码移植时遇到的问题 xff1a 激光打标机项目用到一块新的板子 xff0c 所以
  • IntelliJ IDEA添加JavaDOC注释 方法 快捷键

    第一种方法 Settings gt Keymap gt Other gt Fix doc comment gt 右键 gt 选择 Add Keyboard Shortcut xff0c 然后输入自定义的快捷键 默认Fix doc comme
  • 关于ElementUI 项目开发的eslint 报错问题的修复

    介绍 使用操作系统为 Win11Win11 使用教程 1 使用 ElementUI 项目进行开发时因为各自的格式化文档不同难免会遇到各种报错 但是大部分都是因为 单引 分号 逗号 的格式存在报错 2 这时我们在项目的根目录下可以新建一个 p
  • CentOS安装lsusb查看设备串号Serial

    安装lsusb span class hljs built in echo span span class hljs string 34 password 34 span span class hljs built in sudo span
  • VMware虚拟机安装CentOS8连不上网问题

    VMware虚拟机安装CentOS8连不上网 改了半天VMware中CentOS的网卡 xff0c 从 桥接模式 改到 NAT模式 34 都不管用 终极解决方案 1 选中你的虚拟机 xff0c 比如我的CentOS8 2 点击菜单栏中的 编
  • Ubuntu更换软件源

    更换 Ubuntu 的软件源 对于Ubuntu系统 xff0c 不同的版本的源都不一样 xff0c 每一个版本都有自己专属的源 而对于 Ubuntu 的同一个发行版本 xff0c 它的源又分布在全球范围内的服务器上 Ubuntu 默认使用的
  • Eclipse本地运行MapReduce

    环境说明 xff1a Linux环境下已搭建好hadoop集群 xff0c windows本地安装Eclipse 远程启动MapReduce任务 1 下载并安装eclipse插件 xff1a hadoop eclipse plugin XX
  • nvm 管理你的node

    1 安装nvm 2 nvm 管理命令 nvm ls remote 可以会列出所有可用的 Nodejs 版本 nvm install v11 0 0 就可以把这个版本的 Nodejs 安装到我们的机器上了 nvm list 可以查看当前已安装
  • ssh免密登录配置+调试讲解(超详细)+原理解析

    有写的不对的地方 xff0c 欢迎各位同学评论指正 xff0c 博主会进行修改 前 言 集群搭建中 xff0c 常常需要配置ssh免密登录 xff0c 而每台机器情况不一样 xff0c 本来博主认为没多少东西 xff0c 结果 xff0c
  • Windows和Ubuntu双系统双引导教程

    一 参考资料 Windows和Ubuntu双系统安装教程 二 步骤 1 下载EasyBCD xff0c 并安装 2 设置Windows引导 3 设置Ubuntu引导 4 启动系统 遇到这种情况 xff0c 直接Enter回车 选择系统
  • 大数据技术原理与应用(第七章 MapReduce)

    目录 7 1 MapReduce简介 MapReduce与传统并行计算框架对比 MapReduce模型 MapReduce策略 MapReduce理念 计算向数据靠拢 MapReduce架构 Master Slave Map函数和Reduc
  • FreeRTOS多任务调度原理(基于Cortex-M4)

    目录 1 Cortex M4中SysTick的重要性 2 Cortex M4中的中断管理 3 Cortex M4中影子栈指针 4 Cortex M4中SVC和PendSV异常 5 多任务启动 6 PendSV业务流程 7 系统时钟节拍详解