【RTOS学习】软件定时器 | 中断处理

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

软件定时器 | 中断处理

  • 🏀软件定时器
    • ⚽守护任务
      • 守护任务的调度
    • ⚽使用软件定时器的函数
    • ⚽基本使用
  • 🏀中断处理
    • ⚽两套API函数
    • ⚽对比两套API函数
  • 🏀总结

🏀软件定时器

图
如上图,我们在手机上添加闹钟时,需要指定时间、指定类型(一次性的,还是周期性的)、指定做什么事;还有一些过时的、不再使用的闹钟。

软件定时器和手机闹钟是类似的:

  • 指定时间:启动定时器和运行回调函数,两者的间隔被称为定时器的周期(period)。

  • 指定类型,定时器有两种类型:

  • 一次性(One-shot timers):这类定时器启动后,它的回调函数只会被调用一次; 可以手工再次启动它,但是不会自动启动它。

  • 自动加载定时器(Auto-reload timers ):这类定时器启动后,时间到之后它会自动启动它; 这使得回调函数被周期性地调用。

  • 定要做什么事,就是指定回调函数。

实际的闹钟分为:有效、无效两类。软件定时器也是类似的,它由两种状态:

  • 运行(Running、Active):运行态的定时器,当指定时间到达之后,它的回调函数会被调用 。
  • 冬眠(Dormant):冬眠态的定时器还可以通过句柄来访问它(它仍然存在),但是它不再运行,它的回调函数不会被调用。

图
如上图所示定时器定时器的运行情况:

  • Timer1:它是一次性的定时器,在 t1 启动,周期是 6 个 Tick。经过 6 个tick 后,在 t7 执行回调函数。它的回调函数只会被执行一次,然后该定时器进入冬眠状态。
  • Timer2:它是自动加载的定时器,在 t1 启动,周期是 5 个 Tick。每经过 5个 tick 它的回调函数都被执行,在 t6、t11、t16 都会执行。

⚽守护任务

执行回调函数:

FreeRTOS 中有一个 Tick 中断,软件定时器基于 Tick 来运行。在哪里执行定时器回调函数?第一印象就是在 Tick 中断里执行:

  • 在 Tick 中断中判断定时器是否超时
  • 如果超时了,调用它的回调函数

但是,FreeRTOS 是实时操作系统,它不允许在内核、在中断中执行不确定的代码:如果定时器函数很耗时,就会导致Tick中断迟迟无法结束,影响任务调度,进而会影响整个系统。

  • 所以,FreeRTOS 中,不在 Tick 中断中执行软件定时器的回调函数。

在哪里执行?有资格执行函数的必然是一个任务,这个任务就是:RTOS 守护任务。以前被称为"Timer server",但是这个任务要做并不仅仅是定时器相关,所以改名为:RTOS Damemon Task

  • 除此之外,对软件定时器的具体操作也是由守护任务完成的。

我们用户只是在使用软件定时器,如启动,停止,删除,复位,改变定时周期等等操作。但是只是在用户任务中调用相关的API函数,具体的细节操作并不是由用户完成的,而是由守护任务完成的。

定时器的回调函数的原型如下:

void ATimerCallback(TimerHandle_t xTimer);

定时器的回调函数是在守护任务中被调用的,守护任务不是专为某个定时器服务的,它还要处理其他定时器。

所以,定时器的回调函数不要影响其他人:

  • 回调函数要尽快实行,不能进入阻塞状态。
  • 不要调用会导致阻塞的 API 函数,比如vTaskDelay()
  • 可以调用xQueueReceive()之类的函数,但是超时时间要设为 0:即刻返回,不可阻塞 。

执行用户命令:

图
如上图所示,当用户调用操作软件定时器的API时,其实是给守护任务发生了一些指令,守护任务根据指令做出相应的操作,如启动定,停止定时器等。

用户是在用户任务中调用的API,操作是在守护任务中根据不同用户指令执行的,所以就涉及到了两个任务之间的通信。

  • 这里任务之间的通信使用的是队列。

用户任务将操作软件定时器的命令写入到命令队列中,守护任务从命令队列中读取命令并做出相应的操作。


  • 当 FreeRTOS 的配置项 configUSE_TIMERS 被设置为 1 时,在启动调度器时,会自动创建 RTOS Damemon Task。

  • 因为要创建命令队列,所以要配置configTIMER_QUEUE_LENGTH来指定命令队列长度。

  • 既然守护任务也是一个任务,所以要配置它的优先级configTIMER_TASK_PRIORITY以及栈大小configTIMER_TASK_STACK_DEPTH

守护任务的调度

守护任务的调度,跟普通的任务并无差别。当守护任务是当前优先级最高的就绪态任务时,它就可以运行。它的工作有两类:

  • 处理命令:从命令队列里取出命令、处理。
  • 执行定时器的回调函数。

能否及时处理定时器的命令、能否及时执行定时器的回调函数,严重依赖于守护任务的优先级。

守护任务的优先性级较低:

图
如上图:

  • t1:Task1 处于运行态,守护任务处于阻塞态。
  • t2:Task1 调用 xTimerStart()

要注意的是,xTimerStart()只是把"start timer"的命令发给"定时器命令队列",使得守护任务退出阻塞态。但是此时,Task1 的优先级高于守护任务,所以守护任务无法抢占 Task1。

  • t3:Task1 执行完 xTimerStart()

但是定时器真正的启动工作由守护任务来实现,所以xTimerStart()返回并不表示定时器已经被启动了。

  • t4:Task1 由于某些原因进入阻塞态,现在轮到守护任务运行。

守护任务从队列中取出"start timer"命令,启动定时器。

  • t5:守护任务处理完队列中所有的命令,再次进入阻塞态。Idel 任务时守护任务的优先级最高,它执行。
  • 注意:假设定时器在后续某个时刻 tX 超时了,超时时间是"tX-t2",而非"tX-t4"。
  • 超时时间是从xTimerStart()函数被调用时算起。

守护任务的优先性级较高:

图
如上图:

  • t1:Task1 处于运行态,守护任务处于阻塞态。
  • t2:Task1 调用xTimerStart()

此时守护任务的优先级高于 Task1,所以守护任务抢占 Task1,守护任务开始处理命令队列。

Task1 在执行xTimerStart()的过程中被抢占,这时它无法完成自己的函数。

  • t3:守护任务处理完命令队列中所有的命令,再次进入阻塞态。 此时 Task1 是优先级最高的就绪态任务,它开始执行。
  • t4:Task1之前被守护任务抢占,对xTimerStart()的调用尚未返回。现在开始继续运行此函数、返回。
  • t5:Task1 由于某些原因进入阻塞态,进入阻塞态。Idel 任务时优先级最高的就绪态任务,它执行。
  • 注意,定时器的超时时间是基于调用xTimerStart()的时刻 tX,而不是基于守护任务处理命令的时刻 tY。
  • 假设超时时间是 10 个 Tick,超时时间是"tX+10",而非"tY+10"。

⚽使用软件定时器的函数

创建:

/* 动态创建 */
TimerHandle_t xTimerCreate( 
				const char * const pcTimerName, 
                const TickType_t xTimerPeriodInTicks,
                const UBaseType_t uxAutoReload,
                void * const pvTimerID,
                TimerCallbackFunction_t pxCallbackFunction);
/* 静态创建 */
TimerHandle_t xTimerCreateStatic( 
				const char * const pcTimerName
                const TickType_t xTimerPeriodInTicks,
                const UBaseType_t uxAutoReload,
                void * const pvTimerID,
                TimerCallbackFunction_t pxCallbackFunction,
                StaticTimer_t * pxTimerBuffer);
  • pcTimerName:定时器名字, 用处不大, 尽在调试时用到 。
  • xTimerPeriodInTicks:定时周期, 以 Tick 为单位 。
  • uxAutoReload:定时器类型,pdTRUE表示自动加载, pdFALSE表示一次性 。
  • pvTimerID:回调函数可以使用此参数, 比如分辨是哪个定时器 。
  • pxCallbackFunction:回调函数。
  • 返回值:成功则返回定时器句柄,否则返回 NULL。
  • pxTimerBuffer:静态创建时,需要传入一个StaticTimer_t结构体,在上面构造定时器。

回调函数的类型是:

void ATimerCallback( TimerHandle_t xTimer ); 

tu
如上图定时器结构体,创建好定时器以后,调用xTimerCreate传入的参数都记录到了该结构体中,后面通过定时器句柄就可以访问到这些成员。

启动:

BaseType_t xTimerStart( TimerHandle_t xTimer, 
						TickType_t xTicksToWait);
  • xTimer:哪个定时器。
  • xTicksToWait:超时时间。
  • 返回值:pdFAIL表示"启动命令"在 xTicksToWait 个 Tick 内无法写入队列,pdPASS表示成功 。

这里的超时时间和前面创建定时器的定时周期不是一个东西,这里的超时时间是指用户任务向命令队列中写命令时的超时时间。

停止:

BaseType_t xTimerStop( TimerHandle_t xTimer, 
						TickType_t xTicksToWait );
  • xTimer:哪个定时器。
  • xTicksToWait:超时时间。
  • 返回值:pdFAIL表示"停止命令"在 xTicksToWait 个 Tick 内无法写入队列,pdPASS表示成功 。

修改定时周期:

BaseType_t xTimerChangePeriod( TimerHandle_t xTimer, 
 							TickType_t xNewPeriod, 
 T							ickType_t xTicksToWait );
  • xTimer:哪个定时器。
  • xNewPeriod:新周期。
  • xTicksToWait: 超时时间, 命令写入队列的超时时间。

复位:

BaseType_t xTimerReset( TimerHandle_t xTimer, 
						TickType_t xTicksToWait );
  • xTimer:哪个定时器。
  • xTicksToWait: 超时时间, 命令写入队列的超时时间。

使用 xTimerReset()函数可以让定时器的状态从冬眠态转换为运行态,相当于使用 xTimerReset()函数。

如果定时器已经处于运行态,使用xTimerReset()函数就相当于重新确定超时时间。假设调用xTimerReset()的时刻是 tX,定时器的周期是 n,那么 tX+n 就是重新确定的超时时间。

删除:

BaseType_t xTimerDelete( TimerHandle_t xTimer, 
						TickType_t xTicksToWait );
  • xTimer:哪个定时器。
  • xTicksToWait: 超时时间, 命令写入队列的超时时间。

定时器ID:

tu
如上图,定时器的结构体如下,里面有一项 pvTimerID,它就是定时器 ID。

怎么使用定时器 ID,完全由程序员来决定:

  • 可以用来标记定时器,表示自己是什么定时器。
  • 可以用来保存参数,给回调函数使用。

它的初始值在创建定时器时由xTimerCreate()这类函数传入,后续可以使用这些函数来操作:

  • 更新 ID:使用vTimerSetTimerID()函数。
  • 查询 ID:查询pvTimerGetTimerID()函数。
  • 这两个函数不涉及命令队列,它们是直接操作定时器结构体。

⚽基本使用

图
首先定义这4个宏,然后才能使用软件定时器。

  • 守护任务的优先级要尽可能高,让定时器的命令或者回调函数及时被执行。

一般使用:

图
如上图所示,创建两个定时器,一个是一次性定时器,定时周期是10个Tick,另一个是周期定时器,定时周期是20个Tick。然后再创建一个任务,优先级是1。

图
如上图,在任务1中启动两个定时器,然后在while(1)中持续打印任务运行信息。

在一次性定时器的回调函数中,将一次性定时器运行标志位反转,并将运行次数加加,然后打印。

在周期性定时器回调函数中,将周期性定时器运行标志位反转,并将运行次数增加,然后打印。

  • 两个回调函数中都没有while(1)死循环。

图
如上图运行结果,先看右边串口,任务1在启动两个定时器以后,就不断打印自己的运行信息。其中一次性定时器运行信息打印了一次,周期性定时器运行信息打印了多次。

再看左边逻辑分析仪中标志位的变化,可以看到,一次性定时器的运行标志只发生了一次变化,周期性定时器的运行标志每隔20隔Tick就变化一次。

  • 定时器的回调函数并不是由任务1执行的,任务1中没有操作运行标志,更说明定时器的回调函数是由守护任务执行的。
  • 一次性定时器只起一次作用,而周期性定时器按定时周期起作用,因为回调函数中并没有死循环,定时器每起一次作用就调用一次回调函数。

消除抖动:

在嵌入式开发中,我们使用机械开关时经常碰到抖动问题:引脚电平在短时间内反复变化。

怎么读到确定的按键状态?

  • 连续读很多次,直到数值稳定:浪费 CPU 资源。
  • 使用定时器:要结合中断来使用。

对于第 2 种方法,处理方法如下图所示:
图

  • 在 t1 产生中断,这时不马上确定按键,而是复位定时器,假设周期时20ms,超时时间为"t1+20ms" 。
  • 由于抖动,在 t2 再次产生中断,再次复位定时器,超时时间变为"t2+20ms" 。
  • 由于抖动,在 t3 再次产生中断,再次复位定时器,超时时间变为"t3+20ms" 。
  • 在"t3+20ms"处,按键已经稳定,读取按键值 。

图
如上图代码所示,配置PA0作为按键,且开启按键外部中断,采样双边沿触发模式。

图
如上图,在中断函数中,使用定时器进行消抖,每发生一次按键中断,就打印一次中断发生的信息,并且计数发生次数,然后使用xTimerReset推迟超时时间,实现消抖。

图
如上图代码,将按键及中断初始化,然后创建一个一次性定时器,这里的超时时间设置为2秒,后面讲解原因。然后再创建一个任务。

图
如上图,在任务1中,只有一个死循环,是为了保证程序在一直执行,一次性定时器的回调函数中,打印回调函数的执行信息和执行次数。

  • 如果按键持续抖动,则会持续发生外部中断,定时器也会被不停复位,超时时间就在不断推后,回调函数始终得不到执行。
  • 当按键稳定后,定时器超时,回调函数执行,此时消抖完成,成功读取一次按键。

图
如上图,将程序使用软件仿真,打开模拟器中的GPIOA,如上图所示,其中第4步红色框中的勾用来就用来操作PA0引脚,来模拟按键中断产生。

  • 打上勾时,PA0的电平为高。
  • 没有勾时,PA0的电平为低。

所以我们只需要用鼠标不停电机第4步中的红色框位置,就可以模拟处按键过程中的抖动,当不再点击时,抖动消失,按键状态稳定。

  • 由于手动模拟,无法在20ms内完成,为了看到实验现象,将定时器的超时时间设置成2秒。

2秒钟内,每点击一次,模拟发生一次抖动,超时时间推后2秒。当不再点击时,按键稳定,等待定时器超时,一次按键读取完成。

图
如上图,本喵快速点击多次,产生了多次按键中断,定时器超时时间推迟了多次,最后消去了这几次抖动,读取一次按键状态。

🏀中断处理

在 RTOS 中,需要应对各类事件。这些事件很多时候是通过硬件中断产生,怎么处理中断呢?假设当前系统正在运行 Task1 时,用户按下了按键,触发了按键中断。这个中断的处理流程如下:

  • CPU 跳到固定地址去执行代码,这个固定地址通常被称为中断向量,这个跳转时硬件实现的 。
  • 执行代码做什么?
  • 保存现场:Task1 被打断,需要先保存 Task1 的运行环境,比如各类寄存器的值 。
  • 分辨中断、调用处理函数(这个函数就被称为 ISR(interrupt service routine)
  • 恢复现场:继续运行 Task1,或者运行其他优先级更高的任务 。

ISR 是在内核中被调用的,ISR 执行过程中,用户的任务无法执行。ISR要尽量快,否则:

  • 其他低优先级的中断无法被处理:实时性无法保证。
  • 用户任务无法被执行:系统显得很卡顿 。

如果这个硬件中断的处理,就是非常耗费时间呢?对于这类中断的处理就要分为 2 部分:

  • ISR:尽快做些清理、记录工作,然后触发某个任务。
  • 任务:更复杂的事情放在任务中处理。
  • 所以:需要 ISR 和任务之间进行通信。

要在 FreeRTOS 中熟练使用中断,有几个原则要先说明:

  • FreeRTOS 把任务认为是硬件无关的,任务的优先级由程序员决定,任务何时运行由调度器决定。
  • ISR 虽然也是使用软件实现的,但是它被认为是硬件特性的一部分,因为它跟硬件密切相关 。
  • 何时执行?由硬件决定。
  • 哪个 ISR 被执行?由硬件决定。
  • ISR 的优先级高于任务:即使是优先级最低的中断,它的优先级也高于任务。
  • 任务只有在没有中断的情况下,才能执行。

⚽两套API函数

图
如上图按键中断代码所示,在ISR中调用了xTimerReset函数写命令到命令队列,这是ISR和守护任务在进行通信,该函数的第二个参数是超时时间,如果设置为porMAX_DELAY,ISR就有阻塞的可能,这对于ISR是绝对不允许的,所以需要另外的API函数来和守护任务通信。

图
如上图,应该将用于通信的API换成xTimerResetFromISR,可以看到,该函数相比于之前多了一个FromISR后缀,这是专门用来进行ISR和任务之间的通信的。

  • 每一类任务间通信函数都有两套,一套是用来实现用户任务间通信的,没有FromISR后缀,另一套是带有后缀的。

两套API函数列表:

类型在任务中在ISR中
队列(queue)xQueueSendToBack
xQueueSendToFront
xQueueReceive
xQueueOverwrite
xQueuePeek
xQueueSendToBackFromISR
xQueueSendToFrontFromISR
xQueueReceiveFromISR
xQueueOverwriteFromISR
xQueuePeekFromISR
信号量(semaphore)xSemaphoreGive
xSemaphoreTake
xSemaphoreGiveFromISR
xSemaphoreTakeFromISR
事件组(event group)xEventGroupSetBitsxEventGroupSetBitsFromISR

xHigherPriorityTaskWoken 参数:

所有带FromISR后缀的函数中,都有一个参数xHigherPriorityTaskWoken,该参数的含义是:是否有更高优先级的任务被唤醒了。如果为pdTRUE,则意味着后面要进行任务切换。

图
如上图,在xTimerResetFromISR中会改变xHigherPriorityTaskWoken的值,该函数调用结束以后,需要调用portYIELD_FROM_ISR来判断是否要发起调度。

⚽对比两套API函数

普通任务写队列:
图
如上图代码,是普通任务向队列中写数据xQueueSend的底层部分函数。

  1. 传参时,需要传入超时时间。
  2. 所有关于列表的判断放在一个死循环中,如果没有返回则会不断循环判断。
  3. 如果队列没有满可以写入数据,写入以后会立刻发起调度,好让更高优先级的任务抢占执行。

图
如上图是另外部分的函数。

  1. 队列满了时,无法写入数据。
  • 如果超时时间为0,则该任务不等待直接错误返回。
  • 如果超时时间没有达到,则将该任务放入阻塞链表。
  • 再次发起调度,好让其他任务执行。
  • 普通任务向队列中写数据时,有超时时间,死循环,还会在函数中直接发起任务调度。

ISR写队列:

图

如上图是ISR向队列中写数据xQueueSendFromISR的底层函数。

  1. 传入的参数没有超时时间,而是是否发起调度标志。
  2. 没有死循环,只判断一遍后就返回。
  3. 队列没有满,将数据写入队列后,再将处于等待状态的任务放入到就绪链表,但是并不立刻发起调度,而是将发起调度标志位置一。
  4. 队列满了,直接错误返回,没有其他操作链表的多余动作。
  • ISR向链表中写数据时,没有超时时间,没有死循环式的判断,数据写入以后仅记录需要发起调度,写入失败后直接返回。

差别:

xQueueSendxQueueSendFromISR
参数不同xTicksToWait: 队列满的话阻塞多久没有xTicksToWait
唤醒等待的任务写队列后,会唤醒等待数据的任务写队列后,记录要唤醒等待数据任务的需求
调度如果被唤醒的任务优先级更高,即刻调度如果被唤醒的任务优先级更高,不会调度
只是记录下来表示:需要调度
阻塞如果队列满,可以阻塞如果队列满,不能阻塞

对比两套API,发现ISR的API比普通的API高效很多,除了没有超时时间外,就是不会直接发起调度,而是将需要调度的需求记录下来。等ISR退出后,执行portYIELD_FROM_ISR(xHigherPriorityTaskWoken)函数来发起调度。

  • 发起调度不能以内核的身份发起,只能以普通任务的身份去发起。

其实在ISR中发起调度也没有意义,因为中断的优先级比所有任务都高,在中断中发起调度,调度器任务无法处理这个调度请求,因为是此时代码仍然在执行中断函数,就会直接导致程序阻塞。


这种记录调度需求的处理方式叫"中断的延迟处理"(Deferring interrupt processing)。

图
如上面流程图所示:

  • t1:任务 1 运行,任务 2 阻塞。
  • t2:发生中断,该中断的 ISR 函数被执行,任务 1 被打断。
  • ISR 函数要尽快能快速地运行,它做一些必要的操作(比如清除中断),然后唤醒任务 2 。
  • t3:在创建任务时设置任务 2 的优先级比任务 1 高(这取决于设计者),所以 ISR 返回后,运行的是任务 2,它要完成中断的处理。
  • t4:任务 2 处理完中断后,进入阻塞态以等待下一个中断,任务 1 重新运行 。

这样做有多种好处:

  • 效率高:避免不必要的任务切换。
  • 让 ISR 更可控:中断随机产生,在 API 中进行任务切换的话,可能导致问题更复杂 。
  • 可移植性好。

🏀总结

软件定时器是经常使用的一种定时方式,要清楚的意识到守护任务的存在,以及守护任务所做的工作,用户操作软件定时器的本质就是:用户任务和守护任务通过命令队列进行通信。

要知道中断的优先级是高于所有用户任务的,而且在中断服务函数中不会立刻发起任务调度,而是记录下需要调度的需求,等ISR退出以后,由调度器发起任务调度。

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

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

相关文章

多级缓存入门

文章目录 什么是多级缓存JVM进程缓存环境准备安装MySQL导入Demo工程导入商品查询页面 初识Caffeine Lua语法初识Lua第一个lua程序变量和循环Lua的数据类型声明变量循环 条件控制、函数函数条件控制 多级缓存安装OpenRestyOpenResty快速入门反向代理流程OpenResty监听请求编写it…

【linux】麒麟v10安装Redis哨兵集群(ARM架构)

安装redis单示例的请看:麒麟v10安装Redis(ARM架构) 安装服务器 ​Hostname​IP addressmaster,sentinel192.168.0.1slave1,sentinel192.168.0.2slave2,sentinel192.168.0.3 下载安装包 (三台都操作) wget https://re…

施工进度难以把控,项目失控怎么办?

在施工项目中,施工进度的控制是非常重要的,它直接关系到项目是否能够按时完成以及项目成本的控制。然而,有时候由于各种原因,施工进度可能会难以把控,导致项目失控。那么,当项目失控时,我们应该…

Ansible上通过roles简化playbook演示介绍

目录 一.roles介绍 1.作用 2.role的目录结构 3.role和tasks的执行优先级顺序 二.自定义一个httpd的角色 1.完整目录结构展示 2.主要的各个目录配置 (1)vars目录和templates目录 (2)tasks目录和handlers目录 &#xff08…

云原生Docker数据管理

目录 Docker的数据管理 数据卷 数据卷容器 容器互联 容器中管理数据主要有两种方式: 数据卷(Data Volumes)数据卷容器(Data Volume Dontainers) Docker的数据管理 数据卷 数据卷是一个供容器使用的特殊目录&a…

题目 1120: C语言训练-“水仙花数“问题2python详解)——练气三层后期

✨博主:命运之光 🦄专栏:算法修炼之练气篇(C\C版) 🍓专栏:算法修炼之筑基篇(C\C版) 🍒专栏:算法修炼之练气篇(Python版) ✨…

今天玩到一个微信钓鱼小游戏,很有趣,居然还能玩萝卜刀

这款钓鱼冠军微信小游戏很有创意,除了传统的钓鱼玩法,居然还融合了黄金矿工的玩法,很不错的想法,而且居然还能玩最近比较火的萝卜刀,快来扫码体验一下吧,或者微信里搜索 《钓鱼冠军》小游戏,认…

2023年Q3线上生鲜水产数据分析:市场不景气,销额同比下滑44%

事实上,今年线上的生鲜生意市场并不景气。无论是Q1季度还是Q2季度,线上(京东平台)的销售额均同比去年下滑了10%左右。 然而到了Q3季度,整个下滑态势愈发严峻。可以看到鲸参谋数据显示,7月至9月生鲜水产在京…

Jmeter性能测试 —— TPS拐点寻找

寻找TPS性能拐点1、准备脚本①在本地电脑调试Jmeter压测脚本 ②上传到压测机Jmeter所在的服务器 2、执行压力测试①执行压测脚本 jmeter –n –t xianchengzuse.jmx ②记录业务压测数据 3、监控服务器性能指标 ①监控CPU输入top命令 ②监控内存 free –m ③jstat监控sweep和…

基于WebRTC构建的程序因虚拟内存不足导致闪退问题的排查以及解决办法的探究

目录 1、WebRTC简介 2、问题现象描述 3、将Windbg附加到目标进程上分析 3.1、Windbg没有附加到主程序进程上,没有感知到异常或中断 3.2、Windbg感知到了中断,中断在DebugBreak函数调用上 3.3、32位进程用户态虚拟地址和内核态虚拟地址的划分 …

大数据-Storm流式框架(四)---storm容错机制

1、集群节点宕机 Nimbus服务器 硬件 单点故障?可以搭建HA jStorm搭建 nimbus的HA nimbus的信息存储到zookeeper中,只要下游没问题(进程退出)nimbus退出就不会有问题, 如果在nimbus宕机,也不能提交…

特约|数码转型思考:Web3.0与银行

日前,欧科云链研究院发布重磅报告,引发银行界及金融监管机构广泛关注。通过拆解全球70余家银行的加密布局,报告认为,随着全球采用率的提升与相关技术的成熟,加密资产已成为银行业不容忽视也不能错过的创新领域。 作为…

机器学习实验一:KNN算法,手写数字数据集(使用汉明距离)(2)

KNN-手写数字数据集: 使用sklearn中的KNN算法工具包( KNeighborsClassifier)替换实现分类器的构建,注意使用的是汉明距离; 运行结果:(大概要运行4分钟左右) 代码: import pandas as…

记录一次时序数据库的实战测试

0x1.前言 ​ 本文章仅用于信息安全防御技术分享,因用于其他用途而产生不良后果,作者不承担任何法律责任,请严格遵循中华人民共和国相关法律法规,禁止做一切违法犯罪行为。文中涉及漏洞均以提交至教育漏洞平台。 0x2.背景 ​ 在某…

023-第三代软件开发-自定义Button

第三代软件开发-自定义Button 文章目录 第三代软件开发-自定义Button项目介绍自定义Button第一类型-加声音第二类型-加样式 第三类型-减声音总结一下存在一点小问题 关键字: Qt、 Qml、 Button、 关键字4、 关键字5 项目介绍 欢迎来到我们的 QML & C 项目&…

Redis缓存(缓存预热,缓存穿透,缓存雪崩,缓存击穿)

目录 一, 缓存 1, 什么是缓存 2, 什么是热点数据(热词) 3, 缓存更新策略 3.1 定期生成 3.2 实时生成 二, Redis缓存可能出现的问题 1, 缓存预热 1.1 什么是缓存预热 1.2 缓存预热的过程 2, 缓存穿透 2.1 什么是缓存穿透 2.2 缓存穿透产生的原因 2.3 缓存穿透的解…

优思学院|中质协六西格玛考试形式是什么样的?

中质协的考试形式主要为单选和多选题,近年也有加了一小部分填空题,和国际认证考试有很大区别,因为美质协(ASQ)、国际精益六西格玛研究所(ILSSI),又或者著名的PMP项目管理认证等都是采…

Java性能分析工具

Arthas 官网:简介 | arthas Arthas 是 Alibaba 开源的 Java 诊断工具,深受开发者喜爱。Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业…

Spark SQL概述与基本操作

目录 一、Spark SQL概述 (1)概念 (2)特点 (3)Spark SQL与Hive异同 (4)Spark的数据抽象 二、Spark Session对象执行环境构建 (1)Spark Session对象 (2)代码演…

【深度学习实验】循环神经网络(五):基于GRU的语言模型训练(包括自定义门控循环单元GRU)

文章目录 一、实验介绍二、实验环境1. 配置虚拟环境2. 库版本介绍 三、实验内容(一)自定义门控循环单元(GRU,Gated Recurrent Unit)1. get_params2. init_gru_state3. gru (二)创建模型0. 超参数…