当前位置: 首页 > news >正文

Linux CFS调度器之周期性调度器scheduler_tick函数

文章目录

  • 前言
  • 一、简介
  • 二、源码分析
    • 2.1 scheduler_tick
    • 2.2 task_tick
    • 2.3 entity_tick
    • 2.4 check_preempt_tick
    • 2.5 resched_curr
  • 参考资料

前言

Linux内核调度器主要是主调度器和周期性调度器,主调度器请参考:Linux 进程调度之schdule主调度器

一、简介

每当定时器中断发生时,都会调用定时器中断处理程序。每当调用定时器中断处理程序时,处理程序会调用update_process_times函数,将一个时钟滴答分配给当前进程。在其中,会调用scheduler_tick函数。scheduler_tick函数执行和调度相关的一些操作,如检查是否有进程需要调度和切换。

时钟中断是调度器的脉搏,内核依靠周期性的时钟来处理器CPU的控制权。时钟中断处理程序,检查当前进程的执行时间是否超额,如果超额则设置重新调度标志(_TIF_NEED_RESCHED);时钟中断处理函数返回时,被中断的进程如果在用户模式下运行,需要检查是否有重新调度标志,设置了则调用schedule()调度。

周期性调度器scheduler_tick()以固定的频率检测是否有必要进行进程调度和切换。在CFS调度类中,scheduler_tick会检测一个进程执行的时间是否过长,以避免过程的延时,是时候让其他CFS就绪队列中的进程运行.

注意周期性调度器scheduler_tick()设置TIF_NEED_RESCHED标志来对进程进行标记需要被抢占,设置该位则表明需要进行调度切换,没有进行实际的抢占,只是将当前进程标记为应该被抢占。而实际的切换将在抢占执行点来完成。

如果当前进程需要重新调度的条件成立,这里只是会设置TIF_NEED_RESCHED标志,并不会马上调用schedule()来进行调度。真正的调度时机发生在从中断/异常返回时,会判断当前进程有没有被设置TIF_NEED_RESCHED,如果设置则调用schedule()来进行调度。

二、源码分析

流程图如下图左边所示:
在这里插入图片描述

2.1 scheduler_tick

// linux-4.10.1/kernel/sched/core.c/** This function gets called by the timer code, with HZ frequency.* We call it with interrupts disabled.*/
void scheduler_tick(void)
{(1)int cpu = smp_processor_id();struct rq *rq = cpu_rq(cpu);struct task_struct *curr = rq->curr;(2)raw_spin_lock(&rq->lock);update_rq_clock(rq);curr->sched_class->task_tick(rq, curr, 0);cpu_load_update_active(rq);calc_global_load_tick(rq);raw_spin_unlock(&rq->lock);(3)
#ifdef CONFIG_SMPrq->idle_balance = idle_cpu(cpu);trigger_load_balance(rq);
#endif}

这段代码是调度器的定时器中断处理函数,用于处理定时器中断事件。以下是对代码的详细说明:
(1)
首先,获取当前处理器的ID,并根据ID获取对应的运行队列(rq)和当前正在运行的任务(curr)。

(2)
使用原子自旋锁(raw_spin_lock)锁定运行队列,确保原子操作的执行。
调用update_rq_clock()函数,更新运行队列的时钟。
通过curr->sched_class->task_tick()函数调用,调用当前任务所属调度类的task_tick()函数,执行任务级别的时钟滴答处理。
调用cpu_load_update_active()函数,更新运行队列的活跃CPU负载。即就绪队列的cpu_load[]数据。
调用calc_global_load_tick()函数,计算全局负载的时钟滴答。
解锁运行队列,使用raw_spin_unlock。

(3)
如果编译选项中启用了SMP(对称多处理器)支持,会进行一些额外的处理:

将rq->idle_balance设置为idle_cpu(cpu),表示当前运行队列是否处于空闲状态。
调用trigger_load_balance()函数,触发负载平衡操作。

其中主要是:

curr->sched_class->task_tick(rq, curr, 0);

2.2 task_tick

curr->sched_class->task_tick(rq, curr, 0);
// kernel/sched/fair.cconst struct sched_class fair_sched_class = {.task_tick		= task_tick_fair,
}
// kernel/sched/fair.c/** scheduler tick hitting a task of our scheduling class:*/
static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued)
{struct cfs_rq *cfs_rq;struct sched_entity *se = &curr->se;for_each_sched_entity(se) {cfs_rq = cfs_rq_of(se);entity_tick(cfs_rq, se, queued);}if (static_branch_unlikely(&sched_numa_balancing))task_tick_numa(rq, curr);
}

这段代码是调度器中的公平调度类(fair)的任务时钟滴答处理函数。以下是对代码的详细说明:
(1)首先,定义了一个指向当前任务的调度实体(sched_entity)的指针se,并获取与该实体相关联的CFS运行队列(cfs_rq)。

(2)使用for_each_sched_entity迭代当前任务的调度实体,对每个实体执行以下操作:

获取与该实体相关联的CFS运行队列(cfs_rq)。
调用entity_tick()函数,处理该实体的时钟滴答事件。

其中entity_tick函数最为重要,检查该任务是否需要调度,这里表明需要进行调度切换,没有进行实际的抢占,只是将当前进程标记为应该被抢占。而实际的切换将在抢占执行点来完成。

* 在不支持组调度条件下, 只循环一次
* 在组调度的条件下, 调度实体存在层次关系,
* 更新子调度实体的同时必须更新父调度实体
#ifdef CONFIG_FAIR_GROUP_SCHED
/* Walk up scheduling entities hierarchy */
#define for_each_sched_entity(se) \for (; se; se = se->parent)#else	/* !CONFIG_FAIR_GROUP_SCHED */#define for_each_sched_entity(se) \for (; se; se = NULL)
#endif	/* CONFIG_FAIR_GROUP_SCHED */
static inline struct task_struct *task_of(struct sched_entity *se)
{return container_of(se, struct task_struct, se);
}#define task_rq(p)		cpu_rq(task_cpu(p))static inline struct cfs_rq *cfs_rq_of(struct sched_entity *se)
{struct task_struct *p = task_of(se);struct rq *rq = task_rq(p);return &rq->cfs;
}

(3)如果静态分支(static_branch)sched_numa_balancing为真,表示启用了NUMA(非统一内存访问)平衡功能,则调用task_tick_numa()函数,处理与NUMA平衡相关的任务时钟滴答。

2.3 entity_tick

static void
entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued)
{/** Update run-time statistics of the 'current'.*/(1)update_curr(cfs_rq);/** Ensure that runnable average is periodically updated.*/(2)update_load_avg(curr, UPDATE_TG);......(3)if (cfs_rq->nr_running > 1)check_preempt_tick(cfs_rq, curr);
}

(1)update_curr用来更新当前任务调度实体的 vruntime 值和更新cfs_rq就绪队列的min_vruntime成员。

(2)update_load_avg更新该进程调度实体的负载和CFS就绪队列的赋值。

(3)如果CFS运行队列中的可运行任务数大于1,则调用check_preempt_tick()函数,检查是否需要进行抢占,即当前进程是否需要调度。

2.4 check_preempt_tick

/** Preempt the current task with a newly woken task if needed:*/
static void
check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr)
{unsigned long ideal_runtime, delta_exec;struct sched_entity *se;s64 delta;ideal_runtime = sched_slice(cfs_rq, curr);delta_exec = curr->sum_exec_runtime - curr->prev_sum_exec_runtime;if (delta_exec > ideal_runtime) {resched_curr(rq_of(cfs_rq));/** The current task ran long enough, ensure it doesn't get* re-elected due to buddy favours.*/clear_buddies(cfs_rq, curr);return;}/** Ensure that a task that missed wakeup preemption by a* narrow margin doesn't have to wait for a full slice.* This also mitigates buddy induced latencies under load.*/if (delta_exec < sysctl_sched_min_granularity)return;se = __pick_first_entity(cfs_rq);delta = curr->vruntime - se->vruntime;if (delta < 0)return;if (delta > ideal_runtime)resched_curr(rq_of(cfs_rq));
}

这段代码是调度器中的检查任务抢占的函数。以下是对代码的详细说明:
(1)首先,定义了一些变量来保存理想运行时间(ideal_runtime)和已执行时间的增量(delta_exec)。
(2)使用sched_slice()函数计算出当前调度实体的理想运行时间。
(3)计算当前调度实体的已执行时间的增量,即sum_exec_runtime减去prev_sum_exec_runtime。
(4)如果已执行时间的增量大于理想运行时间,表示当前任务运行时间超过了预期,将当前任务重新调度,并清除与当前任务相关的伙伴(buddy)任务的优先级。
(5)如果已执行时间的增量小于sysctl_sched_min_granularity(最小调度粒度),则直接返回,避免任务因为执行时间过短而被抢占。
(6)选取CFS运行队列中的第一个调度实体,并计算当前调度实体的虚拟运行时间与选取的调度实体的虚拟运行时间之间的差值(delta)。
(7)如果delta小于0,表示当前调度实体的虚拟运行时间较小,不进行抢占。
(8)如果delta大于理想运行时间,表示当前调度实体的虚拟运行时间较大,将当前任务重新调度。

这段代码用于检查是否需要抢占当前任务。它比较当前任务的已执行时间与理想运行时间的差异,并根据一定的条件决定是否重新调度当前任务。如果当前任务的运行时间超过了预期,或者与其他任务的虚拟运行时间相比较大,将触发任务的重新调度。

因此抢占决策很容易做出决定, 如果检查发现当前进程运行需要被抢占, 那么通过resched_task发出重调度请求.这会在task_struct中设置TIF_NEED_RESCHED标志, 核心调度器会在下一个适当的时机发起重调度.

其实需要抢占的条件有下面两种可能性:
(1)curr进程的实际运行时间delta_exec比期望的时间间隔ideal_runtime长
此时说明curr进程已经运行了足够长的时间

(2)curr进程与红黑树中最左进程left虚拟运行时间的差值大于curr的期望运行时间ideal_runtime
此时说明红黑树中最左结点left与curr节点更渴望处理器, 已经接近于饥饿状态, 这个我们可以这样理解, 相对于curr进程来说, left进程如果参与调度, 其期望运行时间应该域curr进程的期望时间ideal_runtime相差不大, 而此时如果curr->vruntime - se->vruntime > curr.ideal_runtime, 我们可以初略的理解为curr进程已经优先于left进程多运行了一个周期, 而left又是红黑树总最饥渴的那个进程, 因此curr进程已经远远领先于队列中的其他进程, 此时应该补偿其他进程。

如果检查需要发生抢占, 则内核通过resched_curr(rq_of(cfs_rq))设置重调度标识, 从而触发延迟调度

2.5 resched_curr

/** resched_curr - mark rq's current task 'to be rescheduled now'.** On UP this means the setting of the need_resched flag, on SMP it* might also involve a cross-CPU call to trigger the scheduler on* the target CPU.*/
void resched_curr(struct rq *rq)
{struct task_struct *curr = rq->curr;int cpu;if (test_tsk_need_resched(curr))return;cpu = cpu_of(rq);if (cpu == smp_processor_id()) {set_tsk_need_resched(curr);set_preempt_need_resched();return;}
}

这段代码是调度器中的重新调度当前任务的函数。以下是对代码的详细说明:
(1)首先,获取当前运行队列的当前任务指针curr。

(2)如果当前任务的need_resched标志已经被设置,则直接返回,无需进行重新设置。

(3)如果当前处理器ID等于当前运行队列的处理器ID(即在本处理器上执行),则设置当前任务的need_resched标志,并设置调度器的preempt_need_resched标志,表示当前任务需要重新调度。

周期性调度器并不显式进行调度, 而是采用了延迟调度的策略, 如果发现需要抢占, 周期性调度器就设置进程的重调度标识PREEMPT_NEED_RESCHED, 然后由主调度器完成调度工作.

TIF_NEED_RESCHED标识, 表明进程需要被调度, TIF前缀表明这是一个存储在进程thread_info中flag字段的一个标识信息

在内核的一些关键位置, 会检查当前进程是否设置了重调度标志TLF_NEDD_RESCHED, 如果该进程被其他进程设置了TIF_NEED_RESCHED标志, 则函数重新执行进行调度

前面我们在check_preempt_tick中如果发现curr进程已经运行了足够长的时间, 其他进程已经开始饥饿, 那么我们就需要通过resched_curr来设置重调度标识TIF_NEED_RESCHED

参考资料

https://kernel.blog.csdn.net/article/details/52068050
https://xiaolizai.blog.csdn.net/article/details/128646726
https://www.cnblogs.com/LoyenWang/p/12249106.html
https://www.cnblogs.com/LoyenWang/p/12495319.html

https://scslab-intern.gitbooks.io/linux-kernel-hacking/content/chapter04.html

相关文章:

Linux CFS调度器之周期性调度器scheduler_tick函数

文章目录 前言一、简介二、源码分析2.1 scheduler_tick2.2 task_tick2.3 entity_tick2.4 check_preempt_tick2.5 resched_curr 参考资料 前言 Linux内核调度器主要是主调度器和周期性调度器&#xff0c;主调度器请参考&#xff1a;Linux 进程调度之schdule主调度器 一、简介 …...

git生成密钥(免密)

生成SSH密钥对的方法如下&#xff1a; 打开Git Bash。 输入以下命令生成新的SSH密钥对&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 这里的 -C 参数后面跟的是你的邮箱地址&#xff0c;通常用于标识这个密钥。 当系统提示你“Enter a fil…...

山东大学软件学院2021级编译原理回忆版

一、判断题 1、正则文法可以表示一般的高级程序语言&#xff0c;构成其语法成分和生成句子&#xff08;&#xff09; 2、NFA的状态和符号有且只有一条边&#xff0c;因此看起来更直观&#xff08;&#xff09; 3、DFA无法表示这样的语言{anbn,n>1}&#xff08;&#xff09; …...

为什么都说视频号小店值得做,具体该怎么做?新手必学

大家好&#xff0c;我是电商花花。 所有人都在告诉你2024年应该做视频号小店&#xff0c;但没有人告诉你到底应该怎么做。 今天给大家说一下为什么2024年都推荐大家去做视频号小店&#xff0c;以及分享一些视频号小店的实操干货&#xff0c;可以帮助大家更快更稳的做店。 首先…...

网络安全岗秋招面试题及面试经验分享

Hello&#xff0c;各位小伙伴&#xff0c;我作为一名网络安全工程师曾经在秋招中斩获&#x1f51f;个offer&#x1f33c;&#xff0c;并在国内知名互联网公司任职过的职场老油条&#xff0c;希望可以将我的面试的网络安全大厂面试题和好运分享给大家~ 转眼2024年秋招又快到了金…...

如何实现一个AI聊天功能

最近公司的网站上需要对接一个AI聊天功能&#xff0c;领导把这个任务分给了我&#xff0c;从最初的调研&#xff0c;学习&#xff0c;中间也踩过一些坑&#xff0c;碰到过问题&#xff0c;但最后对接成功&#xff0c;还是挺有成就感的&#xff0c;今天把这个历程和项目整理一下…...

实战16:基于apriori关联挖掘FP-growth算法挖掘关联规则的手机销售分析-代码+数据

直接看视频演示: 基于apriori关联挖掘关联规则的手机销售分析与优化策略 直接看结果: 这是数据展示: 挖掘结果展示: 数据分析展示:...

Linux基础指令及其作用之系统信息和管理

系统信息和管理 ps ps 命令用于显示当前系统的进程信息。它是 Unix 和类 Unix 操作系统中的一个重要工具&#xff0c;可以用于监控和管理系统进程。以下是 ps 命令的详细用法和常见选项&#xff1a; ps [选项]常用选项![在这里插入图片描述](https://img-blog.csdnimg.cn/di…...

FinRobot:一个由大型语言模型(LLM)支持的新型开源AI Agent平台,支持多个金融专业AI Agent

财务分析一直是解读市场趋势、预测经济结果和提供投资策略的关键。这一领域传统上依赖数据&#xff0c;但随着时间的推移&#xff0c;越来越多地使用人工智能&#xff08;AI&#xff09;和算法方法来处理日益增长的复杂数据。AI在金融领域的作用显著增强&#xff0c;它自动化了…...

【SQL学习进阶】从入门到高级应用(七)

文章目录 ✨数据处理函数✨if函数✨cast函数✨加密函数 ✨分组函数✨max✨min✨avg✨sum✨count✨分组函数组合使用✨分组函数注意事项 ✨分组查询✨group by✨having✨组内排序 ✨总结单表的DQL语句 &#x1f308;你好呀&#xff01;我是 山顶风景独好 &#x1f495;欢迎来到我…...

20231911 2023-2024-2 《网络攻防实践》实践十一报告

实践内容 &#xff08;1&#xff09;web浏览器渗透攻击 任务&#xff1a;使用攻击机和Windows靶机进行浏览器渗透攻击实验&#xff0c;体验网页木马构造及实施浏览器攻击的实际过程。 实验步骤&#xff1a; ①选择使用Metasploit中的MS06-014渗透攻击模块②选择PAYLOAD为任意…...

5G专网驻网失败分析(suci无效)

suci 5G终端第一次驻网时&#xff0c;注册消息Registartion request中携带的5GS mobile identity要携带suci类型的mobile identity。 注册消息协议规范见5G NAS 协议3gpp TS24.501 8.2.6 Registration request。 suci协议规范参见3gpp TS24.501 9.11.3.4 5GS mobile identity …...

【PHP项目实战训练】——laravel框架的实战项目中可以做模板的增删查改功能(1)

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…...

go语言使用model Gorm MySQL查询数据 定时十分钟查询一次 查询十分钟前新建的数据

在Go语言中&#xff0c;使用GORM库与MySQL数据库交互并定时查询数据是常见的需求。以下是一个基本的示例&#xff0c;展示了如何设置定时任务&#xff0c;并使用GORM查询十分钟前新建的数据&#xff1a; 首先&#xff0c;你需要安装GORM和MySQL驱动&#xff1a; bash go get -…...

透视AI技术:探索折射技术在去衣应用中的奥秘

引言&#xff1a; 随着人工智能技术的飞速发展&#xff0c;其在图像处理和计算机视觉领域的应用日益广泛。其中&#xff0c;AI去衣技术作为一种颇具争议的应用&#xff0c;引发了广泛的讨论和关注。本文将深入探讨折射技术在AI去衣中的应用及其背后的原理。 一、AI去衣技术简介…...

计算机网络工程师需要掌握的知识点

网络基础 网络协议OSI参考模型TCP/IP 体系结构广域网与接入网技术&#xff1a;HDLC、PPP。xDSL、HFCIEEE802标准、以太网技术。网桥、交换机、无线局域网&#xff08;WLAN&#xff09;、VLAN、TRUNK、GVRP、STP、综合布线系统IP地址、子网划分、CIDR、ARP、ICMP、IPV6、TCP、UD…...

Java-Collection家族(List接口)

集合-Collection家族-List接口 List接口 1 特点 ​ 有序且可重复&#xff08;因为List接口中添加了许多针对下标操作的方法&#xff09; 2 四种实现类的数据类型与特点 ​ a. ArrayList ​ 数据结构&#xff1a;一维数组 ​ 特点&#xff1a;存储数据 ​ b. LinkedList ​ 数…...

成绩发布小程序哪个好用?

大家好&#xff0c;今天我要来跟大家分享一个超级实用的小秘密——易查分小程序&#xff01;作为老师&#xff0c;你是不是还在为发放成绩而头疼&#xff1f;是不是还在为通知家长而烦恼&#xff1f;别急&#xff0c;易查分小程序来帮你啦&#xff01; 易查分简直是老师们的贴心…...

如何让大模型在智能时代背景下更加先进:一种基于时代特征的探讨

随着人工智能技术的飞速发展&#xff0c;大型模型如深度学习网络和强化学习算法已经在各个领域取得了显著的成果。然而&#xff0c;随着应用场景的不断扩展和复杂化&#xff0c;如何让这些大模型在智能时代背景下更加先进&#xff0c;已经成为了一个亟待解决的问题。本文将从以…...

Mac 分享 WIFI 后,iPhone 连接 WIFI,但无法上网

0x00 分享WIFI 如何分享&#xff0c;可查看这篇&#xff1a; MacOS系统如何创建热点并共享Wi-Fi连接 0x01 iPhone 无法上网 打开设置&#xff0c;点击所连 WIFI 进入 配置 DNS 选择 手动 添加 服务器&#xff1a; 公用的有&#xff1a; 114.114.114.114 、180.76.76.76、1…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台

淘宝扭蛋机小程序系统的开发&#xff0c;旨在打造一个互动性强的购物平台&#xff0c;让用户在购物的同时&#xff0c;能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机&#xff0c;实现旋转、抽拉等动作&#xff0c;增…...

五子棋测试用例

一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏&#xff0c;有着深厚的文化底蕴。通过将五子棋制作成网页游戏&#xff0c;可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家&#xff0c;都可以通过网页五子棋感受到东方棋类…...

Python实现简单音频数据压缩与解压算法

Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中&#xff0c;压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言&#xff0c;提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...

Tauri2学习笔记

教程地址&#xff1a;https://www.bilibili.com/video/BV1Ca411N7mF?spm_id_from333.788.player.switch&vd_source707ec8983cc32e6e065d5496a7f79ee6 官方指引&#xff1a;https://tauri.app/zh-cn/start/ 目前Tauri2的教程视频不多&#xff0c;我按照Tauri1的教程来学习&…...