Linux内核源码分析 (3)调度器的实现
Linux内核源码分析 (3)调度器的实现
文章目录
- Linux内核源码分析 (3)调度器的实现
- 一、概述
- 二、调度器数据结构
- 1、task_struct中与调度有关的的成员
- 2、调度器类
- 3、就绪队列
- 4、调度实体
- 三、处理优先级
- 1、优先级的内核表示
- 2、计算优先级
- 3、计算负荷权重
- 四、核心调度器
- 1、周期性调度器
- 2、主调度器
- 3、与fork交互
- 4、上下文切换
一、概述
- 每次调用调度器时,它会挑选具有最高等待时间的进程,把CPU提供给该进程。如果经常发生这种情况,那么进程的不公平待遇不会累积,不公平会均匀分布到系统中的所有进程。
- 下图说明了调度器如何记录哪个进程已经等待了多长时间。由于可运行进程是排队的,该结构称之为就绪队列
所有的可运行进程都按时间在一个红黑树中排序,所谓时间即其等待时间。等待CPU时间最长的进程是最左侧的项,调度器下一次会考虑该进程。等待时间稍短的进程在该树上从左至右排序。在一个调度周期里面,所有进程的虚拟运行时间是相同的,所以在进程调度时,只需要找到虚拟运行时间最小的进程调度运行即可。
二、调度器数据结构
- 调度器使用一系列数据结构,来排序和管理系统中的进程。调度器的工作方式与这些结构的设计密切相关。几个组件在许多方面彼此交互。下面概述了这些组件的关联。下文中将这两个组件称为通用调度器(
generic scheduler
)或核心调度器(core scheduler
)。
- 主调度器:通过调用
schedule()
函数来完成进程的选择和切换。 - 周期性调度器:根据固定频率自动调用
scheduler_tick()
函数,不时检测是否有必要进行进程切换。 - 上下文切换:主要做两个事情(切换地址空间、切换寄存器和栈空间)
- 主调度器:通过调用
- 调度器类用于判断接下来运行哪个进程。内核支持不同的调度策略(完全公平调度、实时调度、在无事可做时调度空闲进程),调度类使得能够以模块化方法实现这些策略,即一个类的代码不需要与其他类的代码交互。在调度器被调用时,它会查询调度器类,得知接下来运行哪个进程。
- 在选中将要运行的进程之后,必须执行底层任务切换。这需要与CPU的紧密交互。
- 每个进程都刚好属于某一调度类,各个调度类负责管理所属的进程。通用调度器自身完全不涉及进程管理,其工作都委托给调度器类。
1、task_struct中与调度有关的的成员
include/linux/<sched.h>
struct task_struct { ... /*prio和normal_prio表示动态优先级,static_prio表示进程的静态优先级*/int prio, static_prio, normal_prio; /*表示实时进程的优先级,范围是[0,99]*/unsigned int rt_priority; /*表示该进程所属的调度器类*/const struct sched_class *sched_class; /*调度实体的实例。注意:调度器不限于调度进程,还可以处理更大的实体。这可以用于实现组调度:可用的CPU时间可以首先在一般的进程组(例如,所有进程可以按所有者分组)之间分配,接下来分配的时间在组内再次分配。*/struct sched_entity se; /*policy保存了对该进程应用的调度策略,Linux支持5种可能的值:SCHED_NORMAL: 通过完全公平调度器来处理,用于普通进程SCHED_BATCH: 通过完全公平调度器来处理,用于非交互、CPU使用密集的批处理进程,不会干扰交互式进程SCHED_IDLE: 通过完全公平调度器来处理,其相对权重总是最小的。SCHED_RR: 通过实时调度器处理,用于实现软实时进程,实现了一种循环方法SCHED_FIFO: 通过实时调度器处理,用于实现软实时进程,实现了一种先进先出机制。*/unsigned int policy; /*一个位域,在多处理器系统上使用,用来限制进程可以在哪些CPU上运行*/cpumask_t cpus_allowed; /*是一个表头,用于维护包含各进程的一个运行表,该成员实时调度器需要,不用于CFS*/struct list_head run_list; /*指定进程可使用CPU的剩余时间段,该成员实时调度器需要,不用于CFS*/unsigned int time_slice; ...
}
2、调度器类
-
调度器类提供了通用调度器和各个调度方法之间的关联,Linux内核抽象一个调度类
struct sched_class
结构体表示调度类,具体内核源码如下:
kernel/sched/<sched.h>struct sched_class {/*系统当中有多个调度类,按照调度优先级排成一个链表,下一优先级的高类*/const struct sched_class *next;#ifdef CONFIG_UCLAMP_TASKint uclamp_enabled; #endif/*将进程加入到执行队列当中,即将调度实体(进程)存放到红黑树中,并对nr_running变量自动会加1。(nr_running指定了队列上可运行进程的数目,不考虑其优先级或调度类)*/void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags);/*从执行队列当中删除进程,并对nr_running变量自动减1 */void (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags);/*放弃CPU执行权,实际上该函数执行先出队后入队,在这种情况下,它直接将调度实体放在红黑树的最右端*/void (*yield_task) (struct rq *rq);bool (*yield_to_task)(struct rq *rq, struct task_struct *p, bool preempt);/*用于检杳当前进程是否可被新进程抢占*/void (*check_preempt_curr)(struct rq *rq, struct task_struct *p, int flags);/*选择下一个应用要运行的进程*/struct task_struct *(*pick_next_task)(struct rq *rq);/*将进程放回到运行队列当中*/void (*put_prev_task)(struct rq *rq, struct task_struct *p);void (*set_next_task)(struct rq *rq, struct task_struct *p, bool first);#ifdef CONFIG_SMPint (*balance)(struct rq *rq, struct task_struct *prev, struct rq_flags *rf);/*为进程选择一个合适的CPU */int (*select_task_rq)(struct task_struct *p, int task_cpu, int sd_flag, int flags);/*迁移任务到处一个CPU */void (*migrate_task_rq)(struct task_struct *p, int new_cpu);/*专门用于唤醍进程*/void (*task_woken)(struct rq *this_rq, struct task_struct *task);/*修改进程在CPU的亲和力*/void (*set_cpus_allowed)(struct task_struct *p,const struct cpumask *newmask);/*启动运行队列*/void (*rq_online)(struct rq *rq);/*禁止运行队列*/void (*rq_offline)(struct rq *rq); #endif/*调用自time tick函数,它可能引起进程切换,将驱动运行时(running)抢占*/void (*task_tick)(struct rq *rq, struct task_struct *p, int queued);/* 进程创建时调用,不同调度策略的进程初始化也是不一样的 */void (*task_fork)(struct task_struct *p);/*进程退出时会使用*/void (*task_dead)(struct task_struct *p);/** The switched_from() call is allowed to drop rq->lock, therefore we* cannot assume the switched_from/switched_to pair is serliazed by* rq->lock. They are however serialized by p->pi_lock.*//*专门用于进程切换操作*/void (*switched_from)(struct rq *this_rq, struct task_struct *task);void (*switched_to) (struct rq *this_rq, struct task_struct *task);/*更改进程的优先级*/void (*prio_changed) (struct rq *this_rq, struct task_struct *task,int oldprio);unsigned int (*get_rr_interval)(struct rq *rq,struct task_struct *task);void (*update_curr)(struct rq *rq);#define TASK_SET_GROUP 0 #define TASK_MOVE_GROUP 1#ifdef CONFIG_FAIR_GROUP_SCHEDvoid (*task_change_group)(struct task_struct *p, int type); #endif };
-
调度器类可分为:
stop_sched_class
、dl_sched_class
、rt_sched_class
、fair_sched_class
和idle_sched_class
kernel/sched/<sched.h>extern const struct sched_class stop_sched_class;//停机调度类 extern const struct sched_class dl_sched_class;//限期调度类 extern const struct sched_class rt_sched_class;//实时调度类 extern const struct sched_class fair_sched_class;//公平调度类 extern const struct sched_class idle_sched_class;//空闲调度类
这5种调度类的优先级从高到低依次为:停机调度类、限期调度类、实时调度类、公平调度类、空闲调度类。其中,
SCHED_NORMAL
、SCHED_BATCH
和SCHED_IDLE
直接被映射到fair_sched_class
;SCHED_FIFO
和SCHED_RR
与rt_sched_class
向关联。Linux
调度核心选择下一个合适的task
运行时,会按照优先级顺序遍历调度类的pick_next_task
函数- 停机调度类:优先级是最高的调度类,停机进程是优先级最高的进程,可以抢占所有其它进程,其他进程不可能抢占停机进程.
const struct sched_class stop_sched_class = {.next = &dl_sched_class,.enqueue_task = enqueue_task_stop,.dequeue_task = dequeue_task_stop,.yield_task = yield_task_stop,.check_preempt_curr = check_preempt_curr_stop,.pick_next_task = pick_next_task_stop,.put_prev_task = put_prev_task_stop,.set_next_task = set_next_task_stop,... };
- 限期调度类:最早使用优先算法,使用红黑树把进程按照绝对截止期限从小到大排序,每次调度时选择绝对截止期限最小的进程。
const struct sched_class dl_sched_class = {.next = &rt_sched_class,.enqueue_task = enqueue_task_dl,.dequeue_task = dequeue_task_dl,.yield_task = yield_task_dl,.check_preempt_curr = check_preempt_curr_dl,.pick_next_task = pick_next_task_dl,.put_prev_task = put_prev_task_dl,.set_next_task = set_next_task_dl,... };
- 实时调度类:为每个调度优先级维护一个队列。
const struct sched_class rt_sched_class = {.next = &fair_sched_class,.enqueue_task = enqueue_task_rt,.dequeue_task = dequeue_task_rt,.yield_task = yield_task_rt,.check_preempt_curr = check_preempt_curr_rt,.pick_next_task = pick_next_task_rt,.put_prev_task = put_prev_task_rt,.set_next_task = set_next_task_rt,... };
- 公平调度类:使用完全公平调度算法。完全公平调度算法引入虚拟运行时间的相关概念:
虚拟运行时间 = 实际运行时间 * nice为0对应的权重 / 进程的权重
。const struct sched_class fair_sched_class = {.next = &idle_sched_class,.enqueue_task = enqueue_task_fair,.dequeue_task = dequeue_task_fair,.yield_task = yield_task_fair,.yield_to_task = yield_to_task_fair,.check_preempt_curr = check_preempt_wakeup,.pick_next_task = __pick_next_task_fair,.put_prev_task = put_prev_task_fair,.set_next_task = set_next_task_fair,... };
- 空闲调度类:每个CPU上有一个空闲线程,即
0
号线程。空闲调度类优先级最低,仅当没有其他进程可以调度的时候,才会调度空闲线程。const struct sched_class idle_sched_class = {/* .next is NULL *//* no enqueue/yield_task for idle tasks *//* dequeue is not valid, we print a debug message there: */.dequeue_task = dequeue_task_idle,.check_preempt_curr = check_preempt_curr_idle,.pick_next_task = pick_next_task_idle,.put_prev_task = put_prev_task_idle,.set_next_task = set_next_task_idle,... };
- 停机调度类:优先级是最高的调度类,停机进程是优先级最高的进程,可以抢占所有其它进程,其他进程不可能抢占停机进程.
-
观察上述5个调度类的
next
成员变量,可以发现他们是串联在一起的。Linux
调度核心选择下一个合适的task
运行时,会按照优先级顺序遍历调度类的pick_next_task
函数。
3、就绪队列
- 核心调度器用于管理活动进程的主要数据结构称之为就绪队列。各个CPU都有自身的就绪队列,各个活动进程只出现在一个就绪队列中。在多个CPU上同时运行一个进程是不可能的(但发源于同一进程的各线程可以在不同处理器上执行,因为进程管理对进程和线程不作重要的区分)。
- 注意,进程并不是由就绪队列(
rq
)的成员直接管理的!这是各个调度器类(如stop_sched_class
、dl_sched_class
、rt_sched_class
、fair_sched_class
和idle_sched_class
)的职责,因此在各个就绪队列中嵌入了特定于调度器类的子就绪队列(struct cfs_rq cfs;
和struct rt_rq rt;
)。 - 就绪队列主要使用下列数据结构实现
kernel/sched.cstruct rq { /*指定了队列上可运行进程的数目,不考虑其优先级或调度类*/unsigned long nr_running; ... /*提供了就绪队列当前负荷的度量,队列的负荷本质上与队列上当前活动进程的数目成正比。*/struct load_weight load; #define CPU_LOAD_IDX_MAX 5 /*用于跟踪此前的负荷状态*/unsigned long cpu_load[CPU_LOAD_IDX_MAX]; /*cfs和rt是嵌入的子就绪队列,分别用于完全公平调度器和实时调度器。*/struct cfs_rq cfs; struct rt_rq rt; /*curr指向当前运行的进程的task_struct实例。idle指向idle进程的task_struct实例,该进程亦称为idle线程,在无其他可运行进程时执行*/struct task_struct *curr, *idle; /*用于实现就绪队列自身的时钟。每次调用周期性调度器时,都会更新clock的值。*/u64 clock; ... };
- 系统的所有就绪队列都在
runqueues
数组中,该数组的每个元素分别对应于系统中的一个CPU
。在单处理器系统中,由于只需要一个就绪队列,数组只有一个元素。
kernel/sched.cstatic DEFINE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues);
4、调度实体
- 由于调度器可以操作比进程更一般的实体(进程嵌入了
sched_entity
实例,所以进程是可调度实体,还有更大的可调度实体比如组调度等 ),因此需要一个适当的数据结构来描述此类实体。其定义如下:
include/linux/<sched.h>struct sched_entity { /*用于负载均衡,决定了各个实体占队列总负荷的比例*/struct load_weight load; /*run_node是标准的树结点,使得实体可以在红黑树上排序*/struct rb_node run_node; /*表示该实体当前是否在就绪队列上接受调度*/unsigned int on_rq; /*记录消耗的CPU时间,以用于完全公平调度器。跟踪运行时间是由update_curr不断累积完成的,调度器中许多地方都会调用该函数,例如,新进程加入就绪队列时,或者周期性调度器中。每次调用时,会计算当前时间和exec_start之间的差值,exec_start则更新到当前时间。差值则被加到sum_exec_runtime。*/u64 sum_exec_runtime; u64 exec_start; /*在进程执行期间虚拟时钟上流逝的时间数量*/u64 vruntime; /*在进程被撤销CPU时,其当前sum_exec_runtime值保存到prev_exec_runtime。此后,在进程抢占时又需要该数据。注意:此过程并不会更新sum_exec_runtime。*/u64 prev_sum_exec_runtime; ... }
三、处理优先级
1、优先级的内核表示
- 在用户空间可以通过
nice
命令设置进程的静态优先级,这在内部会调用nice
系统调用。进程的nice
值为[-20,+19]
。值越低,表明优先级越高。内核使用范围[0,139]
来表示内部优先级。同样是值越低,优先级越高。实时进程范围为[0,99]
。nice
值[-20, +19]
映射到范围100
到139
。如下图所示,实时进程的优先级总是比普通进程更高。
- Linux内核优先级源码如下:
include/linux/sched/<prio.h>#define MAX_NICE 19 #define MIN_NICE -20/*nice值的范围*/ #define NICE_WIDTH (MAX_NICE - MIN_NICE + 1)/*实时进程最大优先级(不包含)*/ #define MAX_USER_RT_PRIO 100 #define MAX_RT_PRIO MAX_USER_RT_PRIO/*普通进程最大优先级(不包含)*/ #define MAX_PRIO (MAX_RT_PRIO + NICE_WIDTH) // 140#define DEFAULT_PRIO (MAX_RT_PRIO + NICE_WIDTH / 2) // 120
2、计算优先级
- 由前文可知,
task_struct
采用了4个成员来表示进程的优先级:prio
和normal_prio
表示动态优先级,static_prio
表示进程的静态优先级。rt_priority
表示进程的实时优先级,只对实时进程有用。- 静态优先级
static_prio
是进程启动时分配的优先级。它可以用nice
和sched_setscheduler
系统调用修改,否则在进程运行期间会一直保持恒定。 - 正常优先级
normal_priority
表示基于进程的静态优先级和调度策略计算出的优先级。因此,即使普通进程和实时进程具有相同的静态优先级,其普通优先级也是不同的。进程分支时,子进程会继承普通优先级。 - 调度优先级
prio
被调度器考虑,数值越小 , 优先级越高 。一般情况下prio
字段 等于normal_prio
字段。 特殊情况 : 在锁同步机制中 , 如果 A 进程 占有了 实时互斥锁 , B 进程 等待该 实时互斥锁 , 假如 B 进程的优先级 高于 A 进程 的优先级 , 此时就会将 占有 实时互斥锁 的 A 进程的 prio 优先级 提高到与 B 进程 prio 优先级相等的地位 ; - 实时优先级
rt_priority
:对于“限期进程”和“普通进程”来说字段值总为0
, 没有意义。对于“实时进程”来说,值为1~99
, 其数值越大 , 优先级越高。
- 静态优先级
- 各种进程的四种优先级总结
3、计算负荷权重
- 进程的重要性不仅是由优先级指定的,而且还需要考虑保存在
task_struct->se.load
的负荷权重。负荷权重包含在数据结构load_weight
中。set_load_weight
负责根据进程类型及其静态优先级计算负荷权重。 - 进程每降低一个
nice
值,则多获得10%
的CPU
时间,每升高一个nice
值,则放弃10%
的CPU
时间。为执行该策略,内核将优先级转换为权重值。我们首先看一下转换表。
kernel/sched.c
对内核使用的范围static const int prio_to_weight[40] = { /* -20 */ 88761, 71755, 56483, 46273, 36291, /* -15 */ 29154, 23254, 18705, 14949, 11916, /* -10 */ 9548, 7620, 6100, 4904, 3906, /* -5 */ 3121, 2501, 1991, 1586, 1277, /* 0 */ 1024, 820, 655, 526, 423, /* 5 */ 335, 272, 215, 172, 137, /* 10 */ 110, 87, 70, 56, 45, /* 15 */ 36, 29, 23, 18, 15, };
[0, 39]
中的每个nice
级别,该数组中都有一个对应项。在计算CFS算法虚拟运行时间是会用到nice
级别为0
权重值,该权重值为1024
。执行转换的代码也需要考虑实时进程。实时进程的权重是普通进程的两倍。另一方面,SCHED_IDLE
进程的权重总是非常小。
四、核心调度器
1、周期性调度器
2、主调度器
3、与fork交互
4、上下文切换
相关文章:

Linux内核源码分析 (3)调度器的实现
Linux内核源码分析 (3)调度器的实现 文章目录 Linux内核源码分析 (3)调度器的实现一、概述二、调度器数据结构1、task_struct中与调度有关的的成员2、调度器类3、就绪队列4、调度实体 三、处理优先级1、优先级的内核表示2、计算优先级3、计算负荷权重 四、核心调度器1、周期性调…...

网络安全法+网络安全等级保护
网络安全法 网络安全法21条 网络安全法31条 网络安全等级保护 网络安全等级保护分为几级? 一个中心,三重防护 等级保护2.0网络拓扑图 安全区域边界 安全计算环境 等保安全产品 物理机房安全设计...
持续集成对软件项目管理的作用
l、对项目目标管理的作用 软件项目的目标是开发出可运行的、客户满意的软件系统持续集成有统一的代 码库。要求开发人员定期地、不断地向代码库提交代码。新近提交的代码会经过编 译与测试.与代码库中旧有的代码相整合,形成安全稳定运行的代码库&…...

【Qt QAxObject】使用 QAxObject 高效任意读写 Excel 表
1. 用什么操作 Excel 表 Qt 的官网库中是不包含 Microsoft Excel 的操作库,关于对 Microsoft Excel 的操作库可选的有很多,包含基于 Windows 系统本身的 ActiveX、Qt Xlsx、xlsLib、LibXL、qtXLS、BasicExcel、Number Duck。 库.xls.xlsx读写平台Qt Xls…...

java八股文面试[多线程]——自旋锁
优点: 1. 自旋锁尽可能的减少线程的阻塞,这对于锁的竞争不激烈,且占用锁时间非常短的代码块来说性能能大幅度的提升,因为自旋的消耗会小于线程阻塞挂起再唤醒的操作的消耗 ,这些操作会导致线程发生两次上下文切换&…...

分布式系统的多数据库,实现分布式事务回滚(1.7.0 seata整合2.0.4nacos)
正文 1、解决的应用场景是分布式事务,每个服务有独立的数据库。 2、例如:A服务的数据库是A1,B服务的数据库是B2,A服务通过feign接口调用B服务,B涉及提交数据到B2,业务是在B提交数据之后,在A服…...

PDF可以修改内容吗?有什么注意的事项?
PDF是一种跨平台的电子文档格式,可以在各种设备上轻松阅读和共享。许多人喜欢将文档转换为PDF格式以确保格式的一致性和易读性。但是,PDF文件一般被认为是“只读”文件,即无法编辑。那么,PDF文件是否可以修改呢? 答案是…...

自动泊车的自动驾驶控制算法
1. 自动泊车系统 自动泊车系统(AutomatedParkingASSiSt,APA)利用车辆搭载的传感器感知车辆周边环境,扫描满足当前车辆停放的障碍物空间车位或线车位,并通过人机交互(HumanMachine Interface,HMI)获取驾驶员对目标车位的选择或自动确定目标车位,自动规划泊车路径,通过控制器向车…...
Java doc等文件生成PDF、多个PDF合并
之前写过一遍文章是 图片生成PDF。 今天继续来对 doc等文件进行pdf合并以及多个pdf合并为一个pdf。 兄弟们,还是开箱即用。 1、doc生成pdf 依赖 <!-- doc生成pdf --><dependency><groupId>com.aspose</groupId><artifactId>aspose…...

【C++】list类的模拟实现
🏖️作者:malloc不出对象 ⛺专栏:C的学习之路 👦个人简介:一名双非本科院校大二在读的科班编程菜鸟,努力编程只为赶上各位大佬的步伐🙈🙈 目录 前言一、list类的模拟实现1.1 list的…...

机械臂+2d相机实现复合机器人定位抓取
硬件参数 机械臂:艾利特 相机:海康相机 2d识别库:lindmod,github可以搜到 光源:磐鑫光源 软件参数 系统:windows / Linux 开发平台:Qt 开发语言:C 开发视觉库:OpenCV …...

网络编程 http 相关基础概念
文章目录 表单是什么http请求是什么http请求的结构和说明关于http方法 GET和POST区别http常见状态码http响应http 请求是无状态的含义html是什么 (前端内容,了解即可)html 常见标签 (前端内容,了解即可)关于…...
LatexEasy公式渲染教程
LatexEasy使用简单的URL渲染公式为图片 https://r.latexeasy.com/image.svg?1-sin^2(x) 使用单个HTML图像标签将公式添加到任何现有网站 <img src"https://r.latexeasy.com/image.svg?1-sin^2(x)" />...

十年测试工程师叙述自动化测试学习思路
自动化测试介绍 自动化测试(Automated Testing),是指把以人为驱动的测试行为转化为机器执行的过程。实际上自动化测试往往通过一些测试工具或框架,编写自动化测试用例,来模拟手工测试过程。比如说,在项目迭代过程中,持…...

SpringAOP详解(下)
proxyFactory代理对象创建方式和代理对象调用方法过程: springaop创建动态代理对象和代理对象调用方法过程: 一、TargetSource的使用 Lazy注解,当加在属性上时,会产生一个代理对象赋值给这个属性,产生代理对象的代码为…...
主流软件漏洞跟踪 Apache RocketMQ NameServer 远程代码执行漏洞(CVE-2023-37582)
主流软件漏洞跟踪 Apache RocketMQ NameServer 远程代码执行漏洞(CVE-2023-37582) 漏洞描述影响版本安全版本如何修复可供参考的资料主流软件漏洞跟踪 Apache RocketMQ NameServer 远程代码执行漏洞(CVE-2023-37582) CVE编号 : CVE-2023-37582 利用情况 : EXP 已公开 …...

Element table根据字段合并表格(可多字段合并),附带拖拽列动态合并
效果如图,姓名 数值1 字段进行自动合并 封装合并列js - tableMerge.js // 获取列合并的行数 // params // tableData: 表格数据 // mergeId: 合并的列的字段名 export const tagRowSpan (tableData, mergeId) >{const tagArr [];let pos 0;tableData.map((i…...

C++标准库STL容器详解
目录 C标准模板库STL容器容器分类容器通用接口 顺序容器vectorlistdeque 容器适配器queuestackpriority_queue 关联容器:红黑树setmultisetmapmultimap 关联容器:哈希表unordered_set和unordered_multisetunordered_map和unordered_multimap 附1…...
ParNew垃圾收集器(Serial+多线程)是干什么用的?
在Java中,ParNew垃圾收集器是一种垃圾收集算法,它是Serial垃圾收集器的多线程版本。它主要用于新生代(Young Generation)的垃圾收集。新生代是Java堆内存的一部分,主要用于存放新创建的对象。 ParNew垃圾收集器的设计目标是在多核CPU上并行地…...

【Android】AES解密抛出异常Cipher functions:OPENSSL_internal:WRONG_FINAL_BLOCK_LENGTH
Java使用AES加密的时候没得问题,但是在解密的时候就出错了,一起来找找原因吧。 首先,Java运行的代码如下,使用AES加解密 Cipher cipher Cipher.getInstance("AES/CBC/NOPadding"); //...主要问题 可调试运行控制台抛…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...

Ubuntu系统多网卡多相机IP设置方法
目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机,交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息,系统版本:Ubuntu22.04.5 LTS;内核版本…...
WEB3全栈开发——面试专业技能点P7前端与链上集成
一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染(SSR)与静态网站生成(SSG) 框架,由 Vercel 开发。它简化了构建生产级 React 应用的过程,并内置了很多特性: ✅ 文件系…...

Spring AOP代理对象生成原理
代理对象生成的关键类是【AnnotationAwareAspectJAutoProxyCreator】,这个类继承了【BeanPostProcessor】是一个后置处理器 在bean对象生命周期中初始化时执行【org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization】方法时…...
2025.6.9总结(利与弊)
凡事都有两面性。在大厂上班也不例外。今天找开发定位问题,从一个接口人不断溯源到另一个 接口人。有时候,不知道是谁的责任填。将工作内容分的很细,每个人负责其中的一小块。我清楚的意识到,自己就是个可以随时替换的螺丝钉&…...
SQL进阶之旅 Day 22:批处理与游标优化
【SQL进阶之旅 Day 22】批处理与游标优化 文章简述(300字左右) 在数据库开发中,面对大量数据的处理任务时,单条SQL语句往往无法满足性能需求。本篇文章聚焦“批处理与游标优化”,深入探讨如何通过批量操作和游标技术提…...
基于Uniapp的HarmonyOS 5.0体育应用开发攻略
一、技术架构设计 1.混合开发框架选型 (1)使用Uniapp 3.8版本支持ArkTS编译 (2)通过uni-harmony插件调用原生能力 (3)分层架构设计: graph TDA[UI层] -->|Vue语法| B(Uniapp框架)B --&g…...

Selenium 查找页面元素的方式
Selenium 查找页面元素的方式 Selenium 提供了多种方法来查找网页中的元素,以下是主要的定位方式: 基本定位方式 通过ID定位 driver.find_element(By.ID, "element_id")通过Name定位 driver.find_element(By.NAME, "element_name"…...
mcts蒙特卡洛模拟树思想
您这个观察非常敏锐,而且在很大程度上是正确的!您已经洞察到了MCTS算法在不同阶段的两种不同行为模式。我们来把这个关系理得更清楚一些,您的理解其实离真相只有一步之遥。 您说的“select是在二次选择的时候起作用”,这个观察非…...