【RTOS学习】互斥管理 | 调试 | 信息统计

🐱作者:一只大喵咪1201
🐱专栏:《RTOS学习》
🔥格言:你只管努力,剩下的交给时间!
图

互斥管理 | 调试 | 信息统计

  • 🍉互斥管理
    • 🌰屏蔽中断
    • 🌰暂停调度器
  • 🍉调试
  • 🍉信息统计
    • 🌰基本使用:
  • 🍉总结

🍉互斥管理

在前面讲解互斥量时,引入过临界资源的概念,实现了对临界资源的互斥访问。

要独占式地访问临界资源,有 2 种方法:

  • 公平竞争:比如使用互斥量,谁先获得互斥量谁就访问临界资源。
  • 谁要跟我抢,我就灭掉谁:
  • 中断要跟我抢?我屏蔽中断。
  • 其他任务要跟我抢?我禁止调度器,不运行任务切换。

🌰屏蔽中断

图
如上图所示,在M3/M4系列的芯片中,有一个8bit的寄存器来存放各类中断的优先级,其中复位,NMI,硬件错误三种中断优先级最高,不受该寄存器管辖。

该寄存器管理的中断优先级从0到255,数字越低,优先级越高,这256个中断又分为两类:

  • 不会使用到SYSCALL的中断:优先级在0~190范围内的中断。
  • 会使用到SYSCALL的中断:优先级在191~255范围内的中断,由两个宏来确定这个范围。
  • SYSCALL:也就是系统调用,是由FreeRTOS提供的函数接口,比如向队列中写数据xQueueSend等等函数。

屏蔽中断有两套宏:任务中使用、ISR 中使用:

任务中使用:

  • taskENTER_CRITICA():入口处屏蔽中断。

图
如上图代码所示,在函数的入口处调用taskENTER_CRITICAL来屏蔽中断,防止其他中断和当前任务抢夺临界资源。

底层会先调用portDISABLE_INTERRUPTS来实现中断屏蔽,并且会让一个全局变量uxCriticalNesting进行加加。

  • 每屏蔽一次中断,变量uxCriticalNesting就会加加一次。

屏蔽中断的底层函数vPortRaiseBASEPRI中,会将宏configMAX_SYSCALL_INTERRUPT_PRIORITY代表的191赋值给ulNewBASEPRI,然后再通过汇编代码将优先级低于191的所有中断屏蔽掉。

  • 屏蔽中断并不是屏蔽所有中断,而是屏蔽低于191的低优先级中断
  • 高于191的高优先级中断仍然可以产生。

之所以设计为只屏蔽低优先级中断,是因为只有低优先级中断才会调用RTOS的系统调用,这些中断才会涉及到互斥。

  • taskEXIT_CRITICAL():出口处使能中断。

图
如上图所示代码,在函数的出口处调用taskEXIT_CRITICAL来重新使能被屏蔽掉的中断,从而解除屏蔽。

底层会先让全局变量uxCriticalNesting减减,当该全局变量被减为0的时候,再调用portENABLE_INTERRUPTS实现解除屏蔽。

  • 每重新使能一次中断,变量uxCriticalNesting就会减减一次。
  • uxCriticalNesting为0时,才真正解除屏蔽。
  • 全局变量uxCriticalNesting是为了实现递归调用,它的内部会记录嵌套的深度,只有嵌套深度变为 0 时,调用 taskEXIT_CRITICAL()才会重新使能中断。

在任务中屏蔽中断的示例如下:

图
如上图向队列中写入数据的函数,在 taskENTER_CRITICA()/taskEXIT_CRITICAL()之间:进行临界资源的访问。

使用 taskENTER_CRITICA()taskEXIT_CRITICAL()来访问临界资源是很粗鲁的方法:

  • 中断无法正常运行。
  • 任务调度无法进行。
  • 所以,之间的代码要尽可能快速地执行。

ISR中使用:

  • portSET_INTERRUPT_MASK_FROM_ISR():入口处屏蔽中断。

图
如上图代码,在带有后缀FromISR的系统调用函数入口处,调用port_INTERRUPT_MASK_FROM_ISR来屏蔽中断。

可以看到,此时屏蔽中断直接调用ulPortRaiseBASEPRI,中间没有其他封装,也没有记录次数等操作,屏蔽的小于191的低优先级中断,同时将中断状态保存到uxSavedInterruptStatus

  • portCLEAR_INTERRUPT_MASK_FROM_ISR :出口处解除屏蔽。

图
如上图代码,在带有后缀FromISR的函数出口处,调用portCLEAR_INTERRUPT_MASK_FROM_ISR 来解除屏蔽。

可以看到,解除屏蔽时也是直接调用vPortSetBASEPRI,并且将前面屏蔽中断时保存的低优先级中断状态uxSavedInterruptStatus传入,在函数内部将屏蔽的中断解除。


在任ISR中屏蔽中断的示例如下:

图
如上图所示带后缀FromISR的向队列中写入数据的函数,在portSET_INTERRUPT_MASK_FROM_ISR()/portCLEAR_INTERRUPT_MASK_FROM_ISR()之间:访问临界资源。

和在任务中屏蔽中断一样,在访问临界资源的过程中,只有高于191的高优先级中断能产生。

🌰暂停调度器

如果有别的任务来跟你竞争临界资源,你可以把中断关掉,无法产生Tick中断,也就无法调度,这当然可以禁止别的任务运行,但是这代价太大了,它会影响到其他中断的处理。

  • 这种情况是竞争临界资源的既有普通任务,也有中断服务函数,或者只有中断服务函数。

如果只是禁止别的普通任务来跟你竞争,不需要关中断,暂停调度器就可以了:在这期间,中断还是可以发生、处理。

暂停调度器函数

  • vTaskSuspendAll()

图
如上图代码所示,在vTaskSuspendAll函数中,有一个全局变量uxSchedulerSuspended,每暂停一次调度器,该变量就会加加。

恢复调度器

  • xTaskResumeAll()

图
如上图代码所示,在xTaskResumeAll中,会将全局变量uxSchedulerSuspended进行减减,当该变量为0时,恢复调度。

  • uxSchedulerSuspended变量同样也是为了禁止和恢复调度函数的递归调用。

使用示例如下:

图
如上图代码所示,在调用vTaskDelay时,会访问到阻塞链表,这是一个临界资源,竞争该临界资源的只有普通任务。

所以在进入该函数后,先禁止调度,不让其他任务和当前任务竞争,当将该任务的TCB放入到阻塞链表后,再恢复调度。

🍉调试

FreeRTOS提供了很多调试手段:

  • 打印
  • 断言:configASSERT
  • Trace
  • Hook函数(回调函数)

打印:
printf:FreeRTOS工程里使用了microlib,里面实现了printf函数。我们只需实现一下fputc函数即可使用printf:

int fputc( int ch, FILE *f );

这就是本喵一直在使用重定向,将打印信息通过UART1打印出来,当然可以重定向到LCD,OLED等设备上。

断言:

一般的C库里面,断言就是一个函数:

void assert(scalar expression);

它的作用是:确认expression必须为真,如果expression为假的话就中止程序。

在FreeRTOS里,使用的是configASSERT(),比如:

#define configASSERT(x)  if (!x) while(1);

其实就是定义了一个宏函数,如果x为真则能顺利运行,如果为假则会陷入死循环。

我们可以让它提供更多信息,比如:

#define configASSERT(x)  \
	if (!x) \
	{
		printf("%s %s %d\r\n",\
		 __FILE__, __FUNCTION__, __LINE__); \
        while(1); \
 	}

这在很多场合都有使用,比如:

  • 队列操作:
BaseType_t xQueueGenericSend( QueueHandle_t xQueue,
                              const void * const pvItemToQueue,
                              TickType_t xTicksToWait,
                              const BaseType_t xCopyPosition )
{
    BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
    TimeOut_t xTimeOut;
    Queue_t * const pxQueue = xQueue;

    configASSERT( pxQueue );
    configASSERT(!((pvItemToQueue == NULL) && (pxQueue->uxItemSize != (UBaseType_t)0U)));
    configASSERT( !((xCopyPosition == queueOVERWRITE) && (pxQueue->uxLength != 1 )));

队列不能为空,写入的数据也不能是空指针,并且不能超出队列长度,一旦不符合要求就会发生断言错误。

  • 中断级别的判断
void vPortValidateInterruptPriority( void )
{
	uint32_t ulCurrentInterrupt;
	uint8_t ucCurrentPriority;

	/* Obtain the number of the currently executing interrupt. */
	ulCurrentInterrupt = vPortGetIPSR();

	/* Is the interrupt number a user defined interrupt? */
	if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER )
	{
		/* Look up the interrupt's priority. */
		ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ];

		configASSERT( ucCurrentPriority >= ucMaxSysCallPriority );
	}

Trace:

FreeRTOS中定义了很多trace开头的宏,这些宏被放在系统个关键位置。

它们一般都是空的宏,这不会影响代码:不影响编程处理的程序大小、不影响运行时间。

我们要调试某些功能时,可以修改宏:修改某些标记变量、打印信息等待。

图
如上图,在向队列中写数据的时候会调用traceQUEUE_SEND宏,它原本是一个空的宏,现在本喵让他打印当前任务的名字。

trace宏描述
traceTASK_INCREMENT_TICK(xTickCount)当tick计数自增之前此宏函数被调用。参数xTickCount当前的Tick值还没有增加。
traceTASK_SWITCHED_OUT()vTaskSwitchContext中,把当前任务切换出去之前调用此宏函数。
traceTASK_SWITCHED_IN()vTaskSwitchContext中,新的任务已经被切换进来了,就调用此函数。
traceBLOCKING_ON_QUEUE_RECEIVE(pxQueue)当正在执行的当前任务因为试图去读取一个空的队列、信号或者互斥量而进入阻塞状态时,此函数会被立即调用。参数pxQueue保存的是试图读取的目标队列、信号或者互斥量的句柄,传递给此宏函数。
traceBLOCKING_ON_QUEUE_SEND(pxQueue)当正在执行的当前任务因为试图往一个已经写满的队列或者信号或者互斥量而进入了阻塞状态时,此函数会被立即调用。参数pxQueue保存的是试图写入的目标队列、信号或者互斥量的句柄,传递给此宏函数。
traceQUEUE_SEND(pxQueue)当一个队列或者信号发送成功时,此宏函数会在内核函数xQueueSend(),xQueueSendToFront(),xQueueSendToBack(),以及所有的信号give函数中被调用,参数pxQueue是要发送的目标队列或信号的句柄,传递给此宏函数。
traceQUEUE_SEND_FAILED(pxQueue)当一个队列或者信号发送失败时,此宏函数会在内核函数xQueueSend(),xQueueSendToFront(),xQueueSendToBack(),以及所有的信号give函数中被调用,参数pxQueue是要发送的目标队列或信号的句柄,传递给此宏函数。
traceQUEUE_RECEIVE(pxQueue)当读取一个队列或者接收信号成功时,此宏函数会在内核函数xQueueReceive()以及所有的信号take函数中被调用,参数pxQueue是要接收的目标队列或信号的句柄,传递给此宏函数。
traceQUEUE_RECEIVE_FAILED(pxQueue)当读取一个队列或者接收信号失败时,此宏函数会在内核函数xQueueReceive()以及所有的信号take函数中被调用,参数pxQueue是要接收的目标队列或信号的句柄,传递给此宏函数。
traceQUEUE_SEND_FROM_ISR(pxQueue)当在中断中发送一个队列成功时,此函数会在xQueueSendFromISR()中被调用。参数pxQueue是要发送的目标队列的句柄。
traceQUEUE_SEND_FROM_ISR_FAILED(pxQueue)当在中断中发送一个队列失败时,此函数会在xQueueSendFromISR()中被调用。参数pxQueue是要发送的目标队列的句柄。
traceQUEUE_RECEIVE_FROM_ISR(pxQueue)当在中断中读取一个队列成功时,此函数会在xQueueReceiveFromISR()中被调用。参数pxQueue是要发送的目标队列的句柄。
traceQUEUE_RECEIVE_FROM_ISR_FAILED(pxQueue)当在中断中读取一个队列失败时,此函数会在xQueueReceiveFromISR()中被调用。参数pxQueue是要发送的目标队列的句柄。
traceTASK_DELAY_UNTIL()当一个任务因为调用了vTaskDelayUntil()进入了阻塞状态的前一刻此宏函数会在vTaskDelayUntil()中被立即调用。
traceTASK_DELAY()当一个任务因为调用了vTaskDelay()进入了阻塞状态的前一刻此宏函数会在vTaskDelay中被立即调用。

Malloc Hook函数:

编程时,一般的逻辑错误都容易解决。难以处理的是内存越界、栈溢出等。内存越界经常发生在堆的使用过程总:堆,就是使用malloc得到的内存。

并没有很好的方法检测内存越界,但是FreeRTOS提供了一些回调函数:

  • 使用pvPortMalloc失败时,如果在FreeRTOSConfig.h里配置configUSE_MALLOC_FAILED_HOOK为1,会调用:
void vApplicationMallocFailedHook( void );

该函数和空闲任务的钩子函数一样,也需要我们自己定义,可以在该函数内输出一些错误信息,提示程序员动态开辟内存失败了。

栈溢出Hook函数:

在切换任务(vTaskSwitchContext)时调用taskCHECK_FOR_STACK_OVERFLOW来检测栈是否溢出,如果溢出会调用:

void vApplicationStackOverflowHook( TaskHandle_t xTask, char * pcTaskName );

该函数需要程序员自己定义实现,可以在里面添加一些大于信息等等。

FreeRTOS是怎么判断栈溢出?有两种方法:

  • 方法1:

图

  • 当前任务被切换出去之前,它的整个运行现场都被保存在栈里,这时很可能就是它对栈的使用到达了峰值。

此时就会比较任务TCB中的当前栈顶pxTopOfStack和创建任务时设置的最高栈顶pxStack作比较,如果pxTopOfStack <= pxStack则说明栈溢出。

  • 栈是向下生长的。
  • 这方法很高效,但是并不精确。

图
如上图,在任务A中不断调用FunctionA函数,该函数会创建一个1024字节大小的数组,但是创建任务A时指定的栈大小就是4*100字节,所以在调用FunctionA函数的过程中,会产生栈溢出。

但是在红色线条位置,FunctionA函数函数已经执行完一次了,它创建的1024字节所用的空间也被回收了,也就是pxTopOfStack + 1024,栈顶向上移动回去了。

这个时候任务A被切换走了,而pxTopOfStack > pxStack,此时就会判断没有栈溢出,实际上已经发生过溢出了。

  • 方法2

图

  • 创建任务时,它的整个栈被填入固定的值,比如:0xa5

  • 检测栈里最后16字节的数据,如果不是0xa5的话表示栈即将、或者已经被用完了

  • 没有方法1快速,但是也足够快,能捕获几乎所有的栈溢出

  • 为什么是几乎所有?可能有些函数使用栈时,非常凑巧地把栈最后16个字节设置为0xa5:几乎不可能。
  • configCHECK_FOR_STACK_OVERFLOW = 1时使用第一种方法检测栈溢出,configCHECK_FOR_STACK_OVERFLOW > 1时使用第二种方法检测栈溢出。

🍉信息统计

在Windows中,当系统卡顿时我们可以查看任务管理器找到最消耗CPU资源的程序。在FreeRTOS中,我们也可以查看任务使用CPU的情况、使用栈的情况,然后针对性地进行优化。

栈使用情况:

在创建任务时分配了栈,可以填入固定的数值比如0xa5,以后可以使用以下函数查看栈的高水位,也就是还有多少空余的栈空间:

UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );
  • 任务运行时、任务被切换时,都会用到栈,栈里原来值(0xa5)就会被覆盖。
  • 该函数从栈的尾部开始判断,连续为0xa5的个数,就是栈的空闲空间个数(字节为单位)。
参数/返回值说明
xTask哪个任务
返回值任务运行过程中空闲内存容量的最小值。
  • 假设从栈尾开始连续为0xa5的栈空间是N字节,返回值是N/4。
  • 因为创建任务时,指定栈大小时的单位是字(Word)。

任务运行时间统计:

对于同优先级的任务,它们按照时间片轮流运行:你执行一个Tick,我执行一个Tick。是否可以在Tick中断函数中,统计当前任务的累计运行时间?

不行!很不精确,因为有更高优先级的任务就绪时,当前任务还没运行一个完整的Tick就被抢占了。

  • 我们需要比Tick更快的时钟,Tick周期时1ms,我们可以使用另一个定时器,让它发生中断的周期时0.1ms甚至更短。

图
如上图,来看task1对CPU的占用率:

  • 切换到Task1时,使用更快的定时器记录当前时间T1。
  • Task1被切换出去时,使用更快的定时器记录当前时间T4。
  • (T4-T1)就是它运行的时间。

任务1,2,3一共占用CPU的时间是13T,task1两次调度占用的时间是(4 + 2 = 6T),所以:

图

  • 关键点:在vTaskSwitchContext函数中,切换任务的时候,会使用更快的定时器统计运行时间。
  • 定时器超时时间越短,得到的CPU占用率精度越高。

🌰基本使用:

函数:

  • 获得任务统计信息:
UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray,
                                        const UBaseType_t uxArraySize,
                                        uint32_t * const pulTotalRunTime );
参数描述
pxTaskStatusArray指向一个TaskStatus_t结构体数组,用来保存任务的统计信息。
有多少个任务?可以用uxTaskGetNumberOfTasks()来获得。
uxArraySize数组大小、数组项个数,必须大于或等于uxTaskGetNumberOfTasks()
pulTotalRunTime用来保存当前总的运行时间(更快的定时器),可以传入NULL
返回值传入的pxTaskStatusArray数组,被设置了几个数组项。
注意:如果传入的uxArraySize小于uxTaskGetNumberOfTasks(),返回值就是0
  • 获得任务的统计信息,形式为可读的字符串:
void vTaskList( signed char *pcWriteBuffer );
  • pcWriteBuffer:系统输出信息到pcWriteBuffer,必须足够大。

图
如上图,使用该函数可以得到上面的信息格式,包括任务的状态,优先级,空闲栈(水位),任务号等等信息。

  • 获得任务的运行信息,形式为可读的字符串:
void vTaskGetRunTimeStats( signed char *pcWriteBuffer );
  • pcWriteBuffer:系统输出信息到pcWriteBuffer,必须足够大。

tu
如上图,可以获得上面格式的运行信息,包括任务名,运行时间,CPU占用率等等。

配置:

首先要配置一些宏开关,配置:

#define configGENERATE_RUN_TIME_STATS 1
#define configUSE_TRACE_FACILITY    1
#define configUSE_STATS_FORMATTING_FUNCTIONS  1

还需要实现宏portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(),它用来初始化更快的定时器,具体的定时器初始化函数需要我们自己实现。

实现这两个宏之一,它们用来返回当前时钟值(更快的定时器)

  • portGET_RUN_TIME_COUNTER_VALUE():直接返回时钟值
  • portALT_GET_RUN_TIME_COUNTER_VALUE(&Time):设置Time变量等于时钟值

初始化定时器:

配置一个片内的定时器作为更快的定时器,详细过程本喵就不讲解了,直接贴代码:
图
如上图所示定时器初始化代码,初始化函数是TimerInit()

图
如上图,在定时器中断函数中,反转运行标志位test_cnt,然后将计数值g_timer_cnt加加,每产生一次超时加加一次。

还实现了一个TimerGetCount函数来获取计数值g_timer_cnt,该值可以反应时长。


图
如上图,原本是需要在main函数中初始化定时器的,但是此时是让系统自动初始化,所以需要将初始化定义到宏中:

#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS 	TimerInit

图
如上图,在启动调度器的时候,会调用portCONFIGURE_TIMER_FOR_RUN_TIME_STATS ,从而就实现了更快定时器的自动初始化。

然后就是将获取计数值值的函数定义到宏中:

#define portGET_RUN_TIME_COUNTER_VALUE 			TimerGetCount

图
如上图代码,在切换任务的时候,先调用portGET_RUN_TIME_COUNTER_VALUE拿到当前时间,然后计算出运行时间,再累加运行时间,最后更新当前时间。

  • 此时就得到了该任务此次执行所花费的时间。

图
如上图红色框中所示,此时就完成了使用更快定时器来统计系统信息的所有配置。

演示:

tu
如上图代码所示,创建两个任务,任务1用来获取统计信息,任务2此时什么都不做,只是和任务1来竞争CPU资源。

图
如上图,此时的统计信息中,任务有四个,任务1和任务2,空闲任务,以及更快的定时器中断任务,以及它们的优先级水位线等等信息。


图

如上图,让任务2获取并打印运行信息,任务1什么都不做,只是和任务2竞争CPU资源。

图
如上图所示结果,任务仍然是这四个,还有每个任务的运行时间,以及CPU占用率,任务1几乎在一直运行,所以它的CPU占用率接近百分之百。

🍉总结

对于互斥的管理,虽然是简单粗暴的禁止其他任务,但是禁止又有两类:

  • 如果和当前任务竞争的既有普通任务也有中断任务,或者仅有中断任务,就需要禁止中断(包括普通任务禁止中断和ISR禁止中断两种)。
  • 如果和当前任务竞争的只是普通任务,那么就只需要禁止调度器即可。

禁止中断时,普通任务禁止会考虑到递归调用,有一个计数值,ISR禁止不考虑递归调用,直接禁止,没有计数值。


调试和信息统计虽然看着有点鸡肋,但是有时候还是还是非常有用的,要知道有这样的功能。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android问题笔记四十二:signal 11 (SIGSEGV), code 1 (SEGV_MAPERR) 的解决方法

点击跳转>Unity3D特效百例点击跳转>案例项目实战源码点击跳转>游戏脚本-辅助自动化点击跳转>Android控件全解手册点击跳转>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&…

阿里云服务器—ECS快速入门

这里对标阿里云的课程&#xff0c;一步步学习&#xff0c;链接在下面&#xff0c;学习完考试及格即可获取阿里云开发认证和领取证书&#xff0c;大家可以看看这个&#xff0c;这里我当作笔记&#xff0c;记一下提升印象&#xff01; 内容很长&#xff0c;请耐心看完&#xff0…

论文-分布式-并发控制-并发控制问题的解决方案

目录 参考文献 问题 解法与证明 易读版本 参考文献 Dijkstra于1965年发表文章Solution of a Problem in Concurrent Programming Control&#xff0c;引出并发系统下的互斥(mutual exclusion)问题&#xff0c;自此开辟了分布式计算领域Dijkstra在文中给出了基于共享存储原子…

论文阅读——ELECTRA

论文下载&#xff1a;https://openreview.net/pdf?idr1xMH1BtvB 另一篇分析文章&#xff1a;ELECTRA 详解 - 知乎 一、概述 对BERT的token mask 做了改进。结合了GAN生成对抗模型的思路&#xff0c;但是和GAN不同。 不是对选择的token直接用mask替代&#xff0c;而是替换为…

Maven配置阿里云中央仓库settings.xml

Maven配置阿里云settings.xml 前言一、阿里云settings.xml二、使用步骤1.任意目录创建settings.xml2.使用阿里云仓库 总结 前言 国内网络从maven中央仓库下载文件通常是比较慢的&#xff0c;所以建议配置阿里云代理镜像以提高jar包下载速度&#xff0c;IDEA中我们需要配置自己…

C++常见容器实现原理

引言 如果有一天&#xff01;你骄傲离去&#xff01;&#xff08;抱歉搞错了&#xff09;如果有一天&#xff0c;你在简历上写下了这段话&#xff1a; 那么你不得不在面试前实现一下STL常见的容器了。C的常用容器有&#xff1a;vector、string、deque、stack、queue、list、se…

Docker:安装MySQL

Docker&#xff1a;安装MySQL 1. 部署MySQL2.部署多个MySQL服务 1. 部署MySQL 首先需要安装Docker&#xff0c;安装Docker地址&#xff1a;http://t.csdnimg.cn/utPGF 安装命令&#xff1a; docker run -d \--name mysql \-p 3306:3306 \-e TZAsia/Shanghai \-e MYSQL_ROOT…

[论文笔记]GTE

引言 今天带来今年的一篇文本嵌入论文GTE, 中文题目是 多阶段对比学习的通用文本嵌入。 作者提出了GTE,一个使用对阶段对比学习的通用文本嵌入。使用对比学习在多个来源的混合数据集上训练了一个统一的文本嵌入模型,通过在无监督预训练阶段和有监督微调阶段显著增加训练数…

IOC课程整理-6 Spring IoC 依赖注入

1 依赖注入的模式和类型 模式 类型 2 自动绑定&#xff08;Autowiring&#xff09; 官方定义 “自动装配是Spring框架中一种机制&#xff0c;用于自动解析和满足bean之间的依赖关系。通过自动装配&#xff0c;Spring容器可以根据类型、名称或其他属性来自动连接协作的bean&…

通道洗牌的思想神了

大家好啊&#xff0c;我是董董灿。 昨天写了一篇关于分组卷积的文章&#xff1a;分组卷积的思想神了&#xff0c;然后有同学希望多了解下通道洗牌。 我个人感觉&#xff0c;通道洗牌这个算法&#xff0c;或者说这个思想&#xff0c;可以称之为小而精&#xff0c;并且是实际解…

Photoshop使用笔记总目录

Photoshop基础学习之工具学习 一、【Photoshop界面认识】 二、【 Photoshop常用快捷键】 三、【色彩模式与颜色填充】 四、【选区】 五、【视图】 六、【常用工具组】 七、【套索工具组】 八、【快速选择工具组】 九、【裁剪工具组】 十、【图框工具组】 十一、【吸取…

1.量化相关了解

前言 深度学习模型部署过程中&#xff0c;我们希望可以快速地对模型进行压缩和推理加速&#xff0c;离线量化是一种常用的压缩加速方法。 一、量化概述 量化是指将连续的信号取值&#xff0c;离散化为有限个取值的过程。 深度学习模型量化是使用低比特定点数表征模型浮点参数…

C#学习相关系列之多线程(七)---Task的相关属性用法

一、Task和Thread的区别 任务是架构在线程之上的,任务最终的执行还是要给到线程去执行的。任务和线程之间不是一对一的关系&#xff0c;任务更像线程池&#xff0c;任务相比线程池有很小的开销和精确的控制。&#xff08;总的来说Task的用法更为先进&#xff0c;在多线程的时候…

Go学习第十三章——Gin入门与路由

Go web框架——Gin入门与路由 1 Gin框架介绍1.1 基础介绍1.2 安装Gin1.3 快速使用 2 路由2.1 基本路由GET请求POST请求 2.2 路由参数2.3 路由分组基本分组带中间件的分组 2.4 重定向 1 Gin框架介绍 github链接&#xff1a;https://github.com/gin-gonic/gin 中文文档&#xf…

logback-classic包中ThrowableProxy递归缺陷StackOverflowError解析

logback-classic&#xff08;<1.2.12版本&#xff09;ThrowableProxy类中存在递归缺陷&#xff0c;会导致java.lang.StackOverflowError。改缺陷在1.2.12以上版本(包含该版本)中已修复。 如何复现&#xff1a; 两个异常彼此设置casue&#xff1a; 运行后报以下错误 以上写…

中文编程开发语言工具系统化教程零基础入门篇和初级1专辑课程已经上线,可以进入轻松学编程

中文编程开发语言工具系统化教程零基础入门篇和初级1专辑课程已经上线&#xff0c;可以进入轻松学编程 学习编程捷径&#xff1a;&#xff08;不论是正在学习编程的大学生&#xff0c;还是IT人士或者是编程爱好者&#xff0c;在学习编程的过程中用正确的学习方法 可以达到事半…

python随手小练10(南农作业题)

题目1&#xff1a; 编写程序&#xff0c;输出1~1000之间所有能被4整除&#xff0c;但是不能被5整除的数 具体操作&#xff1a; for i in range(1,1000): #循环遍历1~999&#xff0c;因为range是左闭右开if (i % 4 0) and (i % 5 ! 0) :print(i) 结果展示&#xff1a; 题目2&…

Vue学习之样式汇总

Vue学习之样式汇总 一 二者左右排版 案例 说明&#xff1a;头部一左一右排版&#xff0c;内容一左一右两个排版&#xff0c;公告栏文字超过点点点显示 代码实现 说明&#xff1a; &#xff08;1&#xff09;头部实现一左一右排版需要使用一下两个样式 display: flex;justify-…

nginx 动静分离 防盗链

一、动静分离环境准备静态资源配置(10.36.192.169)安装nginx修改配置文件重启nginx 动态资源配置(192.168.20.135)yum安装php修改nginx配置文件重启nginx nginx代理机配置&#xff08;192.168.20.134&#xff09;修改nginx子自配置文件重启nginx 客户端访问 二、防盗链nginx防止…

红队专题-从零开始VC++C/S远程控制软件RAT-MFC-远控介绍及界面编写

红队专题 招募六边形战士队员[1]远控介绍及界面编写1.远程控制软件演示及教程简要说明主程序可执行程序 服务端生成器主机上线服务端程序 和 服务文件管理CMD进程服务自启动主程序主对话框操作菜单列表框配置信息 多线程操作非模式对话框 2.环境&#xff1a;3.界面编程新建项目…