From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pl1-f179.google.com (mail-pl1-f179.google.com [209.85.214.179]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 712ED299923 for ; Thu, 18 Dec 2025 23:17:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.179 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766099842; cv=none; b=pasM6OW3uKcsRQXfyVcwmdfQ+7NQXrJPJrV/gWTuaao4bMeMxqkM3m4x8Vfl8GvK6Au+vFnaU/TsAdvz8E5Bm4YiFAtuVtDylNUBRIVI4BMZDSf38epH6DaLFzMd/g/g+lOntgu6Bf0i5kGRDT5c1aDBMC8jT4NKEf7ZnhcWFog= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1766099842; c=relaxed/simple; bh=ApTGCg/BkWNc+2Q5P+sTM0pbwEXXBGaFodpJU1Zd98s=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=dbVpnLW4VlF+8wv+qZzo8zw040k6IFGbQ0VjzvHDY6uRPPK+suY5xtBnmBibQ3y8sNZaezKHDwYGP1GJRaHb79x2ylH+Ek+358QnPiJn2pqthUCy0Nr6G/rWQcMQcRGf0CwdhmYJ5Jtk8N9BC34yV2udEHLyUrxTO4oF0IV4RWQ= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=goKNNc2B; arc=none smtp.client-ip=209.85.214.179 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="goKNNc2B" Received: by mail-pl1-f179.google.com with SMTP id d9443c01a7336-2a0fe77d141so12768085ad.1 for ; Thu, 18 Dec 2025 15:17:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1766099838; x=1766704638; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=MrCEMemSIAOarY9/jOgqH6g+c+kM/mk1DzXfDOTJTK8=; b=goKNNc2Be5tEyuav7ECyPaeaxbLwSqbAeQcL40DtV95HbGHaPI4pImt8Krgd2z46Ph J7J0B5O52syHt12rPRbIaigz3Rurj6mnPLIu3MCJJj9geNhL/aUCT0KdTMu4FwdHWKIH F1bb2O1VLYCbWfcCDlm1eldHJ6/grJM1p5WQkXpz/s+n2oD3wPmohji3/GjXs1u2J3HW hU/gTryLpBsCTvzzWd1Ei/bHvLu0BqOFVK4TTPvzuE1nP1Nx9yLDRrNp85frFUIfM+wj GBNABl9WzMfcMLEQhxVp1Z3KvVn03hcOyLYJ9I93SJ4ZVmWtii3as0/fXjE3Kykj9Rb2 KwwA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1766099838; x=1766704638; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=MrCEMemSIAOarY9/jOgqH6g+c+kM/mk1DzXfDOTJTK8=; b=l6M5rm6kv0Ad8PeCO7ReXFajjFxGv6rQUr/GKvZ6qCNgK9O39tLMqdhHMzpL2wF1Oe 4D+8FtkFpyJZJ3xp8gmBZZT9rriMPvFo/m9UeIc9cfAU+6/17VP+n5FdDMX0M0U36vRM sKFIdgc6J+KCJE5L/VB7PKEDtSchJM1/VfvI9l6tLz842V1+tO6IDh5ZNO4i7zefScu8 yqTn5dUEn67w/qyZGI6pZxnOqwzShaNy1B4xwYhd0tl3bAZWKsNhWvvkIJ8I/jeo3qSa fwLZ23IXcSfmyaAvXyRSqv+2ZO9YQIW/PsrJ2rGWc74zTH9OJat7/42+q0QgQ73aUiyr lkew== X-Forwarded-Encrypted: i=1; AJvYcCXly0aGFr7LcHNTWSUjdvFYD9m6clyCjEY/D1wzk9l4zdrVZws5ozIYu77dHj35WU1TsArhg6PzGn7eykY=@vger.kernel.org X-Gm-Message-State: AOJu0Yw1xQ1vnvOqB8kRVyjjbmrtpIQixJJdCZ+6lTNmrNsKGGrLOFFE oyva7DA1Yg1PG/sXbWm+X/CQOmT53QL+V5eTrFlLZbdFa5feT9+MvZ9C X-Gm-Gg: AY/fxX6IzrUxNms2BpE5oG6yybCTnfcWIRXTdiJa/6nshEcIxOkpw22uDM2xDiYqT8W lWj8ja4JrR1Xu2RIv1ssZuBEyViVIU6vFr809j3L5+wQHt9topMELyz2QZ763FxkSBOGs10yk+w 9N8BZiy/IhIjh8B/zIAVzA+POfIdkjvydCKJZRDD84soBDIz1JaZz6B8mFEnGlbbBXAXQo+e/9a a805Wy+2pOHoYqN9DP7r7SocPGHzlbsAFCvgePQA+DVebfTQsdMZzpyArnD97gfN+zhigmsa0Gh ipjRL/7clDZPtED7w4+3+C76c5f+Ucrj2m3cQ8U7LSY/vrmEH6hTCYJxNZQ+w7PZypJIJHph1Sd WJzrwBYm+EvtAn17+I9xKa6yRiTfZE5i0grk05Gcr5mgkznhuV0+OwS2RmI64P0AWesLtSuP/xh hCnfSOL3MrL/pBWLJsFbjZcd70DW267CYdYry9h9/Jaw== X-Google-Smtp-Source: AGHT+IFIm2WKsJMdHoIhSVSOCl2JmDV9ISKNGcY6ONvUpVYCzWK8uOhW6MkZAWXbeBllCqe7ic1xdg== X-Received: by 2002:a17:903:4b0c:b0:2a2:dc1f:78d8 with SMTP id d9443c01a7336-2a2f293d04cmr6918435ad.42.1766099837966; Thu, 18 Dec 2025 15:17:17 -0800 (PST) Received: from Lingbou.localdomain ([113.225.44.231]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2a2f3d4cb48sm3311955ad.64.2025.12.18.15.17.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 18 Dec 2025 15:17:17 -0800 (PST) From: Xingqiu Xu To: alexs@kernel.org, si.yanteng@linux.dev Cc: dzm91@hust.edu.cn, corbet@lwn.net, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Xingqiu Xu Subject: [PATCH 6/7] docs/zh_CN: Translate timers/no_hz.rst Date: Fri, 19 Dec 2025 07:16:48 +0800 Message-Id: X-Mailer: git-send-email 2.34.1 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Translate .../timers/no_hz.rst into Chinese. Update timers/index.rst to include the translated file. Update translation through commit 5097cbcb38e6 ("sched/isolation: Prevent boot crash when the boot CPU is nohz_full") Signed-off-by: Xingqiu Xu --- .../translations/zh_CN/timers/index.rst | 2 +- .../translations/zh_CN/timers/no_hz.rst | 338 ++++++++++++++++++ 2 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 Documentation/translations/zh_CN/timers/no_hz.rst diff --git a/Documentation/translations/zh_CN/timers/index.rst b/Documentation/translations/zh_CN/timers/index.rst index 101341d1033c..339e87e28baa 100644 --- a/Documentation/translations/zh_CN/timers/index.rst +++ b/Documentation/translations/zh_CN/timers/index.rst @@ -18,11 +18,11 @@ highres hpet hrtimers + no_hz timekeeping TODOList: -* no_hz * delay_sleep_functions .. only:: subproject and html diff --git a/Documentation/translations/zh_CN/timers/no_hz.rst b/Documentation/translations/zh_CN/timers/no_hz.rst new file mode 100644 index 000000000000..3933c9673e5b --- /dev/null +++ b/Documentation/translations/zh_CN/timers/no_hz.rst @@ -0,0 +1,338 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/timers/no_hz.rst + +:翻译: + + 徐兴球 Xingqiu Xu + +============================ +NO_HZ:减少调度时钟刻度 +============================ + + +本文档描述了可以减少调度时钟中断数量的 +Kconfig 选项和引导参数,从而提高能效并减 +少操作系统抖动。减少操作系统抖动对于某些类 +型的计算密集型高性能计算(HPC)应用程序以 +及实时应用程序非常重要。 + +管理调度时钟中断(也称为"调度时钟刻度"或简 +称"刻度")有三种主要方式: + +1. 从不省略调度时钟刻度(CONFIG_HZ_PERIODIC=y + 或对于旧内核为 CONFIG_NO_HZ=n)。您通常 + 不会想要选择此选项。 + +2. 在空闲 CPU 上省略调度时钟刻度 + (CONFIG_NO_HZ_IDLE=y 或对于旧内核为 + CONFIG_NO_HZ=y)。这是最常见的方法,应 + 该是默认值。 + +3. 在空闲或只有一个可运行任务的 CPU 上省 + 略调度时钟刻度(CONFIG_NO_HZ_FULL=y)。 + 除非您正在运行实时应用程序或某些类型的 + HPC 工作负载,否则您通常不会想要此选项。 + +这三种情况将在以下三节中描述,然后是关于 +RCU 特定考虑的第三节,讨论测试的第四节,以 +及列出已知问题的第五节和最后一节。 + + +从不省略调度时钟刻度 +==================== + +来自 1990 年代和 21 世纪初期的非常旧版本 +的 Linux 无法省略调度时钟刻度。事实证明, +在某些情况下,这种老派方法仍然是正确的方法, +例如,在具有大量使用短暂 CPU 突发的任务的 +繁重工作负载中,其中有非常频繁的空闲期,但 +这些空闲期也非常短(数十或数百微秒)。对于 +这些类型的工作负载,调度时钟中断通常无论如 +何都会传递,因为每个 CPU 经常会有多个可运 +行的任务。在这些情况下,尝试关闭调度时钟中 +断除了增加进出空闲以及在用户和内核执行之间 +转换的开销外没有任何效果。 + +可以使用 CONFIG_HZ_PERIODIC=y(或对于旧内 +核为 CONFIG_NO_HZ=n)选择此操作模式。 + +但是,如果您正在运行具有较长空闲期的轻量级 +工作负载,则无法省略调度时钟中断将导致过度 +的功耗。这在电池供电的设备上尤其糟糕,它会 +导致电池寿命极短。如果您正在运行轻量级工作 +负载,因此应该阅读以下部分。 + +此外,如果您正在运行实时工作负载或具有短迭 +代的 HPC 工作负载,调度时钟中断可能会降低 +您的应用程序性能。如果这描述了您的工作负载, +您应该阅读以下两节。 + + +为空闲 CPU 省略调度时钟刻度 +=========================== + +如果 CPU 处于空闲状态,向其发送调度时钟中 +断意义不大。毕竟,调度时钟中断的主要目的是 +强制繁忙的 CPU 在多个任务之间转移其注意力, +而空闲 CPU 没有需要转移注意力的任务。 + +不接收调度时钟中断的空闲 CPU 被称为 +"dyntick-idle"、"处于 dyntick-idle 模式"、 +"处于 nohz 模式"或"无刻度运行"。本文档的其 +余部分将使用"dyntick-idle 模式"。 + +CONFIG_NO_HZ_IDLE=y Kconfig 选项使内核避 +免向空闲 CPU 发送调度时钟中断,这对于电池 +供电的设备和高度虚拟化的大型机都至关重要。 +运行 CONFIG_HZ_PERIODIC=y 内核的电池供电设 +备将非常快速地耗尽其电池,轻松地比运行 +CONFIG_NO_HZ_IDLE=y 内核的同一设备快 2-3 +倍。运行 1,500 个操作系统实例的大型机可能 +会发现其一半的 CPU 时间被不必要的调度时钟 +中断消耗。在这些情况下,有强烈的动机避免向 +空闲 CPU 发送调度时钟中断。也就是说, +dyntick-idle 模式不是免费的: + +1. 它增加了进出空闲循环的路径上执行的指令 + 数量。 + +2. 在许多架构上,dyntick-idle 模式还会增 + 加昂贵的时钟重新编程操作的数量。 + +因此,具有激进实时响应约束的系统通常运行 +CONFIG_HZ_PERIODIC=y 内核(或对于旧内核为 +CONFIG_NO_HZ=n),以避免降低从空闲转换延迟。 + +还有一个引导参数"nohz=",可用于通过指定 +"nohz=off"在 CONFIG_NO_HZ_IDLE=y 内核中禁 +用 dyntick-idle 模式。默认情况下, +CONFIG_NO_HZ_IDLE=y 内核使用"nohz=on"引导, +启用 dyntick-idle 模式。 + + +为只有一个可运行任务的 CPU 省略调度时钟刻度 +============================================= + +如果 CPU 只有一个可运行任务,向其发送调度 +时钟中断意义不大,因为没有其他任务可以切换 +到。请注意,为只有一个可运行任务的 CPU 省 +略调度时钟刻度也意味着为空闲 CPU 省略它们。 + +CONFIG_NO_HZ_FULL=y Kconfig 选项使内核避 +免向具有单个可运行任务的 CPU 发送调度时钟 +中断,此类 CPU 被称为"自适应刻度 CPU"。这 +对于具有激进实时响应约束的应用程序非常重要, +因为它允许它们通过调度时钟中断的最大持续时 +间来改善其最坏情况响应时间。对于计算密集型 +短迭代工作负载也很重要:如果在给定迭代期间 +任何 CPU 被延迟,所有其他 CPU 将被迫在延迟 +的 CPU 完成时空闲等待。因此,延迟乘以比 +CPU 数量少一的数量。在这些情况下,再次有强 +烈的动机避免发送调度时钟中断。 + +默认情况下,没有 CPU 将是自适应刻度 CPU。 +"nohz_full=" 引导参数指定自适应刻度 CPU。 +例如,"nohz_full=1,6-8" 表示 CPU 1、6、7 +和 8 是自适应刻度 CPU。请注意,您被禁止将 +所有 CPU 标记为自适应刻度 CPU:至少必须保 +留一个非自适应刻度 CPU 在线以处理时间保持 +任务,以确保在自适应刻度 CPU 上像 +gettimeofday() 这样的系统调用返回准确的值。 +(这对于 CONFIG_NO_HZ_IDLE=y 不是问题,因 +为没有运行中的用户进程来观察时钟速率的轻微 +漂移。)请注意,这意味着您的系统必须至少有 +两个 CPU,CONFIG_NO_HZ_FULL=y 才能为您做 +任何事情。 + +最后,自适应刻度 CPU 必须卸载其 RCU 回调。 +这在下面的"RCU 影响"部分中介绍。 + +通常,CPU 尽可能长时间保持在自适应刻度模 +式下。特别是,转换到内核模式不会自动更改模 +式。相反,CPU 只有在需要时才会退出自适应刻 +度模式,例如,如果该 CPU 将 RCU 回调排入 +队列。 + +就像 dyntick-idle 模式一样,自适应刻度模 +式的好处不是免费的: + +1. CONFIG_NO_HZ_FULL 选择 CONFIG_NO_HZ_COMMON, + 因此您不能在没有 dyntick idle 的情况下 + 运行自适应刻度。这种依赖关系延伸到实现 + 中,因此 CONFIG_NO_HZ_IDLE 的所有成本 + 也由 CONFIG_NO_HZ_FULL 承担。 + +2. 由于需要通知内核子系统(例如 RCU)有关 + 模式更改,用户/内核转换稍微昂贵一些。 + +3. POSIX CPU 定时器阻止 CPU 进入自适应刻 + 度模式。需要基于 CPU 时间消耗采取行动 + 的实时应用程序需要使用其他方法来实现。 + +4. 如果待处理的 perf 事件多于硬件可以容纳 + 的,它们通常会轮转,以便随着时间的推移 + 收集所有事件。自适应刻度模式可能会阻止 + 这种轮转发生。这可能会通过阻止具有大量 + 待处理 perf 事件的 CPU 进入自适应刻度 + 模式来修复。 + +5. 自适应刻度 CPU 的调度程序统计信息可能 + 与非自适应刻度 CPU 的统计信息计算方式 + 略有不同。这可能反过来扰乱实时任务的负 + 载平衡。 + +尽管预计随着时间的推移会有所改进,但自适应 +刻度对于许多类型的实时和计算密集型应用程序 +非常有用。但是,上面列出的缺点意味着自适应 +刻度不应该(还)默认启用。 + + +RCU 影响 +======== + +在某些情况下,不能允许空闲 CPU 进入 +dyntick-idle 模式或自适应刻度模式,最常见 +的是当该 CPU 有 RCU 回调挂起时。 + +通过使用 CONFIG_RCU_NOCB_CPU=y Kconfig 选 +项将 RCU 回调处理卸载到"rcuo"内核线程来避 +免这种情况。可以使用"rcu_nocbs=" 内核引导 +参数选择要卸载的特定 CPU,该参数接受逗号分 +隔的 CPU 和 CPU 范围列表,例如,"1,3-5"选 +择 CPU 1、3、4 和 5。请注意,由"nohz_full" +内核引导参数指定的 CPU 也会被卸载。 + +卸载的 CPU 将永远不会排队 RCU 回调,因此 +RCU 永远不会阻止卸载的 CPU 进入 +dyntick-idle 模式或自适应刻度模式。也就是 +说,请注意,如果需要,将"rcuo"内核线程固定 +到特定 CPU 取决于用户空间。否则,调度程序 +将决定在哪里运行它们,这可能是也可能不是您 +希望它们运行的地方。 + + +测试 +==== + +因此,您启用了本文档中描述的所有操作系统抖 +动功能,但在工作负载的行为中看不到任何变化。 +这是因为您的工作负载受操作系统抖动的影响不 +大,还是因为其他东西挡在了路上?本节通过提 +供一个简单的操作系统抖动测试套件来帮助回答 +这个问题,该套件可在以下 git 存档的 master +分支上获得: + +git://git.kernel.org/pub/scm/linux/kernel/git/frederic/dynticks-testing.git + +克隆此存档并按照 README 文件中的说明进行 +操作。此测试过程将产生一个跟踪,允许您评估 +是否成功从系统中删除了操作系统抖动。如果此 +跟踪显示您已尽可能多地删除了操作系统抖动, +那么您可以得出结论,您的工作负载对操作系统 +抖动不是那么敏感。 + +注意:此测试要求您的系统至少有两个 CPU。我 +们目前没有从单 CPU 系统中删除操作系统抖动 +的好方法。 + + +已知问题 +======== + +* Dyntick-idle 会稍微减慢进出空闲的转换。 + 在实践中,除了最激进的实时工作负载外, + 这不是问题,它们可以选择禁用 + dyntick-idle 模式,大多数工作负载都采 + 用了这个选项。但是,一些工作负载无疑会 + 希望使用自适应刻度来消除调度时钟中断延 + 迟。以下是这些工作负载的一些选项: + + a. 使用用户空间的 PMQOS 通知内核您的 + 延迟要求(首选)。 + + b. 在 x86 系统上,使用"idle=mwait"引导 + 参数。 + + c. 在 x86 系统上,使用"intel_idle.max_cstate=" + 来限制最大 C 状态深度。 + + d. 在 x86 系统上,使用"idle=poll"引导 + 参数。但是,请注意,使用此参数可能 + 会导致 CPU 过热,这可能会导致热节 + 流降低您的延迟——而且这种降低可能比 + dyntick-idle 更糟糕。此外,此参数 + 有效地禁用了 Intel CPU 上的 Turbo + Mode,这会显著降低最大性能。 + +* 自适应刻度会稍微减慢用户/内核转换。对于 + 计算密集型工作负载,这不会成为问题,因 + 为它们很少有此类转换。需要仔细的基准测 + 试来确定其他工作负载是否受到此效果的显 + 著影响。 + +* 除非给定 CPU 只有一个可运行任务,否则自 + 适应刻度不会执行任何操作,即使还有许多 + 其他情况不需要调度时钟刻度。举一个例子, + 考虑一个 CPU,它有一个可运行的高优先级 + SCHED_FIFO 任务和任意数量的低优先级 + SCHED_OTHER 任务。在这种情况下,需要 + CPU 运行 SCHED_FIFO 任务,直到它阻塞或 + 某个其他更高优先级的任务在此 CPU 上唤醒 + (或被分配给此 CPU),因此向此 CPU 发 + 送调度时钟中断是没有意义的。但是,当前 + 实现仍然向具有单个可运行 SCHED_FIFO 任 + 务和多个可运行 SCHED_OTHER 任务的 CPU + 发送调度时钟中断,即使这些中断是不必要的。 + + 即使在给定 CPU 上有多个可运行任务时,在 + 当前运行任务的时间片到期之前中断该 CPU + 也意义不大,这几乎总是比下一个调度时钟 + 中断的时间长得多。 + + 更好地处理这些情况是未来的工作。 + +* 需要重新启动才能重新配置自适应空闲和 + RCU 回调卸载。如果需要,可以提供运行时 + 重新配置,但是,由于在运行时重新配置 + RCU 的复杂性,需要有一个非常好的理由。 + 特别是考虑到您有简单的选项,即从所有 + CPU 卸载 RCU 回调,并在您想要固定它们时 + 将它们固定到您想要的位置。 + +* 需要额外的配置来处理操作系统抖动的其他 + 来源,包括中断和系统实用程序任务和进程。 + 此配置通常涉及将中断和任务绑定到特定 + CPU。 + +* 某些操作系统抖动源目前只能通过约束工作 + 负载来消除。例如,消除由于全局 TLB + shootdown 引起的操作系统抖动的唯一方法 + 是避免导致这些 shootdown 的取消映射操 + 作(例如内核模块卸载操作)。再举一个例 + 子,通过使用巨页并通过约束应用程序使用 + 的内存量,可以减少(在某些情况下消除) + 页面错误和 TLB 未命中。预先发生工作集 + 也可能有帮助,尤其是与 mlock() 和 + mlockall() 系统调用结合使用时。 + +* 除非所有 CPU 都处于空闲状态,否则至少一 + 个 CPU 必须保持调度时钟中断运行以支持准 + 确的时间保持。 + +* 如果可能有一些自适应刻度 CPU,即使所有 + CPU 都处于空闲状态,也将至少有一个 CPU + 保持调度时钟中断运行。 + + 更好地处理这种情况是正在进行的工作。 + +* 某些进程处理操作仍然需要偶尔的调度时钟 + 刻度。这些操作包括计算 CPU 负载、维护 + sched average、计算 CFS 实体 vruntime、 + 计算 avenrun 以及执行负载平衡。它们当前 + 通过每秒左右的调度时钟刻度来容纳。正在 + 进行的工作将消除对这些不频繁的调度时钟 + 刻度的需求。 + -- 2.34.1