linux中deadline调度原理与代码注释

news2024/9/25 15:22:57

简介

deadline调度是比rt调度更高优先级的调度,它没有依赖于优先级的概念,而是给了每个实时任务一定的调度时间,这样的好处是:使多个实时任务场景的时间分配更合理,不让一些实时任务因为优先级低而饿死。deadline调度不是说必须在这个deadline时间点前跑完任务,而是在一个调度周期内,期望在deadline之前调度一次这个任务。理论上这个任务跑的时间是有可能超过deadline时间点,甚至period时间点。

deadline调度策略

三个参数

关于rt调度中优先级是怎么起作用的可以参考之前的一个文章:https://blog.csdn.net/qq_37517281/article/details/134080158

deadline调度用三个额定参数来控制调度,dl_runtime(允许跑的时间) / dl_deadline(期望在一个调度周期内,等待不超过dl_deadline的时间内被调度一次) / dl_period(调度周期)。其中dl_runtime <= dl_deadline <= dl_period。它期望在一个dl_period时长内,任务只能跑dl_runtime的时长,如果没有跑完这个时长,且在同一时间周期内的下次再调度时,只能跑此时到deadline还剩的时间中dl_runtime/dl_deadline的时长;如果下次唤醒已经跳到下一个周期,则可以重新跑dl_runtime的时长;但如果上次跑的时间超过额定的dl_runtime,导致runtime<0,则从下一周期中可跑时长应减去超出的时长(new_runtime = dl_runtime+runtime,其中runtime<0)。一个例外是有太多deadline任务,导致某个不幸的deadline任务长时间没被调度,则下几个周期后,他能跑的时长还是重置为dl_runtime。

一个cpu上的task按deadline先到的顺序,组织为一个红黑树dl_rq->root,其中支持迁移的task还会额外组织成一个红黑树dl_rq->pushable_dl_tasks_root

一个周期内实际可运行的时长用runtime表示,与dl_runtime都表示时长;但到达的时间点用deadline表示,是一个时间点,与dl_deadline表示一个周期内的时长相区别。

每个cpu运行队列的最早deadline组成了一个大顶堆(顶上的cpu的最早deadline最晚到达),当一个任务需要迁移时,在没有idle cpu的情况下,优先选最早deadline最晚到达的cpu做迁移(find_later_rq

当一个任务执行了runtime后,会有timer让它不再执行(dl_throttled),它的下一次可重新被调度的时间点为deadline+(dl_period - dl_deadline)。(start_dl_timer

bandwidth与density

有两个概念不易理解:bandwidth = runtime/period,density = runtime/deadline。

可以简单认为bandwidth是cpu与cpu负载之间的关系,density是一个cpu上任务与任务之间的关系。

bandwidth

bandwidth与utilization(runtime占整个周期的比例)的概念可以等同,每个cpu默认有global_rt_runtime(0.95e+9) / global_rt_period(1e+9) = 0.95 的bandwidth(init_dl_rq_bw_ratio)。当一个cpu的总bandwith(sum(runtime)/period)小时,有可能在运行deadline任务之后,将剩余的dl_period-dl_deadline的时长用于运行一些非deadline的任务(比如低级别的cfs任务,但grub_reclaim函数会尽可能减少这种可能,尽量让deadline任务先做)。

  • total_bw:一个cpu调度域中所有cpu的deadline task的bandwidth。其中包含所有从其它调度类提升优先级而来的task。
  • bw:(常量)一个cpu调度域的每个cpu的最大bandwidth,等于global_rt_runtime(0.95e+9) / global_rt_period(1e+9)=0.95
  • extra_bw:一个cpu运行队列上可分摊到其它域内cpu的空闲bandwidth,最初没有任务时,extra_bw=max_bw。
  • this_bw:一个cpu运行队列上active 与非active的task的bandwidth和。
  • max_bw:(常量)一个cpu运行队列上的最大bandwidth,等于global_rt_runtime(0.95e+9) / global_rt_period(1e+9)=0.95。
  • running_bw:一个cpu运行队列上非inactive任务的bandwidth,它的更新有延迟。wakeup任务时就可add,但任务block 要sub这个bandwidth要等到快到deadline时间点(称为zerolag_time)(更准确的讲是等到deadline前这个时长的时间点:(runtime/dl_runtime)×dl_period)。可以参考下面的图,图中有三种状态:
    • active contending:唤醒状态,进入运行队列
    • non contending:挂起任务,移出运行队列,但还没有到zerolag_time时间点
    • inactive:挂起任务,移出运行队列,且本周期已结束
    • 另外还有一个状态 throttled:当一个任务执行runtime后没执行完,要先睡至下个周期开始。或者任务主动yield也是throttled状态。两种情况都要移出运行队列。但其bw依然认为是在队列上且running的。可以认为还在active contending状态。
     **                             +------------------+
     *             wakeup           |    ACTIVE        |
     *          +------------------>+   contending     |
     *          | add_running_bw    |                  |
     *          |                   +----+------+------+
     *          |                        |      ^
     *          |                dequeue |      |
     * +--------+-------+                |      |
     * |                |   t >= 0-lag   |      | wakeup
     * |    INACTIVE    |<---------------+      |
     * |                | sub_running_bw |      |
     * +--------+-------+                |      |
     *          ^                        |      |
     *          |              t < 0-lag |      |
     *          |                        |      |
     *          |                        V      |
     *          |                   +----+------+------+
     *          | sub_running_bw    |    ACTIVE        |
     *          +-------------------+                  |
     *            inactive timer    |  non contending  |
     *            fired             +------------------+
     *

当向一个调度域中加入一个task时,调度域的total_bw会增加,但要先校验是否overflow,即total_bw + new > bw * total_capacity(total_capacity指域内所有cpu的算力)。然后在每个cpu上均摊减掉extra_bw。

density

density是一个cpu上各deadline任务之间的一个平衡概念,期望每个deadline任务的density都不超过额定的dl_runtime/dl_deadline(CBS rule),当有任务在一个调度周期内,睡眠一定时间又被唤醒时,这个突发的唤醒可能导致它的density过高(因为这个周期的时间已经只剩一部分了,剩余runtime还按完整周期来算,当然会高),需要尝试减少它可执行时长,来平衡与其它任务的关系(Revised CBS rule),即 runtime = (deadline - now) * density。(update_dl_revised_wakeup

其它名词

dl.overloaded与pushable_queue:overload不是说真的任务忙,而是说这个队列1有可在其它cpu执行的任务a,记在pushable_queue红黑树上。当这个任务a没有在运行时,允许其它队列2与任务a比较deadline的先后,如果比队列2的所有任务deadline都早,就尝试steal这个任务a去执行(特殊情况是,这个任务a比队列2的deadline都早,但任务a又在一些关键区,而被设置了无法迁移(migrate_disable),这时的策略是,给队列1一个信号,让它去检查,是否可以把在运行的任务b迁移出去,从而给这个不能迁移的任务a一个运行机会)。

task生命周期

通用调度过程

调度的大体过程为:

1、选下一个任务(__schedule->pick_next_task):这里会先pull一些其它队列的任务过来(dl_sched_class->balance->pull_dl_task),然后将前一个任务放下(dl_sched_class->put_prev_task),选下一个任务(dl_sched_class->pick_next_task),将它set到队列的next上(pick_next_task>set_next_task),同时注册push的callback(set_next_task->deadline_queue_push_tasks

2、切换上下文(context_switch->switch_to)

3、切换之后,尝试调push callback将一些可迁移任务给其它cpu队列(finish_task_switch->finish_lock_switch->__balance_callbacks->push_dl_tasks

deadline任务生命周期

一个任务被选中的开始是第一步中的set_next_task_dl,在这里开启一个计时器至可运行时长runtime结束(start_hrtick_dl),切换运行前,会将任务从迁移队列移出(dequeue_pushable_dl_task),然后运行一段时间后,可预见两种情况(其它情况,比如优先级提升,主要re):

1、runtime用完,高精计时器到达,触发task_tick_dl

2、主要yield让出cpu,触发yield_task_dl

这两种都会调用update_curr_dl更新剩余runtime。如果runtime用完了,队则任务退出运行列,和可迁移队列(__dequeue_task_dl),标记throtled,并启动计时至下个周期开始(start_dl_timer)才重新回到队列。如果runtime没有用完(比如set_next_task_dl->start_hrtick_dl开始计时后,被其它进程抢占,又回到这个进程执行,则runtime没有用完),这时如果它还是最先到deadline的task,则会对剩余的runtime重新调start_hrtick_dl计时。如果是主动sleep的情况,虽然退出队列了,但不会马上将bandwidth去掉,而是开启inactive_task_timer计时,到deadline时间点才将bandwidth去掉(实际是deadline - dl_period*(runtime/dl_runtime)的时间点)(task_non_contending)。

在中断恢复时,触发一次系统的schedule(),它会走pick_next_task的流程,根据deadline是否还是最先到达来决定是否换到其它任务。它可能会选中其它任务,这时将原任务放回可迁移队列(put_prev_task_dl->enqueue_pushable_dl_task

当throtled到的下一个周期到达时,会将task重新加回运行队列(dl_task_timer->enqueue_task_dl(ENQUEUE_REPLENISH)),并标记resched_curr()来让调度器发现这个任务,重新做抉择。

剩余runtime的扣除

上面提到的更新剩余runtime的方法,在grub(Greedy Reclamation of Unused Bandwidth)算法中不是直接减去运行时长,而是

bw_{active} = bw_{max} -bw_{extra} - (bw_{this} - bw_{running}) \\ runtime -= duration \times \frac{max(bw_{task}, bw_{active})}{bw_{max}}

这个公式在扣除runtime时考虑了总体空闲的情况,将总体的非active的时长(not running 和 extra的时长)分一部分给这个task,让他能再多跑一会(可以看到runtime减去的时间一定小于真实duration),从而减少一个周期内deadline任务都被延迟到下一周期,而本周期中没有deadline任务可跑的情况。(参考的这个pdf:http://retis.santannapisa.it/luca/ospm-summit/2017/Downloads/deadline_reclaiming.pdf,不确定我的理解对不对)

代码注释

enqueue_task_dl

enqueue_task_dl():
  // 如果task是从其它优先级提升到deadline的
  // 则不需要throttled到下个周期,因为它只是个临时行为
  //(比如一个cfs进程1拿了一个锁,而一个deadline进程2在等这个锁,
  //  则进程1会临时提升为deadline调度级别)
  if (is_dl_boosted() || !dl_prio(p->normal_prio)) {
    p->dl.dl_throttled = 0;
  }

  // 如果一个task还没有throttled,且超过了deadline,则标记throttled,
  // 并启动计时期至下个周期才解除throttled(deadline + (dl_period - dl_deadline))
  // 和重新装填runtime 和 deadline并在deadline前合适的时候调度
  if (!p->dl.dl_throttled && !dl_is_implicit(&p->dl))
    dl_check_constrained_dl(&p->dl);

  enqueue_dl_entity():
    if (flags & ENQUEUE_WAKEUP) {
      // 如果是一个从suspend状态激活的任务,且当前时间还没有超过deadline
      // 由于这个周期的时间已经只剩一部分了,而原来的runtime是按完整周期来算,
      // 算出的density(=runtime/deadline)会比额定的(dl_runtime/dl_period)要大。
      // 所以要对可运行时间做修剪:
      // runtime = (dl_runtime / dl_deadline) * (deadline - now())
      //
      // 对于超过deadline,说明这个周期内都没有调度机会
      // 或已经用完了这个周期的运行时间后睡眠了dl_period-dl_deadline时长
      // 这时可以直接重置 runtime = dl_runtime,  deadline = now + dl_deadline
      update_dl_entity();

    } else if (flags & ENQUEUE_REPLENISH) {
      // 通过优先级提升来的task,或主要释放cpu的task,
      // 或时间已经超过一个周期的task可以重置runtime 与 deadline
      // 而因为上个周期执行太久或cpu主频变化导致的执行时间超过可运行时间的场景
      // runtime是负的,需要在下个周期中将多的可运行时间减掉 (runtime += dl_runtime)
      replenish_dl_entity(dl_se);

    } else if (flags & ENQUEUE_RESTORE) {
      // 因修改task参数(比如修改优先级或可运行的cpu集合)
      // 导致的dequeue和重新enqueue,强制重置runtime与deadline
      setup_new_dl_entity()
    }

    __enqueue_dl_entity():
      // task 加入 deadline 运行队列
      rb_add_cached(&dl_se->rb_node, &dl_rq->root, __dl_less);

      // 更新cpu队列上最早的deadline值
      // 并更新这个cpu的最早deadline值在整个调度域中的排名
      //(按从deadline晚到早排序,用堆结构来组织,
      // 可用于为一个任务选择下个周期在哪个cpu上执行,优选deadline晚的执行)
	  inc_dl_tasks(dl_se, dl_rq);
  
    // 如果这个任务支持在其它cpu上跑,则可以被别人拿走执行
    // 按deadline先后进入pushable排序队列
	enqueue_pushable_dl_task()

dequeue_task_dl

dequeue_task_dl()
  // 更新任务的runtime,如果runtime用完,则要throtle(退出队列,并在下个周期开始时回到队列) 
  update_curr_dl();
  __dequeue_task_dl():

    // 除去运行队列中的项,更新cpu的最先到达deadline
    // 和在所有cpu最先到达deadline大顶堆中的位置
    dequeue_dl_entity();

    // 从可迁移队列移出,如果没有可迁移task,则清除overload标记
    dequeue_pushable_dl_task();
  
  // 对于 sleep 导致的出队,需要开启计时inactive_timer到deadline的时间点再更新bandwidth
  task_non_contending();

update_curr_dl

按grub算法扣除可运行时长,如果没有可运行时长了,则throttle至下个周期

// 在运行一段时间后中断中调用。
update_curr_dl()
  // 更新执行时间 
  update_current_exec_runtime();

  // 扣除时长为 dq = -(max{u, (Umax - Uinact - Uextra)} / Umax) dt
  dl_se->runtime -= grub_reclaim();
  
  // 如果剩余runtime小于0或主动让出cpu,则标记throttled,但这不会改变其bandwidth
  if (dl_runtime_exceeded(dl_se) || dl_se->dl_yielded) {
    dl_se->dl_throttled = 1;
    // 将它移出队列
    __dequeue_task_dl();

    // 如果它是其它调度类通过优先级提升而来的任务(这种临时任务不用睡眠等待)
    // 或dl任务启动计时失败,则再加回来
    if (unlikely(is_dl_boosted(dl_se) || !start_dl_timer(curr)))
      enqueue_task_dl(rq, curr, ENQUEUE_REPLENISH);
    
    // 大概率它不在运行队列上了,所以,标记重调度
    if (!is_leftmost(curr, &rq->dl))
      resched_curr(rq);
 

pull_dl_task

从其它cpu的可迁移队列拉任务过来。

比如从deadline类型转为其它类型,发现没有deadline任务可以调度了(switched_from_dl)

pull_dl_task():
  // 遍历每个有可迁移任务的cpu
  for_each_cpu(cpu, this_rq->rd->dlo_mask) {
    // 找到比当前cpu的deadline先到的task,从这些task中找到最早到deadline的
    ..

    // 如果可在本cpu运行,则拿来。
    deactivate_task(src_rq);
    set_task_cpu(this_cpu);
    activate_task(this_rq);

    // 如果因为一些临时原因不能迁移,则尝试在它所在cpu上换掉正运行的
    // (前提是正运行的task可迁移,为它找合适的迁移队列)
    stop_one_cpu_nowait(push_cpu_stop);
  }

push_dl_tasks

将可迁移任务主动推到其它cpu执行

比如下一个task不能抢占当前task,且下一个可以迁移(task_woken_dl)

这里代码为纯逻辑顺序,没按原代码组织,原代码版本linux v6.6

push_dl_tasks():
  // 尝试迁移每个pushable task,直到有一个迁移失败。
  while (push_dl_task(rq)):
    next_task = pick_next_pushable_dl_task(rq);
    find_lock_later_rq(next_task):
      // 为这个任务找合适的队列迁移过去
      find_later_rq();

      // 如果找到了下一个队列,则入那个队,并resched_curr那个队列。
      deactivate_task(rq, next_task, 0);
      set_task_cpu(next_task, later_rq->cpu);
      activate_task(later_rq, next_task, 0);
      resched_curr(later_rq);

      // 如果没找到下一个队列,且没有新的pushable task加入,则不再尝试
      if (!dl_task_is_earliest_deadline(task, later_rq)) break;

      // 如果pushable task变了,则有可能有新task,重试最多三次。
      ..


find_later_rq():
  // 找调度域中idle的cpu中bandwidth能满足这个task的cpu集合
  // 如果bandwidth都不满足,则找算力最大的cpu
  // 如果没有 free 的 cpu,则找最早deadline大顶堆中最晚到达deadline的那个cpu,
  // 确认这个任务的deadline在那个cpu的最早deadline之前,并选中它
  // 找到的cpu或cpu集合标记为A
  cpudl_find(&task_rq(task)->rd->cpudl, task, later_mask);

  // 从原cpu的domain里一层层向上找,与上面备选集合A的交集B
  // 如果有cpu要求在唤醒过程中考虑亲和性(SD_WAKE_AFFINE标记)则优先考虑

  for_each_domain(cpu, sd) {
    // 如果当前cpu在这个B集合中,则用当前cpu是最好的,不做迁移
    // 否则用round-robin方式在cpu集合B中找一个cpu
    cpumask_any_and_distribute(later_mask, sd);
  }

  如果所有备选cpu都没有SD_WAKE_AFFINE标记,则用round-robin方式在备选集合A中找一个cpu
  cpumask_any_distribute(later_mask);

set_next_task_dl

标记即将运行的任务是这个任务

set_next_task_dl():
  // 将要在本cpu运行的task,不再支持被其它cpu倫过去执行
  dequeue_pushable_dl_task();

  // 启动定时到可运行时长runtime耗尽
  start_hrtick_dl();
  
  // 更新load值
  // 参考:https://blog.csdn.net/qq_37517281/article/details/134039766
  update_dl_rq_load_avg非deadline调度类,load sum 累加0,load avg 累加1,

  // 在切换至这个task后,尝试将下一个到deadline的进程迁移到其它cpu执行
  deadline_queue_push_tasks(push_dl_tasks);

wakeup_preempt_dl

判断是否可抢占当前task

wakeup_preempt_dl():
  // 两任务deadline不同,选deadline先到的
  // 两任务deadline相同,原则是尽可能不抢占,可抢占的条件:
  // 运行中任务可移动且超过了deadline,且要换入的任务不可移动且deadline没到。

put_prev_task_dl

将前一个任务从运行队列移除

put_prev_task_dl():
  // 扣除运行时长
  update_curr_dl();
  
  // 累积load_sum/avg
  update_dl_rq_load_avg();

  // 如果任务可在其它cpu执行,则加入可迁移队列,并在下次调度时尝试迁移
  enqueue_pushable_dl_task();

switched_to_dl

从其它调度类型切至deadline调度

switched_to_dl():
  // 如果这个任务在一个周期时间内迁走又迁回,则将计时器去掉
  if (hrtimer_try_to_cancel(&p->dl.inactive_timer) == 1)
    put_task_struct(p);
  
  // 任务不是当前队列的任务
  if (rq->curr != p) {
    // 如果它可迁移,加入pushable队列
    deadline_queue_push_tasks();

    // 尝试抢当前在跑的task
    wakeup_preempt_dl();
  }

  // 如果提升优先级变为deadline任务,或用户指定它调度类型变更
  // 它的cpu没有变,还是之前运行的cpu,需要更新下load
  update_dl_rq_load_avg();

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

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

相关文章

未来五年工业AI的八大发展趋势

随着ChatGPT和生成式人工智能&#xff08;AI&#xff09;进入到大众的视线&#xff0c;突然之间&#xff0c;它成为世界上最热门的讨论话题之一。 不过&#xff0c;在制造业&#xff0c;这并不完全是件新鲜事。十多年来&#xff0c;机器学习&#xff08;ML&#xff09;技术一直…

1-交易系统设计的一些原则

高并发原则 无状态 如果设计的应用是无状态的&#xff0c;那么应用比较容易进行水平扩展。实际生产环境可能是这样的&#xff1a;应用无状态&#xff0c;配置文件有状态。比如&#xff0c;不同的机房需要读取不同的数据源&#xff0c;此时&#xff0c;就需要通过配置文件或配…

管理类联考——数学——真题篇——按题型分类——充分性判断题——蒙猜E

老老规矩&#xff0c;看目录&#xff0c;平均每年2E&#xff0c;跟2D一样&#xff0c;D是全对&#xff0c;E是全错&#xff0c;侧面也看出10道题&#xff0c;大概是3A/B&#xff0c;3C&#xff0c;2D&#xff0c;2E&#xff0c;其实还是蛮平均的。但E为1道的情况居多。 第20题…

架构设计系列之前端架构和后端架构的区别和联系

前端架构和后端架构都是软件系统中最关键的架构层&#xff0c;负责处理不同方面的任务和逻辑&#xff0c;两者之间是存在一些区别和联系的&#xff0c;我会从以下几个方面来阐述&#xff1a; 一、定位和职责 前端架构 主要关注用户界面和用户体验&#xff0c;负责处理用户与…

day53_vue+easyexcel+springboot

EasyExcel 一、初识EasyExcel 1. Apache POI 先说POI&#xff0c;有过报表导入导出经验的同学&#xff0c;应该听过或者使用。 Apache POI是Apache软件基金会的开源函式库&#xff0c;提供跨平台的Java API实现Microsoft Office格式档案读写。但是存在如下一些问题&#xf…

数据结构:图解手撕B-树以及B树的优化和索引

文章目录 为什么需要引入B-树&#xff1f;B树是什么&#xff1f;B树的插入分析B树和B*树B树B*树分裂原理 B树的应用 本篇总结的内容是B-树 为什么需要引入B-树&#xff1f; 回忆一下前面的搜索结构&#xff0c;有哈希&#xff0c;红黑树&#xff0c;二分…等很多的搜索结构&a…

超结MOS/低压MOS在5G基站电源上的应用-REASUNOS瑞森半导体

一、前言 5G基站是5G网络的核心设备&#xff0c;实现有线通信网络与无线终端之间的无线信号传输&#xff0c;5G基站主要分为宏基站和小基站。5G基站由于通信设备功耗大&#xff0c;采用由电源插座、交直流配电、防雷器、整流模块和监控模块组成的电气柜。所以顾名思义&#xf…

谈思生物医疗直播|“靶向双硫死亡在肿瘤治疗中的应用”

细胞死亡是维持生物发育和内部环境稳态的生理过程。靶向细胞死亡相关通路杀死癌细胞是癌症治疗的一大方向。今年年初&#xff0c;有研究团队发现和鉴定了一种全新的细胞死亡类型——双硫死亡(Disulfidptosis)&#xff0c;为癌治疗开辟了新的可能性。 溶质载体家族成员 SLC7A11…

Linux网络编程(二):Socket 编程

参考引用 黑马程序员-Linux 网络编程 1. 套接字概念 Socket 本身有 “插座” 的意思&#xff0c;在 Linux 环境下&#xff0c;用于表示进程间网络通信的特殊文件类型 本质为内核借助缓冲区形成的伪文件 既然是文件&#xff0c;那么可以使用文件描述符引用套接字 与管道类似&am…

CGAL中流线的二维放置

本章介绍CGAL 2D流线放置包。定义一节给出了基本定义和概念。基本概念一节对整合过程进行了描述。最远点播种策略一节简要介绍了该算法。“实现”一节介绍了包的实现&#xff0c;“示例”一节详细介绍了两个示例放置。 该算法的核心思想是对域中最大空腔中心的流线进行积分&am…

HuggingFace下载模型

目录 方式一&#xff1a;网页下载 方式二&#xff1a;Git下载 方式一&#xff1a;网页下载 方式二&#xff1a;Git下载 有些模型的使用方法页面会写git clone的地址&#xff0c;有些没写&#xff0c;直接复制网页地址即可 网页地址&#xff1a; ​https://huggingface.co/…

12.19_黑马数据结构与算法笔记Java

目录 203 排序算法 选择排序 204 排序算法 堆排序 205 排序算法 插入排序 206 排序算法 希尔排序 207 排序算法 归并排序 自顶至下 208 排序算法 归并排序 自下至上 209 排序算法 归并加插入 210 排序算法 单边快排 211 排序算法 双边快排 212 排序算法 快排 随机基准…

技术博客:市面上加密混淆软件的比较和推荐

引言 市面上有许多加密混淆软件可供开发者使用&#xff0c;但哪些软件是最好用的&#xff1f;哪些软件受到开发者的喜爱&#xff1f;本文将根据一次在CSDN上的投票结果&#xff0c;为大家介绍几款在程序员中普及度较高的加密软件。以下是投票结果&#xff0c;希望能对大家的选…

【jvm从入门到实战】(十) 实战篇-内存调优

内存溢出和内存泄漏&#xff1a;在Java中如果不再使用一个对象&#xff0c;但是该对象依然在GC ROOT的引用链上&#xff0c;这个对象就不会被垃圾回收器回收&#xff0c;这种情况就称之为内存泄漏。内存泄漏绝大多数情况都是由堆内存泄漏引起的。少量的内存泄漏可以容忍&#x…

MySQL5.x与8.0

大致区别 1. 性能&#xff1a;MySQL 8.0 的速度要比 MySQL 5.7 快 2 倍 MySQL 8.0 在以下方面带来了更好的性能&#xff1a;读/写工作负载、IO 密集型工作负载、以及高竞争&#xff08;"hot spot"热点竞争问题&#xff09;工作负载2. NoSQL&#xff1a;MySQL 从 5.7 …

CPU算力分配 - 华为OD统一考试

OD统一考试 题解: Java / Python / C++ 题目描述 现有两组服务器A和B,每组有多个算力不同的CPU,其中 A 是A组第个CPU的运算能力,是 B组 第个CPU的运算能力。一组服务器的总算力是各CPU的算力之和。 为了让两组服务器的算力相等,允许从每组各选出一个CPU进行一次交换。 求…

基于PHP的蛋糕购物商城系统

有需要请加文章底部Q哦 可远程调试 基于PHP的蛋糕购物商城系统 一 介绍 此蛋糕购物商城基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。系统角色分为用户和管理员。 技术栈&#xff1a;phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销…

做一个wiki页面是体验HTML语义的好方法

HTML语义&#xff1a;如何运用语义类标签来呈现Wiki网页 在上一篇文章中&#xff0c;我花了大量的篇幅和你解释了正确使用语义类标签的好处和一些场景。那么&#xff0c;哪些场景适合用到语义类标签呢&#xff0c;又如何运用语义类标签呢&#xff1f; 不知道你还记不记得在大…

爱芯派pro通过无线网卡rtl8188eu连接热点

爱芯派pro通过无线网卡rtl8188eu连接热点 爱芯派pro目前的底板的pcie的复位有问题&#xff0c;所以pcie接口无法挂载上去&#xff0c;所以自己购买的rtl8822网卡也用不了&#xff0c;然后想起来自己还有正点原子的rtl8188eu网卡&#xff0c;但是没有和工作人员进行摸索后才知道…

Swagger升级指南:Swagger2与Swagger3注解差异揭秘

在API开发的世界里&#xff0c;Swagger已经成为了一个不可或缺的工具&#xff0c;它让API的文档化和前后端的协作变得前所未有地简单。随着Swagger的进化&#xff0c;我们迎来了Swagger3&#xff0c;也被称为OpenAPI Specification 3.0。本篇博客将带大家深入了解Swagger2和Swa…