FreeRTOS --(9)任务管理之启动调度器
转载自 https://blog.csdn.net/zhoutaopower/article/details/107057528
在使用 FreeRTOS 的时候,一般的,先创建若干任务,但此刻任务并没有被调度起来,仅仅是创建了,如果想要真正的跑起来,那么还需要调用让调度器跑起来的函数:
vTaskStartScheduler
典型的用法是:
xTaskCreate(.."task_1"..);
xTaskCreate(.."task_2"..);
xTaskCreate(.."task_3"..); vTaskStartScheduler(); // Never reach here
DUMP_ERROR();
现在就来看看 vTaskStartScheduler 具体做了些什么;
1、vTaskStartScheduler
vTaskStartScheduler 的实现在 task.c 中:
void vTaskStartScheduler( void )
{
BaseType_t xReturn; /* Add the idle task at the lowest priority. */
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
{
StaticTask_t *pxIdleTaskTCBBuffer = NULL;
StackType_t *pxIdleTaskStackBuffer = NULL;
uint32_t ulIdleTaskStackSize; /* The Idle task is created using user provided RAM - obtain the
address of the RAM then create the idle task. */
vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize );
xIdleTaskHandle = xTaskCreateStatic( prvIdleTask,
configIDLE_TASK_NAME,
ulIdleTaskStackSize,
( void * ) NULL,
portPRIVILEGE_BIT,
pxIdleTaskStackBuffer,
pxIdleTaskTCBBuffer ); if( xIdleTaskHandle != NULL )
{
xReturn = pdPASS;
}
else
{
xReturn = pdFAIL;
}
}
#else
{
/* The Idle task is being created using dynamically allocated RAM. */
xReturn = xTaskCreate( prvIdleTask,
configIDLE_TASK_NAME,
configMINIMAL_STACK_SIZE,
( void * ) NULL,
portPRIVILEGE_BIT,
&xIdleTaskHandle );
}
#endif /* configSUPPORT_STATIC_ALLOCATION */ #if ( configUSE_TIMERS == 1 )
{
if( xReturn == pdPASS )
{
xReturn = xTimerCreateTimerTask();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_TIMERS */ if( xReturn == pdPASS )
{
/* freertos_tasks_c_additions_init() should only be called if the user
definable macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is
the only macro called by the function. */
#ifdef FREERTOS_TASKS_C_ADDITIONS_INIT
{
freertos_tasks_c_additions_init();
}
#endif /* 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(); #if ( configUSE_NEWLIB_REENTRANT == 1 )
{
/* Switch Newlib's _impure_ptr variable to point to the _reent
structure specific to the task that will run first. */
_impure_ptr = &( pxCurrentTCB->xNewLib_reent );
}
#endif /* configUSE_NEWLIB_REENTRANT */ xNextTaskUnblockTime = portMAX_DELAY;
xSchedulerRunning = pdTRUE;
xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; /* If configGENERATE_RUN_TIME_STATS is defined then the following
macro must be defined to configure the timer/counter used to generate
the run time counter time base. NOTE: If configGENERATE_RUN_TIME_STATS
is set to 0 and the following line fails to build then ensure you do not
have portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() defined in your
FreeRTOSConfig.h file. */
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); traceTASK_SWITCHED_IN(); /* 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
{
/* This line will only be reached if the kernel could not be started,
because there was not enough FreeRTOS heap to create the idle task
or the timer task. */
configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
} /* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0,
meaning xIdleTaskHandle is not used anywhere else. */
( void ) xIdleTaskHandle;
}
在 vTaskStartScheduler 函数中,首先通过 xTaskCreate 创建了一个 Idle 任务,优先级为最低 0;(关于 Idle 任务,后面专门来讲);
调用 portDISABLE_INTERRUPTS(); 关闭全局中断,因为后面要初始化 TICK 中断;
接着初始化了全局变量:
xNextTaskUnblockTime = portMAX_DELAY;
xSchedulerRunning = pdTRUE;
xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;
下一个未阻塞的任务时间为 0xFFFF_FFFF;调度器启动的标志位为 TRUE,代表调度器已经初始化,Tick 的计数器为 0;
2、xPortStartScheduler
接着调用 xPortStartScheduler 来配置和体系结构相关调度,这里主要是一些和处理器相关的寄存器(比如 SYSTICK 等);这里还是以 Cortex-M3 作为例子,xPortStartScheduler 实现在 port.c 文件:
/* Constants required to check the validity of an interrupt priority. */
#define portFIRST_USER_INTERRUPT_NUMBER ( 16 )
#define portNVIC_IP_REGISTERS_OFFSET_16 ( 0xE000E3F0 )
#define portAIRCR_REG ( * ( ( volatile uint32_t * ) 0xE000ED0C ) )
#define portMAX_8_BIT_VALUE ( ( uint8_t ) 0xff )
#define portTOP_BIT_OF_BYTE ( ( uint8_t ) 0x80 )
#define portMAX_PRIGROUP_BITS ( ( uint8_t ) 7 )
#define portPRIORITY_GROUP_MASK ( 0x07UL << 8UL )
#define portPRIGROUP_SHIFT ( 8UL ) BaseType_t xPortStartScheduler( void )
{
#if(configASSERT_DEFINED == 1 )
{
volatile uint32_t ulOriginalPriority;
/* 中断优先级寄存器0: PRI_0 */
volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) (portNVIC_IP_REGISTERS_OFFSET_16 +portFIRST_USER_INTERRUPT_NUMBER );
volatile uint8_t ucMaxPriorityValue; /* 这一大段代码用来确定一个最高ISR优先级,在这个ISR或者更低优先级的ISR中可以安全的调用以FromISR结尾的API函数.*/ /* 保存中断优先级值,因为下面要覆写这个寄存器(PRI_0) */
ulOriginalPriority = *pucFirstUserPriorityRegister; /* 确定有效的优先级位个数. 首先向所有位写1,然后再读出来,由于无效的优先级位读出为0,然后数一数有多少个1,就能知道有多少位优先级.*/
*pucFirstUserPriorityRegister= portMAX_8_BIT_VALUE;
ucMaxPriorityValue = *pucFirstUserPriorityRegister; /* 冗余代码,用来防止用户不正确的设置RTOS可屏蔽中断优先级值 */
ucMaxSysCallPriority =configMAX_SYSCALL_INTERRUPT_PRIORITY &ucMaxPriorityValue; /* 计算最大优先级组值 */
ulMaxPRIGROUPValue =portMAX_PRIGROUP_BITS;
while( (ucMaxPriorityValue &portTOP_BIT_OF_BYTE ) ==portTOP_BIT_OF_BYTE )
{
ulMaxPRIGROUPValue--;
ucMaxPriorityValue <<= ( uint8_t ) 0x01;
}
ulMaxPRIGROUPValue <<=portPRIGROUP_SHIFT;
ulMaxPRIGROUPValue &=portPRIORITY_GROUP_MASK; /* 将PRI_0寄存器的值复原*/
*pucFirstUserPriorityRegister= ulOriginalPriority;
}
#endif /*conifgASSERT_DEFINED */ /* 将PendSV和SysTick中断设置为最低优先级*/
portNVIC_SYSPRI2_REG |=portNVIC_PENDSV_PRI;
portNVIC_SYSPRI2_REG |=portNVIC_SYSTICK_PRI; /* 启动系统节拍定时器,即SysTick定时器,初始化中断周期并使能定时器*/
vPortSetupTimerInterrupt(); /* 初始化临界区嵌套计数器 */
uxCriticalNesting = 0; /* 启动第一个任务 */
prvStartFirstTask(); /* 永远不会到这里! */
return 0;
}
首先,如果定义 configASSERT_DEFINED 了的话,那么先:
volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) (portNVIC_IP_REGISTERS_OFFSET_16 +portFIRST_USER_INTERRUPT_NUMBER );
根据 Cortex-M3 的数据手册可以知道,这个地方获取到的是:NVIC 中断优先级寄存器的基地址,Cortex-M3 的 NVIC 中断优先级寄存器定义如下:
Name | Access | Base Address | Reset Value | Description |
PRI_0 | R/W | 0xE000_E400 | 0 (8 bits) | 外中断 #0 的优先级 |
PRI_1 | R/W | 0xE000_E401 | 0 (8 bits) | 外中断 #1 的优先级 |
..... | R/W | ..... | 0 (8 bits) | ..... |
PRI_239 | R/W | 0xE000_E4EF | 0 (8 bits) | 外中断 #239 的优先级 |
这里注意一下,每个优先级 8 bit;
这里,首先将中断 0 优先级读出来,放置到 ulOriginalPriority;
在往这个 PRI_0 优先级寄存器中写全 1,也就是 0xFF;
再将这个寄存器读出来读到 ucMaxPriorityValue 中;
这样做的目的是为了判断这个中断优先级寄存器哪些 bit 是可写(可配置)的;这个和处理器对这方面的定义相关;
在 Cortex-M3 处理器上,NVIC 中断控制器支持中断优选级配置,它分为了组优先级和组内优先级概念,虽然看起来每个优先级使用 8 bits 来表示,看似最大配置到 0xFF,也就是 255,其实不然,因为引入了组优先级和组内优先级的概念,让这个 8 bits 配置得有点玄机;
优先级组也叫抢占优先级,组内优先级也叫子优先级;抢占优先级高的中断可以嵌套抢占优先级低的,同样抢占优先级的中断,则比较的是子优先级;
Cortex-M3 只是处理器的架构,实际上,芯片公司在利用这种架构实现芯片设计的时候,优先级组和组内优先级,并不是这 8 个 bit 都用到了,因为大量的优先级会增加 NVIC 的复杂度;所以,一般的,具体芯片,PRI_0 的 8 bit 只会用到一部分的 bit,其中中用几个 bit 来表示抢占优先级,用几个 bit 来表示子优先级,这个是可以配置的;具体一点的话,比如:
一款 Cortex-M3 内核做的处理器,它在设计的时候,就定义了 PRI_x 优先级的 8 bit,只有高 3 bit 有效:
一款 Cortex-M3 内核做的处理器,它在设计的时候,就定义了 PRI_x 优先级的 8 bit,只有高 4 bit 有效:
比如ST的STM32F1xx和F4xx只使用了这个8位中的高4位[7:4],低四位取零,这样2^4=16,只能表示16级中断嵌套。
那么用高 4 bit 表示优先级,那么这 4 个 bit 哪几个 bit 代表抢占优先级?哪几个 bit 代表子优先级呢?
这个由另一个寄存器的值说了算,SCB->AIRCR[10:8](0xE000_ED00) 寄存器的 PRIGROUP 的值说了算;
#define NVIC_PriorityGroup_0 ((u32)0x700) /* 0 bits for pre-emption priority 4 bits for subpriority */
#define NVIC_PriorityGroup_1 ((u32)0x600) /* 1 bits for pre-emption priority 3 bits for subpriority */
#define NVIC_PriorityGroup_2 ((u32)0x500) /* 2 bits for pre-emption priority 2 bits for subpriority */
#define NVIC_PriorityGroup_3 ((u32)0x400) /* 3 bits for pre-emption priority 1 bits for subpriority */
#define NVIC_PriorityGroup_4 ((u32)0x300) /* 4 bits for pre-emption priority 0 bits for subpriority */
Group | AIRCR[10:8] Value | PRI_x bit[7:4] 分配情况 | 分配结果 |
0 | 3‘b111 | 0:4 | 0位抢占优先级,4位响应优先级 |
1 | 3‘b110 | 1:3 | 1位抢占优先级,3位响应优先级 |
2 | 3‘b101 | 2:2 | 2位抢占优先级,2位响应优先级 |
3 | 3‘b100 | 3:1 | 3位抢占优先级,1位响应优先级 |
4 | 3‘b011 | 4:0 | 4位抢占优先级,0位响应优先 |
Cortex-M3 中断优先级数值越大,表示优先级越低。而 FreeRTOS 的任务优先级则与之相反:优先级数值越大的任务,优先级越高。
好了,言归正传,这里应该说清楚为何要写进去 0xF,在读出来,就是因为这 8bit 并不是全部都用了,这样便可以得到最大支持的优先级个数 ucMaxPriorityValue;
接着计算最大优先级组的值,从读出来有效的 PRI_0 的最高位开始判断(因为)优先级组和子优先级是从最高位开始;
接着配置 PendSV 和 SysTick 的优先级为最低:
/* Make PendSV and SysTick the lowest priority interrupts. */
portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;
3、vPortSetupTimerInterrupt
调用 vPortSetupTimerInterrupt 配置 SysTick,这个是操作系统的心跳,和体系架构相关:
void vPortSetupTimerInterrupt( void )
{
/* Calculate the constants required to configure the tick interrupt. */
#if( configUSE_TICKLESS_IDLE == 1 )
{
ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );
xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick;
ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ );
}
#endif /* configUSE_TICKLESS_IDLE */ /* Stop and clear the SysTick. */
portNVIC_SYSTICK_CTRL_REG = 0UL;
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; /* 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 );
}
STM32 的 SysTick 是一个向下计数的计数器,可以配置产生 Tick 中断;
configSYSTICK_CLOCK_HZ 定义了 CPU 的时钟频率,需要和处理器同步;
configTICK_RATE_HZ 定义了 Tick 来的频率,比如:
configTICK_RATE_HZ为100,则系统节拍时钟周期为10ms,设置宏configTICK_RATE_HZ为1000,则系统节拍时钟周期为1ms
太频繁的 Tick 中断会导致过频繁的上下文切换,增加系统负担,过于长的上下文切换,会导致任务响应不及时;
典型的,STM32 的 configTICK_RATE_HZ 为 1000,也就是 1ms 一次 Tick 中断;
然后配置寄存器,使能了 SysTick,使能了 SysTick 中断;
接着初始化了嵌套深度:
uxCriticalNesting = 0;
4、prvStartFirstTask
最后调用了 prvStartFirstTask(); 启动第一个任务,它的实现使用汇编写的:
__asm void prvStartFirstTask( void )
{
PRESERVE8 /* Cortext-M3硬件中,0xE000ED08 地址处为VTOR(向量表偏移量)寄存器,存储向量表起始地址*/
/* 将 0xE000ED08 加载到 R0 */
ldr r0, =0xE000ED08
/* 将 0xE000ED08 中的值,也就是向量表的实际地址加载到 R0 */
ldr r0, [r0]
/* 根据向量表实际存储地址,取出向量表中的第一项,向量表第一项存储主堆栈指针MSP的初始值*/
ldr r0, [r0] /* 将堆栈地址写入主堆栈指针 */
msr msp, r0
/* 使能全局中断*/
cpsie i
cpsie f
dsb
isb
/* 调用SVC启动第一个任务 */
svc 0
nop
nop
}
PRESERVE8 用于 8 字节对齐;
从 0xE000ED08 获取向量表的偏移,为啥要获得向量表呢?因为向量表的第一个是 MSP 指针!
取 MSP 的初始值的思路是先根据向量表的位置寄存器 VTOR (0xE000ED08) 来获取向量表存储的地址;
在根据向量表存储的地址,来访问第一个元素,也就是初始的 MSP;
此刻呢,将初始的 MSP 存入到了 R0 中,通过 MSR 指令,写到 MSP 中:
打个比方,Cortex-M3 处理器,上电默认进入线程的特权模式,使用 MSP 作为堆栈指针,从上电跑到这里,经过一系列的函数调用,出栈,入栈,MSP 自然已经不是最开始的初始化的位置,这里通过 MSR 重新复制了 MSP,岂不是堆栈都没了么?是的,因为这是一条不归路,代码跑到这里,首先不会返回,之前压栈的内容再也不会用到,所以破坏之前的堆栈也没关系;其次既然不会用到,那么岂不是之前的压栈空间都废了,如果把 MSP 重新初始化到头,就 OK 了嘛,大不了就是破坏了堆栈,反正再也回不去啦;
OK,堆栈指针 MSP 刷完,赋予了新的生命,此刻开中断,开异常,刷流水线;
调用 svc 并传入系统调用号为 0 手动拉 SVC 中断;
5、vPortSVCHandler
手动拉了 SVC 中断,而且开启了中断,那么就会进入它的 ISR:vPortSVCHandler,它的实现也是和处理器体系结构相关,在 port.c 中实现:
__asm void vPortSVCHandler( void )
{
PRESERVE8 ldr r3, =pxCurrentTCB /* pxCurrentTCB指向处于最高优先级的就绪任务TCB */
ldr r1, [r3] /* 获取任务TCB地址 */
ldr r0, [r1] /* 获取任务TCB的第一个成员,即当前堆栈栈顶pxTopOfStack */
ldmia r0!, {r4-r11} /* 出栈,将寄存器r4~r11出栈 */
msr psp, r0 /* 最新的栈顶指针赋给线程堆栈指针PSP */
isb
mov r0, #0
msr basepri, r0
orr r14, #0xd /* 这里0x0d表示:返回后进入线程模式,从进程堆栈中做出栈操作,返回Thumb状态*/
bx r14
}
首先还是 PRESERVE8 的 8字节对齐操作;
还记得吗,pxCurrentTCB 指向的是最高优先级的 Ready 状态的任务指针;
根据 pxCurrentTCB 获取到对应 TCB 的地址;然后获取第一个成员变量,也就是当前栈顶地址 pxTopOfStack;这个值在任务分配的时候,就已经计算好,并且模拟的 Cortex-M3 的异常入栈顺序,手动入栈了;
使用 LDMIA 指令,以 pxTopOfStack 开始顺序出栈,先出 R4~R11(在创建任务的时候,最后入栈的就是这些个),同时 R0 递增;
将此刻的 R0 赋值给 PSP(因为弹栈的时候,处理器会按照入栈的顺序去取 xPSR、PC、LR、R12、R3、R2、R1、R0,而这些寄存器在我们创建任务的时候已经手动压栈);
ISB 指令屏障,刷流水线;
将 BASEPRI 寄存器赋值为 0,也就是允许任何中断;
ORR 指令时按位或,所以 ORR R14, #0xd 相当于 R14 |= 0xd;这个操作也和体系架构相关,R14 是链接寄存器 LR,在 ISR 中(此刻我们在 SVC 的 ISR 中),它记录了异常返回值 EXC_RETURN(更多细节参考《Cortex-M3 处理器窥探》Chapter 7.4);
因为当前在 ISR 中还是使用的 MSP,启动任务后,我们期望在任务执行过程中,处于线程模式,并使用 PSP(前面几行已经给 PSP 赋值了),所以我们需要将 LR 设计成为 0xFFFF_FFFD,让处理器知道返回的时候呢,使用线程模式+PSP堆栈;
最后执行 bx R14,告诉处理器 ISR 完成,需要返回,此刻处理器便会使用 PSP 做为堆栈指针,进行出栈操作,将xPSR、PC、LR、R12、R3~R0 出栈,初始化的时候,PC 被我们赋值成为了执行任务的函数的入口,所以呢,就正常跳入到了优先级最高的 Ready 状态的第一个任务的入口函数了;
处理器相关的部分,可以参考《Cortex-M3 处理器窥探》,创建任务的部分参考《FreeRTOS --(8)任务管理之创建任务》
大致的流程如下:
紫色部分,是和体系架构相关的,黑色的是开关中断的地方,蓝色的是 FreeRTOS 的代码;
FreeRTOS --(9)任务管理之启动调度器的更多相关文章
- 【freertos】005-启动调度器分析
前言 本节主要讲解启动调度器. 这些都是与硬件相关,所以会分两条线走:posix和cortex m3. 原文:李柱明博客:https://www.cnblogs.com/lizhuming/p/160 ...
- [Spring]支持注解的Spring调度器
概述 如果想在Spring中使用任务调度功能,除了集成调度框架Quartz这种方式,也可以使用Spring自己的调度任务框架. 使用Spring的调度框架,优点是:支持注解(@Scheduler),可 ...
- 搭建Airflow数据流调度器
服务器使用的是centos系统,需要安装好pip和setuptools,同时注意更新安装的版本 接下来参考安装好Airflow Airflow 1.8 工作流平台搭建 http://blog.csdn ...
- 【TencentOS tiny】深度源码分析(2)——调度器
温馨提示:本文不描述与浮点相关的寄存器的内容,如需了解自行查阅(毕竟我自己也不懂) 调度器的基本概念 TencentOS tiny中提供的任务调度器是基于优先级的全抢占式调度,在系统运行过程中,当有比 ...
- Superior Scheduler:带你了解FusionInsight MRS的超级调度器
摘要:Superior Scheduler是一个专门为Hadoop YARN分布式资源管理系统设计的调度引擎,是针对企业客户融合资源池,多租户的业务诉求而设计的高性能企业级调度器. 本文分享自华为云社 ...
- 【技术干货】华为云FusionInsight MRS的自研超级调度器Superior Scheduler
Superior Scheduler是一个专门为Hadoop YARN分布式资源管理系统设计的调度引擎,是针对企业客户融合资源池,多租户的业务诉求而设计的高性能企业级调度器. Superior Sch ...
- FreeRTOS调度器
FreeRTOS----调度器 调度器的启动流程分析 当创建完任务之后,会调用vTaskStartScheduler()函数,启动任务调度器: void vTaskStartScheduler( vo ...
- FreeRTOS 任务与调度器(1)
前言: Task.c和Task.h文件内是FreeRTOS的核心内容,所有任务和调度器相关的API函数都在这个文件中,它包括下图这些内容FreeRTOS文件如下: Task.c和Task.h文件内是F ...
- 关于windows中的任务管理调度器
windows中的任务管理调度器 任务管理调度器大概就是给windows设置一个任务,同时还可以设置这个任务的执行时间,执行次数等. 这个任务管理调度器是公司培训同事在讲studio中的job可以在s ...
随机推荐
- SpingMvc中的控制器的注解一般用那个,有没有别的注解可以替代?
答:一般用@Controller注解,也可以使用@RestController,@RestController注解相当于@ResponseBody + @Controller,表示是表现层,除此之外, ...
- java-网络通信--socket实现多人聊天(基于命令行)
先编写最简答的服务器 思路 1编写一个实现Runnable接口的静态内部类 ServerC,便于区分每个客户端 1.1 获取客户端数据函数 public String remsg() 1.2 转发消息 ...
- SVN在拉取(更新)代码的时候出现Error:svn: E155037: Previous operation has not finished; run 'cleanup' if it was interrupted问题 ---window版
简易方法1 今天朋友看到朋友报错这个错误,偷偷学习了下他的方法并做记录以防忘记 简易方法2 今天使用svn时报了一个这个错,网上搜索时都说是要使用sqllite来删除svn队列. 其实可以直接使用id ...
- Simulink仿真时间、步长、精度和解法器设置
在Simulink模型中Configuration Parameters里的Solver页设置仿真时间.步长.精度和解法器. 一.仿真时间:注意这里的时间概念与真实的时间并不一样,只是计算机仿真中对时 ...
- Day 19: EmberJS 入门指南
编者注:我们发现了有趣的系列文章<30天学习30种新技术>,正在翻译,一天一篇更新,年终礼包.下面是第19天的内容. 到目前为止,我们这一系列文章涉及了Bower.AngularJS.Gr ...
- 微信小程序列表拖动排序Demo
wxml页面编写 <view class="container"> <view bindtap="box" class="box&q ...
- Linux---远程连接、命令行基础、文件及目录管理
远程连接管理Linux实践(centos) 连接小知识 IP地址:访问连接服务器,需要通过服务器的IP地址来实现,服务器的IP地址就相当于服务器的具体地址.一计算机都会有一个唯一的32位的IP地址,8 ...
- 轻量化安装 TKEStack:让已有 K8s 集群拥有企业级容器云平台的能力
关于我们 更多关于云原生的案例和知识,可关注同名[腾讯云原生]公众号~ 福利: ①公众号后台回复[手册],可获得<腾讯云原生路线图手册>&<腾讯云原生最佳实践>~ ②公 ...
- GEOS 使用记录
GEOS 使用记录 官网 https://trac.osgeo.org/geos/ https://libgeos.org/ 下载地址 https://libgeos.org/usage/downlo ...
- 帝国CMS只备份栏目和模板的方法
方法一:不备份所有帝国cms数据内容表 我们知道帝国cms有8大模型,分别是 1.新闻系统数据表 ( phome_ecms_news )2.下载系统数据表 ( phome_ecms_download ...