引言:CPU是厨房,调度器是主厨

要真正理解Linux如何处理成千上万个并发任务,不妨把它想象成一个繁忙的专业厨房。这个比喻不仅能让抽象概念变得具体,更能揭示其背后深刻的设计哲学。

  • 厨房 (The Kitchen): 代表整个计算机系统。

  • 厨师 (CPU Cores): 他们是真正干活的人,负责执行任务。只有一个厨师的厨房就是单核CPU;有多个厨师就是多核CPU。

  • 菜谱 (A Program/Process): 这是一本书,里面记载了制作一道大菜所需的所有配方和步骤。它定义了需要哪些资源,比如共享的储藏室和香料架(即进程的内存地址空间)。

  • 菜谱里的步骤 (Threads): 菜谱中具体、连续的操作指令,比如“切洋葱”、“煎牛排”、“熬酱汁”。每个厨师在同一时间只能处理一个步骤。多个厨师可以同时处理不同菜谱里的不同步骤(并行),或者在同一个厨师身上快速切换不同的步骤(并发)。

  • 主厨 (The Linux Scheduler): 这是厨房的大脑,也就是Linux内核中的调度器。主厨的工作就是审视所有已经准备好、可以开工的“菜谱步骤”(即可运行的线程),然后决定:哪个厨师去做哪个步骤?做多久?按什么顺序做?最终目标是让整个厨房高效运转,所有菜品准时上桌,让顾客(用户)满意。

image.png

基本工作单元:“任务”

在Linux的世界里,进程和线程之间的界限并不像其他操作系统那样泾渭分明。内核调度器实际上并不区分“进程”和“线程”,它只看到一种可调度的实体,称为“任务”(task),由一个名为task_struct的数据结构表示。这让我们的厨房比喻变得更简单:主厨只管理一张“待办步骤清单”(任务列表),而不必过多关心这些步骤是来自同一本菜谱(进程)还是不同的菜谱。这是现代Linux中“原生POSIX线程库”(NPTL)的核心思想,它采用了用户线程与内核任务一一对应的“1:1”模型。

这个厨房比喻不仅仅是一个简单的噱头,它是一个强大的框架,用以理解调度中那些根本性的权衡。主厨的每一个决定都涉及到在相互竞争的目标之间取得平衡:公平性(让每道菜都得到关注)、吞吐量(每小时能出多少道菜)、延迟(从服务员下单到厨师开始动手的时间)以及满足硬性截止日期(比如一个必须在特定时间准备好的婚礼蛋糕)。这些,也正是Linux调度器每天需要面对的挑战。这个框架让我们能够将不同的调度策略,看作是主厨采用的不同管理哲学,每一种都适用于不同类型的餐厅——比如一家繁忙的快餐店和一家米其林星级餐厅。

第一部分:家常菜馆 —— 完全公平调度器 (CFS)

这一部分将介绍Linux默认的调度类别,它为绝大多数工作负载而设计,就像一家需要同时服务好众多不同顾客的繁忙大众餐厅。

经营哲学:“理想的多任务厨房”

完全公平调度器(Completely Fair Scheduler, CFS)的核心设计目标是模拟一个理论上存在的、“理想的多任务CPU”。

比喻: 想象一个神奇的厨房,如果同时有四道菜(任务)要做,厨房里的厨师们可以精确地将25%的精力同时投入到每一道菜上。这样,所有的菜品都能以完美并行的状态同时被制作。

现实: 然而,现实中的厨师一次只能做一件事。因此,CFS引入了一个概念来近似地实现这个理想状态:虚拟运行时 (vruntime)

vruntime:厨房的“公平计量表”

vruntime是与每个任务关联的一个数值,它记录了该任务已经获得了多少CPU时间,并根据当前运行的任务总数进行了标准化处理。

比喻: 你可以把vruntime想象成每张订单上都附带的一个“公平计量表”。厨师每处理一道菜,这道菜的计量表读数就会上升。主厨的核心规则非常简单:永远选择那张“公平计量表”读数最低的订单来做

这确保了随着时间的推移,每道菜都能获得公平的关注,所有订单的计量表读数都大致保持平衡。为了高效管理这些按vruntime排序的订单,调度器使用了一种高效的数据结构——红黑树(Red-Black Tree),这样它就能瞬间找到那个值最低的订单(即树的“最左侧节点”)。

image.png

SCHED_OTHER:为所有顾客提供的标准服务

这是几乎所有用户进程的默认策略。它使用的就是我们上面描述的CFS逻辑。

nice值比喻: 顾客可以通过表现得“友善”(nice)或“不友善”来影响这个过程。nice值的范围从-20(最不友善,最高优先级)到+19(最友善,最低优先级),它像一个权重一样影响着vruntime的增长速度。一个“不友善”的任务,其公平计量表走得更慢,意味着它能分得更大比例的CPU时间。而一个更“友善”的任务,其计量表走得更快,因此分得的时间份额就更小。这并没有改变“选择vruntime最低者”的基本规则,只是改变了vruntime的累积方式。

image.png

SCHED_BATCH:慢炖锅里的特色菜

这个策略专为那些CPU密集型、非交互式的任务设计,比如大数据处理或视频编码。

比喻: 这就像厨房里一大锅正在慢炖的浓汤,或者熏制箱里的一块牛胸肉。它需要很长的烹饪时间,但并不紧急,也不需要厨师持续、即时的关注。主厨会告诉厨师们:“这是一个‘批量订单’,别让它影响到那些现点现做的菜。你们有空的时候就去照看一下。”

调度器假定SCHED_BATCH任务对延迟不敏感。当它们被唤醒时,调度器会对其施加轻微的惩罚,防止它们不公平地抢占交互式任务。这使得它们成为“友好的CPU邻居”,其目标是优化整体吞吐量,而非追求低延迟。

image.png

SCHED_IDLE:打烊后的清理工作

这个策略适用于优先级极低的后台任务。

比喻: 这是厨房里优先级最低的工作:深度清洁烤箱或擦亮银器。主厨的指令是,_只有_在没有任何菜单上的订单,也没有批量任务在进行时,厨师们才能去做这些事。这些工作迟早要完成,但在任何情况下都不能妨碍真正的烹饪工作。

image.png

CFS的天才与妥协

CFS的“公平”模型之所以对桌面和交互式系统如此出色,关键在于它处理I/O密集型任务的方式,这通常被称为“睡眠者公平”(sleeper fairness)。

思考一下这个过程:一个“交互式”应用程序,如网页浏览器或文字处理器,其特点是什么?它大部分时间都在等待(即“睡眠”),等待用户点击鼠标或敲击键盘(即I/O事件)。当一个任务处于睡眠状态时,它的vruntime(公平计量表)是静止不动的。与此同时,一个CPU密集型任务,比如编译代码,在持续不断地运行,导致其vruntime变得非常高。当用户最终点击了某个按钮,交互式任务被唤醒。调度器查看它的vruntime,发现这个值与编译任务的vruntime相比小得可怜。遵循其唯一的简单规则,CFS会立即选择这个交互式任务来运行,因为它拥有“最低的公平计量表读数”。

最终,这造就了现代桌面系统那种“随叫随到”的响应感。系统并非通过复杂的启发式算法来猜测哪个任务是交互式的;vruntime机制以一种自然而优雅的方式,自动地优先处理那些一直在等待输入的任务。这是一种比老式调度器远为健壮和简洁的设计。

同时,SCHED_BATCHSCHED_IDLE策略的存在也揭示了CFS的根本性妥协。通过优化交互延迟,CFS在本质上就_没有_为最大化批处理吞吐量进行优化。这两个策略是对这一权衡的明确承认,它们为开发者提供了一个“逃生通道”,让他们可以告诉调度器:“我的任务不符合标准模型,请区别对待”。

第二部分:米其林星级餐厅 —— 实时保障

这一部分将超越“公平”,探讨那些时间性不仅是目标,更是硬性要求的策略。这里是高风险领域,就像一家上菜晚10秒就算失败的餐厅。

当“公平”还不够:对可预测时间的需求

实时(Real-time)策略适用于那些时间关键型应用,在这些应用中,满足时间要求比公平更重要。例如工业控制系统、机器人技术和专业音频处理。

比喻: 比如一道精致的舒芙蕾。无论它在烤箱里被多么“公平”地对待,如果出炉后没有被_立即_送上桌,它就会塌陷,这道菜就毁了。它的时间性至关重要。同样,一个机器人手臂控制器如果接收下一条指令晚了,就可能导致动作生涩、不准确。

优先级系统: 实时任务在一个从1(最低)到99(最高)的固定优先级范围内运行。至关重要的一点是,_任何_实时任务,即便是优先级为1的任务,其调度优先级也永远高于_任何_普通(CFS)任务。主厨会放下手头的一切,优先处理实时订单。

SCHED_FIFO:VIP特快订单

FIFO代表“先进先出”(First-In, First-Out)。这是一种简单而激进的实时策略。

比喻: 这是一份VIP的特快订单。主厨会指派一名厨师专门负责它,而这名厨师会_心无旁骛_地制作这道菜。他不会停下来,直到这道菜完成(线程阻塞或退出),或者直到服务员冲进厨房,递上一份来自 更重要 VIP的订单(一个更高优先级的SCHED_FIFO任务抢占了它)。

关键行为: 如果两个SCHED_FIFO任务具有相同的优先级,那么先来的那个会一直运行直到完成,然后第二个才能开始。它们之间没有时间共享。

image.png

SCHED_RR:VIP品鉴套餐

RR代表“轮询”(Round-Robin)。它是SCHED_FIFO的一个增强版,专为多个同等重要的实时任务设计。

比喻: 想象一桌同等重要的VIP,他们都点了一份品鉴套餐。主厨不能只顾着给一位VIP上完全部菜品,而让其他人干等。相反,厨师们会接到指令,为每位VIP的第一道菜工作一小段固定的时间(这就是时间片quantum)。他们会轮流为餐桌上的每位VIP服务,确保每位客人都得到同等的关注。

关键行为: 当一个SCHED_RR任务用尽了它的时间片,它会被移到其优先级队列的末尾,然后该优先级下的下一个任务开始运行。这确保了CPU时间在_同一优先级的实时任务之间_得到公平分配。

image.png

实时策略的力量与风险

FIFORR之间的选择,完全取决于在单一优先级下工作负载的性质。FIFO适用于单个、庞大的关键任务,而RR则适用于一组需要协作、同等关键的任务。在实际中,FIFO通常更受青睐,这暗示了许多实时问题往往围绕着每个优先级下的单个主导任务来构建。

FIFO更简单、更具确定性:任务A一直运行直到完成。而RR引入了时间片,这会增加少量开销,如果时间片长度与任务的工作模式不匹配,还可能引入抖动。因此,如果你只有一个关键任务(例如,一个线程管理着高速数据采集卡),FIFO是确保其运行的最直接、最简单的方式。如果你有多个对等任务(例如,几个线程分别控制机器人手臂上的马达),那么RR就是必要的,以确保所有马达都能得到并发更新。对FIFO的偏爱表明,前一种场景更为常见,或者开发者在可能的情况下更喜欢其原始的简洁性。

SCHED_FIFOSCHED_RR最大的危险也正源于它们的强大。一个编程错误,比如在高优先级的FIFO线程中出现无限循环,可以完全锁死整个系统,甚至阻止必要的系统任务(它们通常作为较低优先级的CFS任务运行)执行。系统将变得毫无响应。这就是为什么使用这些策略需要root权限。

一个更深层次的观察是,Linux内核自身对SCHED_FIFO的使用方式也很有启发性。一份讨论中的补丁集显示出一种趋势,即_减少_内核内部使用的FIFO优先级数量,将大多数内核线程标准化到优先级1或50。其理由是,开发者们过去常常在没有清晰的全局视角的情况下,随意选择优先级数值。这是一个深刻的教训:即使是内核专家,在管理复杂的优先级方案时也会遇到困难。这也证实了一个观点:对于应用程序开发者来说,使用一小组定义明确、易于理解的优先级,远比试图创建一个复杂的、细粒度的优先级层次结构更安全、更有效。

第三部分:前卫厨房 —— SCHED_DEADLINE

这是最高级的调度类别,代表着从“基于优先级”到“基于合约”的范式转变。这是一家烹饪学院里未来派的、高度自动化的厨房。

超越优先级:与时间赛跑

SCHED_DEADLINE是优先级最高的调度类别,它会抢占所有其他任务,包括FIFORR。它基于“最早截止时间优先”(Earliest Deadline First, EDF)算法。

范式转变: 你不再只是告诉主厨“这道菜很重要”(设置优先级),而是给他一份精确的合约:“这道菜需要X单位的工作量,必须在Y时间点前完成,并且每隔Z分钟就会有一份新订单进来。”

主厨的合约:运行时、周期和截止时间

该策略由每个任务的三个参数定义:

  • runtime(运行时): 任务完成一个作业所需的最大CPU时间。(比喻:一块牛排总共需要5分钟的烧烤时间)。

  • period(周期): 新作业到达的时间间隔。(比喻:每10分钟会来一份新的牛排订单)。

  • deadline(截止时间): 从周期开始计算,runtime必须在此时间内完成。(比喻:牛排必须在下单后8分钟内烤好)。

调度器利用这些参数进行“准入测试”。它可以从数学上判断自己是否有足够的CPU容量来履行所有DEADLINE任务的合约。如果不能,它会拒绝新的任务加入。

时间隔离:防止厨房灾难

这是SCHED_DEADLINE的杀手级特性。如果一个任务试图使用超过其runtime预算的CPU时间,调度器会将其暂停(“节流”),直到它的下一个period开始。

比喻: 如果负责煎牛排的厨师试图花7分钟而不是预算的5分钟来做,主厨会立刻上前阻止他,并说:“你做这块牛排的时间已经用完了。继续做别的。等下一份牛排订单来的时候,你才能继续处理。”

这种机制可以防止单个行为不当或耗时超预期的任务引发连锁延迟,导致其他关键任务错过它们的截止时间。它在实时任务之间提供了一道“防火墙”,这是FIFORR无法做到的。

image.png

可预测系统的未来

SCHED_DEADLINE是解决复杂、混合实时系统问题的关键,在这些系统中,多个独立的、时间关键型的应用程序必须在互不信任的情况下共存。

想象一下现代汽车的中央计算单元。它可能同时运行着一个实时媒体播放器(多媒体)、发动机控制单元(工业控制)和用户界面,其中一些任务甚至可能是虚拟化的。如果使用SCHED_FIFO,媒体播放器的一个小bug就可能饿死发动机控制单元,这将是灾难性的。

而使用SCHED_DEADLINE,每个组件都可以被赋予一份合约。媒体播放器得到一份合约:runtime=4毫秒,period=16毫秒,deadline=16毫秒(用于解码一帧60fps的视频)。发动机控制单元则有它自己的合约。如果媒体播放器的代码效率低下,试图占用6毫秒,调度器会在4毫秒时将其节流。这可能会导致视频掉一帧(一个微不足道的小问题),但它_保证_了发动机控制单元完全不受影响,并能获得其预算内的CPU时间。

因此,SCHED_DEADLINE实现了实时组件的安全组合,提供了基于优先级的方案无法实现的隔离和可预测性。它用复杂的保证换取了简单的优先级。

然而,SCHED_DEADLINE的复杂性不容小觑。在真实世界的问题中,例如有用户报告其DEADLINE线程在PREEMPT_RT系统上被内核线程意外抢占,这表明与内核其他部分(如定时器中断和内核工作线程)的交互极其微妙。这说明DEADLINE并非一个“一劳永逸”的解决方案,而是一个强大的专家工具,使用者必须仔细考虑整个系统的行为,包括cgroups和CPU亲和性,才能实现真正的确定性。

结论:为成功选择正确的“菜谱”

Linux调度器提供了一个丰富的工具箱。理解每种策略类别背后的核心哲学,是为你的应用需求选择正确工具——即正确的“菜谱”——的关键。

  • CFS (NORMAL, BATCH, IDLE): 这是“公平”的默认哲学,非常适合通用计算,能确保桌面系统的响应速度。它就像一家繁忙快餐店里多才多艺的主厨。

  • 实时 (FIFO, RR): 这是“优先级”的哲学,适用于某些任务比其他任务更重要,必须可预测地运行的场景。它就像一家专注于为VIP提供完美服务的米其林星级餐厅的主厨。

  • SCHED_DEADLINE: 这是“合约”的哲学,用于保证多个复杂、独立的实时任务的时间性。它就像未来派烹饪实验室里那个高科技、自动化的主厨。

虽然CFS是适用于大多数情况的卓越默认选项,但专门的实时策略提供了构建高度可预测和健壮系统的能力,尤其是在性能和时间性至关重要的场合。

表1:Linux调度策略速览

策略名称厨房比喻主要目标优先级机制关键特性典型用例
SCHED_OTHER标准服务公平性与交互性动态 (vruntime + nice)睡眠者公平,响应迅速桌面应用、通用服务器
SCHED_BATCH慢炖锅特色菜吞吐量动态 (vruntime + nice)非交互式,对唤醒不敏感视频编码、科学计算、批处理
SCHED_IDLE打烊后清理仅在空闲时运行极低优先级永不干扰其他任务系统日志清理、索引构建
SCHED_FIFOVIP特快订单绝对优先级静态 (1-99)运行至完成或被更高优先级抢占单一关键任务、低延迟音频、数据采集
SCHED_RRVIP品鉴套餐同级任务公平静态 (1-99)同级任务间使用时间片轮询机器人控制、多个协作实时任务
SCHED_DEADLINE主厨的合约合约式截止时间基于截止时间 (EDF)时间隔离,可预测的带宽保证实时多媒体、虚拟化、汽车电子

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

笔记本电脑安装win10哪个版本好_笔记本装win10专业版图文教程

笔记本电脑安装win10哪个版本好?笔记本还是建议安装win10专业版。Win分为多个版本,其中家庭版(Home)和专业版(Pro)是用户选择最多的两个版本。win10专业版在功能以及安全性方面有着明显的优势,所…

微服务循环依赖调用引发的血案

问题表现 最近的迭代转测后遇到了一个比较有意思的问题。在测试环境整体运行还算平稳,但是过一段时间之后,就开始有接口超时了,日志中出现非常多的 “java.net.SocketTimeoutException: Read timed out”。试了几次重启大法,每次…

LeetCode - 852. 山脉数组的峰顶索引

题目 852. 山脉数组的峰顶索引 - 力扣&#xff08;LeetCode&#xff09; 思路 使用二分查找来定位峰顶 对于中间元素&#xff0c;比较它与其右侧元素的大小&#xff1a; 如果 arr[mid] < arr[mid1]&#xff0c;说明我们在上坡阶段&#xff0c;峰顶在右侧 如果 arr[mid…

国产ARM/RISCV与OpenHarmony物联网项目(二)网关数据显示

本文需要Web服务器开发基础&#xff0c;可参考下述博文&#xff1a; 物联网网关Web服务器--lighttpd服务器部署与应用测试 物联网网关Web服务器--CGI开发接口 一、数据显示界面与功能设计 1、功能设计说明 程序代码结构如下&#xff0c;调用关系见彩色部分标示。 数据显示界面…

Robyn高性能Web框架系列01:Robyn快速入门

Robyn快速入门 安装 Robyn1、仅安装基础 HTTP 路由功能2、带扩展功能的安装 第一个Robyn程序1、创建Robyn应用2、Say Hello!3、启动Robyn应用 Python世界从来不缺少对于性能的追求&#xff0c;Robyn就是其中之一&#xff0c;它将 Python 的异步功能与 Rust 相结合&#xff0c;在…

微信小程序 -----无限新增删除,同时算出总合算金额。

<view class="refuelMoney-main" style="padding-bottom: 200rpx;"><!-- <view class="add_record">添加加油记录</view> --><view class="refuel-itemTextArea"><text style="width: 35%;&quo…

linux “Permission Denied“解决方案

Linux 编译错误排查 在软件开发过程中&#xff0c;编译错误和版本控制问题是开发者每天都会遇到的挑战。本文将结合实际案例&#xff0c;详细讲解 Linux 环境下常见编译错误的排查方法 权限拒绝错误&#xff08;Permission Denied&#xff09; 当执行脚本或程序时&#xff0…

【慧游鲁博】【15】后台管理系统功能完善:仪表盘、多模态交互日志、简单问答词条管理

文章目录 本次更新多模态交互日志效果涉及代码文件 仪表盘&#xff08;部分&#xff09;效果涉及代码文件 简单问答服务词条管理效果涉及代码文件 本次更新 代码真的太多太多了&#xff0c;不放代码了 多模态交互日志 数据概览与筛选功能 时间范围筛选&#xff1a;提供"…

【力扣 简单 C】21. 合并两个有序链表

目录 题目 解法一&#xff1a;迭代 解法二&#xff1a;递归 题目 解法一&#xff1a;迭代 struct ListNode* merge(struct ListNode* head1, struct ListNode* head2) {struct ListNode* virHead malloc(sizeof(*virHead));struct ListNode* curNode virHead;struct List…

【开源工具】Windows屏幕控制大师:息屏+亮度调节+快捷键一体化解决方案

🖥️ 从零打造Windows屏幕控制大师:息屏+亮度调节+快捷键一体化解决方案 🌈 个人主页:创客白泽 - CSDN博客 🔥 系列专栏:🐍《Python开源项目实战》 💡 热爱不止于代码,热情源自每一个灵感闪现的夜晚。愿以开源之火,点亮前行之路。 🐋 希望大家多多支持,我们一…

pyhton基础【11】函数一

目录 一.函数说明 二.函数介绍 函数简介 作用 函数分类 三.自定义函数 定义函数 调用函数 pass关键字 定义一次执行多次 一.函数说明 Python中的函数是一个重要的编程概念&#xff0c;它允许编程者封装代码块以实现特定的功能。函数的作用和应用场景非常广泛&#xf…

使用Kotlin开发后端服务的核心方法

一、开发步骤 选择框架 Kotlin后端开发常用框架包括Spring Boot、Ktor和Micronaut。Spring Boot生态成熟&#xff0c;适合企业级应用&#xff1b;Ktor轻量且协程友好&#xff0c;适合高性能异步服务&#xff1b;Micronaut以低内存占用和快速启动见长。 搭建项目结构 通过Grad…

java面试总结-20250616

题目1: 求一个int类型正整数二进制中最高位1的位置&#xff1f; 比如10&#xff0c;二进制位1010&#xff0c;最高位1所在位置位4。 解体思路&#xff1a; 使用高位扩散&#xff0c;将1010扩散位1111使用二分法&#xff0c;计算32位二进制中1111前面0的位数n&#xff1b;结果…

Black自动格式化工具

文章目录 一、Black自动格式化工具二、格式化行为的核心内容1. 统一缩进和空格规则2. 括号换行&#xff1a;一致的多行结构展开3. 字符串风格统一4. 函数/类定义中的空行规则5. import 排序&#xff08;建议搭配 isort&#xff09;6. 注释不动、换行优雅7. 可配置项极少&#x…

项目拓展-简易SQL监控,P6SPY拦截所有jdbc连接并打印执行SQL

介绍一下P6spy驱动 p6spy 是一款开源的数据库监控框架&#xff0c;主要用于 拦截和记录应用程序与数据库之间的所有交互&#xff08;如 SQL 语句、参数、执行时间等&#xff09; 它通过包装现有的 JDBC 驱动&#xff08;如 MySQL JDBC 驱动&#xff09;&#xff0c;在不修改业…

洛谷B3951 [GESP样题 五级] 小杨的队列

题目描述 小杨的班级里共有 N N N 名同学&#xff0c;学号从 0 0 0 至 N − 1 N-1 N−1。某节课上&#xff0c;老师要求同学们进行列队。具体来说&#xff0c;老师会依次点名 M M M 名同学&#xff0c;让他们加入队伍。每名新入队的同学需要先站到队伍末尾&#xff08;刚开…

Java编程之外观模式

前言 想象你要去一家很复杂的餐厅吃饭&#xff0c;但不想自己点菜、排队、找位置&#xff0c;也不想管厨房、洗碗、送餐这些后端流程。你只需要告诉餐厅服务员“我要一份牛排套餐”&#xff0c;然后坐等就好。这个服务员&#xff0c;就是外观模式&#xff08;Facade Pattern&a…

告别 Java 开发困境!飞算 JavaAI 开发助手开启智能编程新时代

在 Java 开发的世界里&#xff0c;需求不明确、加班写重复代码、被 BUG 搞得焦头烂额&#xff0c;是许多开发者难以摆脱的 “三座大山”。需求文档模糊不清&#xff0c;让开发者在项目起始阶段就陷入迷茫&#xff1b;大量重复性的代码编写工作&#xff0c;不仅消耗时间和精力&a…

Node.js 中两种模块导出方式区别

两种模块到处方式 exports.xxx ... module.exports ... 1. exports.xxx ... exports 是 module.exports 的一个引用&#xff08;快捷方式&#xff09;。 当你写 exports.foo function() {}&#xff0c;实际上就是给 module.exports 对象添加了一个 foo 属性。 这种方式…

电脑出问题了,无网络环境下一键快速重装系统

在电脑使用过程中&#xff0c;系统故障、卡顿、崩溃等问题屡见不鲜。面对这些情况&#xff0c;重装系统往往是解决问题的最有效手段之一。然而对于刚接触计算机操作的新用户来说&#xff0c;如何安全、稳定地完成系统重装&#xff0c;仍是一个颇具挑战的任务。 这一款专为新手…