FREERTOS任务调度和切换

news2024/11/16 20:35:39

我们已经学会了 FreeRTOS 的任务创建和删除,挂起和恢复等基本操作,并且也学习了分析FreeRTOS 源码所必须掌握的知识:列表和列表项。但是任务究竟如何被创建、删除、挂起和恢复的?系统是怎么启动的等等这些我们还不了解,一个操作系统最核心的内容就是多任务管理,所以我们非常有必要去学习一下 FreeRTOS 的任务创建、删除、挂起、恢复和系统启动等,这样才能对 FreeRTOS 有一个更深入的了解。

本章和下一章要讲解的内容和 Cortex-M 处理器的内核架构联系非常紧密!阅读本章必须先对 Cortex-M 处理器的架构有一定的了解,在学习本章的时候一定要配合《权威指南》来学习,

推荐大家仔细阅读《权威指南》中的如下章节:

1、第 3 章 技术综述,通过阅读本章可以对 Cortex-M 处理器的架构有一个大体的了解。

2、第 4 章 架构,强烈建议仔细阅读本章内容,尤其是要理解其中讲解到的各个寄存器。

3、第 5 章 指令集,本章和下一章的内容会涉及到一些有关 ARM 的汇编指令,在阅读的

时遇到不懂的指令可以查阅《权威指南》的第 5 章中相关指令的讲解。

4、第 7 章 异常和中断,大概了解一下 。

5、第 8 章 深入了解异常处理,强烈建议仔细阅读!

6、第 10 OS 支持特性, 强烈建议仔细阅读!

《权威指南》中的其他章节大家依据个人爱好来阅读,由于《权威指南》讲解的内容非常的“底层”,所以看起来可能会感觉晦涩难懂,如果看不懂的话不要着急,看不懂的地方就跳过,先对 Cortex-M 的处理器有一个大概的了解就行了。

任务调度器开启

前面的所有例程中我们都是在 main()函数中先创建一个开始任务 start_task,后面紧接着调用函数 vTaskStartScheduler()。这个函数的功能就是开启任务调度器的,这个函数在文件tasks.c中有定义,具体可自行查阅。

内部实现流程大致如下:

(1)、创建空闲任务,如果使用静态内存的话使用函数 xTaskCreateStatic()来创建空闲任务,优先级为 tskIDLE_PRIORITY,宏 tskIDLE_PRIORITY 0,也就是说空闲任务的优先级为最低。

(2)、如果使用软件定时器的话还需要通过函数 xTimerCreateTimerTask()来创建定时器服务任务。定时器服务任务的具体创建过程是在函数 xTimerCreateTimerTask()中完成的,这个函数很简单,大家就自行查阅一下。

(3)、关闭中断,在 SVC 中断服务函数 vPortSVCHandler()中会打开中断。

(4)、变量 xSchedulerRunning 设置为 pdTRUE,表示调度器开始运行。

(5)、当宏 configGENERATE_RUN_TIME_STATS 1 的时候说明使能时间统计功能,此时需要用户实现宏 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS,此宏用来配置一个定时器/计数器。

(6)、调用函数 xPortStartScheduler()来初始化跟调度器启动有关的硬件,比如滴答定时器、 FPU 单元和 PendSV 中断等等。

内核相关硬件初始化函数分析

关于上面的最后一点,内核相关硬件初始化函数 xPortStartScheduler()分析如下:

FreeRTOS 系统时钟是由滴答定时器来提供的,而且任务切换也会用到 PendSV 中断,这些硬件的初始化由函数 xPortStartScheduler()来完成,缩减后的函数代码如下:

(1)、设置 PendSV 的中断优先级,为最低优先级。

(2)、设置滴答定时器的中断优先级,为最低优先级。

(3)、调用函数 vPortSetupTimerInterrupt()来设置滴答定时器的定时周期,并且使能滴答定时

器的中断,函数比较简单,大家自行查阅分析。

(4)、初始化临界区嵌套计数器。

(5)、调用函数 prvStartFirstTask()开启第一个任务。

有一个问题,那就是,滴答定时器的定时周期以及中断开启不需要我们在移植初始化的时候配置吗?待解决。

启动第一个任务

经过上面的操作以后我们就可以启动第一个任务了,函数 prvStartFirstTask()用于启动第一个任务,这是一个汇编函数,函数源码如下:

(1)、将 0XE000ED08 保存在寄存器 R0 中。一般来说向量表应该是从起始地(0X00000000)开始存储的,不过,有些应用可能需要在运行时修改或重定义向量表,Cortex-M 处理器为此提供了一个叫做向量表重定位的特性。向量表重定位特性提供了一个名为向量表偏移寄存器(VTOR)的可编程寄存器。VTOR 寄存器的地址就是 0XE000ED08,通过这个寄存器可以重新定义向量表,比如在 STM32F103 ST 官方库中会通过函数 SystemInit()来设置VTOR 寄存器,代码如下:

SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; //VTOR=0x08000000+0X00

通过上面一行代码就将向量表开始地址重新定义到了 0X08000000,向量表的起始地址存储的就是 MSP 初始值。关于向量表和向量表重定位的详细内容请参阅《权威指南》的“第 7 章 异常和中断”的 7.5 小节

(2)、读取 R0 中存储的地址处的数据并将其保存在 R0 寄存器,也就是读取寄存器 VTOR 中的值,并将其保存在 R0 寄存器中。这一行代码执行完就以后 R0 的值应该为0X08000000

(3)、读取 R0 中存储的地址处的数据并将其保存在 R0 寄存器,也就是读取地址0X08000000处存储的数据,并将其保存在 R0 寄存器中。我们知道向量表的起始地址保存的就是主栈指针MSP 的初始值,这一行代码执行完以后寄存器 R0 就存储 MSP 的初始值。现在来看(1)(2)(3)这三步起始就是为了获取 MSP 的初始值而已!

(4)、复位 MSPR0 中保存了 MSP 的初始值,将其赋值给 MSP 就相当于复位 MSP

(5)(6)、使能中断,关于这两个指令的详细内容请参考《权威指南》的“第 4 章 架构”的第 4.2.3 小节

(7)(8)、数据同步和指令同步屏障,这两个指令的详细内容请参考《权威指南》的“第 5 章 指令集”的 5.6.13 小节

(9),调用 SVC 指令触发 SVC 中断,SVC 也叫做请求管理调用,SVC PendSV 异常对于OS 的设计来说非常重要。SVC 异常由 SVC 指令触发。关于 SVC 的详细内容请参考《权威指南》的“第 10 OS 支持特性”的 10.3 小节。在 FreeRTOS中仅仅使用 SVC 异常来启动第一个任务,后面的程序中就再也用不到 SVC 了。

SVC中断服务函数

在函数 prvStartFirstTask()中通过调用 SVC 指令触发了 SVC 中断,而第一个任务的启动就是在 SVC 中断服务函数中完成的,SVC 中断服务函数应该为 SVC_Handler(),但是FreeRTOSConfig.h 中通过#define 的方式重新定义为了 xPortPendSVHandler(),如下:

#define vPortSVCHandler SVC_Handler

函数 vPortSVCHandler()在文件 port.c 中定义,这个函数也是用汇编写的,函数源码如下:

详细过程参考视频,此处不赘述。

RTOS 系统的核心是任务管理,而任务管理的核心是任务切换,任务切换决定了任务的执行顺序,任务切换效率的高低也决定了一款系统的性能,尤其是对于实时操作系统。

任务切换场合

有两种场合会进行任务切换

● 可以执行一个系统调用

● 系统滴答定时器(SysTick)中断。

执行系统调用 

执行系统调用就是执行 FreeRTOS系统提供的相关API函数,比如任务切换函数 taskYIELD()FreeRTOS 有些 API 函数也会调用函数 taskYIELD(),这些 API 函数都会导致任务切换,这些 API 函数和任务切换函数 taskYIELD()都统称为系统调用。函数 taskYIELD()其实就是个宏,在文件 task.h中有如下定义:

#define taskYIELD()  portYIELD()

函数 portYIELD()也是个宏,在文件 portmacro.h 中有如下定义:

通过向中断控制和状态寄存器 ICSR bit28 写入 1 挂起 PendSV 来启动 PendSV 中断。 这样就可以在 PendSV 中断服务函数中进行任务切换了。

中断级的任务切换函数为 portYIELD_FROM_ISR(),定义如下:

可以看出 portYIELD_FROM_ISR()最终也是通过调用函数 portYIELD()来完成任务切换的。

系统滴答定时器(SysTick)中断

FreeRTOS 中滴答定时器(SysTick)中断服务函数中也会进行任务切换,滴答定时器中断服务函数如下:

在滴答定时器中断服务函数中调用了 FreeRTOS API 函数 xPortSysTickHandler(),此函数源码如下:

(1)、关闭中断

(2)、通过向中断控制和状态寄存器 ICSR bit28 写入 1 挂起 PendSV 来启动 PendSV 中断。这样就可以在 PendSV 中断服务函数中进行任务切换了。

(3)、打开中断。

PendSV异常

PendSV(可挂起的系统调用)异常对 OS 操作非常重要,其优先级可以通过编程设置。可以通过将中断控制和状态寄存器 ICSR bit28,也就是 PendSV 的挂起位置 1 来触发PendSV 中断。与 SVC 异常不同,它是不精确的,因此它的挂起状态可在更高优先级异常处理内设置,且会在高优先级处理完成后执行。利用该特性,若将 PendSV 设置为最低的异常优先级,可以让 PendSV 异常处理在所有其他中断处理完成后执行,这对于上下文切换非常有用,也是各种 OS 设计中的关键。

中断的优先级永远高于任务的优先级,用中断最低优先级的中断来切换任务,既不会影响高优先级的中断的执行,又能实现任务的切换。

在具有嵌入式 OS 的典型系统中,处理时间被划分为了多个时间片。若系统中只有两个任 务,这两个任务会交替执行,如下图所示:

OS 中,任务调度器决定是否应该执行上下文切换,如上图中任务切换都是由 SysTick中断执行,每次它都会决定切换到一个不同的任务中。

若中断请求(IRQ)SysTick 异常前产生,则 SysTick 异常可能会抢占 IRQ 的处理,在这种情况下,OS 不应该执行上下文切换,否则中断请求 IRQ 处理就会被延迟,而且在真实系统中延迟时间还往往不可预知——任何有一丁点实时要求的系统都决不能容忍这种事。对于 Cortex-M3 Cortex-M4 处理器,当存在活跃的异常服务时,设计默认不允许返回到线程模式,若存在活跃中断服务,且 OS 试图返回到线程模式,则将触发用法 fault,如下图 所示。

在一些 OS 设计中,要解决这个问题,可以在运行中断服务时不执行上下文切换,此时可以检查栈帧中的压栈 xPSR NVIC 中的中断活跃状态寄存器。不过,系统的性能可能会受到影响,特别时当中断源在 SysTick 中断前后持续产生请求时,这样上下文切换可能就没有执行的机会了。

为了解决这个问题,PendSV 异常将上下文切换请求延迟到所有其他 IRQ 处理都已经完成后,此时需要将 PendSV 设置为最低优先级。若 OS 需要执行上下文切换,他会设置PendSV 的挂起状态,并在 PendSV 异常内执行上下文切换。如下图所示:

上图中事件的流水账记录如下:

(1) 任务 A 呼叫 SVC 来请求任务切换(例如,等待某些工作完成)

(2) OS 接收到请求,做好上下文切换的准备,并且 pend 一个 PendSV 异常。

(3) CPU 退出 SVC 后,它立即进入 PendSV,从而执行上下文切换。

(4) PendSV 执行完毕后,将返回到任务 B,同时进入线程模式。

(5) 发生了一个中断,并且中断服务程序开始执行。

(6) ISR 执行过程中,发生 SysTick 异常,并且抢占了该 ISR

(7) OS 执行必要的操作,然后 pend PendSV 异常以作好上下文切换的准备。

(8) SysTick 退出后,回到先前被抢占的 ISR 中, ISR 继续执行

(9) ISR 执行完毕并退出后, PendSV 服务例程开始执行,并且在里面执行上下文切换。

(10) PendSV 执行完毕后,回到任务 A,同时系统再次进入线程模式。

讲解 PendSV 异常的原因就是让大家知道,FreeRTOS 系统的任务切换最终都是在 PendSV 中断服务函数中完成的,UCOS 也是在 PendSV 中断中完成任务切换的。

PendSV中断服务函数

前面说了 FreeRTOS 任务切换的具体过程是在 PendSV 中断服务函数中完成的,接着我们就来学习PendSV 的中断服务函数,看看任务切换过程究竟是怎么进行的。PendSV 中断服务函数本应该为 PendSV_Handler(),但是 FreeRTOS 使用#define 重定义了,如下:

#define xPortPendSVHandler PendSV_Handler

该函数源码如下:

(1)、读取进程栈指针,保存在寄存器 R0 里面。

(2)(3),获取当前任务的任务控制块,并将任务控制块的地址保存在寄存器 R2 里面。

(4)、保存 r4~r11 R14 这几个寄存器的值。

(5)、将寄存器 R0 的值写入到寄存器 R2 所保存的地址中去,也就是将新的栈顶保存在任务控制块的第一个字段中。此时的寄存器 R0 保存着最新的堆栈栈顶指针值,所以要将这个最新的栈顶指针写入到当前任务的任务控制块第一个字段,而经过(2)(3)已经获取到了任务控制块,并将任务控制块的首地址写如到了寄存器 R2 中。

(6)、将寄存器 R3 R14 的值临时压栈,寄存器 R3 中保存了当前任务的任务控制块,而接下来要调用函数 vTaskSwitchContext(),为了防止 R3 R14 的值被改写,所以这里临时将 R3R14 的值先压栈。

(7)(8)、关闭中断,进入临界区

(9)、调用函数 vTaskSwitchContext(),此函数用来获取下一个要运行的任务,并将 pxCurrentTCB 更新为这个要运行的任务。

(10)(11)、打开中断,退出临界区。

(12)、刚刚保存的寄存器 R3 R14 的值出栈,恢复寄存器 R3 R14 的值。注意,经过(12)步,此时 pxCurrentTCB 的值已经改变了,所以读取 R3 所保存的地址处的数据就会发现其值改变了,成为了下一个要运行的任务的任务控制块。

(13)(14)、获取新的要运行的任务的任务堆栈栈顶,并将栈顶保存在寄存器 R0 中。

(15)R4~R11,R14 出栈,也就是即将运行的任务的现场。

(16)、更新进程栈指针 PSP 的值。

(17)、执行此行代码以后硬件自动恢复寄存器 R0~R3R12LRPC xPSR 的值,确定

异常返回以后应该进入处理器模式还是进程模式,使用主栈指针(MSP)还是进程栈指针(PSP)

很明显这里会进入进程模式,并且使用进程栈指针(PSP),寄存器 PC 值会被恢复为即将运行的任务的任务函数,新的任务开始运行!至此,任务切换成功。

总的来说,其实就是保存之前的任务现场,然后恢复下一个任务的现场。

查找下一个要运行的任务

PendSV 中断服务程序中有调用函数 vTaskSwitchContext()来获取下一个要运行的任务, 也就是查找已经就绪了的优先级最高的任务。

该函数内部实现过程如下:

(1)、如果调度器挂起那就不能进行任务切换。

(2)、调用函数 taskSELECT_HIGHEST_PRIORITY_TASK()获取下一个要运行的任务。

taskSELECT_HIGHEST_PRIORITY_TASK()本质上是一个宏,在 tasks.c 中有定义。

FreeRTOS 中查找下一个要运行的任务有两种方法:一个是通用的方法,另外一个就是使用硬件的方法,这个在我们讲解 FreeRTOSCofnig.h 文件的时候就提到过了,至于选择哪种方法通过宏configUSE_PORT_OPTIMISED_TASK_SELECTION 来决定的。当这个宏为 1 的时候就使用硬件的方法,否则的话就是使用通用的方法,我们来看一下这两个方法的区别。

通用方法

顾名思义,就是所有的处理器都可以用的方法,通用方法是完全通过 C 语言来实现的,肯定适用于不同的芯片和平台,而且对于任务数量没有限制,但是效率肯定相对于使用硬件方法的要低很多。

硬件方法

硬件方法就是使用处理器自带的硬件指令来实现的,比如 Cortex-M 处理器就带有的计算前 导 0 个数指令:CLZ

如果使用硬件方法的话最多只能有 32 个优先级。

可以看出硬件方法借助一个指令就可以快速的获取处于就绪态的最高优先级,但是会限制任务的优先级数,比如 STM32 只能有 32 个优先级,不过 32 个优先级已经完全够用了。要知道FreeRTOS 是支持时间片的,每个优先级可以支持无限多个任务。

FreeRTOS 时间片调度

前面多次提到 FreeRTOS 支持多个任务同时拥有一个优先级,这些任务的调度是一个值得考虑的问题,不过这不是我们要考虑的。在 FreeRTOS 中允许一个任务运行一个时间片(一个时钟节拍的长度)后让出 CPU 的使用权,让拥有同优先级的下一个任务运行,至于下一个要运行哪个任务?在上小节里面已经分析过了,FreeRTOS 中的这种调度方法就是时间片调度。下图展示了运行在同一优先级下的执行时间图,在优先级 N 下有 3 个就绪的任务。

(1)任务3正在运行。

(2)这时一个时钟节拍中断(滴答定时器中断)发生,任务3的时间片用完,但是任务3

没有执行完。

(3)FreeRTOS 将任务切换到任务1,任务1是优先级 N 下的下一个就绪任务。

(4)任务1连续运行至时间片用完。

(5)任务3再次获取到 CPU 使用权,接着运行。

(6)任务3运行完成,调用任务切换函数 portYIELD()强行进行任务切换放弃剩余的时间片, 从而使优先级N下的下一个就绪的任务运行。

(7)FreeRTOS 切换到任务1

(8)任务1执行完其时间片。

要使用时间片调度的话宏 configUSE_PREEMPTION 和宏 configUSE_TIME_SLICING 必须为 1。时间片的长度由宏 configTICK_RATE_HZ 来确定,一个时间片的长度就是滴答定时器的中断周期,比如本教程中 configTICK_RATE_HZ 1000,那么一个时间片的长度就是 1ms。时间片调度发生在滴答定时器的中断服务函数中,前面讲解滴答定时器中断服务函数的时候说了在中断服务函数 SysTick_Handler()中会调用 FreeRTOS API 函数xPortSysTickHandler(),而函数 xPortSysTickHandler() 会 引 发 任 务 调 度 , 但 是 这个 任 务 调 度 是 有 条 件 的 ,函 数xPortSysTickHandler()如下:

上述代码中红色部分表明只有函数 xTaskIncrementTick()的返回值不为pdFALSE的时候就会进行任务调度!查看函数 xTaskIncrementTick()会发现有如下条件编译语句:

(1)、当宏 configUSE_PREEMPTION 和宏 configUSE_PREEMPTION 都为 1 的时候下面的代码才会编译。所以要想使用时间片调度的话这这两个宏都必须为 1,缺一不可!

(2)、判断当前任务所对应的优先级下是否还有其他的任务。

(3)、如果当前任务所对应的任务优先级下还有其他的任务那么就返回 pdTRUE

从上面的代码可以看出,如果当前任务所对应的优先级下有其他的任务存在,那么函数xTaskIncrementTick() 就会返回pdTURE ,由于函数返回值为 pdTURE因此函数xPortSysTickHandler()就会进行一次任务切换。

也就是说,时间片调度只有在同一优先级下还有其他任务时才会进行任务切换。

遗留问题:抢占式调度是怎么实现的?

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

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

相关文章

html--花瓣

代码 <!DOCTYPE html> <html lang"en" ><head> <meta charset"UTF-8"> <title>Petals</title><link rel"stylesheet" href"css/style.css"></head><body><div class"…

探索ChatGPT时代下的下一代信息检索系统:机遇与挑战

1 Introduction 2022 年 11 月 30 日&#xff0c;OpenAI 推出了 ChatGPT&#xff0c;这是一款由先进的 GPT3.5 和更高版本的 GPT-4 生成语言模型提供支持的 AI 聊天机器人应用程序。该应用迅速吸引了全球超亿用户&#xff0c;创下了产品快速传播的新纪录。 它能够以对话的方式…

Linux docker2--镜像及容器操作-nginx部署示例

一、上一篇已经完成了docker的基础环境搭建&#xff0c;和docker的安装。不清楚的小伙伴可以自己找上一篇看一下。本例以部署nginx为例展示 二、镜像相关 1、切换docker的镜像源为阿里云 命令&#xff1a; sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <&l…

数据库管理-第161期 数据库,走着瞧(20240318)

数据库管理161期 2024-03-18 数据库管理-第161期 数据库&#xff0c;走着瞧&#xff08;20240318&#xff09;MySQLSACC走着瞧RACAIOps 总结 数据库管理-第161期 数据库&#xff0c;走着瞧&#xff08;20240318&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文&am…

php7.3.4连接sqlserver(windows平台)

前言 有个项目需要手上laravel连接客户的sqlserver数据库读取数据&#xff0c;故在本地开发的lnmp环境中&#xff0c;php需要增加扩展 过程 从微软官网下载sqlsrv扩展,注意注意php版本&#xff0c;下载地址 解压的文件会有nts和ts两个版本&#xff0c;本地打开phpinfo查看 将…

大型机开发入门

这里写目录标题 零 大型机应用场景1 IBM官方网站1.1 保险业务1.2 证券业务1.3 银行业务 2 日语2.1 常见口语 一 Cobol 古董1 cobol 语言的前景1.1 tiobe编程网站排名1.2 cobol &#xff08;Common Business-Oriented Language&#xff09; 2 环境安装及开发工具1.1 VScode Gnu…

最小化战斗力差距——算法思路

题目链接&#xff1a;1.最小化战斗力差距 - 蓝桥云课 (lanqiao.cn) 可分析&#xff0c;把一个数组分成两组&#xff0c;求一组的最大值与另一组的最小值的差值的绝对值最小&#xff0c;可以转换为求任意两个相邻数字之间的最小插值的绝对值。 可看图示&#xff1a; package lan…

Morris法解决二叉树问题,展开链表及中序遍历

问题一&#xff1a;二叉树展开成单链表 问题二&#xff1a;二叉树中序遍历 咋一看非常简单的两道题&#xff0c;但是如果我们加以一些限制&#xff0c;这两题就不简单了。对于这两道题&#xff0c;我们的空间复杂度都必须控制在O(1)。也就是说&#xff0c;迭代和递归全部失效…

Ventana Veyron V系列处理器架构分析

概述 Ventana的产品策略 计算die IO die&#xff08;友商产品/用户自定义^^&#xff09; Veyron V1 2022年12月发布&#xff0c;Ventana发布了全球首款基于RISC-V架构的服务器CPU——Veyron V1&#xff0c;号称性能可超越AMD EPYC 7763。 服务器级别的CPU IP chiplets解…

考研复习C语言进阶(4)

1. 为什么存在动态内存分配 我们已经掌握的内存开辟方式有&#xff1a; int val 20;//在栈空间上开辟四个字节 char arr[10] {0};//在栈空间上开辟10个字节的连续空间 但是上述的开辟空间的方式有两个特点&#xff1a; 1. 空间开辟大小是固定的。 2. 数组在申明的时候&#…

2.7 ROC曲线相比P-R曲线有什么特点?

2.7 ROC曲线相比P-R曲线有什么特点&#xff1f; 前情提要&#xff1a; P-R曲线详见&#xff1a;2.2 什么是精确率&#xff08;Precision&#xff09;与召回率&#xff08;Recall&#xff09;&#xff1f;二者如何权衡&#xff1f;&#xff09; 2.4 ROC曲线是什么&#xff1f; 2…

海康威视相机SDK二次开发(JAVA语言)

目录 前言客户端创建虚拟相机示例代码保存图片程序运行结果修改需求 二次开发引入外部包对SaveImage.java文件进行修改保存图片saveDataToFile方法选择相机chooseCamera方法主方法 FileUtil类处理过期照片启动类与配置文件application.yml通过实体类读取yml启动类 SaveImage.ja…

2024.3.18

封装一个动物的基类&#xff0c;类中有私有成员&#xff1a;姓名&#xff0c;颜色&#xff0c;指针成员年纪 再封装一个狗这样类&#xff0c;共有继承于动物类&#xff0c;自己拓展的私有成员有&#xff1a;指针成员&#xff1a;腿的个数&#xff08;整型 int count&#xff0…

代码随想录算法训练营第25天|16.组合总和III|17.电话号码的字母组合

代码随想录算法训练营第25天|16.组合总和III|17.电话号码的字母组合 216.组合总和III 如果把 组合问题理解了&#xff0c;本题就容易一些了。 题目链接/文章讲解&#xff1a;https://programmercarl.com/0216.%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8CIII.html 视频讲解&#xf…

如何在开放麒麟系统安装cpolar内网穿透实现公网环境下SSH远程连接

文章目录 1. 安装SSH服务2. 本地SSH连接测试3. openKylin安装Cpolar4. 配置 SSH公网地址5. 公网远程SSH连接小结 6. 固定SSH公网地址7. SSH固定地址连接 openKylin是中国首个基于Linux 的桌面操作系统开发者平台&#xff0c;通过开放操作系统源代码的方式&#xff0c;打造具有自…

强化PaaS平台应用安全:关键策略与措施

PaaS&#xff08;平台即服务&#xff0c;Platform-as-a-Service&#xff09;是一种云计算服务模式&#xff0c;可以为客户提供一个完整的云平台&#xff08;硬件、软件和基础架构&#xff09;以用于快捷开发、运行和管理项目&#xff0c;从而降低了企业云计算应用的高成本和复杂…

【晴问算法】入门篇—贪心算法—整数配对

题目描述 有两个正整数集合S、T&#xff0c;其中S中有n个正整数&#xff0c;T中有m个正整数。定义一次配对操作为&#xff1a;从两个集合中各取出一个数a和b&#xff0c;满足a∈S、b∈T、a≤b&#xff0c;配对的数不能再放回集合。问最多可以进行多少次这样的配对操作。 输入描…

arthas火焰图(async-profiler)在云交易中的运用

在日常工作中经常会遇到系统应用出现full gc、cpu内存飙高等场景&#xff0c;如果想要快速解决这些线上问题就需要首先能快速定位&#xff0c;最好能定位到具体代码。本文旨在通过一款线上监控诊断产品&#xff0c;阿里巴巴的arthas&#xff08;阿尔萨斯&#xff09;内部集成的…

50、C++/类的继承和多态相关学习20240318

一、c编程实现&#xff1a; 封装一个动物的基类&#xff0c;类中有私有成员&#xff1a;姓名&#xff0c;颜色&#xff0c;指针成员年纪&#xff1b; 再封装一个狗这样类&#xff0c;共有继承于动物类&#xff0c;自己拓展的私有成员有&#xff1a;指针成员&#xff1a;腿的个…

数据结构与算法Bonus-KNN问题的代码求解过程

一、问题提出 &#xff08;一&#xff09;要求 1.随机生成>10万个三维点的点云&#xff0c;并以适当方式存储 2.自行实现一个KNN算法&#xff0c;对任意Query点&#xff0c;返回最邻近的K个点 3.不允许使用第三方库(e.g.flann&#xff0c;PCL,opencv)! 4.语言任选(推荐…