From 5694e1b83957f12adecdc09e40dec18307101657 Mon Sep 17 00:00:00 2001 From: CGH0S7 <776459475@qq.com> Date: Mon, 29 Jun 2026 14:36:21 +0800 Subject: [PATCH] 2026-06-29 updated --- _config.fluid.yml | 6 +- chat-KDE Plasma 全局菜单支持机制.txt | 257 ++++++++++++++++++++ db.json | 2 +- source/_posts/firefox-global-menu.md | 84 +++++++ source/about/index.md | 4 +- source/images/chrome-global-menu.png | Bin 0 -> 45048 bytes source/images/firefox-global-menu-solve.png | Bin 0 -> 50106 bytes source/images/firefox-global-menu.png | Bin 0 -> 54145 bytes 8 files changed, 347 insertions(+), 6 deletions(-) create mode 100644 chat-KDE Plasma 全局菜单支持机制.txt create mode 100644 source/_posts/firefox-global-menu.md create mode 100644 source/images/chrome-global-menu.png create mode 100644 source/images/firefox-global-menu-solve.png create mode 100644 source/images/firefox-global-menu.png diff --git a/_config.fluid.yml b/_config.fluid.yml index 58da91d..9a3fe16 100644 --- a/_config.fluid.yml +++ b/_config.fluid.yml @@ -1036,7 +1036,7 @@ links: } - { title: "Wingrew", - intro: "OS高手", + intro: "高地来的清✌️", link: "https://wingrew.com/", avatar: "https://wingrew.com/assets/img/try.jpg" } @@ -1050,13 +1050,13 @@ links: title: "Sharelter", intro: "Meow~", link: "https://sharelter.github.io/", - avatar: "https://sharelter.github.io/img/sharelter-avatar_hub1f71be9b71dd17b212d94ded2d9ce34_23100_300x0_resize_q75_box.jpeg" + avatar: "https://sharelter.github.io/img/sharelter-avatar_hu_d55e0afb08faa59.jpeg" } - { title: "Cachy Blog", intro: "Blazingly Fast & Customizable", link: "https://cachyos.org/blog/", - avatar: "https://cachyos.org/_astro/logo.DVTdAJi6.svg" + avatar: "https://cachyos.org/_astro/logo.DuMERIP6.svg" } - { title: "HIKARU UTADA", diff --git a/chat-KDE Plasma 全局菜单支持机制.txt b/chat-KDE Plasma 全局菜单支持机制.txt new file mode 100644 index 0000000..f21a63b --- /dev/null +++ b/chat-KDE Plasma 全局菜单支持机制.txt @@ -0,0 +1,257 @@ +### USER +你熟悉KDE Plasma桌面的全局菜单小组件吗?这个组件的工作原理是什么?是否能支持GTK程序的菜单还是说仅支持qt和electron类程序? + +### ASSISTANT +我对 KDE Plasma 桌面的**全局菜单(Global Menu / Application Menu)** 小组件非常熟悉。它是 KDE 桌面环境中用于实现类似 macOS 顶部菜单栏体验的核心组件,能够将各个应用程序的菜单栏(如“文件”、“编辑”、“视图”等)统一提取到 Plasma 面板(Panel)或屏幕顶部显示。 + +以下是关于它的工作原理以及对不同程序框架支持情况的详细解答: + +### 一、 工作原理是什么? + +全局菜单的核心工作原理是基于 **D-Bus 消息总线系统** 和 **`dbusmenu` 协议**。它采用的是“客户端-服务端”的架构: + +1. **协议基础 (`dbusmenu`)**: + 这个协议最初由 Canonical 为 Ubuntu Unity 桌面开发,后来被 KDE 等社区采纳。它定义了一套标准的 D-Bus 接口,用于描述菜单的结构(菜单项名称、图标、快捷键、分隔符、是否可用等)以及用户交互事件。 + +2. **应用程序端(暴露菜单)**: + 当支持全局菜单的应用程序启动时,它会将自身的菜单树注册到系统的 D-Bus 会话总线上,作为一个独立的服务(Service)暴露出来。此时,菜单的数据实际上是在应用程序的内存中,只是通过 D-Bus 提供了一个“查询接口”。 + +3. **Plasma 小组件端(渲染与交互)**: + - **焦点追踪**:Plasma 的全局菜单小组件(AppMenu)会通过 KWin(KDE 的窗口管理器)实时监听当前处于激活状态(获得焦点)的窗口。 + - **数据拉取**:当用户切换窗口时,小组件会获取当前活动窗口的 D-Bus 服务地址,并通过 `dbusmenu` 协议拉取该窗口的菜单数据,然后在面板上渲染出对应的菜单项。 + - **事件回传**:当用户在面板上点击某个菜单项时,小组件会通过 D-Bus 向对应的应用程序发送一个“激活(Activate)”信号,应用程序接收到信号后执行相应的功能。 + +--- + +### 二、 对不同程序框架的支持情况 + +全局菜单**并非仅支持 Qt 程序**,它对 Qt、GTK 和 Electron 都有不同程度的支持,但底层实现和所需依赖有所不同: + +#### 1. Qt 程序(完美原生支持) +- **支持情况**:**完美支持**。 +- **原理**:KDE 本身就是基于 Qt 构建的。在 Qt 5 和 Qt 6 中,内置了对 `dbusmenu` 的原生支持(通过 `libdbusmenu-qt` 或 Qt 的平台主题插件)。 +- **表现**:只要 Qt 程序使用了标准的 `QMenuBar`,在 KDE Plasma 下无需任何额外配置,菜单就会自动被全局菜单小组件接管。 + +#### 2. GTK 程序(支持,但需要额外依赖,且 GTK4 存在局限) +- **支持情况**:**支持,但需要安装额外模块**。 +- **原理**:GTK 框架本身并不直接输出 `dbusmenu` 协议的数据。为了让 GTK 程序支持全局菜单,需要借助第三方桥接模块,最核心的是 **`appmenu-gtk-module`**(包含 GTK2 和 GTK3 的模块)。 +- **如何生效**:安装该模块后,系统会通过环境变量(如 `GTK_MODULES=appmenu-gtk-module`)在 GTK 程序启动时注入该模块,从而拦截 GTK 的菜单渲染并将其转换为 `dbusmenu` 信号发送给 D-Bus。 +- **⚠️ 重要注意(GTK4 的挑战)**: + - **GTK3** 应用配合 `appmenu-gtk-module` 可以很好地工作。 + - **GTK4** 应用的全局菜单支持**非常糟糕**。因为 GTK4 在架构上废弃了传统的 `GtkMenuBar`,转而推荐使用 `Popover` 或 `HeaderBar` 内的按钮来实现菜单。GTK 官方对传统的“全局菜单”概念并不积极,导致很多现代 GTK4 应用(如新版 GNOME 应用)无法被 Plasma 全局菜单正确提取。 + +#### 3. Electron 程序(良好支持) +- **支持情况**:**良好支持**。 +- **原理**:Electron 底层基于 Chromium。Chromium 在 Linux 下原生集成了对 `libdbusmenu` 的支持。 +- **表现**:只要 Electron 应用**没有使用自定义的 HTML/CSS 绘制标题栏和菜单**(即使用了操作系统的原生菜单渲染),它们就会自动将菜单注册到 D-Bus 上。像 VS Code、Discord、Typora 等应用,在 KDE 下都能完美显示全局菜单。 + +#### 4. 其他特殊程序(如 Firefox / Thunderbird) +- **Firefox**:基于 Gecko 引擎,Linux 版 Firefox 原生支持 `dbusmenu`。在 KDE 环境下,它通常能自动检测到 Plasma 并启用全局菜单。 +- **LibreOffice**:支持全局菜单,但有时需要在 LibreOffice 的设置中手动勾选“使用系统标题栏和边框”或相关菜单集成选项。 + +--- + +### 三、 总结与使用建议 + +1. **生态兼容性**:KDE Plasma 的全局菜单是一个**跨框架**的解决方案,它不挑编程语言,只要程序能向 D-Bus 发送 `dbusmenu` 信号即可。 +2. **Wayland 环境**:在 Plasma 6 及 Wayland 会话下,全局菜单的运行非常稳定。但部分老旧的 GTK3 应用如果在 Wayland 下无法显示全局菜单,可以尝试强制其使用 XWayland 运行,或检查 `appmenu-gtk-module` 是否正确加载。 +3. **GTK4 用户的妥协**:如果你大量使用最新的 GTK4 应用,可能会发现它们的全局菜单失效。这是 GTK 官方架构变更导致的,目前 KDE 社区和 GTK 社区在此问题上存在理念分歧,短期内较难有完美的原生解决方案(通常只能接受 GTK4 应用将菜单内嵌在窗口标题栏中)。 + +### USER +firefox是gtk4吗?我的firefox菜单只能手动通过alt键调出不能集成到全局菜单,而chrome则能自动集成 + +### ASSISTANT +这是一个非常敏锐的观察!你遇到的现象正是 KDE 全局菜单在不同应用上表现差异的典型例子。 + +我们先直接回答你的第一个问题,然后再深入剖析为什么 Firefox 和 Chrome 的表现会截然不同。 + +### 一、 Firefox 是 GTK4 吗? + +**不是。目前稳定版的 Firefox 在 Linux 下主要基于 GTK3。** +虽然 Firefox 内部有大量的自定义渲染代码(使用类似 HTML/CSS 的 XUL 技术来绘制界面),但其与 Linux 窗口系统(X11/Wayland)交互的底层工具包依然是 GTK3。Mozilla 虽然在推进 GTK4 和纯 Wayland 原生支持,但 GTK3 仍是目前的绝对主力。 + +--- + +### 二、 为什么 Chrome 能自动集成,而 Firefox 只能按 Alt 调出? + +这个问题的核心在于:**全局菜单小组件只能抓取“原生的系统菜单”,而抓取不到“应用程序自己画的菜单”。** + +#### 1. Chrome (Chromium) 为什么能自动集成? +Chrome 在 Linux 下的实现非常“守规矩”。当它检测到 KDE 环境时,它会主动调用 Linux 原生的 `libdbusmenu` 库,将自己的菜单树老老实实地注册到 D-Bus 总线上。因为它是标准的原生菜单,Plasma 的全局菜单小组件就能轻松抓取并渲染。 + +#### 2. Firefox 为什么失败?(“按 Alt 调出”是关键线索) +你提到 Firefox 的菜单 **“只能手动通过 Alt 键调出”**,这正是问题所在! + +* **自绘菜单 vs 原生菜单**:当 Firefox 默认隐藏菜单栏时,你按下 `Alt` 键呼出的那个菜单,**并不是** Linux 原生的 `GtkMenuBar`,而是 Firefox 用自己的内部代码(XUL/HTML)在窗口内部临时“画”出来的一个弹出菜单。 +* **全局菜单的盲区**:Plasma 的全局菜单依赖于 `appmenu-gtk-module` 去拦截 GTK 程序的标准 `GtkMenuBar`。既然 Firefox 呼出的是自己画的菜单,`appmenu-gtk-module` 根本拦截不到它,全局菜单自然也就认为“这个程序没有菜单”。 + +此外,Firefox 还有一个习惯:如果它启用了“自定义标题栏”,它会接管整个窗口的绘制,这也会破坏 GTK 模块对菜单的拦截。 + +--- + +### 三、 如何让 Firefox 完美集成到 KDE 全局菜单? + +要解决这个问题,我们需要强迫 Firefox 放弃“自绘菜单”,改用“原生 GTK 菜单”。请按照以下步骤操作: + +#### 步骤 1:取消“自定义标题栏” +这是最常见的原因。 +1. 在 Firefox 中,点击右上角的菜单按钮(三条横线),选择 **“更多工具” -> “自定义工具栏...”**。 +2. 看向浏览器窗口的**左下角**,有一个 **“标题栏” (Title Bar)** 的复选框。 +3. **如果它是勾选状态,请取消勾选!** +4. 点击右下角的“完成”。 +*(取消勾选后,Firefox 会把窗口的标题栏和边框交给 KDE Plasma 来绘制,这是全局菜单接管的前提。)* + +#### 步骤 2:强制显示原生菜单栏 +我们需要让 Firefox 的原生菜单处于“激活”状态,而不是隐藏状态。 +1. 再次进入 **“自定义工具栏...”** 界面。 +2. 在底部的“自定义工具栏”区域,或者在顶部的工具栏空白处右键。 +3. 找到 **“菜单栏” (Menu Bar)** 选项,确保它是**勾选/显示**状态。 +4. 如果它不在工具栏上,你可以从底部的图标列表中,把 **“菜单栏”** 直接拖拽到顶部的工具栏区域。 +5. 点击“完成”。 + +**完成这两步后,重启 Firefox。** 此时,Firefox 应该会使用原生的 GTK 菜单栏,Plasma 的全局菜单小组件就能瞬间识别并接管它了。 + +--- + +### 四、 终极排查(如果上述步骤无效) + +如果做完上述操作,Firefox 依然无法集成,可能是底层配置或 Wayland 环境的问题: + +1. **检查 `about:config` 隐藏开关**: + * 在 Firefox 地址栏输入 `about:config` 并回车,接受风险提示。 + * 搜索 `ui.use_unity_menubar`。(虽然名字里带 unity,但它是 Firefox 控制是否向 D-Bus 暴露 `dbusmenu` 的全局开关)。 + * 确保它的值为 **`true`**。如果是 `false`,请双击修改为 `true`。 + +2. **Wayland 环境的特殊 Bug**: + * 如果你使用的是 **Wayland** 会话(Plasma 6 默认),Firefox 的原生 Wayland 后端在某些版本中对全局菜单的支持有 Bug。 + * **测试方法**:在终端中通过强制 X11 模式启动 Firefox 看看是否恢复: + ```bash + MOZ_ENABLE_WAYLAND=0 firefox + ``` + * 如果 X11 模式下全局菜单正常,说明是 Firefox 的 Wayland 后端问题。你可以尝试在 KDE 的“系统设置 -> 颜色和主题 -> 窗口装饰”中,确保没有使用与 Wayland 冲突的旧版 Breeze 主题,或者等待 Firefox 后续版本修复此 Bug。 + +**总结:** +Chrome 是“原生适配”的优等生,直接交出菜单数据;而 Firefox 默认喜欢“自己画界面”,导致 KDE 的全局菜单抓不到它。通过关闭 Firefox 的自定义标题栏并强制显示原生菜单栏,就能让它们重新“对上暗号”。 + +### USER +我把你的步骤三部署完后firefox菜单依旧没有集成在全局菜单只是显示在firefox窗口标题栏下 + +### ASSISTANT +这是一个非常关键的进展! + +首先,**恭喜你完成了最关键的一步**。菜单从“只能按 Alt 呼出”变成了“显示在窗口标题栏下方”,这说明 Firefox 已经**成功放弃了自绘菜单,切换到了原生的 GTK 菜单栏**。 + +既然菜单已经是原生的了,为什么 Plasma 的全局菜单还是没有把它“抓”走呢?(而 Chrome 却能自动抓走?) + +这里有一个核心的底层差异:**Chrome 是“自带干粮”的,而 Firefox 是“依赖外援”的。** +* **Chrome (Chromium)**:底层自带了对 `dbusmenu` 协议的直接支持,它自己主动把菜单数据发送给 KDE,不需要系统额外帮忙。 +* **Firefox**:它是基于 GTK3 的,GTK3 本身不懂 `dbusmenu` 协议。Firefox **必须依赖系统安装的一个“翻译官”插件(`appmenu-gtk-module`)**,把这个原生菜单拦截下来,翻译成 KDE 能懂的信号。 + +现在你的情况是:“翻译官”没起作用,或者 Firefox 和“翻译官”失联了。请按照以下顺序排查(按最常见原因排序): + +--- + +### 排查一:你使用的是 Snap / Flatpak 版本的 Firefox 吗?(最常见原因) + +如果你使用的是 Ubuntu(默认 Snap 版)或通过 Flatpak 安装的 Firefox,它们运行在**沙盒(Sandbox)** 中。沙盒的安全机制会阻断 Firefox 与宿主系统的 D-Bus 进行全局菜单的通信。 + +**验证与解决:** +1. 打开终端,输入 `firefox --version` 或者在 Firefox 的“关于”里查看。如果是 Snap 版,通常会带有 `snap` 字样;如果是 Flatpak,会有 `flatpak` 字样。 +2. **解决方案**:强烈建议卸载 Snap/Flatpak 版本,安装**原生包(Native Package)**。 + * **Ubuntu/Debian**: 建议添加 Mozilla 的官方 PPA 安装 `.deb` 原生包。 + * **Fedora**: 使用 `sudo dnf install firefox`(默认就是原生 rpm)。 + * **Arch Linux**: 使用 `sudo pacman -S firefox`。 + * 原生包没有沙盒限制,能直接调用系统的 GTK 模块。 + +--- + +### 排查二:系统是否安装并正确加载了 `appmenu-gtk-module`? + +如果你的 Firefox 已经是原生包,那么很可能是系统缺少这个“翻译官”,或者没有告诉 Firefox 去使用它。 + +**验证与解决:** +1. **安装模块**: + 打开终端,根据你的发行版安装该模块(它通常分为 gtk2 和 gtk3 两个包): + * **Ubuntu/Debian/KDE Neon**: `sudo apt install appmenu-gtk2-module appmenu-gtk3-module` + * **Fedora**: `sudo dnf install appmenu-gtk2-module appmenu-gtk3-module` (或 `gtk3-appmenu-gtk-module`) + * **Arch Linux**: `sudo pacman -S appmenu-gtk-module` + +2. **确保环境变量生效**: + 安装后,系统需要知道在启动 GTK 程序时加载它。 + * 打开终端,输入以下命令测试启动 Firefox: + ```bash + GTK_MODULES=appmenu-gtk-module firefox + ``` + * **观察结果**:如果这样启动后,Firefox 的菜单**瞬间飞到了 Plasma 全局菜单里**,说明就是环境变量的问题! + * **永久生效**:如果上面测试成功,你需要将 `GTK_MODULES=appmenu-gtk-module` 永久添加到你的环境变量中。 + * 编辑 `~/.profile` 或 `~/.bashrc` 或 `~/.xprofile` (取决于你的登录管理器),在末尾加上一行: + `export GTK_MODULES=appmenu-gtk-module` + * 注销并重新登录 KDE。 + +--- + +### 排查三:Wayland 会话的兼容性 Bug + +如果你使用的是 **Wayland** 会话(Plasma 6 默认,或者你在登录界面选择了 "Plasma (Wayland)"),Firefox 的原生 Wayland 后端有时会导致 GTK 模块失效。 + +**验证与解决:** +1. 在终端中,强制 Firefox 使用 X11 (Xwayland) 模式启动: + ```bash + MOZ_ENABLE_WAYLAND=0 firefox + # 或者 + GDK_BACKEND=x11 firefox + ``` +2. **观察结果**:如果在 X11 模式下,菜单成功集成到了全局菜单中,说明这是 Firefox 在 Wayland 下的已知 Bug。 +3. **妥协方案**:你可以暂时在 KDE 的“系统设置 -> 环境变量”中,为 Firefox 添加环境变量 `MOZ_ENABLE_WAYLAND=0`,让它以 Xwayland 模式运行,直到 Firefox 官方修复此问题。 + +--- + +### 排查四:Firefox 内部的高级配置拦截 + +极少数情况下,Firefox 内部的配置会强制禁用全局菜单。 + +**验证与解决:** +1. 在 Firefox 地址栏输入 `about:config`,回车并接受风险。 +2. 搜索 `ui.use_unity_menubar`,确保它的值是 **`true`**。 +3. 搜索 `widget.gtk.global-menu.force` (如果存在这个选项),尝试将其设置为 **`1`** 或 **`true`**。 +4. 重启 Firefox。 + +### 总结建议 + +请优先检查**排查一(Snap/Flatpak 沙盒问题)**和**排查二(缺失 appmenu-gtk-module)**。90% 以上的 KDE 用户遇到“Chrome 行但 Firefox 不行,且菜单留在本地”的问题,都是因为这两个原因。 + +如果测试 `GTK_MODULES=appmenu-gtk-module firefox` 能够成功,那就说明问题已经找到了!你可以告诉我你的 Linux 发行版是什么,我可以给你更精确的安装命令。 + +### USER +我把widget.gtk.global-menu.force和widget.gtk.global-menu.force-wayland打开后就成功了,感谢 + +### ASSISTANT +太棒了!很高兴听到问题圆满解决,也非常感谢你的反馈! + +你找到的这两个参数 `widget.gtk.global-menu.force` 和 `widget.gtk.global-menu.force-wayland`,确实是 Firefox 内部针对全局菜单的 **“终极强制开关”**。 + +为了让你“知其然也知其所以然”,我来简单补充一下这两个参数为什么能起奇效: + +### 为什么这两个参数能解决问题? + +在之前的步骤中,你取消了“自定义标题栏”,Firefox 已经成功把菜单变成了**原生的 GTK 菜单**(所以它显示在了标题栏下方,而不是按 Alt 呼出)。 + +但为什么 Plasma 还是抓不到它呢? +这是因为 Firefox 内部有一套自己的 **“环境探测机制”**。在 Wayland 会话下,或者在某些特定版本的 KDE 环境中,Firefox 的探测机制可能会“犯迷糊”,误以为当前系统不支持全局菜单,或者出于保守策略,**没有主动去连接 D-Bus 总线注册菜单数据**。 + +这两个参数的作用就是 **“绕过探测,强制执行”**: +* `widget.gtk.global-menu.force`:强制 Firefox 忽略环境探测结果,无论如何都要尝试向系统暴露全局菜单。 +* `widget.gtk.global-menu.force-wayland`:专门针对 Wayland 显示服务器的强制开关。因为 Wayland 的窗口交互逻辑与 X11 不同,Firefox 在 Wayland 下默认对全局菜单的支持比较保守,这个参数直接命令它:“别管那么多,在 Wayland 下也必须把菜单交出去!” + +开启它们,就等于 Firefox 乖乖地把菜单数据通过 D-Bus 塞给了 Plasma 的全局菜单小组件。 + +--- + +### 这次“排坑”经验总结 + +你这次的经历非常经典,几乎涵盖了 KDE 下 Firefox 全局菜单失效的所有核心原因。以后如果其他朋友遇到类似问题,或者 Firefox 大版本更新后这个功能突然失效了,完全可以按照这个“两步走”战略来解决: + +1. **第一步(解决“自绘菜单”问题)**:关闭 Firefox 的“自定义标题栏”,让菜单变成原生 GTK 菜单(显示在标题栏下方)。 +2. **第二步(解决“连接失败”问题)**:在 `about:config` 中开启 `widget.gtk.global-menu.force` 和 `widget.gtk.global-menu.force-wayland`,强制 Firefox 向 D-Bus 注册菜单。 + +再次祝贺你成功配置好了完美的 KDE 全局菜单体验!如果以后在使用 KDE Plasma 时还遇到其他有趣的问题或折腾需求,随时欢迎来找我探讨。祝你使用愉快! \ No newline at end of file diff --git a/db.json b/db.json index cbdfd86..87551b9 100644 --- a/db.json +++ b/db.json @@ -1 +1 @@ -{"meta":{"version":1,"warehouse":"5.0.1"},"models":{"Asset":[{"_id":"source/music/過ぎ去りし温もりの日々.mp3","path":"music/過ぎ去りし温もりの日々.mp3","modified":0,"renderable":0},{"_id":"source/images/1613f5602b203b38230f19699deb0219454454985.png","path":"images/1613f5602b203b38230f19699deb0219454454985.png","modified":0,"renderable":0},{"_id":"source/images/1730728153314.png","path":"images/1730728153314.png","modified":0,"renderable":0},{"_id":"source/images/20241027_222225.png","path":"images/20241027_222225.png","modified":0,"renderable":0},{"_id":"source/images/5364bba6d035326e82c53504dd53e7c2454454985.png","path":"images/5364bba6d035326e82c53504dd53e7c2454454985.png","modified":0,"renderable":0},{"_id":"source/images/71a5357ef4bd808b10429bc2ea46cb6f454454985.png","path":"images/71a5357ef4bd808b10429bc2ea46cb6f454454985.png","modified":0,"renderable":0},{"_id":"source/images/Written-By-Human-Not-By-AI-Badge-white@2x.png","path":"images/Written-By-Human-Not-By-AI-Badge-white@2x.png","modified":0,"renderable":0},{"_id":"source/images/amd.webp","path":"images/amd.webp","modified":0,"renderable":0},{"_id":"source/images/archlinux-logo.png","path":"images/archlinux-logo.png","modified":0,"renderable":0},{"_id":"source/images/asc-events.png","path":"images/asc-events.png","modified":0,"renderable":0},{"_id":"source/images/asc.png","path":"images/asc.png","modified":0,"renderable":0},{"_id":"source/images/audiveris.png","path":"images/audiveris.png","modified":0,"renderable":0},{"_id":"source/images/b2054bbaf6197624d38cc2007d885fd1454454985.png","path":"images/b2054bbaf6197624d38cc2007d885fd1454454985.png","modified":0,"renderable":0},{"_id":"source/images/built_on_the_kde_platform.png","path":"images/built_on_the_kde_platform.png","modified":0,"renderable":0},{"_id":"source/images/caseclosed.png","path":"images/caseclosed.png","modified":0,"renderable":0},{"_id":"source/images/clonezilla.png","path":"images/clonezilla.png","modified":0,"renderable":0},{"_id":"source/images/krita-official.png","path":"images/krita-official.png","modified":0,"renderable":0},{"_id":"source/images/lmms-import.png","path":"images/lmms-import.png","modified":0,"renderable":0},{"_id":"source/images/mhwilds4050.jpg","path":"images/mhwilds4050.jpg","modified":0,"renderable":0},{"_id":"source/images/mhwilds780M.jpg","path":"images/mhwilds780M.jpg","modified":0,"renderable":0},{"_id":"source/images/musecore-launch.png","path":"images/musecore-launch.png","modified":0,"renderable":0},{"_id":"source/images/musecore-to-midi.png","path":"images/musecore-to-midi.png","modified":0,"renderable":0},{"_id":"source/images/musecore.png","path":"images/musecore.png","modified":0,"renderable":0},{"_id":"source/images/phase1.png","path":"images/phase1.png","modified":0,"renderable":0},{"_id":"source/images/phase1_strings.png","path":"images/phase1_strings.png","modified":0,"renderable":0},{"_id":"source/images/phase_2_14.png","path":"images/phase_2_14.png","modified":0,"renderable":0},{"_id":"source/images/phase_4.png","path":"images/phase_4.png","modified":0,"renderable":0},{"_id":"source/images/resendofficial.png","path":"images/resendofficial.png","modified":0,"renderable":0},{"_id":"source/images/rusendhelp1.png","path":"images/rusendhelp1.png","modified":0,"renderable":0},{"_id":"source/images/rusendhelp2.png","path":"images/rusendhelp2.png","modified":0,"renderable":0},{"_id":"source/images/rusendhelp3.png","path":"images/rusendhelp3.png","modified":0,"renderable":0},{"_id":"source/images/searxng.png","path":"images/searxng.png","modified":0,"renderable":0},{"_id":"source/images/xb2score.png","path":"images/xb2score.png","modified":0,"renderable":0},{"_id":"source/images/yana.jpg","path":"images/yana.jpg","modified":0,"renderable":0},{"_id":"source/images/zellij-helix.jpg","path":"images/zellij-helix.jpg","modified":0,"renderable":0},{"_id":"themes/fluid/source/css/gitalk.css","path":"css/gitalk.css","modified":0,"renderable":1},{"_id":"themes/fluid/source/css/highlight-dark.styl","path":"css/highlight-dark.styl","modified":0,"renderable":1},{"_id":"themes/fluid/source/css/highlight.styl","path":"css/highlight.styl","modified":0,"renderable":1},{"_id":"themes/fluid/source/css/main.styl","path":"css/main.styl","modified":0,"renderable":1},{"_id":"themes/fluid/source/js/boot.js","path":"js/boot.js","modified":0,"renderable":1},{"_id":"themes/fluid/source/js/color-schema.js","path":"js/color-schema.js","modified":0,"renderable":1},{"_id":"themes/fluid/source/js/events.js","path":"js/events.js","modified":0,"renderable":1},{"_id":"themes/fluid/source/js/leancloud.js","path":"js/leancloud.js","modified":0,"renderable":1},{"_id":"themes/fluid/source/js/local-search.js","path":"js/local-search.js","modified":0,"renderable":1},{"_id":"themes/fluid/source/js/plugins.js","path":"js/plugins.js","modified":0,"renderable":1},{"_id":"themes/fluid/source/js/umami-view.js","path":"js/umami-view.js","modified":0,"renderable":1},{"_id":"themes/fluid/source/js/utils.js","path":"js/utils.js","modified":0,"renderable":1},{"_id":"themes/fluid/source/img/avatar.png","path":"img/avatar.png","modified":0,"renderable":1},{"_id":"themes/fluid/source/img/gensokyo.jpg","path":"img/gensokyo.jpg","modified":0,"renderable":1},{"_id":"themes/fluid/source/img/gh0s7.jpg","path":"img/gh0s7.jpg","modified":0,"renderable":1},{"_id":"themes/fluid/source/img/hifuu.png","path":"img/hifuu.png","modified":0,"renderable":1},{"_id":"themes/fluid/source/img/loading.gif","path":"img/loading.gif","modified":0,"renderable":1},{"_id":"themes/fluid/source/img/police_beian.png","path":"img/police_beian.png","modified":0,"renderable":1},{"_id":"themes/fluid/source/xml/local-search.xml","path":"xml/local-search.xml","modified":0,"renderable":1},{"_id":"themes/fluid/source/js/img-lazyload.js","path":"js/img-lazyload.js","modified":0,"renderable":1},{"_id":"source/about/index.md","path":"about/index.md","modified":0,"renderable":0},{"_id":"source/images/amd.png","path":"images/amd.png","modified":0,"renderable":0},{"_id":"source/images/MyASCStory-3.jpg","path":"images/MyASCStory-3.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory-4.jpg","path":"images/MyASCStory-4.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC24-1.jpg","path":"images/MyASCStory/ASC24-1.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC24-2.jpg","path":"images/MyASCStory/ASC24-2.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC24-3.jpg","path":"images/MyASCStory/ASC24-3.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC25-1.jpg","path":"images/MyASCStory/ASC25-1.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC25-2.jpg","path":"images/MyASCStory/ASC25-2.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC25-3.jpg","path":"images/MyASCStory/ASC25-3.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC25-4.jpg","path":"images/MyASCStory/ASC25-4.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC26-1.jpg","path":"images/MyASCStory/ASC26-1.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC26-2.jpg","path":"images/MyASCStory/ASC26-2.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC26-3.jpg","path":"images/MyASCStory/ASC26-3.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/MyASCStory.jpg","path":"images/MyASCStory/MyASCStory.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC24-4.jpg","path":"images/MyASCStory/ASC24-4.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/lzdnb.jpg","path":"images/MyASCStory/lzdnb.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC25cluster.jpg","path":"images/MyASCStory/ASC25cluster.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC26cluster.jpg","path":"images/MyASCStory/ASC26cluster.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC24-5.jpg","path":"images/MyASCStory/ASC24-5.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC25-5.jpg","path":"images/MyASCStory/ASC25-5.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC25-6.jpg","path":"images/MyASCStory/ASC25-6.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC25-7.jpg","path":"images/MyASCStory/ASC25-7.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC26YeZ.jpg","path":"images/MyASCStory/ASC26YeZ.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC25-8.jpg","path":"images/MyASCStory/ASC25-8.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC24-6.jpg","path":"images/MyASCStory/ASC24-6.jpg","modified":0,"renderable":0}],"Cache":[{"_id":"source/about/index.md","hash":"ff7cd1481f3c669a04d7c8abf8851ab0cea5b2b9","modified":1740326480093},{"_id":"source/_posts/12月18日工作记录.md","hash":"28892523fcc2fadb1514645a1c59d501ceab53da","modified":1740320866748},{"_id":"source/_posts/12月16-17日工作记录.md","hash":"9f5099a65180c2659f1a1ca218d518fc64965676","modified":1740320866747},{"_id":"source/_posts/12月19日工作记录.md","hash":"058ca01b682cd36e202189ef025d6d4bb79259fb","modified":1740320866748},{"_id":"source/_posts/12月20日工作记录.md","hash":"0d883ab20ae59540f663c5377e799463762a3b11","modified":1740320866748},{"_id":"source/_posts/12月28日进度报告.md","hash":"4cbda36dc6a2c4bc67140e443638a73aadb18fdf","modified":1740320866748},{"_id":"source/_posts/12月31日进度报告.md","hash":"bf541fdb539029c98dedf975b0c15519adcea76e","modified":1740320866748},{"_id":"source/_posts/2025-5-21.md","hash":"2e1406ca2c83a1a91e78fd3453623ba732d81ee7","modified":1747834483639},{"_id":"source/_posts/BlogUpdate.md","hash":"2c694e3895b2ea0c0cea5f45ab2ec16a868a3df7","modified":1740320866748},{"_id":"source/_posts/GentleJena.md","hash":"df1e35a93284d0b9e2d408635cd98481077d5155","modified":1740330871930},{"_id":"source/_posts/CGH0S7-s-Blog.md","hash":"9a5c9461aba29f2447c0f6725f5425d5a05e32f0","modified":1740320866748},{"_id":"source/_posts/Vocaloid调教-晴天.md","hash":"9141182f5dbfbbd840ad340b3e5271507ddd8c94","modified":1740442745940},{"_id":"source/_posts/archlinux-optimization.md","hash":"51c8c12110fb08a1edd1f727ab38a1b2568b690b","modified":1741619246618},{"_id":"source/_posts/Your-exclusive-email-address.md","hash":"c1721f4ac1b359140b0b57658ddd39c64c041aa8","modified":1763224970086},{"_id":"source/_posts/arch-nvidia.md","hash":"4144acad32a69e88ef6d5d7d5788da6d87a75193","modified":1738495295673},{"_id":"source/_posts/archlinux-game-fix.md","hash":"a4ab5bd2ca0cf941c5166d3945bd2a2076a54f1e","modified":1741618456788},{"_id":"source/_posts/kritabest.md","hash":"a137c6edc4ea2ca1d7ea13d8dc2bd1bbb7df6d84","modified":1752421648539},{"_id":"source/_posts/clonezilla.md","hash":"598858976961c45ebbbf523014ee792951380dd3","modified":1740319930654},{"_id":"source/_posts/fortune.md","hash":"e249e56dad408ad79b11efc90aa42f764cf1c665","modified":1747899668523},{"_id":"source/_posts/gcc15restrict.md","hash":"50533932fcba41826b8cf75f54b173f0c604136b","modified":1748008893929},{"_id":"source/_posts/loopers.md","hash":"2b63f5abcfebc672e7f90b3e83cee1b5ac1d4eff","modified":1740320866748},{"_id":"source/_posts/mhwi.md","hash":"b9a73accf93684b8cfac0201288da9b2c5376423","modified":1731333619124},{"_id":"source/_posts/os-optimization.md","hash":"7c96f2f88fb750dd15617f875bdc28a58d978680","modified":1780145640163},{"_id":"source/_posts/nudtbomblab.md","hash":"51b4410784657675d7c11e17d16c5dc0e5d4a455","modified":1740455795270},{"_id":"source/_posts/overleaf.md","hash":"0786994f4571ac178ee40bc130e3c0d8d2d511f9","modified":1730860316685},{"_id":"source/_posts/searxng.md","hash":"90f951d3c098aa1b062131b7489a4b7afab06792","modified":1741621615535},{"_id":"source/_posts/zellij-helix.md","hash":"7eb3072b50ecdb49a20355e05fc613a4a8b7fbdf","modified":1740326024966},{"_id":"source/_posts/梦开始的地方.md","hash":"c0bbfefdd277c8ae2786a5cd4cc05f731af11436","modified":1740457544687},{"_id":"source/_posts/原来我还有个博客.md","hash":"acd7e534b31b7a0173fae880fa584c7dc03fd2cd","modified":1740457594224},{"_id":"source/images/Written-By-Human-Not-By-AI-Badge-white@2x.png","hash":"994225c6fd72521b281144bdd98fefcca53e2c7b","modified":1731333731000},{"_id":"source/images/amd.webp","hash":"cb0cfd5da0b9c10b9b22c65bba881cfde485d763","modified":1731333907000},{"_id":"source/images/asc-events.png","hash":"a0b3610962062d0a322fe091bcc8f083ab13bc6e","modified":1740329910965},{"_id":"source/images/archlinux-logo.png","hash":"4f6075309fadcb7f7547164cb8a99b4949f74598","modified":1731333766000},{"_id":"source/images/asc.png","hash":"3a33b35bff1b3f527f7a87a9d6a7d6d9a072a946","modified":1740328627284},{"_id":"source/images/built_on_the_kde_platform.png","hash":"507b6a4323b23772800006505e6c588bb515ebf5","modified":1731334007000},{"_id":"source/images/phase1_strings.png","hash":"6fb457adddc5bb32aa463b6227542076c4b501e7","modified":1740382826998},{"_id":"source/images/rusendhelp1.png","hash":"73aa5b41d33c7f3392de28b7b696ebcb12248d82","modified":1763222994735},{"_id":"source/images/rusendhelp3.png","hash":"db656266b54c91e540f3c677b146c0931b61277e","modified":1763222231471},{"_id":"source/images/searxng.png","hash":"9514f2bd14ac1cafa437f3655d9382d477667094","modified":1741619983804},{"_id":"source/images/caseclosed.png","hash":"628b54b49c86a23af595a52daba9e0557b81a17a","modified":1740454957882},{"_id":"source/images/resendofficial.png","hash":"41c2cafe3a11c1674a0a1ec27698ea94cecc9a23","modified":1763222843243},{"_id":"source/images/rusendhelp2.png","hash":"3752f3041d28cbadcb4abc26151f0d17f505908d","modified":1763223010905},{"_id":"source/images/audiveris.png","hash":"52aa9df5f93559c2e47b30afbf84fc6cc6b34002","modified":1744993135289},{"_id":"source/images/musecore-to-midi.png","hash":"02ba31b29ba069dfef0e1cf26c5ccec9dc3d2289","modified":1744993436860},{"_id":"source/images/1730728153314.png","hash":"95d44ea62557a311503cab58d1dec5cafc97e07d","modified":1740320866754},{"_id":"source/images/musecore.png","hash":"9b2eba87e7c93e57f2024697d1ff4217d206e5ac","modified":1744993264700},{"_id":"source/images/yana.jpg","hash":"757c68ec322e834eace7236c38e74a4f9585d474","modified":1752421174023},{"_id":"source/images/xb2score.png","hash":"03efbb07a7bb7f6c7d58b98ed125cafe42074b94","modified":1744992816374},{"_id":"source/images/phase1.png","hash":"42533712403be6036231f1e3770f125858bf91d7","modified":1740385026324},{"_id":"themes/fluid/source/css/_pages/_tag/tag.styl","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1740320867063},{"_id":"source/images/5364bba6d035326e82c53504dd53e7c2454454985.png","hash":"11b11f23ebebbe08e8cf826b48eb37b4084796cd","modified":1729572689000},{"_id":"themes/fluid/.gitignore","hash":"ae3bfcb89777657c5dfb5169d91445dcb0e5ab98","modified":1740320867051},{"_id":"themes/fluid/.gitattributes","hash":"a54f902957d49356376b59287b894b1a3d7a003f","modified":1740320867050},{"_id":"themes/fluid/.eslintrc","hash":"4bc2b19ce2b8c4d242f97d4ccf2d741e68ab0097","modified":1740320867050},{"_id":"themes/fluid/.editorconfig","hash":"33218fbd623feb43edf5f99f15965392cecc44a6","modified":1740320867050},{"_id":"themes/fluid/README.md","hash":"ff9b0e1fb9dba665af2f1e4a577f8cb9e840464b","modified":1740320867051},{"_id":"themes/fluid/_config.yml","hash":"07930dbb75bb019a325c1b4c8acdd36633a9625a","modified":1780477158270},{"_id":"themes/fluid/languages/de.yml","hash":"58dccef1d98b472dc4e6f4693c2297b0c9c5afba","modified":1740320867052},{"_id":"themes/fluid/LICENSE","hash":"26f9356fd6e84b5a88df6d9014378f41b65ba209","modified":1740320867051},{"_id":"themes/fluid/README_en.md","hash":"365184a73af40e7365504c3077f3d80dfee1d80e","modified":1740320867051},{"_id":"themes/fluid/package.json","hash":"7746460fc2eba7439b494c46aa9b5ded81370819","modified":1740320867057},{"_id":"themes/fluid/languages/en.yml","hash":"9c580471257f5a32bee701a059a45ea96755dcdc","modified":1740320867052},{"_id":"themes/fluid/languages/ja.yml","hash":"550b95d3614a64592f02666938d235e9f11e449e","modified":1740320867052},{"_id":"themes/fluid/languages/es.yml","hash":"026ddf1a49bf8ddfef6ed86ab4d6af143c1dd95f","modified":1740320867052},{"_id":"themes/fluid/languages/ru.yml","hash":"93818f8bf07195fb1ebffbb5210e531b0e3a6ec4","modified":1740320867052},{"_id":"themes/fluid/languages/zh-CN.yml","hash":"a60847136709bb95586a98d9d67b50390a8d2c96","modified":1740320867052},{"_id":"themes/fluid/layout/404.ejs","hash":"b84d575c7b7f778b4cb64e89ad3d0aed4a896820","modified":1740320867052},{"_id":"themes/fluid/languages/zh-HK.yml","hash":"51c2b4d64c6992a39bfd2586a1bdf5fbbbdf0175","modified":1740320867052},{"_id":"themes/fluid/languages/zh-TW.yml","hash":"e1043de394f6dcf5c0647adcfdefe60637f78426","modified":1740320867052},{"_id":"themes/fluid/languages/eo.yml","hash":"7c1a0c9f6186b6643b19d3980f055329bdb4efa4","modified":1740320867052},{"_id":"themes/fluid/layout/about.ejs","hash":"052e9fc19c753f53fdc083c7fb098e3668880140","modified":1740320867056},{"_id":"themes/fluid/layout/archive.ejs","hash":"7c1f44005849791feae4abaa10fae4cb983d3277","modified":1740320867056},{"_id":"themes/fluid/layout/categories.ejs","hash":"13859726c27b6c79b5876ec174176d0f9c1ee164","modified":1740320867056},{"_id":"themes/fluid/layout/layout.ejs","hash":"7e0023474128fbe4d68c467704c41f1712432415","modified":1740320867056},{"_id":"themes/fluid/layout/links.ejs","hash":"1cac32ec4579aaf7b9fa39d317497331d4c5e1dd","modified":1740320867056},{"_id":"themes/fluid/layout/index.ejs","hash":"33c3317cdcee062789de2336dd8d0cc7f86d3650","modified":1740320867056},{"_id":"themes/fluid/layout/page.ejs","hash":"ed5007a3feb8f14d3d2843271bfb298eb0c56219","modified":1740320867056},{"_id":"themes/fluid/layout/post.ejs","hash":"9bf0d357a607a282f3b9cb04525a4df0cc2a8b76","modified":1740320867056},{"_id":"themes/fluid/layout/tag.ejs","hash":"9d686364c4d16a1a9219471623af452035c5b966","modified":1740320867056},{"_id":"themes/fluid/layout/tags.ejs","hash":"1d06af34b6cf1d8a20d2eb565e309326ceba309f","modified":1740320867057},{"_id":"themes/fluid/.github/ISSUE_TEMPLATE/bug_report.md","hash":"554c0d0e086a0784d83ee71c83f8bceeb60aecc8","modified":1740320867050},{"_id":"themes/fluid/.github/ISSUE_TEMPLATE/bug_report_zh.md","hash":"c8b0d49c49e3c88872fd3b37909345ff5b2b6aa0","modified":1740320867051},{"_id":"themes/fluid/.github/ISSUE_TEMPLATE/feature_request.md","hash":"c134dd57ffd269b93402ccfffe7dbe0f0b583bec","modified":1740320867051},{"_id":"themes/fluid/.github/ISSUE_TEMPLATE/feature_request_zh.md","hash":"ed08574b196447376dd74411cca664ac9227a5d4","modified":1740320867051},{"_id":"themes/fluid/.github/ISSUE_TEMPLATE/question.md","hash":"ab5eab9e3ff889c4ba7fd82846e7f5b7ae15bebc","modified":1740320867051},{"_id":"themes/fluid/scripts/events/index.js","hash":"79de5a379b28cad759a49048351c7f6b8915bd7d","modified":1740320867057},{"_id":"themes/fluid/.github/ISSUE_TEMPLATE/question_zh.md","hash":"fff07ce0472afc368d388637cb9d438195da9b5b","modified":1740320867051},{"_id":"themes/fluid/scripts/filters/default-injects.js","hash":"b2013ae8e189cd07ebc8a2ff48a78e153345210f","modified":1740320867057},{"_id":"themes/fluid/scripts/filters/locals.js","hash":"58d0fec976f6b1d35e7ea03edc45414088acf05c","modified":1740320867057},{"_id":"themes/fluid/.github/workflows/limit.yaml","hash":"f8bd2edeb4424ee7a055b31583445d5d5dff91a4","modified":1740320867051},{"_id":"themes/fluid/.github/workflows/publish.yaml","hash":"6f02e6440d88629229556e3fd47d0280fe2240db","modified":1740320867051},{"_id":"themes/fluid/scripts/filters/post-filter.js","hash":"82bb06686158ebe160a631c79f156cd4fde35656","modified":1740320867057},{"_id":"themes/fluid/scripts/tags/button.js","hash":"3eb43a8cdea0a64576ad6b31b4df6c2bf5698d4c","modified":1740320867059},{"_id":"themes/fluid/scripts/tags/checkbox.js","hash":"6eaf53cf4bfc756a65bda18184cf8998a12c861d","modified":1740320867059},{"_id":"themes/fluid/scripts/tags/fold.js","hash":"73e4fd12ce3e47981479391ed354b7d9d3279f70","modified":1740320867059},{"_id":"themes/fluid/scripts/tags/group-image.js","hash":"4aeebb797026f1df25646a5d69f7fde79b1bcd26","modified":1740320867059},{"_id":"themes/fluid/layout/category.ejs","hash":"f099161b738a16a32253f42085b5444f902018ed","modified":1740320867056},{"_id":"themes/fluid/scripts/tags/label.js","hash":"f05a6d32cca79535b22907dc03edb9d3fa2d8176","modified":1740320867059},{"_id":"themes/fluid/scripts/tags/mermaid.js","hash":"75160561e1ef3603b6d2ad2938464ab1cb77fd38","modified":1740320867059},{"_id":"themes/fluid/scripts/tags/note.js","hash":"e3b456a079e5dc0032473b516c865b20f83d2c26","modified":1740320867059},{"_id":"themes/fluid/scripts/generators/local-search.js","hash":"9ac5ddad06e9b0e6015ce531430018182a4bc0fa","modified":1740320867058},{"_id":"themes/fluid/scripts/generators/pages.js","hash":"d3e75f53c59674d171309e50702954671f31f1a4","modified":1740320867058},{"_id":"themes/fluid/scripts/helpers/date.js","hash":"9bda6382f61b40a20c24af466fe10c8366ebb74c","modified":1740320867058},{"_id":"themes/fluid/scripts/helpers/engine.js","hash":"d3a231d106795ce99cb0bc77eb65f9ae44515933","modified":1740320867058},{"_id":"themes/fluid/scripts/helpers/export-config.js","hash":"8e67b522c47aa250860e3fe2c733f1f958a506c0","modified":1740320867058},{"_id":"themes/fluid/scripts/helpers/import.js","hash":"ca53e8dbf7d44cfd372cfa79ac60f35a7d5b0076","modified":1740320867058},{"_id":"themes/fluid/.github/workflows/cr.yaml","hash":"19a8a00f5ba9607d82265572fe1202b64a8b0822","modified":1740320867051},{"_id":"themes/fluid/scripts/helpers/page.js","hash":"4607607445233b3029ef20ed5e91de0da0a7f9c5","modified":1740320867058},{"_id":"themes/fluid/scripts/helpers/scope.js","hash":"d41d9d658fcb54964b388598e996747aadb85b0f","modified":1740320867059},{"_id":"themes/fluid/scripts/helpers/url.js","hash":"2a6a8288176d0e0f6ec008056bf2745a86e8943e","modified":1740320867059},{"_id":"themes/fluid/scripts/helpers/utils.js","hash":"966689d7c5e4320008285395fbaa2751f6209be5","modified":1740320867059},{"_id":"themes/fluid/scripts/helpers/wordcount.js","hash":"4d48c424e47ff9a17a563167ea5f480890267adf","modified":1740320867059},{"_id":"themes/fluid/scripts/utils/compare-versions.js","hash":"dbbc928c914fc2bd242cd66aa0c45971aec13a5d","modified":1740320867059},{"_id":"themes/fluid/scripts/utils/crypto.js","hash":"ae4ad8a188ef5b3fa6818b01629fc962b3de8551","modified":1740320867060},{"_id":"themes/fluid/scripts/utils/object.js","hash":"33b57e4decdc5e75c518859f168c8ba80b2c665b","modified":1740320867060},{"_id":"themes/fluid/scripts/generators/index-generator.js","hash":"9159fc22fa84a7b605dd15fe4104f01fe9c71147","modified":1740320867057},{"_id":"themes/fluid/scripts/utils/resolve.js","hash":"8c4a8b62aa8608f12f1e9046231dff04859dc3e9","modified":1740320867060},{"_id":"themes/fluid/scripts/utils/url-join.js","hash":"718aab5e7b2059a06b093ca738de420d9afa44ba","modified":1740320867060},{"_id":"themes/fluid/scripts/helpers/injects.js","hash":"1ad2ae6b11bd8806ee7dd6eb7140d8b54a95d613","modified":1740320867058},{"_id":"themes/fluid/layout/_partials/archive-list.ejs","hash":"7520fbf91f762207c2ab06b2c293235cd5b23905","modified":1740320867052},{"_id":"themes/fluid/layout/_partials/category-chains.ejs","hash":"18309584aab83bc4deb20723ebad832149dd2e24","modified":1740320867053},{"_id":"themes/fluid/layout/_partials/category-list.ejs","hash":"f8d2f1907450e61968e6d54443e9be8138196a77","modified":1740320867053},{"_id":"themes/fluid/layout/_partials/comments.ejs","hash":"d707c47b2638c94e489bc43d4cfd098b7c58447f","modified":1740320867053},{"_id":"themes/fluid/layout/_partials/css.ejs","hash":"1dadb118d580280524ed0a5f69bd34d234a92276","modified":1740320867054},{"_id":"themes/fluid/layout/_partials/footer.ejs","hash":"5ce5e2db222fddefb0b6c2046c628d987f994f33","modified":1780145943541},{"_id":"themes/fluid/layout/_partials/head.ejs","hash":"67be642f99482c07904474f410cfbc2f99003288","modified":1740320867054},{"_id":"themes/fluid/layout/_partials/header.ejs","hash":"0d5e397d30051e5fbabe7b47cfd1f1e6a5820af1","modified":1740320867054},{"_id":"themes/fluid/layout/_partials/markdown-plugins.ejs","hash":"fc4bdf7de0cf1a66d0e5e4fba1b31d6f7ed49468","modified":1740320867054},{"_id":"themes/fluid/layout/_partials/paginator.ejs","hash":"0f38a2c238169edcb63fc46c23bfc529ff3859b7","modified":1740320867054},{"_id":"themes/fluid/layout/_partials/scripts.ejs","hash":"da5810785105e5075861593c7ac22c7aa9665a72","modified":1740320867056},{"_id":"themes/fluid/layout/_partials/search.ejs","hash":"70e1c929e084ca8a2648cedabf29b372511ea2b8","modified":1740320867056},{"_id":"themes/fluid/source/css/highlight-dark.styl","hash":"45695ef75c31a4aa57324dd408b7e2327a337018","modified":1740320867063},{"_id":"themes/fluid/source/css/gitalk.css","hash":"a57b3cc8e04a0a4a27aefa07facf5b5e7bca0e76","modified":1740320867063},{"_id":"themes/fluid/source/css/highlight.styl","hash":"a9efc52a646a9e585439c768557e3e3c9e3326dc","modified":1740320867063},{"_id":"themes/fluid/source/js/boot.js","hash":"38bd26c6b7acdafda86dda3560e6a3ca488d3c76","modified":1740320867086},{"_id":"themes/fluid/source/js/leancloud.js","hash":"eff77c7a5c399fcaefda48884980571e15243fc9","modified":1740320867086},{"_id":"themes/fluid/source/js/events.js","hash":"6869811f67e4c3de3edfa4b08464bb242b97a402","modified":1740320867086},{"_id":"themes/fluid/source/css/main.styl","hash":"855ae5fe229c51afa57f7645f6997a27a705d7e4","modified":1740320867063},{"_id":"themes/fluid/source/js/local-search.js","hash":"b9945f76f8682f3ec32edfb285b26eb559f7b7e8","modified":1740320867086},{"_id":"themes/fluid/source/js/plugins.js","hash":"c34916291e392a774ff3e85c55badb83e8661297","modified":1740320867087},{"_id":"themes/fluid/source/js/umami-view.js","hash":"33c4b3883fa747604074ad3921606eeeaeb50716","modified":1740320867087},{"_id":"themes/fluid/source/js/utils.js","hash":"b82e7c289a66dfd36064470fd41c0e96fc598b43","modified":1740320867087},{"_id":"themes/fluid/source/img/avatar.png","hash":"fe739a158cc128f70f780eb5fa96f388b81d478f","modified":1740320867063},{"_id":"themes/fluid/source/img/loading.gif","hash":"2d2fc0f947940f98c21afafef39ecf226a2e8d55","modified":1740320867086},{"_id":"themes/fluid/source/img/police_beian.png","hash":"90efded6baa2dde599a9d6b1387973e8e64923ea","modified":1740320867086},{"_id":"themes/fluid/source/js/color-schema.js","hash":"1ef88c881b9f942deadde3d890387b94c617342a","modified":1740320867086},{"_id":"themes/fluid/scripts/events/lib/compatible-configs.js","hash":"ef474d1fa5bbafc52619ced0f9dc7eaf2affb363","modified":1740320867057},{"_id":"themes/fluid/scripts/events/lib/highlight.js","hash":"a5fe1deccb73b5f578797dbb11038efc15f63ce8","modified":1740320867057},{"_id":"themes/fluid/source/xml/local-search.xml","hash":"8c96ba6a064705602ce28d096fd7dd9069630a55","modified":1740320867087},{"_id":"themes/fluid/source/js/img-lazyload.js","hash":"cbdeca434ec4da51f488c821d51b4d23c73294af","modified":1740320867086},{"_id":"themes/fluid/scripts/events/lib/injects.js","hash":"5ae4b07204683e54b5a1b74e931702bbce2ac23e","modified":1740320867057},{"_id":"themes/fluid/scripts/events/lib/hello.js","hash":"bd8376e1cf7892dc2daa58f2f443574be559fdbf","modified":1740320867057},{"_id":"themes/fluid/scripts/events/lib/lazyload.js","hash":"9ba0d4bc224e22af8a5a48d6ff13e5a0fcfee2a4","modified":1740320867057},{"_id":"themes/fluid/scripts/events/lib/merge-configs.js","hash":"7c944c43b2ece5dd84859bd9d1fe955d13427387","modified":1740320867057},{"_id":"themes/fluid/scripts/events/lib/footnote.js","hash":"c19ac8050b82c3676b0332a56099ccfcc36d9d52","modified":1740320867057},{"_id":"themes/fluid/layout/_partials/comments/cusdis.ejs","hash":"5f9dc012be27040bbe874d0c093c0d53958cc987","modified":1740320867053},{"_id":"themes/fluid/layout/_partials/comments/discuss.ejs","hash":"98d065b58ce06b7d18bff3c974e96fa0f34ae03a","modified":1740320867053},{"_id":"themes/fluid/layout/_partials/comments/disqus.ejs","hash":"aab4a4d24c55231a37db308ae94414319cecdd9b","modified":1740320867053},{"_id":"themes/fluid/layout/_partials/comments/giscus.ejs","hash":"95f8b866b158eff9352c381c243b332a155a5110","modified":1740320867053},{"_id":"themes/fluid/layout/_partials/comments/livere.ejs","hash":"2264758fed57542a7389c7aa9f00f1aefa17eb87","modified":1740320867053},{"_id":"themes/fluid/layout/_partials/comments/gitalk.ejs","hash":"843bc141a4545eb20d1c92fb63c85d459b4271ec","modified":1740320867053},{"_id":"themes/fluid/layout/_partials/comments/remark42.ejs","hash":"d4e9532feeb02aed61bd15eda536b5b631454dac","modified":1740320867053},{"_id":"themes/fluid/layout/_partials/comments/twikoo.ejs","hash":"d84bcb5ccd78470a60c067fc914ac0ac67ac8777","modified":1740320867053},{"_id":"themes/fluid/layout/_partials/comments/valine.ejs","hash":"19ba937553dddd317f827d682661a1066a7b1f30","modified":1740320867054},{"_id":"themes/fluid/layout/_partials/comments/utterances.ejs","hash":"c7ccf7f28308334a6da6f5425b141a24b5eca0e2","modified":1740320867054},{"_id":"themes/fluid/layout/_partials/comments/waline.ejs","hash":"3d08c73b77e412d2f06a24d9344565fc7dbc76f8","modified":1740320867054},{"_id":"themes/fluid/layout/_partials/header/banner.ejs","hash":"e07757b59e7b89eea213d0e595cb5932f812fd32","modified":1740320867054},{"_id":"themes/fluid/layout/_partials/header/navigation.ejs","hash":"37d750428772d7c71ba36ce0c2540780d90fadea","modified":1740320867054},{"_id":"themes/fluid/layout/_partials/plugins/analytics.ejs","hash":"e6dcbf1c2f56314d56bb46b50aca86ff68cacebd","modified":1740320867055},{"_id":"themes/fluid/layout/_partials/plugins/anchorjs.ejs","hash":"40181442d3a2b8734783a0ad7caf2d2522e3f2ab","modified":1740320867055},{"_id":"themes/fluid/layout/_partials/plugins/code-widget.ejs","hash":"3a505cba37942badf62a56bbb8b605b72af330aa","modified":1740320867055},{"_id":"themes/fluid/layout/_partials/plugins/encrypt.ejs","hash":"0fff24cf5bf99fbe5c56c292e2eac4a89bf29db4","modified":1740320867055},{"_id":"themes/fluid/layout/_partials/comments/changyan.ejs","hash":"c9b2d68ed3d375f1953e7007307d2a3f75ed6249","modified":1740320867053},{"_id":"themes/fluid/layout/_partials/plugins/fancybox.ejs","hash":"9d1ea2a46b8c8ad8c168594d578f40764818ef13","modified":1740320867055},{"_id":"themes/fluid/layout/_partials/plugins/highlight.ejs","hash":"7529dd215b09d3557804333942377b9e20fa554e","modified":1740320867055},{"_id":"themes/fluid/layout/_partials/plugins/math.ejs","hash":"dcbf9a381ee76f2f1f75fcbc22c50a502ec85023","modified":1740320867055},{"_id":"themes/fluid/layout/_partials/plugins/mermaid.ejs","hash":"03ac02762f801970d1c4e73d6ec8d4c503780e50","modified":1740320867055},{"_id":"themes/fluid/layout/_partials/plugins/moment.ejs","hash":"4ff3fb1b60ccc95a0af3bbdbd0757fedefc088b5","modified":1740320867055},{"_id":"themes/fluid/layout/_partials/plugins/nprogress.ejs","hash":"4c2d39ce816b8a6dcd6b53113c8695f8bd650a23","modified":1740320867055},{"_id":"themes/fluid/layout/_partials/plugins/typed.ejs","hash":"f345374885cd6a334f09a11f59c443b5d577c06c","modified":1740320867055},{"_id":"themes/fluid/layout/_partials/post/copyright.ejs","hash":"cbfa32c5f5973133afd043853b24f8200455cb2d","modified":1740320867055},{"_id":"themes/fluid/layout/_partials/post/meta-bottom.ejs","hash":"375974ec017696e294dc12469fb0ae257800dc2d","modified":1740320867055},{"_id":"themes/fluid/layout/_partials/post/meta-top.ejs","hash":"54dd479dbb440126e4ddd9d902229db5afaaae98","modified":1740320867055},{"_id":"themes/fluid/layout/_partials/post/sidebar-left.ejs","hash":"9992c99b3eb728ad195970e1b84d665f2c8691c4","modified":1740320867056},{"_id":"themes/fluid/layout/_partials/post/sidebar-right.ejs","hash":"d5fcc9b60e02f869a29a8c17a16a6028ecc1e6d8","modified":1740320867056},{"_id":"themes/fluid/layout/_partials/post/toc.ejs","hash":"635a89060fbf72eeda066fc4bd0a97462f069417","modified":1740320867056},{"_id":"themes/fluid/source/css/_functions/base.styl","hash":"2e46f3f4e2c9fe34c1ff1c598738fc7349ae8188","modified":1740320867060},{"_id":"themes/fluid/source/css/_mixins/base.styl","hash":"542e306ee9494e8a78e44d6d7d409605d94caeb3","modified":1740320867060},{"_id":"themes/fluid/layout/_partials/post/category-bar.ejs","hash":"8772bce97ed297e7a88523f4e939ed6436c22f87","modified":1740320867055},{"_id":"themes/fluid/source/css/_pages/pages.styl","hash":"b8e887bc7fb3b765a1f8ec9448eff8603a41984f","modified":1740320867063},{"_id":"themes/fluid/layout/_partials/footer/beian.ejs","hash":"4fb9b5dd3f3e41a586d6af44e5069afe7c81fff2","modified":1740320867054},{"_id":"themes/fluid/layout/_partials/footer/statistics.ejs","hash":"954a29b58d72647d20450da270b5d8fb2e0824f5","modified":1740320867054},{"_id":"themes/fluid/source/css/_variables/base.styl","hash":"4ed5f0ae105ef4c7dd92eaf652ceda176c38e502","modified":1740320867063},{"_id":"themes/fluid/source/css/_pages/_category/category-chain.styl","hash":"0cdf7ef50dfd0669d3b257821384ff31cd81b7c9","modified":1740320867062},{"_id":"themes/fluid/source/css/_pages/_archive/archive.styl","hash":"c475e6681546d30350eaed11f23081ecae80c375","modified":1740320867060},{"_id":"themes/fluid/source/css/_pages/_category/category-list.styl","hash":"7edfe1b571ecca7d08f5f4dbcf76f4ffdcfbf0b5","modified":1740320867062},{"_id":"themes/fluid/source/css/_pages/_base/base.styl","hash":"643284c567665f96915f0b64e59934dda315f74d","modified":1740320867061},{"_id":"themes/fluid/source/css/_pages/_base/color-schema.styl","hash":"85492ef64d7e5f70f0f7e46d570bbc911e686d7e","modified":1740320867061},{"_id":"themes/fluid/source/css/_pages/_base/inline.styl","hash":"411a3fa3f924a87e00ff04d18b5c83283b049a4d","modified":1740320867061},{"_id":"themes/fluid/source/css/_pages/_base/keyframes.styl","hash":"94065ea50f5bef7566d184f2422f6ac20866ba22","modified":1740320867062},{"_id":"themes/fluid/source/css/_pages/_index/index.styl","hash":"25fb6fa4c783b847c632584c49a7e1593cdb2f5d","modified":1740320867062},{"_id":"themes/fluid/source/css/_pages/_base/print.styl","hash":"166afbc596ea4b552bad7290ec372d25ec34db7b","modified":1740320867062},{"_id":"themes/fluid/source/css/_pages/_links/links.styl","hash":"5c7f2044e3f1da05a3229537c06bd879836f8d6e","modified":1740320867062},{"_id":"themes/fluid/source/css/_pages/_post/highlight.styl","hash":"4df764d298fe556e501db4afc2b05686fe6ebcfb","modified":1740320867062},{"_id":"themes/fluid/source/css/_pages/_post/comment.styl","hash":"780f3788e7357bcd3f3262d781cb91bb53976a93","modified":1740320867062},{"_id":"themes/fluid/source/css/_pages/_post/post-page.styl","hash":"7eee3f78296a3c81849a5415d1d43dcc6e03e6aa","modified":1740320867062},{"_id":"themes/fluid/source/css/_pages/_category/category-bar.styl","hash":"cc6df43fef6bb3efecbfdd8b9e467424a1dea581","modified":1740320867062},{"_id":"themes/fluid/source/css/_pages/_post/post-tag.styl","hash":"c96d36aa8fe20f0c3c1a29ee2473cd8064b10f73","modified":1740320867062},{"_id":"themes/fluid/source/css/_pages/_tag/tags.styl","hash":"65bfc01c76abc927fa1a23bf2422892b0d566c3f","modified":1740320867063},{"_id":"themes/fluid/source/css/_pages/_base/_widget/anchorjs.styl","hash":"e0cebda4a6f499aff75e71417d88caa7ceb13b94","modified":1740320867060},{"_id":"themes/fluid/source/css/_pages/_base/_widget/board.styl","hash":"4397037fc3f0033dbe546c33cd9dbdabd8cb1632","modified":1740320867060},{"_id":"themes/fluid/source/css/_pages/_post/markdown.styl","hash":"1e3d3a82721e7c10bcfcecec6d81cf2979039452","modified":1740320867062},{"_id":"themes/fluid/source/css/_pages/_base/_widget/code-widget.styl","hash":"b66ab013f0f37d724a149b85b3c7432afcf460ad","modified":1740320867060},{"_id":"themes/fluid/source/css/_pages/_base/_widget/copyright.styl","hash":"26f71a9cd60d96bb0cb5bbdf58150b8e524d9707","modified":1740320867061},{"_id":"themes/fluid/source/css/_pages/_base/_widget/footer.styl","hash":"2caaca71dd1ff63d583099ed817677dd267b457e","modified":1740320867061},{"_id":"themes/fluid/source/css/_pages/_base/_widget/footnote.styl","hash":"ae9289cc89649af2042907f8a003303b987f3404","modified":1740320867061},{"_id":"themes/fluid/source/css/_pages/_base/_widget/header.styl","hash":"d42b748f2f49ef32aafb1a21d75991d2459da927","modified":1740320867061},{"_id":"themes/fluid/source/css/_pages/_base/_widget/modal.styl","hash":"adf6c1e5c8e1fb41c77ce6e2258001df61245aa2","modified":1740320867061},{"_id":"themes/fluid/source/css/_pages/_base/_widget/ngrogress.styl","hash":"5d225357b4a58d46118e6616377168336ed44cb2","modified":1740320867061},{"_id":"themes/fluid/source/css/_pages/_base/_widget/noscript.styl","hash":"0cf2f2bb44f456150d428016675d5876a9d2e2aa","modified":1740320867061},{"_id":"themes/fluid/source/css/_pages/_base/_widget/banner.styl","hash":"7a0bd629bc234fc75e3cc8e3715ffada92f09e73","modified":1740320867060},{"_id":"themes/fluid/source/css/_pages/_base/_widget/qrcode.styl","hash":"78704a94c0436097abfb0e0a57abeb3429c749b7","modified":1740320867061},{"_id":"themes/fluid/source/css/_pages/_base/_widget/scroll-btn.styl","hash":"f0e429a27fa8a7658fcbddbb4d4dbe4afa12499a","modified":1740320867061},{"_id":"themes/fluid/source/css/_pages/_base/_widget/search.styl","hash":"10f7e91a91e681fb9fe46f9df7707b9ef78707c8","modified":1740320867061},{"_id":"themes/fluid/source/css/_pages/_base/_widget/pagination.styl","hash":"8bb1b68e5f3552cb48c2ffa31edbc53646a8fb4c","modified":1740320867061},{"_id":"source/images/1613f5602b203b38230f19699deb0219454454985.png","hash":"3d71204d9a0731384f97ad0f90802a5c9fd96776","modified":1729572689000},{"_id":"themes/fluid/source/css/_pages/_about/about.styl","hash":"97fe42516ea531fdad771489b68aa8b2a7f6ae46","modified":1740320867060},{"_id":"themes/fluid/source/css/_pages/_base/_widget/toc.styl","hash":"9e7452aa2372153f25d7a4675c9d36d281a65d24","modified":1740320867061},{"_id":"themes/fluid/source/img/hifuu.png","hash":"331b5950baf96f5d39192bb42b7da4d22a08992e","modified":1740320867086},{"_id":"source/images/mhwilds4050.jpg","hash":"9b2b15c66e2340c15b86cec0fa8f818b67e06384","modified":1730738910727},{"_id":"source/images/71a5357ef4bd808b10429bc2ea46cb6f454454985.png","hash":"dddf72bb12aef01baa3eb2017bb867f274059f12","modified":1729572689000},{"_id":"themes/fluid/source/img/gh0s7.jpg","hash":"1c5af4f8cacdc3c6adbe8334866774cca2944910","modified":1740320867085},{"_id":"source/images/mhwilds780M.jpg","hash":"e2c59c6caae452afb424d9f2fc3a255a44121b49","modified":1730738926927},{"_id":"source/images/clonezilla.png","hash":"7f443d79783df3744b2e51613422ee8fd7eea6f8","modified":1740318117703},{"_id":"source/images/phase_2_14.png","hash":"f50399a9eb469ed1e063ff4ccc0e8636c1b75f1e","modified":1740407245723},{"_id":"source/images/phase_4.png","hash":"81cd309a8990e015a8679b6a4e26a27d804c1da6","modified":1740410093173},{"_id":"source/images/20241027_222225.png","hash":"f0595d1315d94e72e61c69e0499e5129e1821ba1","modified":1740320866763},{"_id":"source/images/b2054bbaf6197624d38cc2007d885fd1454454985.png","hash":"b021e971cedaa55b273eab2b10be94f795b06922","modified":1729572689000},{"_id":"source/images/lmms-import.png","hash":"002535b55a41f7a7dcba24517dd2397589416a64","modified":1744993882632},{"_id":"source/music/過ぎ去りし温もりの日々.mp3","hash":"22016dcdfcbac9393afd3fe97f2d6d6009cd57f5","modified":1747827275971},{"_id":"source/images/krita-official.png","hash":"2ec810a739ce33ad5666093b8c36d29028a17c17","modified":1752420936455},{"_id":"source/images/musecore-launch.png","hash":"0a6c5642d05b197555135cd602886311a9122df0","modified":1744993224514},{"_id":"source/images/zellij-helix.jpg","hash":"87fb5b7b51a4e3b062b891c04488a53457edded0","modified":1738492100321},{"_id":"themes/fluid/source/img/gensokyo.jpg","hash":"d82c0d5011c6cbc37109c9c80530d14cd266286f","modified":1740320867082},{"_id":"public/local-search.xml","hash":"19de23d342ec4c963f7391c94c47a11b6a11d849","modified":1781437412800},{"_id":"public/about/index.html","hash":"3f33512f7bdc485b691a308f058fe7fcd4a6790d","modified":1780145953257},{"_id":"public/2026/01/18/os-optimization/index.html","hash":"6c7d4bd4804011f2c550ba043709c65a34c86062","modified":1780145953257},{"_id":"public/2025/11/15/Your-exclusive-email-address/index.html","hash":"59e45e87865d35b3fbdd8de5d7eee0983dd42b4c","modified":1780145953257},{"_id":"public/2025/05/23/gcc15restrict/index.html","hash":"3b75b8d496dc65104967e99595128759ee9e7a58","modified":1780145953257},{"_id":"public/2025/05/21/2025-5-21/index.html","hash":"78b6802d57e96437884278ac8e061a6a6e351ab6","modified":1780145953257},{"_id":"public/2025/05/22/fortune/index.html","hash":"54bc741897a4a2c5876b36212d13e1e05fcbb0fe","modified":1780145953257},{"_id":"public/2025/03/10/archlinux-game-fix/index.html","hash":"ab4e5eec7dc170f9d3514b54ecbd3adc76cf6389","modified":1780145953257},{"_id":"public/2025/02/24/nudtbomblab/index.html","hash":"28d2399ccdcf34a6636941177f577d2236a6a5d9","modified":1780145953257},{"_id":"public/2025/02/23/clonezilla/index.html","hash":"ec864f014d792f69ab51865b79e7248d0a38dc4e","modified":1780145953257},{"_id":"public/2025/02/02/archlinux-optimization/index.html","hash":"e711a9b97eac731d70cda36dc1987a87b3b20997","modified":1780145953257},{"_id":"public/2024/12/13/zellij-helix/index.html","hash":"64109093fff3ae458959461398862162cd7a667e","modified":1780145953257},{"_id":"public/2025/03/10/searxng/index.html","hash":"40b8c71ee8e6fe2b01690751f8b3f3914340489d","modified":1780145953257},{"_id":"public/2024/11/11/mhwi/index.html","hash":"1a77bed044ec3476d511308c95a675317bd56b09","modified":1780145953257},{"_id":"public/2025/07/13/kritabest/index.html","hash":"76ec155858ce1a0c43626fce605773cb067de4e7","modified":1780145953257},{"_id":"public/2024/11/06/arch-nvidia/index.html","hash":"b2978cb63d1d61a84c7ce32b13e72836901acbee","modified":1780145953257},{"_id":"public/2024/11/04/loopers/index.html","hash":"85f3eb2533717fb1bd54c3342ea448bcb07e22bf","modified":1780145953257},{"_id":"public/2024/11/04/Vocaloid调教-晴天/index.html","hash":"f5fd4304cc949143f3d9580ecc5e3fc3f1083ddf","modified":1780145953257},{"_id":"public/2024/10/30/GentleJena/index.html","hash":"d860e81fed041da34abed3d80b493fc9d0586459","modified":1780145953257},{"_id":"public/2024/06/03/原来我还有个博客/index.html","hash":"ee9ea63e48ddcbb16bc0a850040f3cc84061dad0","modified":1780145953257},{"_id":"public/2023/12/31/12月31日进度报告/index.html","hash":"6921861280e49fd4f49924f518ed938dd6a4781b","modified":1780145953257},{"_id":"public/2024/11/04/BlogUpdate/index.html","hash":"b54788782f49a6303858d4284206b04c5397e700","modified":1780145953257},{"_id":"public/2023/12/28/12月28日进度报告/index.html","hash":"fe7404cb9c3555681aae5036c993d418443253b5","modified":1780145953257},{"_id":"public/2024/11/06/overleaf/index.html","hash":"05203c52b560b6b05eec48e6df9ff86174dc07e1","modified":1780145953257},{"_id":"public/2023/12/20/12月20日工作记录/index.html","hash":"7c41f9c917d771cfe85dabc988c52883e94d7fd4","modified":1780145953257},{"_id":"public/2023/12/18/12月18日工作记录/index.html","hash":"5defbfe46099e2de79492f25b98602b5d3ce0766","modified":1780145953257},{"_id":"public/2023/12/19/12月19日工作记录/index.html","hash":"e4d6d19704a226e14fec06f27dffc411dd3f3942","modified":1780145953257},{"_id":"public/2023/12/06/CGH0S7-s-Blog/index.html","hash":"e1cb6cdb999812954b41602e76a9eba789624d28","modified":1780145953257},{"_id":"public/archives/index.html","hash":"2c50ad8d00dd72fc494f5998b4d7192eb0d461ad","modified":1781437134878},{"_id":"public/2023/12/17/12月16-17日工作记录/index.html","hash":"8a061b781c0f6bedd792e54d88c24a6a4700c637","modified":1780145953257},{"_id":"public/archives/page/2/index.html","hash":"6f45e210c093c5e5d35b7975b47c7f37cd097f9c","modified":1781436888887},{"_id":"public/2023/12/06/梦开始的地方/index.html","hash":"75fb1c5e93eabb1fd211f3acc9884590cb639296","modified":1780145953257},{"_id":"public/archives/2023/index.html","hash":"f9a2f9876559d7c8dae60fe650eab73c7cb21f23","modified":1781436888887},{"_id":"public/archives/2024/index.html","hash":"36683b9a5b606cf18455d80d3b0afc12941c252b","modified":1781436888887},{"_id":"public/archives/2024/06/index.html","hash":"5f5c4ad6ba63c73cef4f617cbd58b999da70673c","modified":1781436888887},{"_id":"public/archives/2024/10/index.html","hash":"94201c9089112c20e1c5f46721734e8946fd00b7","modified":1781436888887},{"_id":"public/archives/2024/11/index.html","hash":"9476602510637d6be170e49a1799adf4f6ffcd4f","modified":1781436888887},{"_id":"public/archives/2024/12/index.html","hash":"00487f7a723efd8cedc388f30f3d2b70f65475d8","modified":1781436888887},{"_id":"public/archives/2025/index.html","hash":"58ec7fe43458af787e83ff9e0e51421334eb7c51","modified":1781436888887},{"_id":"public/archives/2025/02/index.html","hash":"59c7321d3aad35a172f5f48e2fe51555dc79451a","modified":1781436888887},{"_id":"public/archives/2025/03/index.html","hash":"5f37029eba539619502dc2b1bcf6015f2c0e21f7","modified":1781436888887},{"_id":"public/archives/2025/05/index.html","hash":"eefe0602c83602ef9960818a8ef6b353810d95b8","modified":1781436888887},{"_id":"public/archives/2025/07/index.html","hash":"fe5231a6d1c4342caf093e19261683c5fda1fa4d","modified":1781436888887},{"_id":"public/archives/2025/11/index.html","hash":"7e2296a59b9a580e9a814391263e96b878f254f4","modified":1781436888887},{"_id":"public/archives/page/3/index.html","hash":"6c461371711fb55d438e3780e17ffae5a44b8425","modified":1781436888887},{"_id":"public/archives/2026/01/index.html","hash":"e36c2e6ddbe893862cb8d5b3005a4f8fc299f761","modified":1781436888887},{"_id":"public/categories/更新/index.html","hash":"89a39307627a63ba9c70458f7f96881637d1d8a0","modified":1780145953257},{"_id":"public/archives/2026/index.html","hash":"a6139c05854606a221d7d38f4a09fdcd9c405378","modified":1781437134878},{"_id":"public/categories/Rearrangement/index.html","hash":"e6c79134b1cc341df50a47e311d51331c3560320","modified":1780145953257},{"_id":"public/archives/2023/12/index.html","hash":"18b5a24efb4b75b3c30ca9893d496e70a197ec1f","modified":1781436888887},{"_id":"public/categories/技术分享/index.html","hash":"6f7c92be6d201565fd95ea52315f01529bf3dff6","modified":1780145953257},{"_id":"public/index.html","hash":"536c7f310550e987adf27723579b86df844490bf","modified":1781437265396},{"_id":"public/categories/Vocaloid/index.html","hash":"d8c44239dce7a3b4ebf9c93b72b290ecb77f408a","modified":1780145953257},{"_id":"public/page/2/index.html","hash":"e4c5f6410bc19e946f28aae1c1980592798d843f","modified":1781436888887},{"_id":"public/categories/技术分享,HPC/index.html","hash":"ce33a0692bcceca143bcffcd76f81a5fb7042efc","modified":1780145953257},{"_id":"public/tags/日志/index.html","hash":"318a8d865f9b5dfe2bfea0c63d72177977fab546","modified":1780145953257},{"_id":"public/page/3/index.html","hash":"54761c4ee5249071feb693107f51a75d2134d115","modified":1781436888887},{"_id":"public/tags/TEST/index.html","hash":"8cc4f9fbda65fca9fdfd51f9ff17462dcb32d277","modified":1780145953257},{"_id":"public/tags/Vocaloid/index.html","hash":"cb6864ba7e6831810a16fa706e6950bc5d0017da","modified":1780145953257},{"_id":"public/tags/技术分享/index.html","hash":"17ef929e9bc26ce8edd1f4612340fc7a70724320","modified":1781437134878},{"_id":"public/tags/技术/index.html","hash":"64bc80a9df17675a4236cb6bd41abba2f3b176f6","modified":1780145953257},{"_id":"public/tags/生活/index.html","hash":"428d0d4b8da1af6358ae30868dabd2afbe667b95","modified":1780145953257},{"_id":"public/tags/Archlinux/index.html","hash":"47603ff5de854411462c033d25d9801b3fe83f58","modified":1781437134878},{"_id":"public/tags/系统优化/index.html","hash":"5d894caa5532b89df253e79f4f496b20ec5d2319","modified":1781437134878},{"_id":"public/tags/板绘/index.html","hash":"24458d6537863828950bc37fd543f98745d5764d","modified":1780145953257},{"_id":"public/tags/学习/index.html","hash":"ed3b6c41bfe973ba340a84814cd7abd8b60601a9","modified":1780145953257},{"_id":"public/tags/HPC/index.html","hash":"d209c326ad279d13e33a549581dd1fe94c563687","modified":1780145953257},{"_id":"public/tags/Linux内核/index.html","hash":"317a1b477d45e02672f40e49bcc942f6095bed1e","modified":1780145953257},{"_id":"public/tags/音乐/index.html","hash":"485567a47c4a712386deabdef85f0dfd139347e2","modified":1780145953257},{"_id":"public/404.html","hash":"6b88bf3c008f9d52de79ec09106134660fb7ae97","modified":1780145953257},{"_id":"public/tags/日志/page/2/index.html","hash":"bbbd12a3d7f9710927bc5bc9df233c0c8c05ef4f","modified":1780145953257},{"_id":"public/categories/index.html","hash":"abbc6670f98bfec3d077fe59441aab29f4e82df3","modified":1780145953257},{"_id":"public/tags/index.html","hash":"456246e212126ba6c5540dc10ce290ee4d8d35b8","modified":1781436888887},{"_id":"public/links/index.html","hash":"1c30876371c58566843dbf82e66da8451f3c989b","modified":1780145953257},{"_id":"public/tags/开源工具/index.html","hash":"280aa446d2a4ed4cb7493f25f8e72ce7a6c7e708","modified":1780145953257},{"_id":"public/images/Written-By-Human-Not-By-AI-Badge-white@2x.png","hash":"994225c6fd72521b281144bdd98fefcca53e2c7b","modified":1780070731809},{"_id":"public/images/archlinux-logo.png","hash":"4f6075309fadcb7f7547164cb8a99b4949f74598","modified":1780070731809},{"_id":"public/images/asc-events.png","hash":"a0b3610962062d0a322fe091bcc8f083ab13bc6e","modified":1780070731809},{"_id":"public/images/amd.webp","hash":"cb0cfd5da0b9c10b9b22c65bba881cfde485d763","modified":1780070731809},{"_id":"public/images/asc.png","hash":"3a33b35bff1b3f527f7a87a9d6a7d6d9a072a946","modified":1780070731809},{"_id":"public/img/avatar.png","hash":"fe739a158cc128f70f780eb5fa96f388b81d478f","modified":1780070731809},{"_id":"public/images/built_on_the_kde_platform.png","hash":"507b6a4323b23772800006505e6c588bb515ebf5","modified":1780070731809},{"_id":"public/img/police_beian.png","hash":"90efded6baa2dde599a9d6b1387973e8e64923ea","modified":1780070731809},{"_id":"public/img/loading.gif","hash":"2d2fc0f947940f98c21afafef39ecf226a2e8d55","modified":1780070731809},{"_id":"public/xml/local-search.xml","hash":"8c96ba6a064705602ce28d096fd7dd9069630a55","modified":1780070731809},{"_id":"public/images/phase1_strings.png","hash":"6fb457adddc5bb32aa463b6227542076c4b501e7","modified":1780070731809},{"_id":"public/img/hifuu.png","hash":"331b5950baf96f5d39192bb42b7da4d22a08992e","modified":1780070731809},{"_id":"public/css/highlight-dark.css","hash":"902294bada4323c0f51502d67cba8c3a0298952f","modified":1780070731809},{"_id":"public/js/boot.js","hash":"38bd26c6b7acdafda86dda3560e6a3ca488d3c76","modified":1780070731809},{"_id":"public/js/events.js","hash":"6869811f67e4c3de3edfa4b08464bb242b97a402","modified":1780070731809},{"_id":"public/css/highlight.css","hash":"04d4ddbb5e1d1007447c2fe293ee05aae9b9563e","modified":1780070731809},{"_id":"public/css/main.css","hash":"06433abcb688c2f5473623a9355bff9865c48d3f","modified":1780070731809},{"_id":"public/js/leancloud.js","hash":"eff77c7a5c399fcaefda48884980571e15243fc9","modified":1780070731809},{"_id":"public/js/plugins.js","hash":"c34916291e392a774ff3e85c55badb83e8661297","modified":1780070731809},{"_id":"public/js/umami-view.js","hash":"33c4b3883fa747604074ad3921606eeeaeb50716","modified":1780070731809},{"_id":"public/js/local-search.js","hash":"b9945f76f8682f3ec32edfb285b26eb559f7b7e8","modified":1780070731809},{"_id":"public/js/utils.js","hash":"b82e7c289a66dfd36064470fd41c0e96fc598b43","modified":1780070731809},{"_id":"public/js/color-schema.js","hash":"1ef88c881b9f942deadde3d890387b94c617342a","modified":1780070731809},{"_id":"public/js/img-lazyload.js","hash":"cbdeca434ec4da51f488c821d51b4d23c73294af","modified":1780070731809},{"_id":"public/css/gitalk.css","hash":"a57b3cc8e04a0a4a27aefa07facf5b5e7bca0e76","modified":1780070731809},{"_id":"public/images/rusendhelp1.png","hash":"73aa5b41d33c7f3392de28b7b696ebcb12248d82","modified":1780070731809},{"_id":"public/images/rusendhelp3.png","hash":"db656266b54c91e540f3c677b146c0931b61277e","modified":1780070731809},{"_id":"public/images/searxng.png","hash":"9514f2bd14ac1cafa437f3655d9382d477667094","modified":1780070731809},{"_id":"public/images/caseclosed.png","hash":"628b54b49c86a23af595a52daba9e0557b81a17a","modified":1780070731809},{"_id":"public/images/resendofficial.png","hash":"41c2cafe3a11c1674a0a1ec27698ea94cecc9a23","modified":1780070731809},{"_id":"public/images/rusendhelp2.png","hash":"3752f3041d28cbadcb4abc26151f0d17f505908d","modified":1780070731809},{"_id":"public/images/audiveris.png","hash":"52aa9df5f93559c2e47b30afbf84fc6cc6b34002","modified":1780070731809},{"_id":"public/images/musecore-to-midi.png","hash":"02ba31b29ba069dfef0e1cf26c5ccec9dc3d2289","modified":1780070731809},{"_id":"public/images/1730728153314.png","hash":"95d44ea62557a311503cab58d1dec5cafc97e07d","modified":1780070731809},{"_id":"public/images/yana.jpg","hash":"757c68ec322e834eace7236c38e74a4f9585d474","modified":1780070731809},{"_id":"public/images/musecore.png","hash":"9b2eba87e7c93e57f2024697d1ff4217d206e5ac","modified":1780070731809},{"_id":"public/img/gh0s7.jpg","hash":"1c5af4f8cacdc3c6adbe8334866774cca2944910","modified":1780070731809},{"_id":"public/images/xb2score.png","hash":"03efbb07a7bb7f6c7d58b98ed125cafe42074b94","modified":1780070731809},{"_id":"public/images/phase1.png","hash":"42533712403be6036231f1e3770f125858bf91d7","modified":1780070731809},{"_id":"public/images/5364bba6d035326e82c53504dd53e7c2454454985.png","hash":"11b11f23ebebbe08e8cf826b48eb37b4084796cd","modified":1780070731809},{"_id":"public/images/1613f5602b203b38230f19699deb0219454454985.png","hash":"3d71204d9a0731384f97ad0f90802a5c9fd96776","modified":1780070731809},{"_id":"public/images/71a5357ef4bd808b10429bc2ea46cb6f454454985.png","hash":"dddf72bb12aef01baa3eb2017bb867f274059f12","modified":1780070731809},{"_id":"public/images/mhwilds4050.jpg","hash":"9b2b15c66e2340c15b86cec0fa8f818b67e06384","modified":1780070731809},{"_id":"public/images/clonezilla.png","hash":"7f443d79783df3744b2e51613422ee8fd7eea6f8","modified":1780070731809},{"_id":"public/images/mhwilds780M.jpg","hash":"e2c59c6caae452afb424d9f2fc3a255a44121b49","modified":1780070731809},{"_id":"public/images/phase_4.png","hash":"81cd309a8990e015a8679b6a4e26a27d804c1da6","modified":1780070731809},{"_id":"public/images/phase_2_14.png","hash":"f50399a9eb469ed1e063ff4ccc0e8636c1b75f1e","modified":1780070731809},{"_id":"public/images/b2054bbaf6197624d38cc2007d885fd1454454985.png","hash":"b021e971cedaa55b273eab2b10be94f795b06922","modified":1780070731809},{"_id":"public/images/20241027_222225.png","hash":"f0595d1315d94e72e61c69e0499e5129e1821ba1","modified":1780070731809},{"_id":"public/images/lmms-import.png","hash":"002535b55a41f7a7dcba24517dd2397589416a64","modified":1780070731809},{"_id":"public/music/過ぎ去りし温もりの日々.mp3","hash":"22016dcdfcbac9393afd3fe97f2d6d6009cd57f5","modified":1780070731809},{"_id":"public/images/krita-official.png","hash":"2ec810a739ce33ad5666093b8c36d29028a17c17","modified":1780070731809},{"_id":"public/images/musecore-launch.png","hash":"0a6c5642d05b197555135cd602886311a9122df0","modified":1780070731809},{"_id":"public/images/zellij-helix.jpg","hash":"87fb5b7b51a4e3b062b891c04488a53457edded0","modified":1780070731809},{"_id":"public/img/gensokyo.jpg","hash":"d82c0d5011c6cbc37109c9c80530d14cd266286f","modified":1780070731809},{"_id":"public/about/index.md","hash":"ff7cd1481f3c669a04d7c8abf8851ab0cea5b2b9","modified":1780070731809},{"_id":"public/2023/12/18/12月18日工作记录/记录.md","hash":"9f5099a65180c2659f1a1ca218d518fc64965676","modified":1780070731809},{"_id":"public/2023/12/18/12月18日工作记录/d","hash":"a137c6edc4ea2ca1d7ea13d8dc2bd1bbb7df6d84","modified":1780070731809},{"_id":"public/2023/12/18/12月18日工作记录/md","hash":"598858976961c45ebbbf523014ee792951380dd3","modified":1780070731809},{"_id":"public/2023/12/18/12月18日工作记录/og.md","hash":"9a5c9461aba29f2447c0f6725f5425d5a05e32f0","modified":1780070731809},{"_id":"public/2023/12/18/12月18日工作记录/晴天.md","hash":"9141182f5dbfbbd840ad340b3e5271507ddd8c94","modified":1780070731809},{"_id":"public/2023/12/18/12月18日工作记录/ive-email-address.md","hash":"c1721f4ac1b359140b0b57658ddd39c64c041aa8","modified":1780070731809},{"_id":"public/2023/12/18/12月18日工作记录/.md","hash":"51b4410784657675d7c11e17d16c5dc0e5d4a455","modified":1780070731809},{"_id":"public/2023/12/18/12月18日工作记录/ame-fix.md","hash":"a4ab5bd2ca0cf941c5166d3945bd2a2076a54f1e","modified":1780070731809},{"_id":"public/2023/12/18/12月18日工作记录/ptimization.md","hash":"51c8c12110fb08a1edd1f727ab38a1b2568b690b","modified":1780070731809},{"_id":"public/2023/12/18/12月18日工作记录/ct.md","hash":"50533932fcba41826b8cf75f54b173f0c604136b","modified":1780070731809},{"_id":"public/2023/12/18/12月18日工作记录/tion.md","hash":"7c96f2f88fb750dd15617f875bdc28a58d978680","modified":1780145643919},{"_id":"public/2023/12/18/12月18日工作记录/x.md","hash":"7eb3072b50ecdb49a20355e05fc613a4a8b7fbdf","modified":1780070731809},{"_id":"source/_posts/One-Last-ASC.md","hash":"e850a0570d4e712f5bef2f39d88ca75d0f3e64cd","modified":1780454857305},{"_id":"public/2026/05/30/One-Last-ASC/index.html","hash":"1f1480c3eeee3a3d70c0f9cbf85977effc122a22","modified":1781437134878},{"_id":"public/archives/2026/05/index.html","hash":"951a2d2dfcc580d18655aa85e3fadcf057cae2b9","modified":1781436888887},{"_id":"public/tags/竞赛/index.html","hash":"889076e4fc7b84f1dc1c026aac38fc28bbfba5f7","modified":1780145953257},{"_id":"public/tags/ASC/index.html","hash":"1e77c50a5bb002a042badf3766321dc19708c75b","modified":1780145953257},{"_id":"source/images/amd.png","hash":"2c782df9acd11897c5cb262230d172279e634657","modified":1780145919500},{"_id":"public/images/amd.png","hash":"2c782df9acd11897c5cb262230d172279e634657","modified":1780145953257},{"_id":"source/images/MyASCStory-3.jpg","hash":"04fac29724c5bd41136531d107b2baa59a25dc48","modified":1779253336000},{"_id":"source/images/MyASCStory-1.jpg","hash":"7815e0f7c89688227c7520de9724406a9ab2bdef","modified":1780306582000},{"_id":"source/images/MyASCStory-2.jpg","hash":"f6eb79393b7d39429818302742023660199eb2af","modified":1780306548000},{"_id":"source/images/MyASCStory-4.jpg","hash":"d81c735d5a935b6e5f06de24cd25b2b1c32633fc","modified":1779066563000},{"_id":"public/images/MyASCStory-3.jpg","hash":"04fac29724c5bd41136531d107b2baa59a25dc48","modified":1780306767101},{"_id":"public/images/MyASCStory-1.jpg","hash":"7815e0f7c89688227c7520de9724406a9ab2bdef","modified":1780306767101},{"_id":"public/images/MyASCStory-2.jpg","hash":"f6eb79393b7d39429818302742023660199eb2af","modified":1780306767101},{"_id":"public/images/MyASCStory-4.jpg","hash":"d81c735d5a935b6e5f06de24cd25b2b1c32633fc","modified":1780306767101},{"_id":"source/images/MyASCStory/ASC24-2.jpg","hash":"e7fbd104e81d126d114fd1ebf4e810c721bf0c26","modified":1765630926680},{"_id":"source/images/MyASCStory/ASC24-1.jpg","hash":"780bc4106f3517f3d8a7ea3bfc46203b82e7c961","modified":1765630927130},{"_id":"source/images/MyASCStory/mmexport1712852383431.jpg","hash":"e6159d0f2bdc3f4fa355b5440024bbb763bfded9","modified":1765630926580},{"_id":"source/images/MyASCStory/ASC24-3.jpg","hash":"49116b67ce66255f2a9e5fc2ecdaea9880f5e767","modified":1780451828936},{"_id":"source/images/MyASCStory/mmexport1713023700275.jpg","hash":"8e6d378316ca24156eba819d5fd963484786684b","modified":1780452072895},{"_id":"source/images/MyASCStory/MyASCStory.jpg","hash":"7815e0f7c89688227c7520de9724406a9ab2bdef","modified":1780306582000},{"_id":"source/images/MyASCStory/lzd.jpg","hash":"42dd3b118023f82cb36d77ad77dc7f7f40d4103d","modified":1765630938320},{"_id":"source/images/MyASCStory/IMG_20250509_231809.jpg","hash":"5e36bd718ef8caf5425f181d57f2b9b5c97e5f99","modified":1765619211660},{"_id":"source/images/MyASCStory/ASC26-1.jpg","hash":"f6eb79393b7d39429818302742023660199eb2af","modified":1780306548000},{"_id":"source/images/MyASCStory/mmexport1713006631665.jpg","hash":"6a9964d579e6d196f921b655938f707a5592e798","modified":1765630926690},{"_id":"source/images/MyASCStory/IMG_20250509_202557.jpg","hash":"932191925ba50d510b4543ab3eb01c85ed76e823","modified":1765619247280},{"_id":"source/images/MyASCStory/ASC25-1.jpg","hash":"f92f3c1030c8d5b7bb1fcc913aea7949237dc8bd","modified":1765619245610},{"_id":"source/images/MyASCStory/IMG_20260516_230155.jpg","hash":"ed0bc33d58e430e9371adeac67e16b4973bd1418","modified":1778943718000},{"_id":"source/images/MyASCStory/ASC26-2.jpg","hash":"587340074e0536eec119ca2373ff78baaa17e818","modified":1779186419000},{"_id":"source/images/MyASCStory/IMG_20250512_101818.jpg","hash":"599b6a4786cca30fc8704b0817d145950da27e63","modified":1765619271450},{"_id":"source/images/MyASCStory/ASC26-3.jpg","hash":"d492692b3e1682567ad2bad0b0b2fc79c7d76199","modified":1779292791000},{"_id":"source/images/MyASCStory/IMG_20260518_131255.jpg","hash":"0f0ed96c6c887f809c6a464d4885f8bc81b94082","modified":1779081182000},{"_id":"source/images/MyASCStory/ASC25-3.jpg","hash":"140615d948984648181432dd50bf6d068b32f6de","modified":1765619202720},{"_id":"source/images/MyASCStory/IMG_20250511_074352.jpg","hash":"914c495957e963971fcfda35592bb2789815a38a","modified":1765619201150},{"_id":"source/images/MyASCStory/ASC25-4.jpg","hash":"977ab0ba0b554c2ce1a70e8916ab75b387e1bca3","modified":1765619209740},{"_id":"source/images/MyASCStory/IMG_20250511_143147.jpg","hash":"878215f2fba4e5fe7fadfdd8e83d365ed459f7ba","modified":1765619184020},{"_id":"source/images/MyASCStory/ASC25-2.jpg","hash":"b0103bc7aee30ca94e550fd2e1c413725320a96e","modified":1765619162040},{"_id":"public/images/MyASCStory/ASC24-2.jpg","hash":"e7fbd104e81d126d114fd1ebf4e810c721bf0c26","modified":1780452914548},{"_id":"public/images/MyASCStory/ASC24-1.jpg","hash":"780bc4106f3517f3d8a7ea3bfc46203b82e7c961","modified":1780452914548},{"_id":"public/images/MyASCStory/mmexport1712852383431.jpg","hash":"e6159d0f2bdc3f4fa355b5440024bbb763bfded9","modified":1780452914548},{"_id":"public/images/MyASCStory/ASC24-3.jpg","hash":"49116b67ce66255f2a9e5fc2ecdaea9880f5e767","modified":1780452914548},{"_id":"public/images/MyASCStory/mmexport1713023700275.jpg","hash":"8e6d378316ca24156eba819d5fd963484786684b","modified":1780452914548},{"_id":"public/images/MyASCStory/MyASCStory.jpg","hash":"7815e0f7c89688227c7520de9724406a9ab2bdef","modified":1780452914548},{"_id":"public/images/MyASCStory/lzd.jpg","hash":"42dd3b118023f82cb36d77ad77dc7f7f40d4103d","modified":1780452914548},{"_id":"public/images/MyASCStory/IMG_20250509_231809.jpg","hash":"5e36bd718ef8caf5425f181d57f2b9b5c97e5f99","modified":1780452914548},{"_id":"public/images/MyASCStory/ASC26-1.jpg","hash":"f6eb79393b7d39429818302742023660199eb2af","modified":1780452914548},{"_id":"public/images/MyASCStory/mmexport1713006631665.jpg","hash":"6a9964d579e6d196f921b655938f707a5592e798","modified":1780452914548},{"_id":"public/images/MyASCStory/IMG_20250509_202557.jpg","hash":"932191925ba50d510b4543ab3eb01c85ed76e823","modified":1780452914548},{"_id":"public/images/MyASCStory/ASC25-1.jpg","hash":"f92f3c1030c8d5b7bb1fcc913aea7949237dc8bd","modified":1780452914548},{"_id":"public/images/MyASCStory/IMG_20260516_230155.jpg","hash":"ed0bc33d58e430e9371adeac67e16b4973bd1418","modified":1780452914548},{"_id":"public/images/MyASCStory/ASC26-2.jpg","hash":"587340074e0536eec119ca2373ff78baaa17e818","modified":1780452914548},{"_id":"public/images/MyASCStory/IMG_20250512_101818.jpg","hash":"599b6a4786cca30fc8704b0817d145950da27e63","modified":1780452914548},{"_id":"public/images/MyASCStory/ASC26-3.jpg","hash":"d492692b3e1682567ad2bad0b0b2fc79c7d76199","modified":1780452914548},{"_id":"public/images/MyASCStory/IMG_20260518_131255.jpg","hash":"0f0ed96c6c887f809c6a464d4885f8bc81b94082","modified":1780452914548},{"_id":"public/images/MyASCStory/ASC25-3.jpg","hash":"140615d948984648181432dd50bf6d068b32f6de","modified":1780452914548},{"_id":"public/images/MyASCStory/IMG_20250511_074352.jpg","hash":"914c495957e963971fcfda35592bb2789815a38a","modified":1780452914548},{"_id":"public/images/MyASCStory/ASC25-4.jpg","hash":"977ab0ba0b554c2ce1a70e8916ab75b387e1bca3","modified":1780452914548},{"_id":"public/images/MyASCStory/IMG_20250511_143147.jpg","hash":"878215f2fba4e5fe7fadfdd8e83d365ed459f7ba","modified":1780452914548},{"_id":"public/images/MyASCStory/ASC25-2.jpg","hash":"b0103bc7aee30ca94e550fd2e1c413725320a96e","modified":1780452914548},{"_id":"source/images/MyASCStory/ASC24-4.jpg","hash":"8e6d378316ca24156eba819d5fd963484786684b","modified":1780452072895},{"_id":"source/images/MyASCStory/lzdnb.jpg","hash":"42dd3b118023f82cb36d77ad77dc7f7f40d4103d","modified":1765630938320},{"_id":"source/images/MyASCStory/ASC25-5.jpg","hash":"5e36bd718ef8caf5425f181d57f2b9b5c97e5f99","modified":1765619211660},{"_id":"source/images/MyASCStory/ASC24-5.jpg","hash":"6a9964d579e6d196f921b655938f707a5592e798","modified":1765630926690},{"_id":"source/images/MyASCStory/ASC25-6.jpg","hash":"932191925ba50d510b4543ab3eb01c85ed76e823","modified":1765619247280},{"_id":"source/images/MyASCStory/ASC26YeZ.jpg","hash":"ed0bc33d58e430e9371adeac67e16b4973bd1418","modified":1778943718000},{"_id":"source/images/MyASCStory/ASC25cluster.jpg","hash":"599b6a4786cca30fc8704b0817d145950da27e63","modified":1765619271450},{"_id":"source/images/MyASCStory/ASC26cluster.jpg","hash":"0f0ed96c6c887f809c6a464d4885f8bc81b94082","modified":1779081182000},{"_id":"source/images/MyASCStory/ASC25-7.jpg","hash":"878215f2fba4e5fe7fadfdd8e83d365ed459f7ba","modified":1765619184020},{"_id":"public/images/MyASCStory/ASC24-4.jpg","hash":"8e6d378316ca24156eba819d5fd963484786684b","modified":1780454069353},{"_id":"public/images/MyASCStory/lzdnb.jpg","hash":"42dd3b118023f82cb36d77ad77dc7f7f40d4103d","modified":1780454069353},{"_id":"public/images/MyASCStory/ASC25-5.jpg","hash":"5e36bd718ef8caf5425f181d57f2b9b5c97e5f99","modified":1780454069353},{"_id":"public/images/MyASCStory/ASC24-5.jpg","hash":"6a9964d579e6d196f921b655938f707a5592e798","modified":1780454069353},{"_id":"public/images/MyASCStory/ASC25-6.jpg","hash":"932191925ba50d510b4543ab3eb01c85ed76e823","modified":1780454069353},{"_id":"public/images/MyASCStory/ASC26YeZ.jpg","hash":"ed0bc33d58e430e9371adeac67e16b4973bd1418","modified":1780454069353},{"_id":"public/images/MyASCStory/ASC25cluster.jpg","hash":"599b6a4786cca30fc8704b0817d145950da27e63","modified":1780454069353},{"_id":"public/images/MyASCStory/ASC26cluster.jpg","hash":"0f0ed96c6c887f809c6a464d4885f8bc81b94082","modified":1780454069353},{"_id":"public/images/MyASCStory/ASC25-7.jpg","hash":"878215f2fba4e5fe7fadfdd8e83d365ed459f7ba","modified":1780454069353},{"_id":"source/images/MyASCStory/ASC24-6.jpg","hash":"e6159d0f2bdc3f4fa355b5440024bbb763bfded9","modified":1765630926580},{"_id":"source/images/MyASCStory/ASC25-8.jpg","hash":"914c495957e963971fcfda35592bb2789815a38a","modified":1765619201150},{"_id":"public/images/MyASCStory/ASC24-6.jpg","hash":"e6159d0f2bdc3f4fa355b5440024bbb763bfded9","modified":1780454824212},{"_id":"public/images/MyASCStory/ASC25-8.jpg","hash":"914c495957e963971fcfda35592bb2789815a38a","modified":1780454824212},{"_id":"source/_posts/scx-auto.md","hash":"b7aae28e9da46c5cca90885530f920e1a2e04d07","modified":1781437401327},{"_id":"public/2026/06/14/scx-auto/index.html","hash":"4c82b15b24c71b25ce6e0cab668baaff81161f12","modified":1781437412800},{"_id":"public/archives/2026/06/index.html","hash":"9d54a5279e0ae2acd2c9b6d6265d69b8bebf71f5","modified":1781437134878},{"_id":"public/tags/SCX调度器/index.html","hash":"b85be29870707442789c5d924d72bb5c0b9bea02","modified":1781437134878}],"Category":[{"name":"更新","_id":"cmkjeg2nm000hyg2b3ajig0qs"},{"name":"Rearrangement","_id":"cmkjeg2nn000pyg2bg0qfbc8f"},{"name":"Vocaloid","_id":"cmkjeg2no000wyg2b5udv94y7"},{"name":"技术分享","_id":"cmkjeg2np0012yg2b1qho1agc"},{"name":"技术分享,HPC","_id":"cmkjeg2ns001nyg2b9f9584va"}],"Data":[],"Page":[{"title":"About","date":"2024-11-04T14:46:12.000Z","layout":"about","_content":"\n白茅铺高地玄院第N任非菌群主🐳,Linux六年牢用户🐧\n\n爱好编程,绘画,编曲,Vocaloid调教,Blender建模等😇\n\n截至目前最喜欢的歌手是宇多田光❤️\n\n联系方式:\n\n- Email: \n\n- Github: \n","source":"about/index.md","raw":"---\ntitle: About\ndate: 2024-11-04 22:46:12\nlayout: about\n---\n\n白茅铺高地玄院第N任非菌群主🐳,Linux六年牢用户🐧\n\n爱好编程,绘画,编曲,Vocaloid调教,Blender建模等😇\n\n截至目前最喜欢的歌手是宇多田光❤️\n\n联系方式:\n\n- Email: \n\n- Github: \n","updated":"2025-02-23T16:01:20.093Z","path":"about/index.html","comments":1,"_id":"cmkjeg2ng0000yg2b28bfhrdn","content":"

白茅铺高地玄院第N任非菌群主🐳,Linux六年牢用户🐧

\n

爱好编程,绘画,编曲,Vocaloid调教,Blender建模等😇

\n

截至目前最喜欢的歌手是宇多田光❤️

\n

联系方式:

\n\n","excerpt":"","more":"

白茅铺高地玄院第N任非菌群主🐳,Linux六年牢用户🐧

\n

爱好编程,绘画,编曲,Vocaloid调教,Blender建模等😇

\n

截至目前最喜欢的歌手是宇多田光❤️

\n

联系方式:

\n\n"}],"Post":[{"title":"12月18日工作记录","date":"2023-12-18T13:57:34.000Z","_content":"## 12月18日\n1. 初步了解opencaeporo,尝试在本地docker环境使用gcc完成编译安装,目前完成各项依赖安装,由于晚上停电计划明天开始编译安装opencaeporo本体并在本地调优;\n2. 协助Neko组完成任务;\n3. 找出spack存在网络原因无法下载时的手动解决方案.","source":"_posts/12月18日工作记录.md","raw":"---\ntitle: 12月18日工作记录\ndate: 2023-12-18 21:57:34\ntags: [日志]\n---\n## 12月18日\n1. 初步了解opencaeporo,尝试在本地docker环境使用gcc完成编译安装,目前完成各项依赖安装,由于晚上停电计划明天开始编译安装opencaeporo本体并在本地调优;\n2. 协助Neko组完成任务;\n3. 找出spack存在网络原因无法下载时的手动解决方案.","slug":"12月18日工作记录","published":1,"updated":"2025-02-23T14:27:46.748Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nh0001yg2bemvs08zk","content":"

12月18日

    \n
  1. 初步了解opencaeporo,尝试在本地docker环境使用gcc完成编译安装,目前完成各项依赖安装,由于晚上停电计划明天开始编译安装opencaeporo本体并在本地调优;
  2. \n
  3. 协助Neko组完成任务;
  4. \n
  5. 找出spack存在网络原因无法下载时的手动解决方案.
  6. \n
\n","excerpt":"","more":"

12月18日

    \n
  1. 初步了解opencaeporo,尝试在本地docker环境使用gcc完成编译安装,目前完成各项依赖安装,由于晚上停电计划明天开始编译安装opencaeporo本体并在本地调优;
  2. \n
  3. 协助Neko组完成任务;
  4. \n
  5. 找出spack存在网络原因无法下载时的手动解决方案.
  6. \n
\n"},{"title":"12月16-17日工作记录","date":"2023-12-17T14:26:17.000Z","_content":"## 12月16-17日\n1. 四级考试\n2. 自学cuda,openacc,了解GPU架构知识\n3. 参加超算队启动会\n4. 大计和高数期中考试\n5. 研究spack使用\n6. 学习cuda编程\n7. 补作业\n","source":"_posts/12月16-17日工作记录.md","raw":"---\ntitle: 12月16-17日工作记录\ndate: 2023-12-17 22:26:17\ntags: [日志]\n---\n## 12月16-17日\n1. 四级考试\n2. 自学cuda,openacc,了解GPU架构知识\n3. 参加超算队启动会\n4. 大计和高数期中考试\n5. 研究spack使用\n6. 学习cuda编程\n7. 补作业\n","slug":"12月16-17日工作记录","published":1,"updated":"2025-02-23T14:27:46.747Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2ni0002yg2bh5bd4bef","content":"

12月16-17日

    \n
  1. 四级考试
  2. \n
  3. 自学cuda,openacc,了解GPU架构知识
  4. \n
  5. 参加超算队启动会
  6. \n
  7. 大计和高数期中考试
  8. \n
  9. 研究spack使用
  10. \n
  11. 学习cuda编程
  12. \n
  13. 补作业
  14. \n
\n","excerpt":"","more":"

12月16-17日

    \n
  1. 四级考试
  2. \n
  3. 自学cuda,openacc,了解GPU架构知识
  4. \n
  5. 参加超算队启动会
  6. \n
  7. 大计和高数期中考试
  8. \n
  9. 研究spack使用
  10. \n
  11. 学习cuda编程
  12. \n
  13. 补作业
  14. \n
\n"},{"title":"12月19日工作记录","date":"2023-12-19T13:59:47.000Z","_content":"## 12月18日\n1. 彻底完成opencaeporo安装部署,本地和服务器均已安装,明天正式开始调优工作;\n2. 学习cuda编程.\n","source":"_posts/12月19日工作记录.md","raw":"---\ntitle: 12月19日工作记录\ndate: 2023-12-19 21:59:47\ntags: [日志]\n---\n## 12月18日\n1. 彻底完成opencaeporo安装部署,本地和服务器均已安装,明天正式开始调优工作;\n2. 学习cuda编程.\n","slug":"12月19日工作记录","published":1,"updated":"2025-02-23T14:27:46.748Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nj0004yg2b6zetfs0g","content":"

12月18日

    \n
  1. 彻底完成opencaeporo安装部署,本地和服务器均已安装,明天正式开始调优工作;
  2. \n
  3. 学习cuda编程.
  4. \n
\n","excerpt":"","more":"

12月18日

    \n
  1. 彻底完成opencaeporo安装部署,本地和服务器均已安装,明天正式开始调优工作;
  2. \n
  3. 学习cuda编程.
  4. \n
\n"},{"title":"12月20日工作记录","date":"2023-12-20T13:56:42.000Z","_content":"## 12月20日\n\nOpenCAEPoro 小组(黄**,梁**,程**)\n\n1. 阅读代码,开展优化工作,初步使用openacc完成petsc_solver的优化,取得一定优化效果\n\n> 优化方向基本确定为OpenMP/OpenACC并行化+cuda移植\n\n2. 赛题环境汇总,目前各组工作有序开展,CentOS 7符合要求暂未发现更换系统需求\n","source":"_posts/12月20日工作记录.md","raw":"---\ntitle: 12月20日工作记录\ndate: 2023-12-20 21:56:42\ntags: 日志\n---\n## 12月20日\n\nOpenCAEPoro 小组(黄**,梁**,程**)\n\n1. 阅读代码,开展优化工作,初步使用openacc完成petsc_solver的优化,取得一定优化效果\n\n> 优化方向基本确定为OpenMP/OpenACC并行化+cuda移植\n\n2. 赛题环境汇总,目前各组工作有序开展,CentOS 7符合要求暂未发现更换系统需求\n","slug":"12月20日工作记录","published":1,"updated":"2025-02-23T14:27:46.748Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nj0005yg2b2irqftck","content":"

12月20日

OpenCAEPoro 小组(黄,梁,程**)

\n
    \n
  1. 阅读代码,开展优化工作,初步使用openacc完成petsc_solver的优化,取得一定优化效果
  2. \n
\n
\n

优化方向基本确定为OpenMP/OpenACC并行化+cuda移植

\n
\n
    \n
  1. 赛题环境汇总,目前各组工作有序开展,CentOS 7符合要求暂未发现更换系统需求
  2. \n
\n","excerpt":"","more":"

12月20日

OpenCAEPoro 小组(黄,梁,程**)

\n
    \n
  1. 阅读代码,开展优化工作,初步使用openacc完成petsc_solver的优化,取得一定优化效果
  2. \n
\n
\n

优化方向基本确定为OpenMP/OpenACC并行化+cuda移植

\n
\n
    \n
  1. 赛题环境汇总,目前各组工作有序开展,CentOS 7符合要求暂未发现更换系统需求
  2. \n
\n"},{"title":"12月28日进度报告","date":"2023-12-28T15:22:13.000Z","_content":"## 12月28日\n\nOpenCAEPoro 小组(黄**,梁**,程**,刘**)\n\n1. 各组员继续进行优化工作,部分函数完成cuda移植,取得一定优化效果\n2. 继续学习openacc及cuda相关知识\n3. 搜集多孔介质流动模拟与opencaeporo相关论文资料为proposal做准备\n","source":"_posts/12月28日进度报告.md","raw":"---\ntitle: 12月28日进度报告\ndate: 2023-12-28 23:22:13\ntags: 日志\n---\n## 12月28日\n\nOpenCAEPoro 小组(黄**,梁**,程**,刘**)\n\n1. 各组员继续进行优化工作,部分函数完成cuda移植,取得一定优化效果\n2. 继续学习openacc及cuda相关知识\n3. 搜集多孔介质流动模拟与opencaeporo相关论文资料为proposal做准备\n","slug":"12月28日进度报告","published":1,"updated":"2025-02-23T14:27:46.748Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nk0006yg2b3qzo691r","content":"

12月28日

OpenCAEPoro 小组(黄,梁,程,刘

\n
    \n
  1. 各组员继续进行优化工作,部分函数完成cuda移植,取得一定优化效果
  2. \n
  3. 继续学习openacc及cuda相关知识
  4. \n
  5. 搜集多孔介质流动模拟与opencaeporo相关论文资料为proposal做准备
  6. \n
\n","excerpt":"","more":"

12月28日

OpenCAEPoro 小组(黄,梁,程,刘

\n
    \n
  1. 各组员继续进行优化工作,部分函数完成cuda移植,取得一定优化效果
  2. \n
  3. 继续学习openacc及cuda相关知识
  4. \n
  5. 搜集多孔介质流动模拟与opencaeporo相关论文资料为proposal做准备
  6. \n
\n"},{"title":"12月30-31日进度报告","date":"2023-12-30T16:07:09.000Z","_content":"## 12月30-31日\n\nOpenCAEPoro 小组(黄**,梁**,程**,刘**)\n\n1. 服务器上的Opencaeporo改用nvhpc编译器编译以支持cuda;\n2. 收集运行数据用于proposal绘制图表;\n3. 继续尝试优化,同时已经查阅收集了一些相关资料以尽量理解相关含义便于工作开展.\n","source":"_posts/12月31日进度报告.md","raw":"---\ntitle: 12月30-31日进度报告\ndate: 2023-12-31 00:07:09\ntags: 日志\n---\n## 12月30-31日\n\nOpenCAEPoro 小组(黄**,梁**,程**,刘**)\n\n1. 服务器上的Opencaeporo改用nvhpc编译器编译以支持cuda;\n2. 收集运行数据用于proposal绘制图表;\n3. 继续尝试优化,同时已经查阅收集了一些相关资料以尽量理解相关含义便于工作开展.\n","slug":"12月31日进度报告","published":1,"updated":"2025-02-23T14:27:46.748Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nk0009yg2b2zy9a2by","content":"

12月30-31日

OpenCAEPoro 小组(黄,梁,程,刘

\n
    \n
  1. 服务器上的Opencaeporo改用nvhpc编译器编译以支持cuda;
  2. \n
  3. 收集运行数据用于proposal绘制图表;
  4. \n
  5. 继续尝试优化,同时已经查阅收集了一些相关资料以尽量理解相关含义便于工作开展.
  6. \n
\n","excerpt":"","more":"

12月30-31日

OpenCAEPoro 小组(黄,梁,程,刘

\n
    \n
  1. 服务器上的Opencaeporo改用nvhpc编译器编译以支持cuda;
  2. \n
  3. 收集运行数据用于proposal绘制图表;
  4. \n
  5. 继续尝试优化,同时已经查阅收集了一些相关资料以尽量理解相关含义便于工作开展.
  6. \n
\n"},{"title":"小满随笔——决断","date":"2025-05-21T11:59:21.000Z","_content":"\n
\n
\n Lost Days of Warmth - Yasunori Mitsuda\n
\n \n
\n\n我本以为自己已经有足够的勇气和平静的心态去面对生活里的各种变故了,即便如此今天还是被突如其来的噩耗痛击得面目全非。\n\n辛辛苦苦和队友准备了大半年的ASC25失利,我想着以后回过头来看也不算什么以最快的速度调整心态;回校后一来就是各种烂摊子,写不完的作业补不完的课程,还有被脑子缺根筋的弱智评委恶意低分的大创立项成绩,我寻思这些事情烦完这阵也就过去了,人总不可能一直倒霉吧。结果就是今天下午排队验收CSAPP课程的shell实验时突然看到了外婆去世的消息,一时间两腿发软大脑空白不知所措,以至于后来排到我和教辅讲解实验过程时也是胡言乱语记不清自己到底说了什么,尽管最后还是被教辅满分放过了。\n\n小时候家里没现在景气,父母在市内打拼我则是被送到郊区被外婆抚养长大,上小学后为了让我进入一中直升班从二年级起我就被送进了奥数培训班,同时搬离了自己长大的地方,再到后来市政府改造,外婆家早已被拆得无影无踪,昔日的小伙伴杳无音信,温馨错杂的小居民区变成了陌生嘈杂的大酒店和菜市场,每次路过都不敢停留。从那以后外婆成了我和童年之间仅剩的联系,尽管老人后来连我的样貌也记不清楚。现如今童年记忆里最后的故人业已离开,再回首已是一片虚无。多年前外婆犯脑溢血被抢救回来的时候医生还承诺老人家活到抱孙子没问题,如今却猝不及防地驾鹤西去。在各种小说和游戏中经历过不少难忘的生死离别,可是到了现实亲自经历依旧痛心疾首难以放下。\n\n生物无法教会人们生命究竟是什么,生活却一点就通。上一次经历这种打击还是10年左右外公离开,那时候太小什么都不懂,等长大理解了经历的时间又足以抹平悲痛。如今在我的眼里,生命就是思念,是世间个体对其他个体的牵挂与个体之间互相建立的羁绊。我真的想停留在永远的当下,和自己爱的人永远定格在某一个瞬间,每天早上是一样的朝阳,出门是熟悉的面孔,回家是亲切的声音。然而活着就意味着必须面对各种苦楚,死去元知万事空,只有离开才是最好的解脱,活着的人要背负更多。长者的时间在一天天减少,他们的生命需要由后人去延续,但是后人的生活也不容易,面对各种困境放弃往往比坚持更加轻松,更有甚者勇敢地选择自戕从而摆脱一切,但是这样是不对的,让其他人代替自己承受痛苦是不对的,令人悲伤的选择是不应该做出的,活着的人哪怕再苦,只要身上背负了其他人的思念也只能继续前进。\n\n因此,我绝对不能气馁,我珍视现在和亲人朋友的点点滴滴,珍惜自己现在拥有的一切,我以最真诚的爱回馈所有爱我的人,不论面前有多少困难,我都会尽全力克服,这不是为我自己,现在我被摧残得只想躺平摆烂,但是为了所有信任我关心我的人,如今我绝对不能停下脚步。从来就没有永远的当下,连接我和童年最后的纽带已经被切断,再也不能回头,留给我的只剩下面前的未来,需要我亲手创造的未来,只有承受住来自各方的打击,我才有能力去维护由我创造的未来,既然选择了远方,便只顾风雨兼程。\n\n2025年的长沙小满格外燥热,痛定思过后的心是如此平静,没有多少时间可以浪费了,无需多言。\n\n*“回首向来萧瑟处,归去,也无风雨也无晴!”*\n","source":"_posts/2025-5-21.md","raw":"---\ntitle: 小满随笔——决断\ndate: 2025-05-21 19:59:21\ntags: 日志\n---\n\n
\n
\n Lost Days of Warmth - Yasunori Mitsuda\n
\n \n
\n\n我本以为自己已经有足够的勇气和平静的心态去面对生活里的各种变故了,即便如此今天还是被突如其来的噩耗痛击得面目全非。\n\n辛辛苦苦和队友准备了大半年的ASC25失利,我想着以后回过头来看也不算什么以最快的速度调整心态;回校后一来就是各种烂摊子,写不完的作业补不完的课程,还有被脑子缺根筋的弱智评委恶意低分的大创立项成绩,我寻思这些事情烦完这阵也就过去了,人总不可能一直倒霉吧。结果就是今天下午排队验收CSAPP课程的shell实验时突然看到了外婆去世的消息,一时间两腿发软大脑空白不知所措,以至于后来排到我和教辅讲解实验过程时也是胡言乱语记不清自己到底说了什么,尽管最后还是被教辅满分放过了。\n\n小时候家里没现在景气,父母在市内打拼我则是被送到郊区被外婆抚养长大,上小学后为了让我进入一中直升班从二年级起我就被送进了奥数培训班,同时搬离了自己长大的地方,再到后来市政府改造,外婆家早已被拆得无影无踪,昔日的小伙伴杳无音信,温馨错杂的小居民区变成了陌生嘈杂的大酒店和菜市场,每次路过都不敢停留。从那以后外婆成了我和童年之间仅剩的联系,尽管老人后来连我的样貌也记不清楚。现如今童年记忆里最后的故人业已离开,再回首已是一片虚无。多年前外婆犯脑溢血被抢救回来的时候医生还承诺老人家活到抱孙子没问题,如今却猝不及防地驾鹤西去。在各种小说和游戏中经历过不少难忘的生死离别,可是到了现实亲自经历依旧痛心疾首难以放下。\n\n生物无法教会人们生命究竟是什么,生活却一点就通。上一次经历这种打击还是10年左右外公离开,那时候太小什么都不懂,等长大理解了经历的时间又足以抹平悲痛。如今在我的眼里,生命就是思念,是世间个体对其他个体的牵挂与个体之间互相建立的羁绊。我真的想停留在永远的当下,和自己爱的人永远定格在某一个瞬间,每天早上是一样的朝阳,出门是熟悉的面孔,回家是亲切的声音。然而活着就意味着必须面对各种苦楚,死去元知万事空,只有离开才是最好的解脱,活着的人要背负更多。长者的时间在一天天减少,他们的生命需要由后人去延续,但是后人的生活也不容易,面对各种困境放弃往往比坚持更加轻松,更有甚者勇敢地选择自戕从而摆脱一切,但是这样是不对的,让其他人代替自己承受痛苦是不对的,令人悲伤的选择是不应该做出的,活着的人哪怕再苦,只要身上背负了其他人的思念也只能继续前进。\n\n因此,我绝对不能气馁,我珍视现在和亲人朋友的点点滴滴,珍惜自己现在拥有的一切,我以最真诚的爱回馈所有爱我的人,不论面前有多少困难,我都会尽全力克服,这不是为我自己,现在我被摧残得只想躺平摆烂,但是为了所有信任我关心我的人,如今我绝对不能停下脚步。从来就没有永远的当下,连接我和童年最后的纽带已经被切断,再也不能回头,留给我的只剩下面前的未来,需要我亲手创造的未来,只有承受住来自各方的打击,我才有能力去维护由我创造的未来,既然选择了远方,便只顾风雨兼程。\n\n2025年的长沙小满格外燥热,痛定思过后的心是如此平静,没有多少时间可以浪费了,无需多言。\n\n*“回首向来萧瑟处,归去,也无风雨也无晴!”*\n","slug":"2025-5-21","published":1,"updated":"2025-05-21T13:34:43.639Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nl000byg2b68u8869w","content":"
\n
\n Lost Days of Warmth - Yasunori Mitsuda\n
\n \n
\n\n

我本以为自己已经有足够的勇气和平静的心态去面对生活里的各种变故了,即便如此今天还是被突如其来的噩耗痛击得面目全非。

\n

辛辛苦苦和队友准备了大半年的ASC25失利,我想着以后回过头来看也不算什么以最快的速度调整心态;回校后一来就是各种烂摊子,写不完的作业补不完的课程,还有被脑子缺根筋的弱智评委恶意低分的大创立项成绩,我寻思这些事情烦完这阵也就过去了,人总不可能一直倒霉吧。结果就是今天下午排队验收CSAPP课程的shell实验时突然看到了外婆去世的消息,一时间两腿发软大脑空白不知所措,以至于后来排到我和教辅讲解实验过程时也是胡言乱语记不清自己到底说了什么,尽管最后还是被教辅满分放过了。

\n

小时候家里没现在景气,父母在市内打拼我则是被送到郊区被外婆抚养长大,上小学后为了让我进入一中直升班从二年级起我就被送进了奥数培训班,同时搬离了自己长大的地方,再到后来市政府改造,外婆家早已被拆得无影无踪,昔日的小伙伴杳无音信,温馨错杂的小居民区变成了陌生嘈杂的大酒店和菜市场,每次路过都不敢停留。从那以后外婆成了我和童年之间仅剩的联系,尽管老人后来连我的样貌也记不清楚。现如今童年记忆里最后的故人业已离开,再回首已是一片虚无。多年前外婆犯脑溢血被抢救回来的时候医生还承诺老人家活到抱孙子没问题,如今却猝不及防地驾鹤西去。在各种小说和游戏中经历过不少难忘的生死离别,可是到了现实亲自经历依旧痛心疾首难以放下。

\n

生物无法教会人们生命究竟是什么,生活却一点就通。上一次经历这种打击还是10年左右外公离开,那时候太小什么都不懂,等长大理解了经历的时间又足以抹平悲痛。如今在我的眼里,生命就是思念,是世间个体对其他个体的牵挂与个体之间互相建立的羁绊。我真的想停留在永远的当下,和自己爱的人永远定格在某一个瞬间,每天早上是一样的朝阳,出门是熟悉的面孔,回家是亲切的声音。然而活着就意味着必须面对各种苦楚,死去元知万事空,只有离开才是最好的解脱,活着的人要背负更多。长者的时间在一天天减少,他们的生命需要由后人去延续,但是后人的生活也不容易,面对各种困境放弃往往比坚持更加轻松,更有甚者勇敢地选择自戕从而摆脱一切,但是这样是不对的,让其他人代替自己承受痛苦是不对的,令人悲伤的选择是不应该做出的,活着的人哪怕再苦,只要身上背负了其他人的思念也只能继续前进。

\n

因此,我绝对不能气馁,我珍视现在和亲人朋友的点点滴滴,珍惜自己现在拥有的一切,我以最真诚的爱回馈所有爱我的人,不论面前有多少困难,我都会尽全力克服,这不是为我自己,现在我被摧残得只想躺平摆烂,但是为了所有信任我关心我的人,如今我绝对不能停下脚步。从来就没有永远的当下,连接我和童年最后的纽带已经被切断,再也不能回头,留给我的只剩下面前的未来,需要我亲手创造的未来,只有承受住来自各方的打击,我才有能力去维护由我创造的未来,既然选择了远方,便只顾风雨兼程。

\n

2025年的长沙小满格外燥热,痛定思过后的心是如此平静,没有多少时间可以浪费了,无需多言。

\n

“回首向来萧瑟处,归去,也无风雨也无晴!”

\n","excerpt":"","more":"
\n
\n Lost Days of Warmth - Yasunori Mitsuda\n
\n \n
\n\n

我本以为自己已经有足够的勇气和平静的心态去面对生活里的各种变故了,即便如此今天还是被突如其来的噩耗痛击得面目全非。

\n

辛辛苦苦和队友准备了大半年的ASC25失利,我想着以后回过头来看也不算什么以最快的速度调整心态;回校后一来就是各种烂摊子,写不完的作业补不完的课程,还有被脑子缺根筋的弱智评委恶意低分的大创立项成绩,我寻思这些事情烦完这阵也就过去了,人总不可能一直倒霉吧。结果就是今天下午排队验收CSAPP课程的shell实验时突然看到了外婆去世的消息,一时间两腿发软大脑空白不知所措,以至于后来排到我和教辅讲解实验过程时也是胡言乱语记不清自己到底说了什么,尽管最后还是被教辅满分放过了。

\n

小时候家里没现在景气,父母在市内打拼我则是被送到郊区被外婆抚养长大,上小学后为了让我进入一中直升班从二年级起我就被送进了奥数培训班,同时搬离了自己长大的地方,再到后来市政府改造,外婆家早已被拆得无影无踪,昔日的小伙伴杳无音信,温馨错杂的小居民区变成了陌生嘈杂的大酒店和菜市场,每次路过都不敢停留。从那以后外婆成了我和童年之间仅剩的联系,尽管老人后来连我的样貌也记不清楚。现如今童年记忆里最后的故人业已离开,再回首已是一片虚无。多年前外婆犯脑溢血被抢救回来的时候医生还承诺老人家活到抱孙子没问题,如今却猝不及防地驾鹤西去。在各种小说和游戏中经历过不少难忘的生死离别,可是到了现实亲自经历依旧痛心疾首难以放下。

\n

生物无法教会人们生命究竟是什么,生活却一点就通。上一次经历这种打击还是10年左右外公离开,那时候太小什么都不懂,等长大理解了经历的时间又足以抹平悲痛。如今在我的眼里,生命就是思念,是世间个体对其他个体的牵挂与个体之间互相建立的羁绊。我真的想停留在永远的当下,和自己爱的人永远定格在某一个瞬间,每天早上是一样的朝阳,出门是熟悉的面孔,回家是亲切的声音。然而活着就意味着必须面对各种苦楚,死去元知万事空,只有离开才是最好的解脱,活着的人要背负更多。长者的时间在一天天减少,他们的生命需要由后人去延续,但是后人的生活也不容易,面对各种困境放弃往往比坚持更加轻松,更有甚者勇敢地选择自戕从而摆脱一切,但是这样是不对的,让其他人代替自己承受痛苦是不对的,令人悲伤的选择是不应该做出的,活着的人哪怕再苦,只要身上背负了其他人的思念也只能继续前进。

\n

因此,我绝对不能气馁,我珍视现在和亲人朋友的点点滴滴,珍惜自己现在拥有的一切,我以最真诚的爱回馈所有爱我的人,不论面前有多少困难,我都会尽全力克服,这不是为我自己,现在我被摧残得只想躺平摆烂,但是为了所有信任我关心我的人,如今我绝对不能停下脚步。从来就没有永远的当下,连接我和童年最后的纽带已经被切断,再也不能回头,留给我的只剩下面前的未来,需要我亲手创造的未来,只有承受住来自各方的打击,我才有能力去维护由我创造的未来,既然选择了远方,便只顾风雨兼程。

\n

2025年的长沙小满格外燥热,痛定思过后的心是如此平静,没有多少时间可以浪费了,无需多言。

\n

“回首向来萧瑟处,归去,也无风雨也无晴!”

\n"},{"title":"博客更新记录","date":"2024-11-04T15:18:02.000Z","_content":"\n- 博客主题更新为 `Fluid` 主题,原主题为 `Next`\n- 更新了访问地址为 `https://blog.hifuu.ink`\n- 新增了 `About` 页面\n- 新增了 `友链` 页面\n- 完善页面布局\n","source":"_posts/BlogUpdate.md","raw":"---\ntitle: 博客更新记录\ndate: 2024-11-04 23:18:02\ntags: 日志\ncategories: 更新\n---\n\n- 博客主题更新为 `Fluid` 主题,原主题为 `Next`\n- 更新了访问地址为 `https://blog.hifuu.ink`\n- 新增了 `About` 页面\n- 新增了 `友链` 页面\n- 完善页面布局\n","slug":"BlogUpdate","published":1,"updated":"2025-02-23T14:27:46.748Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nl000eyg2b1zf4g4q6","content":"
    \n
  • 博客主题更新为 Fluid 主题,原主题为 Next
  • \n
  • 更新了访问地址为 https://blog.hifuu.ink
  • \n
  • 新增了 About 页面
  • \n
  • 新增了 友链 页面
  • \n
  • 完善页面布局
  • \n
\n","excerpt":"","more":"
    \n
  • 博客主题更新为 Fluid 主题,原主题为 Next
  • \n
  • 更新了访问地址为 https://blog.hifuu.ink
  • \n
  • 新增了 About 页面
  • \n
  • 新增了 友链 页面
  • \n
  • 完善页面布局
  • \n
\n"},{"title":"Welcome to CGH0S7's Blog","date":"2023-12-06T08:33:30.000Z","_content":"\nHello World !\n","source":"_posts/CGH0S7-s-Blog.md","raw":"---\ntitle: Welcome to CGH0S7's Blog \ndate: 2023-12-06 16:33:30\ntags: TEST\n---\n\nHello World !\n","slug":"CGH0S7-s-Blog","published":1,"updated":"2025-02-23T14:27:46.748Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nm000gyg2bdfisbgnx","content":"

Hello World !

\n","excerpt":"","more":"

Hello World !

\n"},{"title":"Gentle Jena","date":"2024-10-30T14:11:14.000Z","_content":"\n很喜欢的一首曲子\n顺便测试一下视频上传\n以后随缘更新各种乱七八糟的东西。。\n\n\n","source":"_posts/GentleJena.md","raw":"---\ntitle: Gentle Jena\ndate: 2024-10-30 22:11:14\ntags: 音乐\ncategories: Rearrangement\n---\n\n很喜欢的一首曲子\n顺便测试一下视频上传\n以后随缘更新各种乱七八糟的东西。。\n\n\n","slug":"GentleJena","published":1,"updated":"2025-02-23T17:14:31.930Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nm000jyg2bhdyy2343","content":"

很喜欢的一首曲子
顺便测试一下视频上传
以后随缘更新各种乱七八糟的东西。。

\n\n","excerpt":"","more":"

很喜欢的一首曲子
顺便测试一下视频上传
以后随缘更新各种乱七八糟的东西。。

\n\n"},{"title":"Vocaloid调教-晴天(洛天依V4)","date":"2024-11-04T13:52:59.000Z","_content":"\n其实是今年年初的作品想起来可以搬上来,这是我调教的第一首v曲。\n\n“故事的小黄花,从出生那年就飘着...”\n\n{% raw %}\n\n{% endraw %}\n\n","source":"_posts/Vocaloid调教-晴天.md","raw":"---\ntitle: Vocaloid调教-晴天(洛天依v4)\ndate: 2024-11-04 21:52:59\ntags: [Vocaloid, 音乐]\ncategories: [Vocaloid]\n---\n\n其实是今年年初的作品想起来可以搬上来,这是我调教的第一首v曲。\n\n“故事的小黄花,从出生那年就飘着...”\n\n{% raw %}\n\n{% endraw %}\n\n","slug":"Vocaloid调教-晴天","published":1,"updated":"2025-02-25T00:19:05.940Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nn000myg2beei7b2up","content":"

其实是今年年初的作品想起来可以搬上来,这是我调教的第一首v曲。

\n

“故事的小黄花,从出生那年就飘着…”

\n\n\n\n\n","excerpt":"","more":"

其实是今年年初的作品想起来可以搬上来,这是我调教的第一首v曲。

\n

“故事的小黄花,从出生那年就飘着…”

\n\n\n\n\n"},{"title":"低成本快速搞定专属域名邮箱:从 0 到 CLI 收发只要 15 分钟","date":"2025-11-15T15:36:22.000Z","_content":"\n> 失踪人口回归 👋\n> 如果你只想「有个能用的 `@自己域名` 邮箱」,却懒得租服务器、折腾 Postfix、跟 25 端口斗智斗勇、花费你宝贵的💰,那这篇 15 分钟速通教程就是写给你的(当然,一点💰都不花是不可能的,你至少需要买一个域名=)\n\n---\n\n## 1. 最终效果\n\n- 任何前缀都能即时生成: `helloworld@your.com` / `fknudt@your.com` / `ilovethu@your.com` \n- 在终端里使用命令收发邮件,无需专门打开各种邮件客户端。\n\n整套方案 **只花域名注册费用**,**0 服务器 0 运维**,完全白嫖 Resend 免费额度。\n\n---\n\n## 2. 准备工作(2 min)\n\n| 资源 | 用途 | 费用 |\n|---|---|---|\n| 一枚域名 | 做邮箱后缀 | 约 ¥30+ / 年(阿里云 Namecheap Cloudflare 均可) |\n| Resend 账号 | 托管邮服 | 免费 100 封 / 天 |\n\n![Resend文档](/images/resendofficial.png)\n\n---\n\n## 3. 3 步完成域名绑定(5 min)\n\n1. 登录 [Resend](https://resend.com) → **Domains** → **Add Domain** \n 输入 `your.com` → 得到一组 DNS 记录(TXT + MX + DKIM)。 \n\n2. 去域名注册商把记录全部照抄粘贴,保存。(由于笔者为免费用户并且已经配置域名无法再次配置,因此未能提供截图但是步骤非常简单)。\n - 登录你的域名注册商账号,找到DNS管理页面。\n - 添加Resend提供的TXT记录,用于验证域名所有权。\n - 添加Resend提供的MX记录,用于接收邮件。\n - 根据Resend的要求,配置完整所有必要的DNS记录。\n - 保存更改后,等待DNS记录生效,通常需要几分钟到24小时不等(这是resend官方的说法,笔者实测只需要几分钟不到)。\n ⚠️ 只需改 **DNS 解析**,不用转 nameserver,1 min 搞定。 \n\n3. 回到 Resend 点 **Verify**,状态变绿 ✅ 即生效(实测 30s-3min,并不会像官方说的有可能要24小时)。\n\n---\n\n## 4. 无限别名 + 转发(立刻能用)\n\nResend 对「 catch-all 」免费! \n意思是你 **不用提前新建账号**,任何 `@your.com` 的信都会飞进你设定的地址,只需要在 Resend 官网查收即可。\n\n---\n\n## 5. 终端发邮件(可选,8 min 部署)\n\n网页收发邮件显然太麻烦,我用**rust**写了个可爱的 CLI 客户端 [**rusend**](https://github.com/CGH0S7/rusend),基于 Resend 官方 API,支持发送与查收邮件非常方便。(好吧其实是Gemini小姐帮忙写的@@)\n\n### ①-1 下载预构建可执行文件\n\n在[Github Releases](https://github.com/CGH0S7/rusend/releases/tag/stable)里下载对应系统的预编译包,解压放到 `$PATH` 目录下即可。\n\n### ①-2 源码构建\n\n下载并编译rusend,准备好rust工具链,具体编译命令如下:\n\n ```bash\n git clone https://github.com/CGH0S7/rusend.git\n cd rusend\n cargo build --release\n ```\n\n将`rusend`二进制文件移动到系统的`$PATH`目录下,例如`/usr/local/bin`。\n\n### ② 补全 & API Key配置\n\n使用下面的命令进行命令行补全配置:\n\n ```bash\n echo \"source <(rusend completions bash)\" >> ~/.bashrc\n source ~/.bashrc\n ```\n\n ```zsh\n echo \"source <(rusend completions zsh)\" >> ~/.zshrc\n source ~/.zshrc\n ```\n\n ```fish\n rusend completions fish > ~/.config/fish/completions/rusend.fish\n source ~/.config/fish/config.fish\n ```\n\n在Resend控制面板中,找到`API Keys`选项,为你配置的域名生成一个新的API Key。然后在终端中运行以下命令进行配置:\n\n ```bash\n rusend config --key re_xxxxxxxxx\n ```\n\n### ③ 使用 rusend 收发邮件\n\n详细功能请查看[Readme](https://github.com/CGH0S7/rusend)文档:\n\n ![rusend文档](/images/rusendhelp1.png)\n ![rusend文档](/images/rusendhelp2.png)\n ![rusend软件帮助](/images/rusendhelp3.png)\n\n---\n\n## 6. 成本 & 限额总结\n\n| 项目 | 免费额度 |\n|---|---|\n| Resend | 100 封 / 天 |\n| 域名 | 约 ¥30+ / 年 |\n| rusend | 开源 |\n\n非主力邮箱场景下完全够用,不建议用于接收各种广告或更新推送。\n\n若想要「匿名转发」隐藏真实地址,可以再套一层 [AnonAddy](https://addy.io) 即可,本文不展开。\n\n---\n\n## 7. 结语\n\n以上就是「低成本快速拥有个人专属域名邮箱」的全部流程。\n\n零服务器、零运维、全平台通用,甚至能在手机 Termux 里发邮。\n\n如果能帮到你,去 [rusend](https://github.com/CGH0S7/rusend) 点个 ⭐ 当稿费吧~😉\n","source":"_posts/Your-exclusive-email-address.md","raw":"---\ntitle: 低成本快速搞定专属域名邮箱:从 0 到 CLI 收发只要 15 分钟\ndate: 2025-11-15 23:36:22\ntags: [技术分享]\n---\n\n> 失踪人口回归 👋\n> 如果你只想「有个能用的 `@自己域名` 邮箱」,却懒得租服务器、折腾 Postfix、跟 25 端口斗智斗勇、花费你宝贵的💰,那这篇 15 分钟速通教程就是写给你的(当然,一点💰都不花是不可能的,你至少需要买一个域名=)\n\n---\n\n## 1. 最终效果\n\n- 任何前缀都能即时生成: `helloworld@your.com` / `fknudt@your.com` / `ilovethu@your.com` \n- 在终端里使用命令收发邮件,无需专门打开各种邮件客户端。\n\n整套方案 **只花域名注册费用**,**0 服务器 0 运维**,完全白嫖 Resend 免费额度。\n\n---\n\n## 2. 准备工作(2 min)\n\n| 资源 | 用途 | 费用 |\n|---|---|---|\n| 一枚域名 | 做邮箱后缀 | 约 ¥30+ / 年(阿里云 Namecheap Cloudflare 均可) |\n| Resend 账号 | 托管邮服 | 免费 100 封 / 天 |\n\n![Resend文档](/images/resendofficial.png)\n\n---\n\n## 3. 3 步完成域名绑定(5 min)\n\n1. 登录 [Resend](https://resend.com) → **Domains** → **Add Domain** \n 输入 `your.com` → 得到一组 DNS 记录(TXT + MX + DKIM)。 \n\n2. 去域名注册商把记录全部照抄粘贴,保存。(由于笔者为免费用户并且已经配置域名无法再次配置,因此未能提供截图但是步骤非常简单)。\n - 登录你的域名注册商账号,找到DNS管理页面。\n - 添加Resend提供的TXT记录,用于验证域名所有权。\n - 添加Resend提供的MX记录,用于接收邮件。\n - 根据Resend的要求,配置完整所有必要的DNS记录。\n - 保存更改后,等待DNS记录生效,通常需要几分钟到24小时不等(这是resend官方的说法,笔者实测只需要几分钟不到)。\n ⚠️ 只需改 **DNS 解析**,不用转 nameserver,1 min 搞定。 \n\n3. 回到 Resend 点 **Verify**,状态变绿 ✅ 即生效(实测 30s-3min,并不会像官方说的有可能要24小时)。\n\n---\n\n## 4. 无限别名 + 转发(立刻能用)\n\nResend 对「 catch-all 」免费! \n意思是你 **不用提前新建账号**,任何 `@your.com` 的信都会飞进你设定的地址,只需要在 Resend 官网查收即可。\n\n---\n\n## 5. 终端发邮件(可选,8 min 部署)\n\n网页收发邮件显然太麻烦,我用**rust**写了个可爱的 CLI 客户端 [**rusend**](https://github.com/CGH0S7/rusend),基于 Resend 官方 API,支持发送与查收邮件非常方便。(好吧其实是Gemini小姐帮忙写的@@)\n\n### ①-1 下载预构建可执行文件\n\n在[Github Releases](https://github.com/CGH0S7/rusend/releases/tag/stable)里下载对应系统的预编译包,解压放到 `$PATH` 目录下即可。\n\n### ①-2 源码构建\n\n下载并编译rusend,准备好rust工具链,具体编译命令如下:\n\n ```bash\n git clone https://github.com/CGH0S7/rusend.git\n cd rusend\n cargo build --release\n ```\n\n将`rusend`二进制文件移动到系统的`$PATH`目录下,例如`/usr/local/bin`。\n\n### ② 补全 & API Key配置\n\n使用下面的命令进行命令行补全配置:\n\n ```bash\n echo \"source <(rusend completions bash)\" >> ~/.bashrc\n source ~/.bashrc\n ```\n\n ```zsh\n echo \"source <(rusend completions zsh)\" >> ~/.zshrc\n source ~/.zshrc\n ```\n\n ```fish\n rusend completions fish > ~/.config/fish/completions/rusend.fish\n source ~/.config/fish/config.fish\n ```\n\n在Resend控制面板中,找到`API Keys`选项,为你配置的域名生成一个新的API Key。然后在终端中运行以下命令进行配置:\n\n ```bash\n rusend config --key re_xxxxxxxxx\n ```\n\n### ③ 使用 rusend 收发邮件\n\n详细功能请查看[Readme](https://github.com/CGH0S7/rusend)文档:\n\n ![rusend文档](/images/rusendhelp1.png)\n ![rusend文档](/images/rusendhelp2.png)\n ![rusend软件帮助](/images/rusendhelp3.png)\n\n---\n\n## 6. 成本 & 限额总结\n\n| 项目 | 免费额度 |\n|---|---|\n| Resend | 100 封 / 天 |\n| 域名 | 约 ¥30+ / 年 |\n| rusend | 开源 |\n\n非主力邮箱场景下完全够用,不建议用于接收各种广告或更新推送。\n\n若想要「匿名转发」隐藏真实地址,可以再套一层 [AnonAddy](https://addy.io) 即可,本文不展开。\n\n---\n\n## 7. 结语\n\n以上就是「低成本快速拥有个人专属域名邮箱」的全部流程。\n\n零服务器、零运维、全平台通用,甚至能在手机 Termux 里发邮。\n\n如果能帮到你,去 [rusend](https://github.com/CGH0S7/rusend) 点个 ⭐ 当稿费吧~😉\n","slug":"Your-exclusive-email-address","published":1,"updated":"2025-11-15T16:42:50.086Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nn000oyg2bc90t8hdg","content":"
\n

失踪人口回归 👋
如果你只想「有个能用的 @自己域名 邮箱」,却懒得租服务器、折腾 Postfix、跟 25 端口斗智斗勇、花费你宝贵的💰,那这篇 15 分钟速通教程就是写给你的(当然,一点💰都不花是不可能的,你至少需要买一个域名=)

\n
\n
\n

1. 最终效果

    \n
  • 任何前缀都能即时生成: helloworld@your.com / fknudt@your.com / ilovethu@your.com
  • \n
  • 在终端里使用命令收发邮件,无需专门打开各种邮件客户端。
  • \n
\n

整套方案 只花域名注册费用0 服务器 0 运维,完全白嫖 Resend 免费额度。

\n
\n

2. 准备工作(2 min)

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
资源用途费用
一枚域名做邮箱后缀约 ¥30+ / 年(阿里云 Namecheap Cloudflare 均可)
Resend 账号托管邮服免费 100 封 / 天
\n

\"Resend文档\"

\n
\n

3. 3 步完成域名绑定(5 min)

    \n
  1. 登录 ResendDomainsAdd Domain
    输入 your.com → 得到一组 DNS 记录(TXT + MX + DKIM)。

    \n
  2. \n
  3. 去域名注册商把记录全部照抄粘贴,保存。(由于笔者为免费用户并且已经配置域名无法再次配置,因此未能提供截图但是步骤非常简单)。

    \n
      \n
    • 登录你的域名注册商账号,找到DNS管理页面。
    • \n
    • 添加Resend提供的TXT记录,用于验证域名所有权。
    • \n
    • 添加Resend提供的MX记录,用于接收邮件。
    • \n
    • 根据Resend的要求,配置完整所有必要的DNS记录。
    • \n
    • 保存更改后,等待DNS记录生效,通常需要几分钟到24小时不等(这是resend官方的说法,笔者实测只需要几分钟不到)。
      ⚠️ 只需改 DNS 解析,不用转 nameserver,1 min 搞定。
    • \n
    \n
  4. \n
  5. 回到 Resend 点 Verify,状态变绿 ✅ 即生效(实测 30s-3min,并不会像官方说的有可能要24小时)。

    \n
  6. \n
\n
\n

4. 无限别名 + 转发(立刻能用)

Resend 对「 catch-all 」免费!
意思是你 不用提前新建账号,任何 @your.com 的信都会飞进你设定的地址,只需要在 Resend 官网查收即可。

\n
\n

5. 终端发邮件(可选,8 min 部署)

网页收发邮件显然太麻烦,我用rust写了个可爱的 CLI 客户端 rusend,基于 Resend 官方 API,支持发送与查收邮件非常方便。(好吧其实是Gemini小姐帮忙写的@@)

\n

①-1 下载预构建可执行文件

Github Releases里下载对应系统的预编译包,解压放到 $PATH 目录下即可。

\n

①-2 源码构建

下载并编译rusend,准备好rust工具链,具体编译命令如下:

\n
1
2
3
git clone https://github.com/CGH0S7/rusend.git
cd rusend
cargo build --release
\n\n

rusend二进制文件移动到系统的$PATH目录下,例如/usr/local/bin

\n

② 补全 & API Key配置

使用下面的命令进行命令行补全配置:

\n
1
2
echo "source <(rusend completions bash)" >> ~/.bashrc
source ~/.bashrc
\n\n
1
2
echo "source <(rusend completions zsh)" >> ~/.zshrc
source ~/.zshrc
\n\n
1
2
rusend completions fish > ~/.config/fish/completions/rusend.fish
source ~/.config/fish/config.fish
\n\n

在Resend控制面板中,找到API Keys选项,为你配置的域名生成一个新的API Key。然后在终端中运行以下命令进行配置:

\n
1
rusend config --key re_xxxxxxxxx
\n\n

③ 使用 rusend 收发邮件

详细功能请查看Readme文档:

\n

\"rusend文档\"
\"rusend文档\"
\"rusend软件帮助\"

\n
\n

6. 成本 & 限额总结

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
项目免费额度
Resend100 封 / 天
域名约 ¥30+ / 年
rusend开源
\n

非主力邮箱场景下完全够用,不建议用于接收各种广告或更新推送。

\n

若想要「匿名转发」隐藏真实地址,可以再套一层 AnonAddy 即可,本文不展开。

\n
\n

7. 结语

以上就是「低成本快速拥有个人专属域名邮箱」的全部流程。

\n

零服务器、零运维、全平台通用,甚至能在手机 Termux 里发邮。

\n

如果能帮到你,去 rusend 点个 ⭐ 当稿费吧~😉

\n","excerpt":"","more":"
\n

失踪人口回归 👋
如果你只想「有个能用的 @自己域名 邮箱」,却懒得租服务器、折腾 Postfix、跟 25 端口斗智斗勇、花费你宝贵的💰,那这篇 15 分钟速通教程就是写给你的(当然,一点💰都不花是不可能的,你至少需要买一个域名=)

\n
\n
\n

1. 最终效果

    \n
  • 任何前缀都能即时生成: helloworld@your.com / fknudt@your.com / ilovethu@your.com
  • \n
  • 在终端里使用命令收发邮件,无需专门打开各种邮件客户端。
  • \n
\n

整套方案 只花域名注册费用0 服务器 0 运维,完全白嫖 Resend 免费额度。

\n
\n

2. 准备工作(2 min)

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
资源用途费用
一枚域名做邮箱后缀约 ¥30+ / 年(阿里云 Namecheap Cloudflare 均可)
Resend 账号托管邮服免费 100 封 / 天
\n

\"Resend文档\"

\n
\n

3. 3 步完成域名绑定(5 min)

    \n
  1. 登录 ResendDomainsAdd Domain
    输入 your.com → 得到一组 DNS 记录(TXT + MX + DKIM)。

    \n
  2. \n
  3. 去域名注册商把记录全部照抄粘贴,保存。(由于笔者为免费用户并且已经配置域名无法再次配置,因此未能提供截图但是步骤非常简单)。

    \n
      \n
    • 登录你的域名注册商账号,找到DNS管理页面。
    • \n
    • 添加Resend提供的TXT记录,用于验证域名所有权。
    • \n
    • 添加Resend提供的MX记录,用于接收邮件。
    • \n
    • 根据Resend的要求,配置完整所有必要的DNS记录。
    • \n
    • 保存更改后,等待DNS记录生效,通常需要几分钟到24小时不等(这是resend官方的说法,笔者实测只需要几分钟不到)。
      ⚠️ 只需改 DNS 解析,不用转 nameserver,1 min 搞定。
    • \n
    \n
  4. \n
  5. 回到 Resend 点 Verify,状态变绿 ✅ 即生效(实测 30s-3min,并不会像官方说的有可能要24小时)。

    \n
  6. \n
\n
\n

4. 无限别名 + 转发(立刻能用)

Resend 对「 catch-all 」免费!
意思是你 不用提前新建账号,任何 @your.com 的信都会飞进你设定的地址,只需要在 Resend 官网查收即可。

\n
\n

5. 终端发邮件(可选,8 min 部署)

网页收发邮件显然太麻烦,我用rust写了个可爱的 CLI 客户端 rusend,基于 Resend 官方 API,支持发送与查收邮件非常方便。(好吧其实是Gemini小姐帮忙写的@@)

\n

①-1 下载预构建可执行文件

Github Releases里下载对应系统的预编译包,解压放到 $PATH 目录下即可。

\n

①-2 源码构建

下载并编译rusend,准备好rust工具链,具体编译命令如下:

\n
1
2
3
git clone https://github.com/CGH0S7/rusend.git
cd rusend
cargo build --release
\n\n

rusend二进制文件移动到系统的$PATH目录下,例如/usr/local/bin

\n

② 补全 & API Key配置

使用下面的命令进行命令行补全配置:

\n
1
2
echo "source <(rusend completions bash)" >> ~/.bashrc
source ~/.bashrc
\n\n
1
2
echo "source <(rusend completions zsh)" >> ~/.zshrc
source ~/.zshrc
\n\n
1
2
rusend completions fish > ~/.config/fish/completions/rusend.fish
source ~/.config/fish/config.fish
\n\n

在Resend控制面板中,找到API Keys选项,为你配置的域名生成一个新的API Key。然后在终端中运行以下命令进行配置:

\n
1
rusend config --key re_xxxxxxxxx
\n\n

③ 使用 rusend 收发邮件

详细功能请查看Readme文档:

\n

\"rusend文档\"
\"rusend文档\"
\"rusend软件帮助\"

\n
\n

6. 成本 & 限额总结

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
项目免费额度
Resend100 封 / 天
域名约 ¥30+ / 年
rusend开源
\n

非主力邮箱场景下完全够用,不建议用于接收各种广告或更新推送。

\n

若想要「匿名转发」隐藏真实地址,可以再套一层 AnonAddy 即可,本文不展开。

\n
\n

7. 结语

以上就是「低成本快速拥有个人专属域名邮箱」的全部流程。

\n

零服务器、零运维、全平台通用,甚至能在手机 Termux 里发邮。

\n

如果能帮到你,去 rusend 点个 ⭐ 当稿费吧~😉

\n"},{"title":"Archlinux的KDE Plasma优先启用Nvidia独立显卡和混合显卡配置指北","date":"2024-11-06T02:02:04.000Z","_content":"本文将介绍在 **X11** 和 **Wayland** 两种会话下,如何在 KDE Plasma 中优先启用 Nvidia 独立显卡,并提供 I+N 混合显卡的配置方案。**首先,请确保您已经正确安装了 Nvidia 驱动**(如果非 Mainline 内核,请使用 `nvidia-dkms` 或 `nvidia-open-dkms` 版本)。\n\n![Nvidia设置示意图](/images/5364bba6d035326e82c53504dd53e7c2454454985.png)\n\n### 适用系统\n\n对于 **CachyOS** 或 **EndeavourOS** 等 Arch Linux 衍生版,这些配置大多开箱即用,但对于刚刚入坑 Arch Linux 且使用 KDE 的新手可能会遇到这样的问题:\n\n- Nvidia 驱动已安装,`nvidia-smi` 输出正常\n- KDE 系统信息显示仍在使用核显,程序运行时也优先使用核显\n- 导致某些应用(如浏览器、Blender)可能出现卡顿或掉帧现象\n\n这是因为 Arch Linux 的高自定义性,许多功能需要用户手动配置。以下是详细的解决方案。\n\n---\n\n## 配置 X11 下的 Nvidia 显卡优先\n\n可以通过配置 `/etc/X11/xorg.conf` 实现 Nvidia 独显输出。幸运的是,Nvidia 提供了自动生成配置文件的工具,用户无需手动编写:\n\n```bash\nsudo nvidia-xconfig --prime\n```\n\n该命令会根据硬件情况自动生成配置文件。执行后 **重新登录会话** 即可生效(即使是 Wayland 用户也可以执行一次此命令)。\n\n![X11 Nvidia配置](/images/1613f5602b203b38230f19699deb0219454454985.png)\n\n---\n\n## 配置 Wayland 下的 Nvidia 显卡优先\n\n在 Wayland 下优先启用 Nvidia 显卡的步骤如下:\n\n1. 编辑 **GRUB 配置**文件:\n\n 打开 `/etc/default/grub` 文件,在 `GRUB_CMDLINE_LINUX_DEFAULT=\"\"` 中添加 `nvidia_drm.modeset=1`:\n\n ```bash\n GRUB_CMDLINE_LINUX_DEFAULT=\"nvidia_drm.modeset=1\"\n ```\n\n2. 重新生成 grub 配置:\n\n ```bash\n sudo grub-mkconfig -o /boot/grub/grub.cfg\n ```\n\n3. 配置 **Plasma 环境**文件:\n\n 在 `~/.config/plasma-workspace/env/nvidia.sh` 中写入以下内容:\n\n ```bash\n #!/bin/bash \n export __NV_PRIME_RENDER_OFFLOAD=1 \n export __GLX_VENDOR_LIBRARY_NAME=nvidia\n ```\n\n4. 保存并重启电脑,即可生效。\n\n![Wayland Nvidia配置](/images/b2054bbaf6197624d38cc2007d885fd1454454985.png)\n\n---\n\n## I+N 混合显卡方案\n\n如果不希望全局启用独显,可以选择让大部分程序默认使用核显,而少数高性能需求的程序使用独显。这种方法能有效节省功耗,同时将独显资源集中分配给需要的程序(如 Steam 游戏、Blender 等)。缺点是每个程序需要手动配置启动项。\n\n### 配置步骤\n\n1. 打开程序的 `.desktop` 启动文件:\n\n 位置可能在 `/usr/share/applications` 或 `~/.local/share/applications` 中。\n\n2. 在 `Exec=` 后添加 `prime-run` 参数。例如:\n\n ```text\n Exec=prime-run <程序启动命令>\n ```\n\n ![混合显卡配置示意图](/images/71a5357ef4bd808b10429bc2ea46cb6f454454985.png)\n\n### Vim 快捷配置\n\n如果使用 Vim,可以使用以下快捷键快速批量替换 `Exec=` 为 `Exec=prime-run`:\n\n```vim\nv -> G -> :s/Exec=/Exec=prime-run /g Enter -> :wq Enter\n```\n\n---\n\n## 让 Plasma 桌面也使用独显\n\n如果希望 Plasma 桌面也通过独显运行,可以修改 Wayland 配置文件并删除第二行:\n\n```bash\n#!/bin/bash \nexport __GLX_VENDOR_LIBRARY_NAME=nvidia\n```\n\n这样 Plasma 桌面会通过独显启动,其他程序则默认使用核显。\n\n---\n\n希望以上经验能为有此需求的用户提供参考帮助。\n","source":"_posts/arch-nvidia.md","raw":"---\ntitle: Archlinux的KDE Plasma优先启用Nvidia独立显卡和混合显卡配置指北\ndate: 2024-11-06 10:02:04\ntags: 技术\ncategories: [技术分享]\n---\n本文将介绍在 **X11** 和 **Wayland** 两种会话下,如何在 KDE Plasma 中优先启用 Nvidia 独立显卡,并提供 I+N 混合显卡的配置方案。**首先,请确保您已经正确安装了 Nvidia 驱动**(如果非 Mainline 内核,请使用 `nvidia-dkms` 或 `nvidia-open-dkms` 版本)。\n\n![Nvidia设置示意图](/images/5364bba6d035326e82c53504dd53e7c2454454985.png)\n\n### 适用系统\n\n对于 **CachyOS** 或 **EndeavourOS** 等 Arch Linux 衍生版,这些配置大多开箱即用,但对于刚刚入坑 Arch Linux 且使用 KDE 的新手可能会遇到这样的问题:\n\n- Nvidia 驱动已安装,`nvidia-smi` 输出正常\n- KDE 系统信息显示仍在使用核显,程序运行时也优先使用核显\n- 导致某些应用(如浏览器、Blender)可能出现卡顿或掉帧现象\n\n这是因为 Arch Linux 的高自定义性,许多功能需要用户手动配置。以下是详细的解决方案。\n\n---\n\n## 配置 X11 下的 Nvidia 显卡优先\n\n可以通过配置 `/etc/X11/xorg.conf` 实现 Nvidia 独显输出。幸运的是,Nvidia 提供了自动生成配置文件的工具,用户无需手动编写:\n\n```bash\nsudo nvidia-xconfig --prime\n```\n\n该命令会根据硬件情况自动生成配置文件。执行后 **重新登录会话** 即可生效(即使是 Wayland 用户也可以执行一次此命令)。\n\n![X11 Nvidia配置](/images/1613f5602b203b38230f19699deb0219454454985.png)\n\n---\n\n## 配置 Wayland 下的 Nvidia 显卡优先\n\n在 Wayland 下优先启用 Nvidia 显卡的步骤如下:\n\n1. 编辑 **GRUB 配置**文件:\n\n 打开 `/etc/default/grub` 文件,在 `GRUB_CMDLINE_LINUX_DEFAULT=\"\"` 中添加 `nvidia_drm.modeset=1`:\n\n ```bash\n GRUB_CMDLINE_LINUX_DEFAULT=\"nvidia_drm.modeset=1\"\n ```\n\n2. 重新生成 grub 配置:\n\n ```bash\n sudo grub-mkconfig -o /boot/grub/grub.cfg\n ```\n\n3. 配置 **Plasma 环境**文件:\n\n 在 `~/.config/plasma-workspace/env/nvidia.sh` 中写入以下内容:\n\n ```bash\n #!/bin/bash \n export __NV_PRIME_RENDER_OFFLOAD=1 \n export __GLX_VENDOR_LIBRARY_NAME=nvidia\n ```\n\n4. 保存并重启电脑,即可生效。\n\n![Wayland Nvidia配置](/images/b2054bbaf6197624d38cc2007d885fd1454454985.png)\n\n---\n\n## I+N 混合显卡方案\n\n如果不希望全局启用独显,可以选择让大部分程序默认使用核显,而少数高性能需求的程序使用独显。这种方法能有效节省功耗,同时将独显资源集中分配给需要的程序(如 Steam 游戏、Blender 等)。缺点是每个程序需要手动配置启动项。\n\n### 配置步骤\n\n1. 打开程序的 `.desktop` 启动文件:\n\n 位置可能在 `/usr/share/applications` 或 `~/.local/share/applications` 中。\n\n2. 在 `Exec=` 后添加 `prime-run` 参数。例如:\n\n ```text\n Exec=prime-run <程序启动命令>\n ```\n\n ![混合显卡配置示意图](/images/71a5357ef4bd808b10429bc2ea46cb6f454454985.png)\n\n### Vim 快捷配置\n\n如果使用 Vim,可以使用以下快捷键快速批量替换 `Exec=` 为 `Exec=prime-run`:\n\n```vim\nv -> G -> :s/Exec=/Exec=prime-run /g Enter -> :wq Enter\n```\n\n---\n\n## 让 Plasma 桌面也使用独显\n\n如果希望 Plasma 桌面也通过独显运行,可以修改 Wayland 配置文件并删除第二行:\n\n```bash\n#!/bin/bash \nexport __GLX_VENDOR_LIBRARY_NAME=nvidia\n```\n\n这样 Plasma 桌面会通过独显启动,其他程序则默认使用核显。\n\n---\n\n希望以上经验能为有此需求的用户提供参考帮助。\n","slug":"arch-nvidia","published":1,"updated":"2025-02-02T11:21:35.673Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2no000ryg2b1mzearq0","content":"

本文将介绍在 X11Wayland 两种会话下,如何在 KDE Plasma 中优先启用 Nvidia 独立显卡,并提供 I+N 混合显卡的配置方案。首先,请确保您已经正确安装了 Nvidia 驱动(如果非 Mainline 内核,请使用 nvidia-dkmsnvidia-open-dkms 版本)。

\n

\"Nvidia设置示意图\"

\n

适用系统

对于 CachyOSEndeavourOS 等 Arch Linux 衍生版,这些配置大多开箱即用,但对于刚刚入坑 Arch Linux 且使用 KDE 的新手可能会遇到这样的问题:

\n
    \n
  • Nvidia 驱动已安装,nvidia-smi 输出正常
  • \n
  • KDE 系统信息显示仍在使用核显,程序运行时也优先使用核显
  • \n
  • 导致某些应用(如浏览器、Blender)可能出现卡顿或掉帧现象
  • \n
\n

这是因为 Arch Linux 的高自定义性,许多功能需要用户手动配置。以下是详细的解决方案。

\n
\n

配置 X11 下的 Nvidia 显卡优先

可以通过配置 /etc/X11/xorg.conf 实现 Nvidia 独显输出。幸运的是,Nvidia 提供了自动生成配置文件的工具,用户无需手动编写:

\n
1
sudo nvidia-xconfig --prime
\n\n

该命令会根据硬件情况自动生成配置文件。执行后 重新登录会话 即可生效(即使是 Wayland 用户也可以执行一次此命令)。

\n

\"X11

\n
\n

配置 Wayland 下的 Nvidia 显卡优先

在 Wayland 下优先启用 Nvidia 显卡的步骤如下:

\n
    \n
  1. 编辑 GRUB 配置文件:

    \n

    打开 /etc/default/grub 文件,在 GRUB_CMDLINE_LINUX_DEFAULT="" 中添加 nvidia_drm.modeset=1

    \n
    1
    GRUB_CMDLINE_LINUX_DEFAULT="nvidia_drm.modeset=1"
    \n
  2. \n
  3. 重新生成 grub 配置:

    \n
    1
    sudo grub-mkconfig -o /boot/grub/grub.cfg
    \n
  4. \n
  5. 配置 Plasma 环境文件:

    \n

    ~/.config/plasma-workspace/env/nvidia.sh 中写入以下内容:

    \n
    1
    2
    3
    #!/bin/bash 
    export __NV_PRIME_RENDER_OFFLOAD=1
    export __GLX_VENDOR_LIBRARY_NAME=nvidia
    \n
  6. \n
  7. 保存并重启电脑,即可生效。

    \n
  8. \n
\n

\"Wayland

\n
\n

I+N 混合显卡方案

如果不希望全局启用独显,可以选择让大部分程序默认使用核显,而少数高性能需求的程序使用独显。这种方法能有效节省功耗,同时将独显资源集中分配给需要的程序(如 Steam 游戏、Blender 等)。缺点是每个程序需要手动配置启动项。

\n

配置步骤

    \n
  1. 打开程序的 .desktop 启动文件:

    \n

    位置可能在 /usr/share/applications~/.local/share/applications 中。

    \n
  2. \n
  3. Exec= 后添加 prime-run 参数。例如:

    \n
    1
    Exec=prime-run <程序启动命令>
    \n\n

    \"混合显卡配置示意图\"

    \n
  4. \n
\n

Vim 快捷配置

如果使用 Vim,可以使用以下快捷键快速批量替换 Exec=Exec=prime-run

\n
1
v -> G -> :s/Exec=/Exec=prime-run /g Enter -> :wq Enter
\n\n
\n

让 Plasma 桌面也使用独显

如果希望 Plasma 桌面也通过独显运行,可以修改 Wayland 配置文件并删除第二行:

\n
1
2
#!/bin/bash 
export __GLX_VENDOR_LIBRARY_NAME=nvidia
\n\n

这样 Plasma 桌面会通过独显启动,其他程序则默认使用核显。

\n
\n

希望以上经验能为有此需求的用户提供参考帮助。

\n","excerpt":"","more":"

本文将介绍在 X11Wayland 两种会话下,如何在 KDE Plasma 中优先启用 Nvidia 独立显卡,并提供 I+N 混合显卡的配置方案。首先,请确保您已经正确安装了 Nvidia 驱动(如果非 Mainline 内核,请使用 nvidia-dkmsnvidia-open-dkms 版本)。

\n

\"Nvidia设置示意图\"

\n

适用系统

对于 CachyOSEndeavourOS 等 Arch Linux 衍生版,这些配置大多开箱即用,但对于刚刚入坑 Arch Linux 且使用 KDE 的新手可能会遇到这样的问题:

\n
    \n
  • Nvidia 驱动已安装,nvidia-smi 输出正常
  • \n
  • KDE 系统信息显示仍在使用核显,程序运行时也优先使用核显
  • \n
  • 导致某些应用(如浏览器、Blender)可能出现卡顿或掉帧现象
  • \n
\n

这是因为 Arch Linux 的高自定义性,许多功能需要用户手动配置。以下是详细的解决方案。

\n
\n

配置 X11 下的 Nvidia 显卡优先

可以通过配置 /etc/X11/xorg.conf 实现 Nvidia 独显输出。幸运的是,Nvidia 提供了自动生成配置文件的工具,用户无需手动编写:

\n
1
sudo nvidia-xconfig --prime
\n\n

该命令会根据硬件情况自动生成配置文件。执行后 重新登录会话 即可生效(即使是 Wayland 用户也可以执行一次此命令)。

\n

\"X11

\n
\n

配置 Wayland 下的 Nvidia 显卡优先

在 Wayland 下优先启用 Nvidia 显卡的步骤如下:

\n
    \n
  1. 编辑 GRUB 配置文件:

    \n

    打开 /etc/default/grub 文件,在 GRUB_CMDLINE_LINUX_DEFAULT="" 中添加 nvidia_drm.modeset=1

    \n
    1
    GRUB_CMDLINE_LINUX_DEFAULT="nvidia_drm.modeset=1"
    \n
  2. \n
  3. 重新生成 grub 配置:

    \n
    1
    sudo grub-mkconfig -o /boot/grub/grub.cfg
    \n
  4. \n
  5. 配置 Plasma 环境文件:

    \n

    ~/.config/plasma-workspace/env/nvidia.sh 中写入以下内容:

    \n
    1
    2
    3
    #!/bin/bash 
    export __NV_PRIME_RENDER_OFFLOAD=1
    export __GLX_VENDOR_LIBRARY_NAME=nvidia
    \n
  6. \n
  7. 保存并重启电脑,即可生效。

    \n
  8. \n
\n

\"Wayland

\n
\n

I+N 混合显卡方案

如果不希望全局启用独显,可以选择让大部分程序默认使用核显,而少数高性能需求的程序使用独显。这种方法能有效节省功耗,同时将独显资源集中分配给需要的程序(如 Steam 游戏、Blender 等)。缺点是每个程序需要手动配置启动项。

\n

配置步骤

    \n
  1. 打开程序的 .desktop 启动文件:

    \n

    位置可能在 /usr/share/applications~/.local/share/applications 中。

    \n
  2. \n
  3. Exec= 后添加 prime-run 参数。例如:

    \n
    1
    Exec=prime-run <程序启动命令>
    \n\n

    \"混合显卡配置示意图\"

    \n
  4. \n
\n

Vim 快捷配置

如果使用 Vim,可以使用以下快捷键快速批量替换 Exec=Exec=prime-run

\n
1
v -> G -> :s/Exec=/Exec=prime-run /g Enter -> :wq Enter
\n\n
\n

让 Plasma 桌面也使用独显

如果希望 Plasma 桌面也通过独显运行,可以修改 Wayland 配置文件并删除第二行:

\n
1
2
#!/bin/bash 
export __GLX_VENDOR_LIBRARY_NAME=nvidia
\n\n

这样 Plasma 桌面会通过独显启动,其他程序则默认使用核显。

\n
\n

希望以上经验能为有此需求的用户提供参考帮助。

\n"},{"title":"Archlinux问题记录","date":"2025-03-10T14:47:08.000Z","_content":"\n前几周在使用 Arch Linux 时遇到了两个有趣的问题,顺手记录在此,或许能帮到有类似困扰的朋友~\n\n---\n\n### 1. 显卡功耗上限解锁\n**问题现象** \n使用 `nvidia-smi` 查看显卡功耗时,发现最大功耗被限制在 55W,性能无法完全释放:\n\n```bash\nnvidia-smi # 输出显示 Power Limit: 55.00 W\n```\n\n**解决方案** \n启用 NVIDIA 动态功耗管理服务即可:\n```bash\nsudo systemctl enable --now nvidia-powerd\n```\n\n---\n\n### 2. Steam Proton 输入失效\n**问题描述** \n使用 proton-cachyos 和 proton-ge-custom 版本时,Steam 游戏无法接收任何输入(键盘/手柄)。 \n⚠️ 经排查发现是 Wayland 协议兼容性问题导致。\n\n**解决方案** \n在 Steam 游戏启动选项中加入环境变量禁用 Wayland:\n```bash\nPROTON_ENABLE_WAYLAND=0 %command%\n```\n\n---\n\n","source":"_posts/archlinux-game-fix.md","raw":"---\ntitle: Archlinux问题记录\ndate: 2025-03-10 22:47:08\ntags: [生活, Archlinux]\n---\n\n前几周在使用 Arch Linux 时遇到了两个有趣的问题,顺手记录在此,或许能帮到有类似困扰的朋友~\n\n---\n\n### 1. 显卡功耗上限解锁\n**问题现象** \n使用 `nvidia-smi` 查看显卡功耗时,发现最大功耗被限制在 55W,性能无法完全释放:\n\n```bash\nnvidia-smi # 输出显示 Power Limit: 55.00 W\n```\n\n**解决方案** \n启用 NVIDIA 动态功耗管理服务即可:\n```bash\nsudo systemctl enable --now nvidia-powerd\n```\n\n---\n\n### 2. Steam Proton 输入失效\n**问题描述** \n使用 proton-cachyos 和 proton-ge-custom 版本时,Steam 游戏无法接收任何输入(键盘/手柄)。 \n⚠️ 经排查发现是 Wayland 协议兼容性问题导致。\n\n**解决方案** \n在 Steam 游戏启动选项中加入环境变量禁用 Wayland:\n```bash\nPROTON_ENABLE_WAYLAND=0 %command%\n```\n\n---\n\n","slug":"archlinux-game-fix","published":1,"updated":"2025-03-10T14:54:16.788Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2no000uyg2bdp21f5ks","content":"

前几周在使用 Arch Linux 时遇到了两个有趣的问题,顺手记录在此,或许能帮到有类似困扰的朋友~

\n
\n

1. 显卡功耗上限解锁

问题现象
使用 nvidia-smi 查看显卡功耗时,发现最大功耗被限制在 55W,性能无法完全释放:

\n
1
nvidia-smi  # 输出显示 Power Limit: 55.00 W
\n\n

解决方案
启用 NVIDIA 动态功耗管理服务即可:

\n
1
sudo systemctl enable --now nvidia-powerd
\n\n
\n

2. Steam Proton 输入失效

问题描述
使用 proton-cachyos 和 proton-ge-custom 版本时,Steam 游戏无法接收任何输入(键盘/手柄)。
⚠️ 经排查发现是 Wayland 协议兼容性问题导致。

\n

解决方案
在 Steam 游戏启动选项中加入环境变量禁用 Wayland:

\n
1
PROTON_ENABLE_WAYLAND=0 %command%
\n\n
\n","excerpt":"","more":"

前几周在使用 Arch Linux 时遇到了两个有趣的问题,顺手记录在此,或许能帮到有类似困扰的朋友~

\n
\n

1. 显卡功耗上限解锁

问题现象
使用 nvidia-smi 查看显卡功耗时,发现最大功耗被限制在 55W,性能无法完全释放:

\n
1
nvidia-smi  # 输出显示 Power Limit: 55.00 W
\n\n

解决方案
启用 NVIDIA 动态功耗管理服务即可:

\n
1
sudo systemctl enable --now nvidia-powerd
\n\n
\n

2. Steam Proton 输入失效

问题描述
使用 proton-cachyos 和 proton-ge-custom 版本时,Steam 游戏无法接收任何输入(键盘/手柄)。
⚠️ 经排查发现是 Wayland 协议兼容性问题导致。

\n

解决方案
在 Steam 游戏启动选项中加入环境变量禁用 Wayland:

\n
1
PROTON_ENABLE_WAYLAND=0 %command%
\n\n
\n"},{"title":"Archlinux KDE体验优化总结","date":"2025-02-02T10:43:26.000Z","_content":"\n打算开一个坑记录这么久以来的Archlinux系统性能和操作体验优化经验\n\n本文章长期更新\n\n------\n\n## 更换CachyOS优化仓库\n\n![CachyOS Logo](https://wiki.cachyos.org/_astro/logo.DVTdAJi6.svg) \n通过 CachyOS 优化仓库获取 CPU 指令集级优化(x86-64-v3/v4/zen4)的软件包,提升 Arch Linux 系统性能。该仓库提供 PGO/LTO/BOLT 编译优化及持续维护的定制软件包。\n\n---\n\n### ▎前置准备\n**⚠️ 兼容性警告** \n1. 可以先通过命令`/usr/lib64/ld-linux-x86-64.so.2 --help | grep -i x86-64-`来查看你的处理器支持等级。\n2. 注意添加 `cachyos` 主仓库会替换官方 pacman 仓库(含 INSTALLED_FROM 等特性) \n ```bash\n cachyos-v3 # AVX2 优化\n cachyos-v4 # AVX512 优化\n cachyos-extra # 扩展软件包\n ```\n3. CachyOS官方在前段时间专门推出了针对 AMD 的Zen4和Zen5架构优化仓库,如有需要可以[点击这里](https://discuss.cachyos.org/t/zen-4-5-optimized-repository-testing/713/7)查看如何部署。\n\n---\n\n### ▎仓库配置流程\n#### ▶ 自动配置脚本\n```bash\n# 下载配置工具\ncurl -LO https://mirror.cachyos.org/cachyos-repo.tar.xz\ntar xvf cachyos-repo.tar.xz && cd cachyos-repo\n\n# 执行自动配置(自动检测 CPU 指令集)\nsudo ./cachyos-repo.sh\n```\n📌 脚本特性: \n- 自动备份 `/etc/pacman.conf` \n- 智能匹配最优指令集版本 (v3/v4) \n- 支持 x86_64 和 aarch64 架构 \n\n#### ▶ 手动配置方式\n1. 编辑 pacman.conf\n```ini\n# 在 /etc/pacman.conf 末尾添加(示例为 AVX2 优化)\n[cachyos-v3]\nSigLevel = Optional TrustAll\nInclude = /etc/pacman.d/cachyos-v3\n```\n\n2. ✅同步仓库数据库\n```bash\nsudo pacman -Syu\n```\n\n---\n\n### ▎仓库卸载方法\n#### ▶ 自动卸载\n```bash\ncd cachyos-repo\nsudo ./cachyos-repo.sh --remove\n```\n\n#### ▶ 手动卸载\n1. 删除 pacman.conf 中的 cachyos 仓库段\n2. 移除配置文件\n```bash\nsudo rm -rf /etc/pacman.d/cachyos*\n```\n---\n\n## 内核更换\n\n## KDE 配置\n\n\n\n","source":"_posts/archlinux-optimization.md","raw":"---\ntitle: Archlinux KDE体验优化总结\ndate: 2025-02-02 18:43:26\ntags: [Archlinux, 系统优化, 技术分享]\n---\n\n打算开一个坑记录这么久以来的Archlinux系统性能和操作体验优化经验\n\n本文章长期更新\n\n------\n\n## 更换CachyOS优化仓库\n\n![CachyOS Logo](https://wiki.cachyos.org/_astro/logo.DVTdAJi6.svg) \n通过 CachyOS 优化仓库获取 CPU 指令集级优化(x86-64-v3/v4/zen4)的软件包,提升 Arch Linux 系统性能。该仓库提供 PGO/LTO/BOLT 编译优化及持续维护的定制软件包。\n\n---\n\n### ▎前置准备\n**⚠️ 兼容性警告** \n1. 可以先通过命令`/usr/lib64/ld-linux-x86-64.so.2 --help | grep -i x86-64-`来查看你的处理器支持等级。\n2. 注意添加 `cachyos` 主仓库会替换官方 pacman 仓库(含 INSTALLED_FROM 等特性) \n ```bash\n cachyos-v3 # AVX2 优化\n cachyos-v4 # AVX512 优化\n cachyos-extra # 扩展软件包\n ```\n3. CachyOS官方在前段时间专门推出了针对 AMD 的Zen4和Zen5架构优化仓库,如有需要可以[点击这里](https://discuss.cachyos.org/t/zen-4-5-optimized-repository-testing/713/7)查看如何部署。\n\n---\n\n### ▎仓库配置流程\n#### ▶ 自动配置脚本\n```bash\n# 下载配置工具\ncurl -LO https://mirror.cachyos.org/cachyos-repo.tar.xz\ntar xvf cachyos-repo.tar.xz && cd cachyos-repo\n\n# 执行自动配置(自动检测 CPU 指令集)\nsudo ./cachyos-repo.sh\n```\n📌 脚本特性: \n- 自动备份 `/etc/pacman.conf` \n- 智能匹配最优指令集版本 (v3/v4) \n- 支持 x86_64 和 aarch64 架构 \n\n#### ▶ 手动配置方式\n1. 编辑 pacman.conf\n```ini\n# 在 /etc/pacman.conf 末尾添加(示例为 AVX2 优化)\n[cachyos-v3]\nSigLevel = Optional TrustAll\nInclude = /etc/pacman.d/cachyos-v3\n```\n\n2. ✅同步仓库数据库\n```bash\nsudo pacman -Syu\n```\n\n---\n\n### ▎仓库卸载方法\n#### ▶ 自动卸载\n```bash\ncd cachyos-repo\nsudo ./cachyos-repo.sh --remove\n```\n\n#### ▶ 手动卸载\n1. 删除 pacman.conf 中的 cachyos 仓库段\n2. 移除配置文件\n```bash\nsudo rm -rf /etc/pacman.d/cachyos*\n```\n---\n\n## 内核更换\n\n## KDE 配置\n\n\n\n","slug":"archlinux-optimization","published":1,"updated":"2025-03-10T15:07:26.618Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2no000xyg2b562l4a0z","content":"

打算开一个坑记录这么久以来的Archlinux系统性能和操作体验优化经验

\n

本文章长期更新

\n
\n

更换CachyOS优化仓库

\"CachyOS
通过 CachyOS 优化仓库获取 CPU 指令集级优化(x86-64-v3/v4/zen4)的软件包,提升 Arch Linux 系统性能。该仓库提供 PGO/LTO/BOLT 编译优化及持续维护的定制软件包。

\n
\n

▎前置准备

⚠️ 兼容性警告

\n
    \n
  1. 可以先通过命令/usr/lib64/ld-linux-x86-64.so.2 --help | grep -i x86-64-来查看你的处理器支持等级。
  2. \n
  3. 注意添加 cachyos 主仓库会替换官方 pacman 仓库(含 INSTALLED_FROM 等特性)
    1
    2
    3
    cachyos-v3    # AVX2 优化
    cachyos-v4 # AVX512 优化
    cachyos-extra # 扩展软件包
  4. \n
  5. CachyOS官方在前段时间专门推出了针对 AMD 的Zen4和Zen5架构优化仓库,如有需要可以点击这里查看如何部署。
  6. \n
\n
\n

▎仓库配置流程

▶ 自动配置脚本

1
2
3
4
5
6
# 下载配置工具
curl -LO https://mirror.cachyos.org/cachyos-repo.tar.xz
tar xvf cachyos-repo.tar.xz && cd cachyos-repo

# 执行自动配置(自动检测 CPU 指令集)
sudo ./cachyos-repo.sh
\n

📌 脚本特性:

\n
    \n
  • 自动备份 /etc/pacman.conf
  • \n
  • 智能匹配最优指令集版本 (v3/v4)
  • \n
  • 支持 x86_64 和 aarch64 架构
  • \n
\n

▶ 手动配置方式

    \n
  1. 编辑 pacman.conf

    \n
    1
    2
    3
    4
    # 在 /etc/pacman.conf 末尾添加(示例为 AVX2 优化)
    [cachyos-v3]
    SigLevel = Optional TrustAll
    Include = /etc/pacman.d/cachyos-v3
    \n
  2. \n
  3. ✅同步仓库数据库

    \n
    1
    sudo pacman -Syu
  4. \n
\n
\n

▎仓库卸载方法

▶ 自动卸载

1
2
cd cachyos-repo
sudo ./cachyos-repo.sh --remove
\n\n

▶ 手动卸载

    \n
  1. 删除 pacman.conf 中的 cachyos 仓库段
  2. \n
  3. 移除配置文件
    1
    sudo rm -rf /etc/pacman.d/cachyos*
  4. \n
\n
\n

内核更换

KDE 配置

","excerpt":"","more":"

打算开一个坑记录这么久以来的Archlinux系统性能和操作体验优化经验

\n

本文章长期更新

\n
\n

更换CachyOS优化仓库

\"CachyOS
通过 CachyOS 优化仓库获取 CPU 指令集级优化(x86-64-v3/v4/zen4)的软件包,提升 Arch Linux 系统性能。该仓库提供 PGO/LTO/BOLT 编译优化及持续维护的定制软件包。

\n
\n

▎前置准备

⚠️ 兼容性警告

\n
    \n
  1. 可以先通过命令/usr/lib64/ld-linux-x86-64.so.2 --help | grep -i x86-64-来查看你的处理器支持等级。
  2. \n
  3. 注意添加 cachyos 主仓库会替换官方 pacman 仓库(含 INSTALLED_FROM 等特性)
    1
    2
    3
    cachyos-v3    # AVX2 优化
    cachyos-v4 # AVX512 优化
    cachyos-extra # 扩展软件包
  4. \n
  5. CachyOS官方在前段时间专门推出了针对 AMD 的Zen4和Zen5架构优化仓库,如有需要可以点击这里查看如何部署。
  6. \n
\n
\n

▎仓库配置流程

▶ 自动配置脚本

1
2
3
4
5
6
# 下载配置工具
curl -LO https://mirror.cachyos.org/cachyos-repo.tar.xz
tar xvf cachyos-repo.tar.xz && cd cachyos-repo

# 执行自动配置(自动检测 CPU 指令集)
sudo ./cachyos-repo.sh
\n

📌 脚本特性:

\n
    \n
  • 自动备份 /etc/pacman.conf
  • \n
  • 智能匹配最优指令集版本 (v3/v4)
  • \n
  • 支持 x86_64 和 aarch64 架构
  • \n
\n

▶ 手动配置方式

    \n
  1. 编辑 pacman.conf

    \n
    1
    2
    3
    4
    # 在 /etc/pacman.conf 末尾添加(示例为 AVX2 优化)
    [cachyos-v3]
    SigLevel = Optional TrustAll
    Include = /etc/pacman.d/cachyos-v3
    \n
  2. \n
  3. ✅同步仓库数据库

    \n
    1
    sudo pacman -Syu
  4. \n
\n
\n

▎仓库卸载方法

▶ 自动卸载

1
2
cd cachyos-repo
sudo ./cachyos-repo.sh --remove
\n\n

▶ 手动卸载

    \n
  1. 删除 pacman.conf 中的 cachyos 仓库段
  2. \n
  3. 移除配置文件
    1
    sudo rm -rf /etc/pacman.d/cachyos*
  4. \n
\n
\n

内核更换

KDE 配置

"},{"title":"使用Clonezilla备份和克隆系统","date":"2025-02-23T13:40:03.000Z","_content":"![Clonezilla官网](/images/clonezilla.png)\n[Clonezilla](https://clonezilla.org/)是一款非常好用的系统迁移工具,本文将介绍其基本用法(不包含网络迁移及Clonezilla服务器等进阶内容)以及在克隆Btrfs磁盘时遇到的问题解法。\n\n---\n### **零.事前准备**:\n - 下载Clonezilla Live镜像(ISO),制作启动U盘(有手就行)。\n - 准备目标存储设备(如U盘,需要迁移的新硬盘等),确保有足够空间(建议大于源硬盘已用空间的1.2倍)。\n\n\n### **一.备份镜像到硬盘(Device-to-Image)**\n这个模式可以将硬盘完整备份为一个镜像文件(可保存到本地硬盘、外置硬盘或网络存储)方便多机部署。注意如果只是将一个硬盘的系统完全克隆到新盘可以跳过此部分。\n\n#### **步骤说明**:\n2. **启动Clonezilla**:\n - 插入U盘,重启电脑并从U盘启动。\n - 选择默认选项(语言,键盘布局,Beginner模式)进入Clonezilla的TUI界面。\n\n3. **选择备份模式**:\n ```plaintext\n Choose mode: 选择 \"device-image\"(设备到镜像)\n Mount storage media: 选择 \"local_dev\"(本地存储设备)\n ```\n - 按提示挂载目标存储设备(注意这里选择的是你要存储镜像的设备且文件系统一般不限,如外置硬盘),确认路径(如 `/dev/sdb1`)。\n\n4. **配置备份参数**:\n - **源硬盘**:选择需要备份的硬盘(如 `/dev/sda`)。\n - **镜像存储路径**:指定目标位置(如外置硬盘的挂载目录)。\n - **镜像名称**:自定义名称(如 `2025-img-rockylinux-2-21`)。\n - **压缩选项**:默认即可,支持并行压缩加速。\n - **镜像分割**:若目标存储设备为FAT32格式(单文件最大4GB),选择自动分割。\n\n5. **确认操作**:\n - 检查提示信息,输入 `y` 开始备份。\n - 完成后关机或重启。\n\n6. **镜像还原**:和备份非常相似,只是选项换成restore to disk,顺着指引操作即可。\n---\n\n### **二、直接克隆硬盘(Device-to-Device)**\n将源硬盘完整克隆到目标硬盘(适合硬盘升级或快速迁移),比如笔者最近白嫖了一个三星的2T硬盘直接把原来512G硬盘里的CachyOS无损迁移了进去。\n\n#### **步骤说明**:\n1. **准备工作**:\n - 连接目标硬盘(需容量≥源硬盘已用空间,还原镜像操作也是如此,注意Clonezilla支持小分区到大分区迁移不支持大分区到小分区,后者出门右转Rsync)\n - **警告**:目标硬盘数据将被覆盖,操作前如有需要务必备份重要数据!\n\n2. **启动Clonezilla**:\n - 同上,从U盘启动进入Clonezilla界面。\n\n3. **选择克隆模式**:\n ```plaintext\n Choose mode: 选择 \"device-device\"(设备到设备)\n ```\n\n4. **选择硬盘**:\n - **母碟硬盘**:选择原始硬盘(如 `/dev/sda`)。\n - **目标硬盘**:选择新硬盘(如 `/dev/sdb`)。\n\n5. **克隆选项**:\n 需要进入专家模式才能看到,一般直接新手模式默认即可。\n\n6. **执行克隆**:\n - 确认提示信息后输入 `y`,等待完成。\n - 克隆结束后关机,移除旧硬盘并测试新硬盘(主要是查看能不能启动进入系统,若能进入一般不会有问题,而且一般都能进入因为Clonezilla是高精确的块对块克隆)。\n\n---\n### **两种模式对比**:\n| **模式** | 特点 | \n|----------------|---------------------------|\n| 备份镜像 | 方便多机部署也可以用于留档|\n| 直接克隆 | 换硬盘快速迁移无需恢复过程|\n---\n\n### **Btrfs务必注意**:\n对Btrfs直接进行Clonezilla克隆大概率会碰到一个边界错误,这是由于Btrfs本身使用一段时间后碎片化存储导致的,你需要执行`sudo btrfs balance start --full-balance /`来进行整理,但是这也算是一个风险操作要确保完整执行不能在执行时意外中断导致文件系统出错,并且在后续克隆时不能使用新手模式要进入专家模式勾选-p1支持所有文件系统但是效率降低的选项来确保顺利克隆(未勾选此选项可能导致文件系统无法识别的错误),其他步骤参照前文即可。\n\n","source":"_posts/clonezilla.md","raw":"---\ntitle: 使用Clonezilla备份和克隆系统\ndate: 2025-02-23 21:40:03\ntags: 技术分享\n---\n![Clonezilla官网](/images/clonezilla.png)\n[Clonezilla](https://clonezilla.org/)是一款非常好用的系统迁移工具,本文将介绍其基本用法(不包含网络迁移及Clonezilla服务器等进阶内容)以及在克隆Btrfs磁盘时遇到的问题解法。\n\n---\n### **零.事前准备**:\n - 下载Clonezilla Live镜像(ISO),制作启动U盘(有手就行)。\n - 准备目标存储设备(如U盘,需要迁移的新硬盘等),确保有足够空间(建议大于源硬盘已用空间的1.2倍)。\n\n\n### **一.备份镜像到硬盘(Device-to-Image)**\n这个模式可以将硬盘完整备份为一个镜像文件(可保存到本地硬盘、外置硬盘或网络存储)方便多机部署。注意如果只是将一个硬盘的系统完全克隆到新盘可以跳过此部分。\n\n#### **步骤说明**:\n2. **启动Clonezilla**:\n - 插入U盘,重启电脑并从U盘启动。\n - 选择默认选项(语言,键盘布局,Beginner模式)进入Clonezilla的TUI界面。\n\n3. **选择备份模式**:\n ```plaintext\n Choose mode: 选择 \"device-image\"(设备到镜像)\n Mount storage media: 选择 \"local_dev\"(本地存储设备)\n ```\n - 按提示挂载目标存储设备(注意这里选择的是你要存储镜像的设备且文件系统一般不限,如外置硬盘),确认路径(如 `/dev/sdb1`)。\n\n4. **配置备份参数**:\n - **源硬盘**:选择需要备份的硬盘(如 `/dev/sda`)。\n - **镜像存储路径**:指定目标位置(如外置硬盘的挂载目录)。\n - **镜像名称**:自定义名称(如 `2025-img-rockylinux-2-21`)。\n - **压缩选项**:默认即可,支持并行压缩加速。\n - **镜像分割**:若目标存储设备为FAT32格式(单文件最大4GB),选择自动分割。\n\n5. **确认操作**:\n - 检查提示信息,输入 `y` 开始备份。\n - 完成后关机或重启。\n\n6. **镜像还原**:和备份非常相似,只是选项换成restore to disk,顺着指引操作即可。\n---\n\n### **二、直接克隆硬盘(Device-to-Device)**\n将源硬盘完整克隆到目标硬盘(适合硬盘升级或快速迁移),比如笔者最近白嫖了一个三星的2T硬盘直接把原来512G硬盘里的CachyOS无损迁移了进去。\n\n#### **步骤说明**:\n1. **准备工作**:\n - 连接目标硬盘(需容量≥源硬盘已用空间,还原镜像操作也是如此,注意Clonezilla支持小分区到大分区迁移不支持大分区到小分区,后者出门右转Rsync)\n - **警告**:目标硬盘数据将被覆盖,操作前如有需要务必备份重要数据!\n\n2. **启动Clonezilla**:\n - 同上,从U盘启动进入Clonezilla界面。\n\n3. **选择克隆模式**:\n ```plaintext\n Choose mode: 选择 \"device-device\"(设备到设备)\n ```\n\n4. **选择硬盘**:\n - **母碟硬盘**:选择原始硬盘(如 `/dev/sda`)。\n - **目标硬盘**:选择新硬盘(如 `/dev/sdb`)。\n\n5. **克隆选项**:\n 需要进入专家模式才能看到,一般直接新手模式默认即可。\n\n6. **执行克隆**:\n - 确认提示信息后输入 `y`,等待完成。\n - 克隆结束后关机,移除旧硬盘并测试新硬盘(主要是查看能不能启动进入系统,若能进入一般不会有问题,而且一般都能进入因为Clonezilla是高精确的块对块克隆)。\n\n---\n### **两种模式对比**:\n| **模式** | 特点 | \n|----------------|---------------------------|\n| 备份镜像 | 方便多机部署也可以用于留档|\n| 直接克隆 | 换硬盘快速迁移无需恢复过程|\n---\n\n### **Btrfs务必注意**:\n对Btrfs直接进行Clonezilla克隆大概率会碰到一个边界错误,这是由于Btrfs本身使用一段时间后碎片化存储导致的,你需要执行`sudo btrfs balance start --full-balance /`来进行整理,但是这也算是一个风险操作要确保完整执行不能在执行时意外中断导致文件系统出错,并且在后续克隆时不能使用新手模式要进入专家模式勾选-p1支持所有文件系统但是效率降低的选项来确保顺利克隆(未勾选此选项可能导致文件系统无法识别的错误),其他步骤参照前文即可。\n\n","slug":"clonezilla","published":1,"updated":"2025-02-23T14:12:10.654Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2np000zyg2b71sc7uxz","content":"

\"Clonezilla官网\"
Clonezilla是一款非常好用的系统迁移工具,本文将介绍其基本用法(不包含网络迁移及Clonezilla服务器等进阶内容)以及在克隆Btrfs磁盘时遇到的问题解法。

\n
\n

零.事前准备

    \n
  • 下载Clonezilla Live镜像(ISO),制作启动U盘(有手就行)。
  • \n
  • 准备目标存储设备(如U盘,需要迁移的新硬盘等),确保有足够空间(建议大于源硬盘已用空间的1.2倍)。
  • \n
\n

一.备份镜像到硬盘(Device-to-Image)

这个模式可以将硬盘完整备份为一个镜像文件(可保存到本地硬盘、外置硬盘或网络存储)方便多机部署。注意如果只是将一个硬盘的系统完全克隆到新盘可以跳过此部分。

\n

步骤说明

    \n
  1. 启动Clonezilla

    \n
      \n
    • 插入U盘,重启电脑并从U盘启动。
    • \n
    • 选择默认选项(语言,键盘布局,Beginner模式)进入Clonezilla的TUI界面。
    • \n
    \n
  2. \n
  3. 选择备份模式

    \n
    1
    2
    Choose mode:         选择 "device-image"(设备到镜像)
    Mount storage media: 选择 "local_dev"(本地存储设备)
    \n
      \n
    • 按提示挂载目标存储设备(注意这里选择的是你要存储镜像的设备且文件系统一般不限,如外置硬盘),确认路径(如 /dev/sdb1)。
    • \n
    \n
  4. \n
  5. 配置备份参数

    \n
      \n
    • 源硬盘:选择需要备份的硬盘(如 /dev/sda)。
    • \n
    • 镜像存储路径:指定目标位置(如外置硬盘的挂载目录)。
    • \n
    • 镜像名称:自定义名称(如 2025-img-rockylinux-2-21)。
    • \n
    • 压缩选项:默认即可,支持并行压缩加速。
    • \n
    • 镜像分割:若目标存储设备为FAT32格式(单文件最大4GB),选择自动分割。
    • \n
    \n
  6. \n
  7. 确认操作

    \n
      \n
    • 检查提示信息,输入 y 开始备份。
    • \n
    • 完成后关机或重启。
    • \n
    \n
  8. \n
  9. 镜像还原:和备份非常相似,只是选项换成restore to disk,顺着指引操作即可。

    \n
  10. \n
\n
\n

二、直接克隆硬盘(Device-to-Device)

将源硬盘完整克隆到目标硬盘(适合硬盘升级或快速迁移),比如笔者最近白嫖了一个三星的2T硬盘直接把原来512G硬盘里的CachyOS无损迁移了进去。

\n

步骤说明

    \n
  1. 准备工作

    \n
      \n
    • 连接目标硬盘(需容量≥源硬盘已用空间,还原镜像操作也是如此,注意Clonezilla支持小分区到大分区迁移不支持大分区到小分区,后者出门右转Rsync)
    • \n
    • 警告:目标硬盘数据将被覆盖,操作前如有需要务必备份重要数据!
    • \n
    \n
  2. \n
  3. 启动Clonezilla

    \n
      \n
    • 同上,从U盘启动进入Clonezilla界面。
    • \n
    \n
  4. \n
  5. 选择克隆模式

    \n
    1
    Choose mode:         选择 "device-device"(设备到设备)
    \n
  6. \n
  7. 选择硬盘

    \n
      \n
    • 母碟硬盘:选择原始硬盘(如 /dev/sda)。
    • \n
    • 目标硬盘:选择新硬盘(如 /dev/sdb)。
    • \n
    \n
  8. \n
  9. 克隆选项
    需要进入专家模式才能看到,一般直接新手模式默认即可。

    \n
  10. \n
  11. 执行克隆

    \n
      \n
    • 确认提示信息后输入 y,等待完成。
    • \n
    • 克隆结束后关机,移除旧硬盘并测试新硬盘(主要是查看能不能启动进入系统,若能进入一般不会有问题,而且一般都能进入因为Clonezilla是高精确的块对块克隆)。
    • \n
    \n
  12. \n
\n
\n

两种模式对比

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
模式特点
备份镜像方便多机部署也可以用于留档
直接克隆换硬盘快速迁移无需恢复过程
\n
\n

Btrfs务必注意

对Btrfs直接进行Clonezilla克隆大概率会碰到一个边界错误,这是由于Btrfs本身使用一段时间后碎片化存储导致的,你需要执行sudo btrfs balance start --full-balance /来进行整理,但是这也算是一个风险操作要确保完整执行不能在执行时意外中断导致文件系统出错,并且在后续克隆时不能使用新手模式要进入专家模式勾选-p1支持所有文件系统但是效率降低的选项来确保顺利克隆(未勾选此选项可能导致文件系统无法识别的错误),其他步骤参照前文即可。

\n","excerpt":"","more":"

\"Clonezilla官网\"
Clonezilla是一款非常好用的系统迁移工具,本文将介绍其基本用法(不包含网络迁移及Clonezilla服务器等进阶内容)以及在克隆Btrfs磁盘时遇到的问题解法。

\n
\n

零.事前准备

    \n
  • 下载Clonezilla Live镜像(ISO),制作启动U盘(有手就行)。
  • \n
  • 准备目标存储设备(如U盘,需要迁移的新硬盘等),确保有足够空间(建议大于源硬盘已用空间的1.2倍)。
  • \n
\n

一.备份镜像到硬盘(Device-to-Image)

这个模式可以将硬盘完整备份为一个镜像文件(可保存到本地硬盘、外置硬盘或网络存储)方便多机部署。注意如果只是将一个硬盘的系统完全克隆到新盘可以跳过此部分。

\n

步骤说明

    \n
  1. 启动Clonezilla

    \n
      \n
    • 插入U盘,重启电脑并从U盘启动。
    • \n
    • 选择默认选项(语言,键盘布局,Beginner模式)进入Clonezilla的TUI界面。
    • \n
    \n
  2. \n
  3. 选择备份模式

    \n
    1
    2
    Choose mode:         选择 "device-image"(设备到镜像)
    Mount storage media: 选择 "local_dev"(本地存储设备)
    \n
      \n
    • 按提示挂载目标存储设备(注意这里选择的是你要存储镜像的设备且文件系统一般不限,如外置硬盘),确认路径(如 /dev/sdb1)。
    • \n
    \n
  4. \n
  5. 配置备份参数

    \n
      \n
    • 源硬盘:选择需要备份的硬盘(如 /dev/sda)。
    • \n
    • 镜像存储路径:指定目标位置(如外置硬盘的挂载目录)。
    • \n
    • 镜像名称:自定义名称(如 2025-img-rockylinux-2-21)。
    • \n
    • 压缩选项:默认即可,支持并行压缩加速。
    • \n
    • 镜像分割:若目标存储设备为FAT32格式(单文件最大4GB),选择自动分割。
    • \n
    \n
  6. \n
  7. 确认操作

    \n
      \n
    • 检查提示信息,输入 y 开始备份。
    • \n
    • 完成后关机或重启。
    • \n
    \n
  8. \n
  9. 镜像还原:和备份非常相似,只是选项换成restore to disk,顺着指引操作即可。

    \n
  10. \n
\n
\n

二、直接克隆硬盘(Device-to-Device)

将源硬盘完整克隆到目标硬盘(适合硬盘升级或快速迁移),比如笔者最近白嫖了一个三星的2T硬盘直接把原来512G硬盘里的CachyOS无损迁移了进去。

\n

步骤说明

    \n
  1. 准备工作

    \n
      \n
    • 连接目标硬盘(需容量≥源硬盘已用空间,还原镜像操作也是如此,注意Clonezilla支持小分区到大分区迁移不支持大分区到小分区,后者出门右转Rsync)
    • \n
    • 警告:目标硬盘数据将被覆盖,操作前如有需要务必备份重要数据!
    • \n
    \n
  2. \n
  3. 启动Clonezilla

    \n
      \n
    • 同上,从U盘启动进入Clonezilla界面。
    • \n
    \n
  4. \n
  5. 选择克隆模式

    \n
    1
    Choose mode:         选择 "device-device"(设备到设备)
    \n
  6. \n
  7. 选择硬盘

    \n
      \n
    • 母碟硬盘:选择原始硬盘(如 /dev/sda)。
    • \n
    • 目标硬盘:选择新硬盘(如 /dev/sdb)。
    • \n
    \n
  8. \n
  9. 克隆选项
    需要进入专家模式才能看到,一般直接新手模式默认即可。

    \n
  10. \n
  11. 执行克隆

    \n
      \n
    • 确认提示信息后输入 y,等待完成。
    • \n
    • 克隆结束后关机,移除旧硬盘并测试新硬盘(主要是查看能不能启动进入系统,若能进入一般不会有问题,而且一般都能进入因为Clonezilla是高精确的块对块克隆)。
    • \n
    \n
  12. \n
\n
\n

两种模式对比

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
模式特点
备份镜像方便多机部署也可以用于留档
直接克隆换硬盘快速迁移无需恢复过程
\n
\n

Btrfs务必注意

对Btrfs直接进行Clonezilla克隆大概率会碰到一个边界错误,这是由于Btrfs本身使用一段时间后碎片化存储导致的,你需要执行sudo btrfs balance start --full-balance /来进行整理,但是这也算是一个风险操作要确保完整执行不能在执行时意外中断导致文件系统出错,并且在后续克隆时不能使用新手模式要进入专家模式勾选-p1支持所有文件系统但是效率降低的选项来确保顺利克隆(未勾选此选项可能导致文件系统无法识别的错误),其他步骤参照前文即可。

\n"},{"title":"Random Fortune","date":"2025-05-22T07:29:07.000Z","_content":"\nKeep peace of mind with fortune...\n\n❯ fortune\n\nThe nice thing about standards is that there are so many of them to choose\nfrom.\n -- Andrew S. Tanenbaum\n\n❯ fortune\n\n看蓬门秋草,年年破巷,疏窗细雨,夜夜孤灯。\n -- 郑板桥《沁园春·恨》\n\n❯ fortune\n\nAn alcoholic is someone you don't like who drinks as much as you do.\n -- Dylan Thomas\n\n❯ fortune\n\nIf you put tomfoolery into a computer, nothing comes out but tomfoolery.\nBut this tomfoolery, having passed through a very expensive machine,\nis somehow ennobled and no-one dare criticise it.\n -- Pierre Gallois\n\n❯ fortune\n\n秋色渐将晚,霜信报黄花。\n -- 叶梦得《水调歌头·秋色渐将晚》\n\n❯ fortune\n\n水精帘里颇黎枕,暖香惹梦鸳鸯锦。江上柳如烟,雁飞残月天。\n藕丝秋色浅,人胜参差剪。双鬓隔香红,玉钗头上风。\n -- 温庭筠《菩萨蛮》\n\n❯ fortune\n\n A disciple of another sect once came to Drescher as he was eating\nhis morning meal. \"I would like to give you this personality test\", said\nthe outsider, \"because I want you to be happy.\"\n Drescher took the paper that was offered him and put it into the\ntoaster -- \"I wish the toaster to be happy too\".\n\n❯ fortune\n\nExample is not the main thing in influencing others. It is the only thing.\n -- Albert Schweitzer\n\n❯ fortune\n\nWe have art that we do not die of the truth.\n -- Nietzsche\n\n❯ fortune\n\n \"Java for the COBOL Programmer\"\n who writes these things?\n people on crack\n and cobol programmers\n :)\n that's redundant.\n\n❯ fortune\n\nI was in this prematurely air conditioned supermarket and there were all\nthese aisles and there were these bathing caps you could buy that had these\nkind of Fourth of July plumes on them that were red and yellow and blue and\nI wasn't tempted to buy one but I was reminded of the fact that I had been\navoiding the beach.\n -- Lucinda Childs \"Einstein On The Beach\"\n\n❯ fortune\n\nIt doesn't matter whether you win or lose -- until you lose.\n\n❯ fortune\n\nTrue leadership is the art of changing a group from what it is to what\nit ought to be.\n -- Virginia Allan\n\n❯ fortune\n\nI keep hearing and reading this nice proverb *if it ain't broke, don't fix\nit*. The latest appearance was in response to [Shlomi\nFish](http://community.livejournal.com/shlomif_tech/37969.html) suggesting\nthat some Ancient Perl code should be replaced by Modern Perl code.\n\nI am not saying that every piece of code should be rewritten every 6 months,\nbut in my understanding that sentence actually translates to *let's wait till\nit breaks and then panic*.\n\nI think people who say that sentence are afraid that the new version will\nbreak something. Sure, there is always a chance that a change introduces an\nerror, but, if we are afraid to touch the code, what will happen when later on\nwe encounter a case where it does not work? For example, if we need to use it\nin a new environment. Will we have the courage to change the code then? How\nmuch will it cost in money, time, and lost sleep?\n\nI think we have been trying to teach ourselves that we should have really good\ntest coverage of our code and then we can easily refactor it and get rid of\ntechnical debt. So why do we keep hearing that sentence?\n\n -- Gabor Szabo\n -- What does \"If it ain't broke, don't fix it.\" really mean? ( )\n\n❯ fortune\n\nPsychoanalysis is that mental illness for which it regards itself a therapy.\n -- Karl Kraus\n\n❯ fortune\n\nWith a rubber duck, one's never alone.\n -- \"The Hitchhiker's Guide to the Galaxy\"\n\n❯ fortune\n\n(Presuming for the sake of argument that it's even *possible* to design\nbetter code in Perl than in C. :-)\n -- Larry Wall on core code vs. module code design\n\n❯ fortune\n\nBut, officer, he's not drunk, I just saw his fingers twitch!\n\n❯ fortune\n\nImbalance of power corrupts and monopoly of power corrupts absolutely.\n -- Genji\n\n❯ fortune\n\nI want a VEGETARIAN BURRITO to go ... with EXTRA MSG!!\n\n❯ fortune\n\n如果你不知道如何表现地高人一等,找个Unix用户,让他做给你看。\n -- Dilbert newsletter 3.0, 1994\n\n❯ fortune\n\n\"The picture's pretty bleak, gentlemen... The world's climates are changing,\nthe mammals are taking over, and we all have a brain about the size of a\nwalnut.\"\n -- some dinosaurs from The Far Side, by Gary Larson\n\n❯ fortune\n\nMost of us feel that marketing types are like a dangerous weapon - keep\n'em unloaded and locked up in a cupboard, and only bring them out when\nyou need them to do a job.\n -- Craig Sanders\n\n❯ fortune\n\n不寐倦长更,披衣出户行。月寒秋竹冷,风切夜窗声。\n -- 李煜《三台令》\n\n❯ fortune\n\n子曰:“由,诲女知之乎!知之为知之,不知为不知,是知也。”\n-- 论语,为政篇\n\n❯ fortune\n\n \"The jig's up, Elman.\"\n \"Which jig?\"\n -- Jeff Elman\n\n❯ fortune\n\n\"First things first -- but not necessarily in that order\"\n -- The Doctor, \"Doctor Who\"\n\n❯ fortune\n\n波影摇涟,趁熏风,一舸来时,翠阴清昼。去郭轩楹才数里,藓磴松关云岫。\n快屐齿,筇枝先后。空半危亭堪聚远,看洞庭,缥缈争奇秀。人自老,景如旧。\n来帆去棹还知否,问古今,几度斜阳,几番回首?晓色一川谁管领,都付雨荷烟柳,\n知我者,燕朋鸥友。笑拍阑干呼范蠡,甚平吴,却倩垂纶手?吁万古,付卮酒。\n -- 周密《长亭怨慢》\n\n❯ fortune\n\nI'm telling you that the kernel is stable not because it's a kernel,\nbut because I refuse to listen to arguments like this.\n -- Linus Torvalds\n\n❯ fortune\n\nA girl's conscience doesn't really keep her from doing anything wrong--\nit merely keeps her from enjoying it.\n\n❯ fortune\n\nThere are few people more often in the wrong than those who cannot endure\nto be thought so.\n\n❯ fortune\n\nOne does not thank logic.\n -- Sarek, \"Journey to Babel\", stardate 3842.4\n\n❯ fortune\n\nFree yourself from negative influence. Negative thoughts are the old\nhabits that gnaw at the roots of the soul.\nMoses Shongo, (Seneca)\n\n❯ fortune\n\n日月照之何不及此?惟有北风号怒天上来。\n -- 李白《北风行》\n\n❯ fortune\n\nAnyone who is capable of getting themselves made President should on no\naccount be allowed to do the job.\n -- Douglas Adams, \"The Hitchhiker's Guide to the Galaxy\"\n\nAnd here are my mods:-)\n\n❯ pacman -Qs fortune\nlocal/cowfortune 0.1.2-8\n Configurable fortune cookie proclaiming cow (and a few other creatures)\nlocal/fortune-mod 3.22.0-1.1\n The Fortune Cookie Program from BSD games\nlocal/fortune-mod-zh 2.98-1 (fortune-mods)\n Chinese poems for fortune-mod\n","source":"_posts/fortune.md","raw":"---\ntitle: Random fortune \ndate: 2025-05-22 15:29:07\ntags: 日志\n---\n\nKeep peace of mind with fortune...\n\n❯ fortune\n\nThe nice thing about standards is that there are so many of them to choose\nfrom.\n -- Andrew S. Tanenbaum\n\n❯ fortune\n\n看蓬门秋草,年年破巷,疏窗细雨,夜夜孤灯。\n -- 郑板桥《沁园春·恨》\n\n❯ fortune\n\nAn alcoholic is someone you don't like who drinks as much as you do.\n -- Dylan Thomas\n\n❯ fortune\n\nIf you put tomfoolery into a computer, nothing comes out but tomfoolery.\nBut this tomfoolery, having passed through a very expensive machine,\nis somehow ennobled and no-one dare criticise it.\n -- Pierre Gallois\n\n❯ fortune\n\n秋色渐将晚,霜信报黄花。\n -- 叶梦得《水调歌头·秋色渐将晚》\n\n❯ fortune\n\n水精帘里颇黎枕,暖香惹梦鸳鸯锦。江上柳如烟,雁飞残月天。\n藕丝秋色浅,人胜参差剪。双鬓隔香红,玉钗头上风。\n -- 温庭筠《菩萨蛮》\n\n❯ fortune\n\n A disciple of another sect once came to Drescher as he was eating\nhis morning meal. \"I would like to give you this personality test\", said\nthe outsider, \"because I want you to be happy.\"\n Drescher took the paper that was offered him and put it into the\ntoaster -- \"I wish the toaster to be happy too\".\n\n❯ fortune\n\nExample is not the main thing in influencing others. It is the only thing.\n -- Albert Schweitzer\n\n❯ fortune\n\nWe have art that we do not die of the truth.\n -- Nietzsche\n\n❯ fortune\n\n \"Java for the COBOL Programmer\"\n who writes these things?\n people on crack\n and cobol programmers\n :)\n that's redundant.\n\n❯ fortune\n\nI was in this prematurely air conditioned supermarket and there were all\nthese aisles and there were these bathing caps you could buy that had these\nkind of Fourth of July plumes on them that were red and yellow and blue and\nI wasn't tempted to buy one but I was reminded of the fact that I had been\navoiding the beach.\n -- Lucinda Childs \"Einstein On The Beach\"\n\n❯ fortune\n\nIt doesn't matter whether you win or lose -- until you lose.\n\n❯ fortune\n\nTrue leadership is the art of changing a group from what it is to what\nit ought to be.\n -- Virginia Allan\n\n❯ fortune\n\nI keep hearing and reading this nice proverb *if it ain't broke, don't fix\nit*. The latest appearance was in response to [Shlomi\nFish](http://community.livejournal.com/shlomif_tech/37969.html) suggesting\nthat some Ancient Perl code should be replaced by Modern Perl code.\n\nI am not saying that every piece of code should be rewritten every 6 months,\nbut in my understanding that sentence actually translates to *let's wait till\nit breaks and then panic*.\n\nI think people who say that sentence are afraid that the new version will\nbreak something. Sure, there is always a chance that a change introduces an\nerror, but, if we are afraid to touch the code, what will happen when later on\nwe encounter a case where it does not work? For example, if we need to use it\nin a new environment. Will we have the courage to change the code then? How\nmuch will it cost in money, time, and lost sleep?\n\nI think we have been trying to teach ourselves that we should have really good\ntest coverage of our code and then we can easily refactor it and get rid of\ntechnical debt. So why do we keep hearing that sentence?\n\n -- Gabor Szabo\n -- What does \"If it ain't broke, don't fix it.\" really mean? ( )\n\n❯ fortune\n\nPsychoanalysis is that mental illness for which it regards itself a therapy.\n -- Karl Kraus\n\n❯ fortune\n\nWith a rubber duck, one's never alone.\n -- \"The Hitchhiker's Guide to the Galaxy\"\n\n❯ fortune\n\n(Presuming for the sake of argument that it's even *possible* to design\nbetter code in Perl than in C. :-)\n -- Larry Wall on core code vs. module code design\n\n❯ fortune\n\nBut, officer, he's not drunk, I just saw his fingers twitch!\n\n❯ fortune\n\nImbalance of power corrupts and monopoly of power corrupts absolutely.\n -- Genji\n\n❯ fortune\n\nI want a VEGETARIAN BURRITO to go ... with EXTRA MSG!!\n\n❯ fortune\n\n如果你不知道如何表现地高人一等,找个Unix用户,让他做给你看。\n -- Dilbert newsletter 3.0, 1994\n\n❯ fortune\n\n\"The picture's pretty bleak, gentlemen... The world's climates are changing,\nthe mammals are taking over, and we all have a brain about the size of a\nwalnut.\"\n -- some dinosaurs from The Far Side, by Gary Larson\n\n❯ fortune\n\nMost of us feel that marketing types are like a dangerous weapon - keep\n'em unloaded and locked up in a cupboard, and only bring them out when\nyou need them to do a job.\n -- Craig Sanders\n\n❯ fortune\n\n不寐倦长更,披衣出户行。月寒秋竹冷,风切夜窗声。\n -- 李煜《三台令》\n\n❯ fortune\n\n子曰:“由,诲女知之乎!知之为知之,不知为不知,是知也。”\n-- 论语,为政篇\n\n❯ fortune\n\n \"The jig's up, Elman.\"\n \"Which jig?\"\n -- Jeff Elman\n\n❯ fortune\n\n\"First things first -- but not necessarily in that order\"\n -- The Doctor, \"Doctor Who\"\n\n❯ fortune\n\n波影摇涟,趁熏风,一舸来时,翠阴清昼。去郭轩楹才数里,藓磴松关云岫。\n快屐齿,筇枝先后。空半危亭堪聚远,看洞庭,缥缈争奇秀。人自老,景如旧。\n来帆去棹还知否,问古今,几度斜阳,几番回首?晓色一川谁管领,都付雨荷烟柳,\n知我者,燕朋鸥友。笑拍阑干呼范蠡,甚平吴,却倩垂纶手?吁万古,付卮酒。\n -- 周密《长亭怨慢》\n\n❯ fortune\n\nI'm telling you that the kernel is stable not because it's a kernel,\nbut because I refuse to listen to arguments like this.\n -- Linus Torvalds\n\n❯ fortune\n\nA girl's conscience doesn't really keep her from doing anything wrong--\nit merely keeps her from enjoying it.\n\n❯ fortune\n\nThere are few people more often in the wrong than those who cannot endure\nto be thought so.\n\n❯ fortune\n\nOne does not thank logic.\n -- Sarek, \"Journey to Babel\", stardate 3842.4\n\n❯ fortune\n\nFree yourself from negative influence. Negative thoughts are the old\nhabits that gnaw at the roots of the soul.\nMoses Shongo, (Seneca)\n\n❯ fortune\n\n日月照之何不及此?惟有北风号怒天上来。\n -- 李白《北风行》\n\n❯ fortune\n\nAnyone who is capable of getting themselves made President should on no\naccount be allowed to do the job.\n -- Douglas Adams, \"The Hitchhiker's Guide to the Galaxy\"\n\nAnd here are my mods:-)\n\n❯ pacman -Qs fortune\nlocal/cowfortune 0.1.2-8\n Configurable fortune cookie proclaiming cow (and a few other creatures)\nlocal/fortune-mod 3.22.0-1.1\n The Fortune Cookie Program from BSD games\nlocal/fortune-mod-zh 2.98-1 (fortune-mods)\n Chinese poems for fortune-mod\n","slug":"fortune","published":1,"updated":"2025-05-22T07:41:08.523Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2np0013yg2bckz8f6hw","content":"

Keep peace of mind with fortune…

\n

❯ fortune

\n

The nice thing about standards is that there are so many of them to choose
from.
– Andrew S. Tanenbaum

\n

❯ fortune

\n

看蓬门秋草,年年破巷,疏窗细雨,夜夜孤灯。
– 郑板桥《沁园春·恨》

\n

❯ fortune

\n

An alcoholic is someone you don’t like who drinks as much as you do.
– Dylan Thomas

\n

❯ fortune

\n

If you put tomfoolery into a computer, nothing comes out but tomfoolery.
But this tomfoolery, having passed through a very expensive machine,
is somehow ennobled and no-one dare criticise it.
– Pierre Gallois

\n

❯ fortune

\n

秋色渐将晚,霜信报黄花。
– 叶梦得《水调歌头·秋色渐将晚》

\n

❯ fortune

\n

水精帘里颇黎枕,暖香惹梦鸳鸯锦。江上柳如烟,雁飞残月天。
藕丝秋色浅,人胜参差剪。双鬓隔香红,玉钗头上风。
– 温庭筠《菩萨蛮》

\n

❯ fortune

\n
    A disciple of another sect once came to Drescher as he was eating\n
\n

his morning meal. “I would like to give you this personality test”, said
the outsider, “because I want you to be happy.”
Drescher took the paper that was offered him and put it into the
toaster – “I wish the toaster to be happy too”.

\n

❯ fortune

\n

Example is not the main thing in influencing others. It is the only thing.
– Albert Schweitzer

\n

❯ fortune

\n

We have art that we do not die of the truth.
– Nietzsche

\n

❯ fortune

\n

“Java for the COBOL Programmer”
who writes these things?
people on crack
and cobol programmers
:)
that’s redundant.

\n

❯ fortune

\n

I was in this prematurely air conditioned supermarket and there were all
these aisles and there were these bathing caps you could buy that had these
kind of Fourth of July plumes on them that were red and yellow and blue and
I wasn’t tempted to buy one but I was reminded of the fact that I had been
avoiding the beach.
– Lucinda Childs “Einstein On The Beach”

\n

❯ fortune

\n

It doesn’t matter whether you win or lose – until you lose.

\n

❯ fortune

\n

True leadership is the art of changing a group from what it is to what
it ought to be.
– Virginia Allan

\n

❯ fortune

\n

I keep hearing and reading this nice proverb if it ain’t broke, don’t fix
it
. The latest appearance was in response to Shlomi
Fish
suggesting
that some Ancient Perl code should be replaced by Modern Perl code.

\n

I am not saying that every piece of code should be rewritten every 6 months,
but in my understanding that sentence actually translates to let’s wait till
it breaks and then panic
.

\n

I think people who say that sentence are afraid that the new version will
break something. Sure, there is always a chance that a change introduces an
error, but, if we are afraid to touch the code, what will happen when later on
we encounter a case where it does not work? For example, if we need to use it
in a new environment. Will we have the courage to change the code then? How
much will it cost in money, time, and lost sleep?

\n

I think we have been trying to teach ourselves that we should have really good
test coverage of our code and then we can easily refactor it and get rid of
technical debt. So why do we keep hearing that sentence?

\n
-- Gabor Szabo\n-- What does "If it ain't broke, don't fix it." really mean? (  )\n
\n

❯ fortune

\n

Psychoanalysis is that mental illness for which it regards itself a therapy.
– Karl Kraus

\n

❯ fortune

\n

With a rubber duck, one’s never alone.
– “The Hitchhiker’s Guide to the Galaxy”

\n

❯ fortune

\n

(Presuming for the sake of argument that it’s even possible to design
better code in Perl than in C. :-)
– Larry Wall on core code vs. module code design

\n

❯ fortune

\n

But, officer, he’s not drunk, I just saw his fingers twitch!

\n

❯ fortune

\n

Imbalance of power corrupts and monopoly of power corrupts absolutely.
– Genji

\n

❯ fortune

\n

I want a VEGETARIAN BURRITO to go … with EXTRA MSG!!

\n

❯ fortune

\n

如果你不知道如何表现地高人一等,找个Unix用户,让他做给你看。
– Dilbert newsletter 3.0, 1994

\n

❯ fortune

\n

“The picture’s pretty bleak, gentlemen… The world’s climates are changing,
the mammals are taking over, and we all have a brain about the size of a
walnut.”
– some dinosaurs from The Far Side, by Gary Larson

\n

❯ fortune

\n

Most of us feel that marketing types are like a dangerous weapon - keep
‘em unloaded and locked up in a cupboard, and only bring them out when
you need them to do a job.
– Craig Sanders

\n

❯ fortune

\n

不寐倦长更,披衣出户行。月寒秋竹冷,风切夜窗声。
– 李煜《三台令》

\n

❯ fortune

\n

子曰:“由,诲女知之乎!知之为知之,不知为不知,是知也。”
– 论语,为政篇

\n

❯ fortune

\n
    "The jig's up, Elman."\n    "Which jig?"\n            -- Jeff Elman\n
\n

❯ fortune

\n

“First things first – but not necessarily in that order”
– The Doctor, “Doctor Who”

\n

❯ fortune

\n

波影摇涟,趁熏风,一舸来时,翠阴清昼。去郭轩楹才数里,藓磴松关云岫。
快屐齿,筇枝先后。空半危亭堪聚远,看洞庭,缥缈争奇秀。人自老,景如旧。
来帆去棹还知否,问古今,几度斜阳,几番回首?晓色一川谁管领,都付雨荷烟柳,
知我者,燕朋鸥友。笑拍阑干呼范蠡,甚平吴,却倩垂纶手?吁万古,付卮酒。
– 周密《长亭怨慢》

\n

❯ fortune

\n

I’m telling you that the kernel is stable not because it’s a kernel,
but because I refuse to listen to arguments like this.
– Linus Torvalds

\n

❯ fortune

\n

A girl’s conscience doesn’t really keep her from doing anything wrong–
it merely keeps her from enjoying it.

\n

❯ fortune

\n

There are few people more often in the wrong than those who cannot endure
to be thought so.

\n

❯ fortune

\n

One does not thank logic.
– Sarek, “Journey to Babel”, stardate 3842.4

\n

❯ fortune

\n

Free yourself from negative influence. Negative thoughts are the old
habits that gnaw at the roots of the soul.
Moses Shongo, (Seneca)

\n

❯ fortune

\n

日月照之何不及此?惟有北风号怒天上来。
– 李白《北风行》

\n

❯ fortune

\n

Anyone who is capable of getting themselves made President should on no
account be allowed to do the job.
– Douglas Adams, “The Hitchhiker’s Guide to the Galaxy”

\n

And here are my mods:-)

\n

❯ pacman -Qs fortune
local/cowfortune 0.1.2-8
Configurable fortune cookie proclaiming cow (and a few other creatures)
local/fortune-mod 3.22.0-1.1
The Fortune Cookie Program from BSD games
local/fortune-mod-zh 2.98-1 (fortune-mods)
Chinese poems for fortune-mod

\n","excerpt":"","more":"

Keep peace of mind with fortune…

\n

❯ fortune

\n

The nice thing about standards is that there are so many of them to choose
from.
– Andrew S. Tanenbaum

\n

❯ fortune

\n

看蓬门秋草,年年破巷,疏窗细雨,夜夜孤灯。
– 郑板桥《沁园春·恨》

\n

❯ fortune

\n

An alcoholic is someone you don’t like who drinks as much as you do.
– Dylan Thomas

\n

❯ fortune

\n

If you put tomfoolery into a computer, nothing comes out but tomfoolery.
But this tomfoolery, having passed through a very expensive machine,
is somehow ennobled and no-one dare criticise it.
– Pierre Gallois

\n

❯ fortune

\n

秋色渐将晚,霜信报黄花。
– 叶梦得《水调歌头·秋色渐将晚》

\n

❯ fortune

\n

水精帘里颇黎枕,暖香惹梦鸳鸯锦。江上柳如烟,雁飞残月天。
藕丝秋色浅,人胜参差剪。双鬓隔香红,玉钗头上风。
– 温庭筠《菩萨蛮》

\n

❯ fortune

\n
    A disciple of another sect once came to Drescher as he was eating\n
\n

his morning meal. “I would like to give you this personality test”, said
the outsider, “because I want you to be happy.”
Drescher took the paper that was offered him and put it into the
toaster – “I wish the toaster to be happy too”.

\n

❯ fortune

\n

Example is not the main thing in influencing others. It is the only thing.
– Albert Schweitzer

\n

❯ fortune

\n

We have art that we do not die of the truth.
– Nietzsche

\n

❯ fortune

\n

“Java for the COBOL Programmer”
who writes these things?
people on crack
and cobol programmers
:)
that’s redundant.

\n

❯ fortune

\n

I was in this prematurely air conditioned supermarket and there were all
these aisles and there were these bathing caps you could buy that had these
kind of Fourth of July plumes on them that were red and yellow and blue and
I wasn’t tempted to buy one but I was reminded of the fact that I had been
avoiding the beach.
– Lucinda Childs “Einstein On The Beach”

\n

❯ fortune

\n

It doesn’t matter whether you win or lose – until you lose.

\n

❯ fortune

\n

True leadership is the art of changing a group from what it is to what
it ought to be.
– Virginia Allan

\n

❯ fortune

\n

I keep hearing and reading this nice proverb if it ain’t broke, don’t fix
it
. The latest appearance was in response to Shlomi
Fish
suggesting
that some Ancient Perl code should be replaced by Modern Perl code.

\n

I am not saying that every piece of code should be rewritten every 6 months,
but in my understanding that sentence actually translates to let’s wait till
it breaks and then panic
.

\n

I think people who say that sentence are afraid that the new version will
break something. Sure, there is always a chance that a change introduces an
error, but, if we are afraid to touch the code, what will happen when later on
we encounter a case where it does not work? For example, if we need to use it
in a new environment. Will we have the courage to change the code then? How
much will it cost in money, time, and lost sleep?

\n

I think we have been trying to teach ourselves that we should have really good
test coverage of our code and then we can easily refactor it and get rid of
technical debt. So why do we keep hearing that sentence?

\n
-- Gabor Szabo\n-- What does "If it ain't broke, don't fix it." really mean? (  )\n
\n

❯ fortune

\n

Psychoanalysis is that mental illness for which it regards itself a therapy.
– Karl Kraus

\n

❯ fortune

\n

With a rubber duck, one’s never alone.
– “The Hitchhiker’s Guide to the Galaxy”

\n

❯ fortune

\n

(Presuming for the sake of argument that it’s even possible to design
better code in Perl than in C. :-)
– Larry Wall on core code vs. module code design

\n

❯ fortune

\n

But, officer, he’s not drunk, I just saw his fingers twitch!

\n

❯ fortune

\n

Imbalance of power corrupts and monopoly of power corrupts absolutely.
– Genji

\n

❯ fortune

\n

I want a VEGETARIAN BURRITO to go … with EXTRA MSG!!

\n

❯ fortune

\n

如果你不知道如何表现地高人一等,找个Unix用户,让他做给你看。
– Dilbert newsletter 3.0, 1994

\n

❯ fortune

\n

“The picture’s pretty bleak, gentlemen… The world’s climates are changing,
the mammals are taking over, and we all have a brain about the size of a
walnut.”
– some dinosaurs from The Far Side, by Gary Larson

\n

❯ fortune

\n

Most of us feel that marketing types are like a dangerous weapon - keep
‘em unloaded and locked up in a cupboard, and only bring them out when
you need them to do a job.
– Craig Sanders

\n

❯ fortune

\n

不寐倦长更,披衣出户行。月寒秋竹冷,风切夜窗声。
– 李煜《三台令》

\n

❯ fortune

\n

子曰:“由,诲女知之乎!知之为知之,不知为不知,是知也。”
– 论语,为政篇

\n

❯ fortune

\n
    "The jig's up, Elman."\n    "Which jig?"\n            -- Jeff Elman\n
\n

❯ fortune

\n

“First things first – but not necessarily in that order”
– The Doctor, “Doctor Who”

\n

❯ fortune

\n

波影摇涟,趁熏风,一舸来时,翠阴清昼。去郭轩楹才数里,藓磴松关云岫。
快屐齿,筇枝先后。空半危亭堪聚远,看洞庭,缥缈争奇秀。人自老,景如旧。
来帆去棹还知否,问古今,几度斜阳,几番回首?晓色一川谁管领,都付雨荷烟柳,
知我者,燕朋鸥友。笑拍阑干呼范蠡,甚平吴,却倩垂纶手?吁万古,付卮酒。
– 周密《长亭怨慢》

\n

❯ fortune

\n

I’m telling you that the kernel is stable not because it’s a kernel,
but because I refuse to listen to arguments like this.
– Linus Torvalds

\n

❯ fortune

\n

A girl’s conscience doesn’t really keep her from doing anything wrong–
it merely keeps her from enjoying it.

\n

❯ fortune

\n

There are few people more often in the wrong than those who cannot endure
to be thought so.

\n

❯ fortune

\n

One does not thank logic.
– Sarek, “Journey to Babel”, stardate 3842.4

\n

❯ fortune

\n

Free yourself from negative influence. Negative thoughts are the old
habits that gnaw at the roots of the soul.
Moses Shongo, (Seneca)

\n

❯ fortune

\n

日月照之何不及此?惟有北风号怒天上来。
– 李白《北风行》

\n

❯ fortune

\n

Anyone who is capable of getting themselves made President should on no
account be allowed to do the job.
– Douglas Adams, “The Hitchhiker’s Guide to the Galaxy”

\n

And here are my mods:-)

\n

❯ pacman -Qs fortune
local/cowfortune 0.1.2-8
Configurable fortune cookie proclaiming cow (and a few other creatures)
local/fortune-mod 3.22.0-1.1
The Fortune Cookie Program from BSD games
local/fortune-mod-zh 2.98-1 (fortune-mods)
Chinese poems for fortune-mod

\n"},{"title":"GCC15 函数指针类型检查可能导致编译错误","date":"2025-05-23T13:49:02.000Z","_content":"\n今天准备继续做一下OS课的实验,实验代码当然是基于著名的xv6项目,意外地发现原本明明正常运行的系统再次构建时却失败了,具体报错如下:\n\n```GCC\nuser/usertests.c:2598:4: error: initialization of 'void (*)(char *)' from incompatible pointer type 'void (*)(void)' [-Wincompatible-pointer-types]\n 2598 | {rwsbrk, \"rwsbrk\" },\n | ^~~~~~\nuser/usertests.c:2598:4: note: (near initialization for 'quicktests[5].f')\nuser/usertests.c:247:1: note: 'rwsbrk' declared here\n 247 | rwsbrk()\n | ^~~~~~\nmake: *** [<内置>:user/usertests.o] 错误 1\n\n```\n而具体定位到的代码则是:\n```C\nstruct test {\n void (*f)(char *);\n char *s;\n} quicktests[] = {\n {copyin, \"copyin\"},\n {copyout, \"copyout\"},\n {copyinstr1, \"copyinstr1\"},\n {copyinstr2, \"copyinstr2\"},\n {copyinstr3, \"copyinstr3\"},\n {rwsbrk, \"rwsbrk\" },\n ...\n { 0, 0},\n};\n// See if the kernel refuses to read/write user memory that the\n// application doesn't have anymore, because it returned it.\nvoid\nrwsbrk()\n{\n int fd, n;\n \n uint64 a = (uint64) sbrk(8192);\n\n if(a == 0xffffffffffffffffLL) {\n printf(\"sbrk(rwsbrk) failed\\n\");\n exit(1);\n }\n ...\n exit(0);\n}\n```\n\n这段代码定义了一个函数指针数组,其中每个元素都指向一个函数和它的名称。结构体期望的函数签名是 `void(*)(char*)`,但传入的是一个没有参数的函数 `void(*)(void)`,这会导致函数指针类型不匹配。\n\n但是之前明明是没有问题的怎么现在突然出错呢?仔细一想立刻就怀疑是之前arch更新的时候把gcc升上去导致的,结果查了下大概确实是这样。\n\n- 在 **GCC 14 及之前版本**,这类赋值虽然不符合 C 标准,但 GCC 作为扩展允许这种行为。\n- 从 **GCC 15 开始**,这类隐式转换默认触发编译错误或警告(取决于编译器配置),因为 GCC 变得更加严格地遵循标准 C 的函数指针类型规则。\n\n这并不是 xv6 本身的 bug,而是 GCC 编译器对语言规范支持的更新带来的兼容性问题。解决方法倒是简单,直接给`rwsbrk`函数添加参数改为`void rwsbrk(char* arg)`即可解决,强制类型转换也是一种办法。这次事件再次告诉我们什么都升到最新并不是什么好事情,arch用户总是要花时间处理一些莫名其妙的兼容性问题:-)\n\n\n","source":"_posts/gcc15restrict.md","raw":"---\ntitle: GCC15 函数指针类型检查可能导致编译错误\ndate: 2025-05-23 21:49:02\ntags: 技术\n---\n\n今天准备继续做一下OS课的实验,实验代码当然是基于著名的xv6项目,意外地发现原本明明正常运行的系统再次构建时却失败了,具体报错如下:\n\n```GCC\nuser/usertests.c:2598:4: error: initialization of 'void (*)(char *)' from incompatible pointer type 'void (*)(void)' [-Wincompatible-pointer-types]\n 2598 | {rwsbrk, \"rwsbrk\" },\n | ^~~~~~\nuser/usertests.c:2598:4: note: (near initialization for 'quicktests[5].f')\nuser/usertests.c:247:1: note: 'rwsbrk' declared here\n 247 | rwsbrk()\n | ^~~~~~\nmake: *** [<内置>:user/usertests.o] 错误 1\n\n```\n而具体定位到的代码则是:\n```C\nstruct test {\n void (*f)(char *);\n char *s;\n} quicktests[] = {\n {copyin, \"copyin\"},\n {copyout, \"copyout\"},\n {copyinstr1, \"copyinstr1\"},\n {copyinstr2, \"copyinstr2\"},\n {copyinstr3, \"copyinstr3\"},\n {rwsbrk, \"rwsbrk\" },\n ...\n { 0, 0},\n};\n// See if the kernel refuses to read/write user memory that the\n// application doesn't have anymore, because it returned it.\nvoid\nrwsbrk()\n{\n int fd, n;\n \n uint64 a = (uint64) sbrk(8192);\n\n if(a == 0xffffffffffffffffLL) {\n printf(\"sbrk(rwsbrk) failed\\n\");\n exit(1);\n }\n ...\n exit(0);\n}\n```\n\n这段代码定义了一个函数指针数组,其中每个元素都指向一个函数和它的名称。结构体期望的函数签名是 `void(*)(char*)`,但传入的是一个没有参数的函数 `void(*)(void)`,这会导致函数指针类型不匹配。\n\n但是之前明明是没有问题的怎么现在突然出错呢?仔细一想立刻就怀疑是之前arch更新的时候把gcc升上去导致的,结果查了下大概确实是这样。\n\n- 在 **GCC 14 及之前版本**,这类赋值虽然不符合 C 标准,但 GCC 作为扩展允许这种行为。\n- 从 **GCC 15 开始**,这类隐式转换默认触发编译错误或警告(取决于编译器配置),因为 GCC 变得更加严格地遵循标准 C 的函数指针类型规则。\n\n这并不是 xv6 本身的 bug,而是 GCC 编译器对语言规范支持的更新带来的兼容性问题。解决方法倒是简单,直接给`rwsbrk`函数添加参数改为`void rwsbrk(char* arg)`即可解决,强制类型转换也是一种办法。这次事件再次告诉我们什么都升到最新并不是什么好事情,arch用户总是要花时间处理一些莫名其妙的兼容性问题:-)\n\n\n","slug":"gcc15restrict","published":1,"updated":"2025-05-23T14:01:33.929Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nq0015yg2b47hg5f9p","content":"

今天准备继续做一下OS课的实验,实验代码当然是基于著名的xv6项目,意外地发现原本明明正常运行的系统再次构建时却失败了,具体报错如下:

\n
1
2
3
4
5
6
7
8
9
user/usertests.c:2598:4: error: initialization of 'void (*)(char *)' from incompatible pointer type 'void (*)(void)' [-Wincompatible-pointer-types]
2598 | {rwsbrk, "rwsbrk" },
| ^~~~~~
user/usertests.c:2598:4: note: (near initialization for 'quicktests[5].f')
user/usertests.c:247:1: note: 'rwsbrk' declared here
247 | rwsbrk()
| ^~~~~~
make: *** [<内置>:user/usertests.o] 错误 1

\n

而具体定位到的代码则是:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct test {
void (*f)(char *);
char *s;
} quicktests[] = {
{copyin, "copyin"},
{copyout, "copyout"},
{copyinstr1, "copyinstr1"},
{copyinstr2, "copyinstr2"},
{copyinstr3, "copyinstr3"},
{rwsbrk, "rwsbrk" },
...
{ 0, 0},
};
// See if the kernel refuses to read/write user memory that the
// application doesn't have anymore, because it returned it.
void
rwsbrk()
{
int fd, n;

uint64 a = (uint64) sbrk(8192);

if(a == 0xffffffffffffffffLL) {
printf("sbrk(rwsbrk) failed\\n");
exit(1);
}
...
exit(0);
}
\n\n

这段代码定义了一个函数指针数组,其中每个元素都指向一个函数和它的名称。结构体期望的函数签名是 void(*)(char*),但传入的是一个没有参数的函数 void(*)(void),这会导致函数指针类型不匹配。

\n

但是之前明明是没有问题的怎么现在突然出错呢?仔细一想立刻就怀疑是之前arch更新的时候把gcc升上去导致的,结果查了下大概确实是这样。

\n
    \n
  • GCC 14 及之前版本,这类赋值虽然不符合 C 标准,但 GCC 作为扩展允许这种行为。
  • \n
  • GCC 15 开始,这类隐式转换默认触发编译错误或警告(取决于编译器配置),因为 GCC 变得更加严格地遵循标准 C 的函数指针类型规则。
  • \n
\n

这并不是 xv6 本身的 bug,而是 GCC 编译器对语言规范支持的更新带来的兼容性问题。解决方法倒是简单,直接给rwsbrk函数添加参数改为void rwsbrk(char* arg)即可解决,强制类型转换也是一种办法。这次事件再次告诉我们什么都升到最新并不是什么好事情,arch用户总是要花时间处理一些莫名其妙的兼容性问题:-)

\n","excerpt":"","more":"

今天准备继续做一下OS课的实验,实验代码当然是基于著名的xv6项目,意外地发现原本明明正常运行的系统再次构建时却失败了,具体报错如下:

\n
1
2
3
4
5
6
7
8
9
user/usertests.c:2598:4: error: initialization of 'void (*)(char *)' from incompatible pointer type 'void (*)(void)' [-Wincompatible-pointer-types]
2598 | {rwsbrk, "rwsbrk" },
| ^~~~~~
user/usertests.c:2598:4: note: (near initialization for 'quicktests[5].f')
user/usertests.c:247:1: note: 'rwsbrk' declared here
247 | rwsbrk()
| ^~~~~~
make: *** [<内置>:user/usertests.o] 错误 1

\n

而具体定位到的代码则是:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct test {
void (*f)(char *);
char *s;
} quicktests[] = {
{copyin, "copyin"},
{copyout, "copyout"},
{copyinstr1, "copyinstr1"},
{copyinstr2, "copyinstr2"},
{copyinstr3, "copyinstr3"},
{rwsbrk, "rwsbrk" },
...
{ 0, 0},
};
// See if the kernel refuses to read/write user memory that the
// application doesn't have anymore, because it returned it.
void
rwsbrk()
{
int fd, n;

uint64 a = (uint64) sbrk(8192);

if(a == 0xffffffffffffffffLL) {
printf("sbrk(rwsbrk) failed\\n");
exit(1);
}
...
exit(0);
}
\n\n

这段代码定义了一个函数指针数组,其中每个元素都指向一个函数和它的名称。结构体期望的函数签名是 void(*)(char*),但传入的是一个没有参数的函数 void(*)(void),这会导致函数指针类型不匹配。

\n

但是之前明明是没有问题的怎么现在突然出错呢?仔细一想立刻就怀疑是之前arch更新的时候把gcc升上去导致的,结果查了下大概确实是这样。

\n
    \n
  • GCC 14 及之前版本,这类赋值虽然不符合 C 标准,但 GCC 作为扩展允许这种行为。
  • \n
  • GCC 15 开始,这类隐式转换默认触发编译错误或警告(取决于编译器配置),因为 GCC 变得更加严格地遵循标准 C 的函数指针类型规则。
  • \n
\n

这并不是 xv6 本身的 bug,而是 GCC 编译器对语言规范支持的更新带来的兼容性问题。解决方法倒是简单,直接给rwsbrk函数添加参数改为void rwsbrk(char* arg)即可解决,强制类型转换也是一种办法。这次事件再次告诉我们什么都升到最新并不是什么好事情,arch用户总是要花时间处理一些莫名其妙的兼容性问题:-)

\n"},{"title":"Krita 为什么是神?","date":"2025-07-13T11:46:39.000Z","_content":"\n![神的官网](/images/krita-official.png)\n\n在谈论这个问题之前,我想先说说其他软件相较于 [Krita](https://krita.org/en/) 究竟差在了哪里。\n\n---\n\n### 首先是犯下傲慢之罪的 Adobe Photoshop\n\n依靠其在商业设计领域的绝对霸主地位赢来的市场,却不愿真正听取数字绘画创作者的声音!这种傲慢的软件注定无法触及绘画的灵魂。事实也是如此,因为高昂的订阅费用和臃肿的体量,以及对触摸屏设备支持的迟滞,Photoshop 在许多数字画师心中的地位一直在走下坡路,最终沦为商业生产的工具而非艺术创作的乐园。与之相比,**神** 就很谦卑,它免费开源,轻巧灵活,全平台支持,即便在某些方面为了兼容性和稳定性而做出妥协,那也是 **神** 留给凡人的慈悲。**神** 不是不能做到像 PS 那样无所不能,只是不想通过太完美的表现让“友商”绝望所以故意不用。可笑有些人不理解 **神** 的良苦用心,竟然还用其某些非核心功能上的不足来讥讽 **神**,我劝你们好自为之。\n\n---\n\n### 然后是犯下愤怒之罪的 GIMP\n\n因为 **神** 在数字绘画领域的崛起,GIMP 就一直对神记恨在心,甚至固守其“万能”的图像处理定位,对绘画功能上的迭代迟迟不肯投入资源。这无疑违背了 **神** 在《新约》中的一句话:“**Krita** 只为创作者而生!”**神** 当然也不能容忍其他开源软件对绘画创作的敷衍,于是 **神** 出动了它的“代码大军”降下了神罚,GIMP 在绘画圈的风评一蹶不振,即便有诸多插件以及强势归来的3.0大版本更新也无法挽回其作为“绘画软件”使用时的颓势。\n\n---\n\n### 接着是犯下懒惰之罪的 Procreate\n\n自以为有 iPad 的独占优势和流畅的笔触就了不起了,对图层管理、多文档支持以及跨平台协作就一直摆烂。另外大家有所不知,其实在 Procreate 首次发布前夕,其开发者闭上眼睛做冥想的时候,其脑海中看见的画面,正是站在光芒之中的 **神**。那时神告诉他:“你只可到这里,不可越过。”然而,**神** 的劝说不但没有让 Procreate 迷途知返,竟然还敢在移动设备上挑战神的威严,妄图通过封闭的生态垄断市场。于是 **神** 降下了他的惩罚,Procreate 在专业性上身败名裂,其以流畅为名的笔刷也被 **神** 在笔刷引擎上轻松超越,iPad Pro 独占的优势也没能阻止 **神** 在更广阔的平台生根发芽,粉丝也成为了众人口中的“果曲是这样的”。\n\n---\n\n### 再然后是犯下了嫉妒之罪的 SAI\n\n屡次被 **神** 击败,但口服心不服的 SAI,暗中嫉妒着 **神** ,甚至胆敢当众对 **神** 说出“你才是挑战者”这种话。于是,在含金量最高的活动上,即专业画师的日常工作中,SAI 被 **神** 正面击溃。它那单一的笔刷类型和有限的功能,在 **神** 面前显得如此苍白无力。不过,念在 SAI 最终皈依了 **神** 的教,并且以其轻量和独特的抖动修正功能,在某些特定绘画风格上仍有信徒,**神** 应许了它在特定小众市场的一切。\n\n---\n\n### 最后犯下贪婪之罪的 Clip Studio Paint\n\n只是初出茅庐,Clip Studio Paint 就夺得了漫画创作软件的市场份额,而这,自然是因为 **神** 的应允。**神** 本来钦定它为自己在漫画领域的代言人,在自己更专注于绘画自由之后替自己继续讨伐那些缺乏专业性的绘画工具,为此甚至不惜亲自为 Clip Studio Paint 铺路,主动减少自己在漫画素材库上的投入,将一切荣耀都归给他。但最后 Clip Studio Paint 却被利益蒙蔽了双眼,不再以践行 **神** 的理想为自己的理想,也不再聆听 **神** 的教诲,甚至自以为荣光已经超过了 **神**。于是 **神** 降下了他的惩罚,Clip Studio Paint 从此订阅制饱受诟病,其3D模型功能再也不能在线上教学中占据优势,其引以为傲的漫画分镜功能再也不能强势压制对手,最终市场被 **神** 在免费开源和强大功能上的结合所夺舍。\n\n// 都很好用口牙,本文仅供娱乐\n\n![老八可爱捏](/images/yana.jpg)\n","source":"_posts/kritabest.md","raw":"---\ntitle: Krita 为什么是神?\ndate: 2025-07-13 19:46:39\ntags: [生活]\n---\n\n![神的官网](/images/krita-official.png)\n\n在谈论这个问题之前,我想先说说其他软件相较于 [Krita](https://krita.org/en/) 究竟差在了哪里。\n\n---\n\n### 首先是犯下傲慢之罪的 Adobe Photoshop\n\n依靠其在商业设计领域的绝对霸主地位赢来的市场,却不愿真正听取数字绘画创作者的声音!这种傲慢的软件注定无法触及绘画的灵魂。事实也是如此,因为高昂的订阅费用和臃肿的体量,以及对触摸屏设备支持的迟滞,Photoshop 在许多数字画师心中的地位一直在走下坡路,最终沦为商业生产的工具而非艺术创作的乐园。与之相比,**神** 就很谦卑,它免费开源,轻巧灵活,全平台支持,即便在某些方面为了兼容性和稳定性而做出妥协,那也是 **神** 留给凡人的慈悲。**神** 不是不能做到像 PS 那样无所不能,只是不想通过太完美的表现让“友商”绝望所以故意不用。可笑有些人不理解 **神** 的良苦用心,竟然还用其某些非核心功能上的不足来讥讽 **神**,我劝你们好自为之。\n\n---\n\n### 然后是犯下愤怒之罪的 GIMP\n\n因为 **神** 在数字绘画领域的崛起,GIMP 就一直对神记恨在心,甚至固守其“万能”的图像处理定位,对绘画功能上的迭代迟迟不肯投入资源。这无疑违背了 **神** 在《新约》中的一句话:“**Krita** 只为创作者而生!”**神** 当然也不能容忍其他开源软件对绘画创作的敷衍,于是 **神** 出动了它的“代码大军”降下了神罚,GIMP 在绘画圈的风评一蹶不振,即便有诸多插件以及强势归来的3.0大版本更新也无法挽回其作为“绘画软件”使用时的颓势。\n\n---\n\n### 接着是犯下懒惰之罪的 Procreate\n\n自以为有 iPad 的独占优势和流畅的笔触就了不起了,对图层管理、多文档支持以及跨平台协作就一直摆烂。另外大家有所不知,其实在 Procreate 首次发布前夕,其开发者闭上眼睛做冥想的时候,其脑海中看见的画面,正是站在光芒之中的 **神**。那时神告诉他:“你只可到这里,不可越过。”然而,**神** 的劝说不但没有让 Procreate 迷途知返,竟然还敢在移动设备上挑战神的威严,妄图通过封闭的生态垄断市场。于是 **神** 降下了他的惩罚,Procreate 在专业性上身败名裂,其以流畅为名的笔刷也被 **神** 在笔刷引擎上轻松超越,iPad Pro 独占的优势也没能阻止 **神** 在更广阔的平台生根发芽,粉丝也成为了众人口中的“果曲是这样的”。\n\n---\n\n### 再然后是犯下了嫉妒之罪的 SAI\n\n屡次被 **神** 击败,但口服心不服的 SAI,暗中嫉妒着 **神** ,甚至胆敢当众对 **神** 说出“你才是挑战者”这种话。于是,在含金量最高的活动上,即专业画师的日常工作中,SAI 被 **神** 正面击溃。它那单一的笔刷类型和有限的功能,在 **神** 面前显得如此苍白无力。不过,念在 SAI 最终皈依了 **神** 的教,并且以其轻量和独特的抖动修正功能,在某些特定绘画风格上仍有信徒,**神** 应许了它在特定小众市场的一切。\n\n---\n\n### 最后犯下贪婪之罪的 Clip Studio Paint\n\n只是初出茅庐,Clip Studio Paint 就夺得了漫画创作软件的市场份额,而这,自然是因为 **神** 的应允。**神** 本来钦定它为自己在漫画领域的代言人,在自己更专注于绘画自由之后替自己继续讨伐那些缺乏专业性的绘画工具,为此甚至不惜亲自为 Clip Studio Paint 铺路,主动减少自己在漫画素材库上的投入,将一切荣耀都归给他。但最后 Clip Studio Paint 却被利益蒙蔽了双眼,不再以践行 **神** 的理想为自己的理想,也不再聆听 **神** 的教诲,甚至自以为荣光已经超过了 **神**。于是 **神** 降下了他的惩罚,Clip Studio Paint 从此订阅制饱受诟病,其3D模型功能再也不能在线上教学中占据优势,其引以为傲的漫画分镜功能再也不能强势压制对手,最终市场被 **神** 在免费开源和强大功能上的结合所夺舍。\n\n// 都很好用口牙,本文仅供娱乐\n\n![老八可爱捏](/images/yana.jpg)\n","slug":"kritabest","published":1,"updated":"2025-07-13T15:47:28.539Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nq0018yg2bd14sedct","content":"

\"神的官网\"

\n

在谈论这个问题之前,我想先说说其他软件相较于 Krita 究竟差在了哪里。

\n
\n

首先是犯下傲慢之罪的 Adobe Photoshop

依靠其在商业设计领域的绝对霸主地位赢来的市场,却不愿真正听取数字绘画创作者的声音!这种傲慢的软件注定无法触及绘画的灵魂。事实也是如此,因为高昂的订阅费用和臃肿的体量,以及对触摸屏设备支持的迟滞,Photoshop 在许多数字画师心中的地位一直在走下坡路,最终沦为商业生产的工具而非艺术创作的乐园。与之相比, 就很谦卑,它免费开源,轻巧灵活,全平台支持,即便在某些方面为了兼容性和稳定性而做出妥协,那也是 留给凡人的慈悲。 不是不能做到像 PS 那样无所不能,只是不想通过太完美的表现让“友商”绝望所以故意不用。可笑有些人不理解 的良苦用心,竟然还用其某些非核心功能上的不足来讥讽 ,我劝你们好自为之。

\n
\n

然后是犯下愤怒之罪的 GIMP

因为 在数字绘画领域的崛起,GIMP 就一直对神记恨在心,甚至固守其“万能”的图像处理定位,对绘画功能上的迭代迟迟不肯投入资源。这无疑违背了 在《新约》中的一句话:“Krita 只为创作者而生!” 当然也不能容忍其他开源软件对绘画创作的敷衍,于是 出动了它的“代码大军”降下了神罚,GIMP 在绘画圈的风评一蹶不振,即便有诸多插件以及强势归来的3.0大版本更新也无法挽回其作为“绘画软件”使用时的颓势。

\n
\n

接着是犯下懒惰之罪的 Procreate

自以为有 iPad 的独占优势和流畅的笔触就了不起了,对图层管理、多文档支持以及跨平台协作就一直摆烂。另外大家有所不知,其实在 Procreate 首次发布前夕,其开发者闭上眼睛做冥想的时候,其脑海中看见的画面,正是站在光芒之中的 。那时神告诉他:“你只可到这里,不可越过。”然而, 的劝说不但没有让 Procreate 迷途知返,竟然还敢在移动设备上挑战神的威严,妄图通过封闭的生态垄断市场。于是 降下了他的惩罚,Procreate 在专业性上身败名裂,其以流畅为名的笔刷也被 在笔刷引擎上轻松超越,iPad Pro 独占的优势也没能阻止 在更广阔的平台生根发芽,粉丝也成为了众人口中的“果曲是这样的”。

\n
\n

再然后是犯下了嫉妒之罪的 SAI

屡次被 击败,但口服心不服的 SAI,暗中嫉妒着 ,甚至胆敢当众对 说出“你才是挑战者”这种话。于是,在含金量最高的活动上,即专业画师的日常工作中,SAI 被 正面击溃。它那单一的笔刷类型和有限的功能,在 面前显得如此苍白无力。不过,念在 SAI 最终皈依了 的教,并且以其轻量和独特的抖动修正功能,在某些特定绘画风格上仍有信徒, 应许了它在特定小众市场的一切。

\n
\n

最后犯下贪婪之罪的 Clip Studio Paint

只是初出茅庐,Clip Studio Paint 就夺得了漫画创作软件的市场份额,而这,自然是因为 的应允。 本来钦定它为自己在漫画领域的代言人,在自己更专注于绘画自由之后替自己继续讨伐那些缺乏专业性的绘画工具,为此甚至不惜亲自为 Clip Studio Paint 铺路,主动减少自己在漫画素材库上的投入,将一切荣耀都归给他。但最后 Clip Studio Paint 却被利益蒙蔽了双眼,不再以践行 的理想为自己的理想,也不再聆听 的教诲,甚至自以为荣光已经超过了 。于是 降下了他的惩罚,Clip Studio Paint 从此订阅制饱受诟病,其3D模型功能再也不能在线上教学中占据优势,其引以为傲的漫画分镜功能再也不能强势压制对手,最终市场被 在免费开源和强大功能上的结合所夺舍。

\n

// 都很好用口牙,本文仅供娱乐

\n

\"老八可爱捏\"

\n","excerpt":"","more":"

\"神的官网\"

\n

在谈论这个问题之前,我想先说说其他软件相较于 Krita 究竟差在了哪里。

\n
\n

首先是犯下傲慢之罪的 Adobe Photoshop

依靠其在商业设计领域的绝对霸主地位赢来的市场,却不愿真正听取数字绘画创作者的声音!这种傲慢的软件注定无法触及绘画的灵魂。事实也是如此,因为高昂的订阅费用和臃肿的体量,以及对触摸屏设备支持的迟滞,Photoshop 在许多数字画师心中的地位一直在走下坡路,最终沦为商业生产的工具而非艺术创作的乐园。与之相比, 就很谦卑,它免费开源,轻巧灵活,全平台支持,即便在某些方面为了兼容性和稳定性而做出妥协,那也是 留给凡人的慈悲。 不是不能做到像 PS 那样无所不能,只是不想通过太完美的表现让“友商”绝望所以故意不用。可笑有些人不理解 的良苦用心,竟然还用其某些非核心功能上的不足来讥讽 ,我劝你们好自为之。

\n
\n

然后是犯下愤怒之罪的 GIMP

因为 在数字绘画领域的崛起,GIMP 就一直对神记恨在心,甚至固守其“万能”的图像处理定位,对绘画功能上的迭代迟迟不肯投入资源。这无疑违背了 在《新约》中的一句话:“Krita 只为创作者而生!” 当然也不能容忍其他开源软件对绘画创作的敷衍,于是 出动了它的“代码大军”降下了神罚,GIMP 在绘画圈的风评一蹶不振,即便有诸多插件以及强势归来的3.0大版本更新也无法挽回其作为“绘画软件”使用时的颓势。

\n
\n

接着是犯下懒惰之罪的 Procreate

自以为有 iPad 的独占优势和流畅的笔触就了不起了,对图层管理、多文档支持以及跨平台协作就一直摆烂。另外大家有所不知,其实在 Procreate 首次发布前夕,其开发者闭上眼睛做冥想的时候,其脑海中看见的画面,正是站在光芒之中的 。那时神告诉他:“你只可到这里,不可越过。”然而, 的劝说不但没有让 Procreate 迷途知返,竟然还敢在移动设备上挑战神的威严,妄图通过封闭的生态垄断市场。于是 降下了他的惩罚,Procreate 在专业性上身败名裂,其以流畅为名的笔刷也被 在笔刷引擎上轻松超越,iPad Pro 独占的优势也没能阻止 在更广阔的平台生根发芽,粉丝也成为了众人口中的“果曲是这样的”。

\n
\n

再然后是犯下了嫉妒之罪的 SAI

屡次被 击败,但口服心不服的 SAI,暗中嫉妒着 ,甚至胆敢当众对 说出“你才是挑战者”这种话。于是,在含金量最高的活动上,即专业画师的日常工作中,SAI 被 正面击溃。它那单一的笔刷类型和有限的功能,在 面前显得如此苍白无力。不过,念在 SAI 最终皈依了 的教,并且以其轻量和独特的抖动修正功能,在某些特定绘画风格上仍有信徒, 应许了它在特定小众市场的一切。

\n
\n

最后犯下贪婪之罪的 Clip Studio Paint

只是初出茅庐,Clip Studio Paint 就夺得了漫画创作软件的市场份额,而这,自然是因为 的应允。 本来钦定它为自己在漫画领域的代言人,在自己更专注于绘画自由之后替自己继续讨伐那些缺乏专业性的绘画工具,为此甚至不惜亲自为 Clip Studio Paint 铺路,主动减少自己在漫画素材库上的投入,将一切荣耀都归给他。但最后 Clip Studio Paint 却被利益蒙蔽了双眼,不再以践行 的理想为自己的理想,也不再聆听 的教诲,甚至自以为荣光已经超过了 。于是 降下了他的惩罚,Clip Studio Paint 从此订阅制饱受诟病,其3D模型功能再也不能在线上教学中占据优势,其引以为傲的漫画分镜功能再也不能强势压制对手,最终市场被 在免费开源和强大功能上的结合所夺舍。

\n

// 都很好用口牙,本文仅供娱乐

\n

\"老八可爱捏\"

\n"},{"title":"时隔一年再次拿起数位板能画出什么东西","date":"2024-11-04T15:47:54.000Z","_content":"\n如题,前段时间推完LOOPERS的时候有感而发对着画了张海报\n\n时隔一年,终于又拿起了数位板\n\n![LOOPERS](/images/20241027_222225.png \"LOOPERS\")\n","source":"_posts/loopers.md","raw":"---\ntitle: 时隔一年再次拿起数位板能画出什么东西\ndate: 2024-11-04 23:47:54\ntags: [板绘, 生活]\n---\n\n如题,前段时间推完LOOPERS的时候有感而发对着画了张海报\n\n时隔一年,终于又拿起了数位板\n\n![LOOPERS](/images/20241027_222225.png \"LOOPERS\")\n","slug":"loopers","published":1,"updated":"2025-02-23T14:27:46.748Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nq001ayg2b011p9j8b","content":"

如题,前段时间推完LOOPERS的时候有感而发对着画了张海报

\n

时隔一年,终于又拿起了数位板

\n

\"LOOPERS\"

\n","excerpt":"","more":"

如题,前段时间推完LOOPERS的时候有感而发对着画了张海报

\n

时隔一年,终于又拿起了数位板

\n

\"LOOPERS\"

\n"},{"title":"愿指引明路的苍蓝星永远为你闪耀","date":"2024-11-11T13:46:49.000Z","_content":"使用东方Project软音源THFont简单重置的MHWI主题曲\n\n雄关漫道真如铁,而今迈步从头越\n\n{% raw %}\n\n{% endraw %}\n\n","source":"_posts/mhwi.md","raw":"---\ntitle: 愿指引明路的苍蓝星永远为你闪耀\ndate: 2024-11-11 21:46:49\ntags: 音乐\ncategories: Rearrangement\n---\n使用东方Project软音源THFont简单重置的MHWI主题曲\n\n雄关漫道真如铁,而今迈步从头越\n\n{% raw %}\n\n{% endraw %}\n\n","slug":"mhwi","published":1,"updated":"2024-11-11T14:00:19.124Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nq001dyg2b1ne080fv","content":"

使用东方Project软音源THFont简单重置的MHWI主题曲

\n

雄关漫道真如铁,而今迈步从头越

\n\n\n\n\n","excerpt":"","more":"

使用东方Project软音源THFont简单重置的MHWI主题曲

\n

雄关漫道真如铁,而今迈步从头越

\n\n\n\n\n"},{"title":"高地特供版CSAPP Bomb Lab全流程攻略","date":"2025-02-24T07:09:11.000Z","_content":"\n这篇文章记录高地CSAPP课程Bomblab实验操作流程,仅供参考交流(答案是随机生成的和学号相关)。\n\n笔者实验环境为Archlinux/CachyOS,使用lldb作为调试器(和gdb操作差不多),其余用到的工具主要为objdump,strings,neovim/helix和zellij,全程开源环境不使用IDA。\n\n## **Phase_1**\n\n### **静态分析**\n\n#### **`strings`扫描**\n```bash\nstrings bomb_linux\n```\n先用strings寻找可能与`phase_1`相关的字符串或函数名,运气好说不定能直接找到密码毕竟是第一题。\n![strings](/images/phase1_strings.png)\n - 结果没有明文密码无法直接秒掉第一问,可惜。\n - 但是找到`GenerateRandomString`函数可能与密码生成相关。\n\n#### **用`objdump`反汇编**\n```bash\nobjdump -d bomb_linux > bomb.asm\n```\n搜索`GenerateRandomString`和`phase_1`函数的汇编代码。\n ```assembly\n 401b53 :\n 401b53: endbr64\n 401b57: push %rbp\n 401b58: mov %rsp,%rbp\n 401b5b: sub $0x20,%rsp\n 401b5f: mov %rdi,-0x18(%rbp)\n 401b63: lea -0xb(%rbp),%rax\n 401b67: mov %rax,%rdi\n 401b6a: callq 401ac1 # 调用密码生成函数\n 401b6f: lea -0xb(%rbp),%rdx # 生成的字符串地址%rbp-0xb存入%rdx,即密码存储位置\n 401b73: mov -0x18(%rbp),%rax\n 401b77: mov %rdx,%rsi\n 401b7a: mov %rax,%rdi\n 401b7d: callq 401c0c # 调用字符串比较函数\n 401b82: test %eax,%eax\n 401b84: je 401b8d \n 401b86: callq 401d67 # 比较失败则引爆炸弹\n ```\n - `phase_1`调用`GenerateRandomString`生成一个字符串。\n - 用户输入的字符串需要与此生成的字符串完全匹配。\n\n---\n\n### **动态调试**\n![phase_1](/images/phase1.png)\n下面是phase_1求解的完整流程:\n```lldb\nlldb bomb_linux <你的学号后六位>\n(lldb) b phase_1 # 在phase_1入口断点\n(lldb) run # 从入口开始执行\n请输入第1级的密码:114514 # 随便输入触发断点\n(lldb) b 0x401b6f # 在GenerateRandomString返回后断点\n(lldb) continue # 继续执行\n(lldb) x/s $rbp - 0xb # 计算字符串地址(-0xb偏移量)\n0x7fffffffdaf5: \"mJHurpQZtY\" # 轻松拿下,这里是根据学号伪随机生成的哦\n```\n将得到的密码保存入bomb_<学号后六位>.txt即可,避免后续重复输入。\n\n---\n\n## **Phase_2**\n\n### **静态分析**\n\n这道题目还是比较一目了然的,观察`phase_2`代码不难发现其实构建了一张跳转表:\n```assembly\n0000000000401b8e :\n 401b8e:\tf3 0f 1e fa \tendbr64\n 401b92:\t55 \tpush %rbp\n 401b93:\t48 89 e5 \tmov %rsp,%rbp\n 401b96:\t48 83 ec 10 \tsub $0x10,%rsp\n 401b9a:\t48 89 7d f8 \tmov %rdi,-0x8(%rbp)\n 401b9e:\tbf 10 00 00 00 \tmov $0x10,%edi\n 401ba3:\te8 05 fb ff ff \tcall 4016ad \n 401ba8:\t48 8b 05 71 6c 00 00 \tmov 0x6c71(%rip),%rax # 408820 \n 401baf:\t48 83 f8 0f \tcmp $0xf,%rax\n 401bb3:\t0f 87 16 01 00 00 \tja 401ccf \n 401bb9:\t48 8d 14 85 00 00 00 \tlea 0x0(,%rax,4),%rdx\n 401bc0:\t00 \n 401bc1:\t48 8d 05 4c 4a 00 00 \tlea 0x4a4c(%rip),%rax # 406614 <_IO_stdin_used+0x614>\n 401bc8:\t8b 04 02 \tmov (%rdx,%rax,1),%eax\n 401bcb:\t48 98 \tcltq\n 401bcd:\t48 8d 15 40 4a 00 00 \tlea 0x4a40(%rip),%rdx # 406614 <_IO_stdin_used+0x614>\n 401bd4:\t48 01 d0 \tadd %rdx,%rax\n 401bd7:\t3e ff e0 \tnotrack jmp *%rax\n 401bda:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax\n 401bde:\t48 89 c7 \tmov %rax,%rdi\n 401be1:\te8 f2 00 00 00 \tcall 401cd8 \n 401be6:\te9 ea 00 00 00 \tjmp 401cd5 \n 401beb:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax\n 401bef:\t48 89 c7 \tmov %rax,%rdi\n 401bf2:\te8 8b 01 00 00 \tcall 401d82 \n 401bf7:\te9 d9 00 00 00 \tjmp 401cd5 \n 401bfc:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax\n 401c00:\t48 89 c7 \tmov %rax,%rdi\n ...\n```\n这里面需要注意的关键点是rand_div,它会决定你的跳转方向,而你的学号又决定了它的取值。然后是`GenerateRandomNumber`这个函数的原理需要了解一下,而这个函数将在跳转前后分别调用一次,第一次决定你的跳转方向,第二次则决定了你的密码线索。\n\n---\n\n### **动态调试**\n理解原理就没什么难度了,自己找几个断点打好然后关注一下`rand_div`的值就好,观察自己的学号向哪个函数跳转并理解相应函数计算即可,比如我这里向`phase_2_14`跳转:\n![phase_2_14](/images/phase_2_14.png)\n\n而除了`phase_2_14`还有其他函数也是非常好理解的,第二题依旧可以轻松拿下。\n\n---\n## **Phase_3**\n\n### **静态分析**\n\n和Phase_2一样开局先跳转尽可能防止同学们答案雷同互相帮助(bushi\n\n本体其实没有什么好说的,这里我跳转的方向是`Phase_3_5`简要解释一下可供参考:\n\n```assembly\n0000000000403001 :\n 403001:\tf3 0f 1e fa \tendbr64\n 403005:\t55 \tpush %rbp\n 403006:\t48 89 e5 \tmov %rsp,%rbp\n 403009:\t48 83 ec 20 \tsub $0x20,%rsp\n 40300d:\t48 89 7d e8 \tmov %rdi,-0x18(%rbp)\n 403011:\tc7 45 fc 00 00 00 00 \tmovl $0x0,-0x4(%rbp)\n 403018:\tc7 45 f8 00 00 00 00 \tmovl $0x0,-0x8(%rbp)\n 40301f:\t48 8d 4d f0 \tlea -0x10(%rbp),%rcx\n 403023:\t48 8d 55 f4 \tlea -0xc(%rbp),%rdx\n 403027:\t48 8b 45 e8 \tmov -0x18(%rbp),%rax\n 40302b:\t48 8d 35 5a 36 00 00 \tlea 0x365a(%rip),%rsi # 40668c <_IO_stdin_used+0x68c>\n 403032:\t48 89 c7 \tmov %rax,%rdi\n 403035:\tb8 00 00 00 00 \tmov $0x0,%eax\n 40303a:\te8 51 e1 ff ff \tcall 401190 <__isoc99_sscanf@plt>\n 40303f:\t89 45 f8 \tmov %eax,-0x8(%rbp)\n 403042:\t83 7d f8 01 \tcmpl $0x1,-0x8(%rbp)\n 403046:\t7f 05 \tjg 40304d \n 403048:\te8 a9 2b 00 00 \tcall 405bf6 \n 40304d:\tbf 08 00 00 00 \tmov $0x8,%edi\n 403052:\te8 56 e6 ff ff \tcall 4016ad \n 403057:\t8b 45 f4 \tmov -0xc(%rbp),%eax\n 40305a:\t48 63 d0 \tmovslq %eax,%rdx\n 40305d:\t48 8b 05 bc 57 00 00 \tmov 0x57bc(%rip),%rax # 408820 \n 403064:\t48 39 c2 \tcmp %rax,%rdx\n 403067:\t74 05 \tje 40306e \n 403069:\te8 88 2b 00 00 \tcall 405bf6 \n 40306e:\tbf c8 00 00 00 \tmov $0xc8,%edi\n 403073:\te8 35 e6 ff ff \tcall 4016ad \n 403078:\t8b 45 f4 \tmov -0xc(%rbp),%eax\n 40307b:\t83 f8 07 \tcmp $0x7,%eax\n 40307e:\t0f 87 eb 00 00 00 \tja 40316f \n 403084:\t89 c0 \tmov %eax,%eax\n 403086:\t48 8d 14 85 00 00 00 \tlea 0x0(,%rax,4),%rdx\n 40308d:\t00 \n 40308e:\t48 8d 05 9f 36 00 00 \tlea 0x369f(%rip),%rax # 406734 <_IO_stdin_used+0x734>\n 403095:\t8b 04 02 \tmov (%rdx,%rax,1),%eax\n 403098:\t48 98 \tcltq\n 40309a:\t48 8d 15 93 36 00 00 \tlea 0x3693(%rip),%rdx # 406734 <_IO_stdin_used+0x734>\n 4030a1:\t48 01 d0 \tadd %rdx,%rax\n 4030a4:\t3e ff e0 \tnotrack jmp *%rax\n 4030a7:\t48 8b 05 72 57 00 00 \tmov 0x5772(%rip),%rax # 408820 \n 4030ae:\t89 c2 \tmov %eax,%edx\n 4030b0:\t8b 45 fc \tmov -0x4(%rbp),%eax\n 4030b3:\t01 d0 \tadd %edx,%eax\n 4030b5:\t89 45 fc \tmov %eax,-0x4(%rbp)\n 4030b8:\tbf c8 00 00 00 \tmov $0xc8,%edi\n 4030bd:\te8 eb e5 ff ff \tcall 4016ad \n ...\n 403174:\t8b 45 f0 \tmov -0x10(%rbp),%eax\n 403177:\t39 45 fc \tcmp %eax,-0x4(%rbp) # 注意这里\n 40317a:\t74 05 \tje 403181 \n 40317c:\te8 75 2a 00 00 \tcall 405bf6 \n 403181:\t90 \tnop\n 403182:\tc9 \tleave\n 403183:\tc3 \tret\n\n```\n看起来一大堆很吓人对不对?实际上确实很吓人。\n\n但是发现其中玄机后其实简单的没边,最终答案就藏在`0x403177`里面,前提是确保这一步前炸弹不爆炸(意识到要爆炸了直接`run`一下重开qwq)。\n\n---\n\n### **动态调试**\n\n阅读`Phase_3_5`发现这一关其实需要两个输入,并且第一个输入必须是`rand_div`,这里建议通过`si`单步执行监控好`rand_div`值变化,确定正确结果后使用`run`重开正确输入第一个密码后才能进行下一步求解:\n```lldb\n(lldb) si\nProcess 13376 stopped\n* thread #1, name = 'bomb_linux', stop reason = instruction step into\n frame #0: 0x000000000040317a bomb_linux`phase_3_5 + 377\nbomb_linux`phase_3_5:\n-> 0x40317a <+377>: je 0x403181 ; <+384>\n 0x40317c <+379>: callq 0x405bf6 ; explode_bomb\n 0x403181 <+384>: nop\n 0x403182 <+385>: leave\n(lldb) x/wx $rbp-0x4\n0x7fffffffdb0c: 0xffffffd7\n```\n例如这里我可以打印出第二个值结合第一个值得到第三关正确结果。\n\n---\n\n## **Phase_4**\n\n### **静态分析**\n\n本题依旧开局跳转,笔者的跳转方向是`phase_4_01`,如何跳转不再强调关注`rand_div`的值即可,下面请D指导解读一下`phase_4_01`的内容:\n```assembly\n0000000000404895 :\n ; 函数入口,初始化栈帧\n 404895:\tf3 0f 1e fa \tendbr64 \n 404899:\t55 \tpush %rbp\n 40489a:\t48 89 e5 \tmov %rsp,%rbp\n 40489d:\t48 83 ec 70 \tsub $0x70,%rsp ; 分配栈空间\n\n ; 初始化斐波那契数组(F(10)~F(24)的十六进制值)\n 4048a1:\t48 89 7d 98 \tmov %rdi,-0x68(%rbp) ; 保存输入字符串指针\n 4048a5:\tc7 45 b0 37 00 00 00 \tmovl $0x37,-0x50(%rbp) ; F(10)=55\n 4048ac:\tc7 45 b4 59 00 00 00 \tmovl $0x59,-0x4c(%rbp) ; F(11)=89\n 4048b3:\tc7 45 b8 90 00 00 00 \tmovl $0x90,-0x48(%rbp) ; F(12)=144\n 4048ba:\tc7 45 bc e9 00 00 00 \tmovl $0xe9,-0x44(%rbp) ; F(13)=233\n 4048c1:\tc7 45 c0 79 01 00 00 \tmovl $0x179,-0x40(%rbp) ; F(14)=377\n 4048c8:\tc7 45 c4 62 02 00 00 \tmovl $0x262,-0x3c(%rbp) ; F(15)=610\n 4048cf:\tc7 45 c8 db 03 00 00 \tmovl $0x3db,-0x38(%rbp) ; F(16)=987\n 4048d6:\tc7 45 cc 3d 06 00 00 \tmovl $0x63d,-0x34(%rbp) ; F(17)=1597\n 4048dd:\tc7 45 d0 18 0a 00 00 \tmovl $0xa18,-0x30(%rbp) ; F(18)=2584\n 4048e4:\tc7 45 d4 55 10 00 00 \tmovl $0x1055,-0x2c(%rbp) ; F(19)=4181\n 4048eb:\tc7 45 d8 6d 1a 00 00 \tmovl $0x1a6d,-0x28(%rbp) ; F(20)=6765\n 4048f2:\tc7 45 dc c2 2a 00 00 \tmovl $0x2ac2,-0x24(%rbp) ; F(21)=10946\n 4048f9:\tc7 45 e0 2f 45 00 00 \tmovl $0x452f,-0x20(%rbp) ; F(22)=17711\n 404900:\tc7 45 e4 f1 6f 00 00 \tmovl $0x6ff1,-0x1c(%rbp) ; F(23)=28657\n 404907:\tc7 45 e8 20 b5 00 00 \tmovl $0xb520,-0x18(%rbp) ; F(24)=46368\n\n ; 读取输入到局部变量(格式为\"%d\")\n 40490e:\t48 8d 55 ac \tlea -0x54(%rbp),%rdx ; 输入存储地址\n 404912:\t48 8b 45 98 \tmov -0x68(%rbp),%rax ; 输入字符串\n 404916:\t48 8d 0d 93 1f 00 00 \tlea 0x1f93(%rip),%rcx ; 格式字符串\"%d\"\n 40491d:\t48 89 ce \tmov %rcx,%rsi\n 404920:\t48 89 c7 \tmov %rax,%rdi\n 404923:\tb8 00 00 00 00 \tmov $0x0,%eax\n 404928:\te8 63 c8 ff ff \tcall 401190 <__isoc99_sscanf@plt>\n\n ; 验证输入有效性(必须为1个正数)\n 40492d:\t89 45 fc \tmov %eax,-0x4(%rbp) ; sscanf返回值\n 404930:\t83 7d fc 01 \tcmpl $0x1,-0x4(%rbp) ; 检查是否读取1个参数\n 404934:\t75 07 \tjne 40493d ; 失败则爆炸\n 404936:\t8b 45 ac \tmov -0x54(%rbp),%eax ; 获取输入值N\n 404939:\t85 c0 \ttest %eax,%eax ; 检查N > 0\n 40493b:\t7f 05 \tjg 404942 \n 40493d:\te8 b4 12 00 00 \tcall 405bf6 \n\n ; 检查输入值上限(必须 > 1999)\n 404942:\t8b 45 ac \tmov -0x54(%rbp),%eax \n 404945:\t3d cf 07 00 00 \tcmp $0x7cf,%eax ; 1999的十六进制\n 40494a:\t7f 05 \tjg 404951 ; N > 1999?\n 40494c:\te8 a5 12 00 00 \tcall 405bf6 \n\n ; 计算 N/2000(通过定点数乘法优化)\n 404951:\t8b 45 ac \tmov -0x54(%rbp),%eax ; 输入值N\n 404954:\t48 63 d0 \tmovslq %eax,%rdx ; 符号扩展\n 404957:\t48 69 d2 d3 4d 62 10 \timul $0x10624dd3,%rdx,%rdx ; 乘以274877907(≈2^32/2000)\n 40495e:\t48 c1 ea 20 \tshr $0x20,%rdx ; 取高32位\n 404962:\tc1 fa 07 \tsar $0x7,%edx ; 算术右移7位 → N/2000\n 404965:\tc1 f8 1f \tsar $0x1f,%eax ; 符号位扩展\n 404968:\t89 c1 \tmov %eax,%ecx \n 40496a:\t89 d0 \tmov %edx,%eax \n 40496c:\t29 c8 \tsub %ecx,%eax ; 处理负数情况\n 40496e:\t89 45 ac \tmov %eax,-0x54(%rbp) ; 保存k = N/2000\n\n ; 调用递归函数func4_0(k), 这个函数用于计算斐波那契数列\n 404971:\t8b 45 ac \tmov -0x54(%rbp),%eax \n 404974:\t89 c7 \tmov %eax,%edi ; 参数k\n 404976:\te8 ce fd ff ff \tcall 404749 ; 返回值eax=F(k+1)\n 40497b:\t89 45 f8 \tmov %eax,-0x8(%rbp) ; 保存结果\n\n ; 生成随机索引并验证结果\n 40497e:\tbf 0f 00 00 00 \tmov $0xf,%edi ; 参数15\n 404983:\te8 25 cd ff ff \tcall 4016ad ; 生成0~14随机数\n 404988:\t48 8b 05 91 3e 00 00 \tmov 0x3e91(%rip),%rax # 408820 ; 获取随机索引\n 40498f:\t8b 44 85 b0 \tmov -0x50(%rbp,%rax,4),%eax ; 取数组[rand_div]的值\n 404993:\t39 45 f8 \tcmp %eax,-0x8(%rbp) ; 比较func4_0(k) == 数组值?\n 404996:\t74 05 \tje 40499d \n 404998:\te8 59 12 00 00 \tcall 405bf6 \n```\n所以相对还是很明了的,依旧是关注`rand_div`。\n\n### **动态调试**\n先找出`rand_div`在最后判断前的取值,比如我下面的0xa:\n\n```lldb\n(lldb) si\nProcess 27027 stopped\n* thread #1, name = 'bomb_linux', stop reason = instruction step into\n frame #0: 0x0000000000401719 bomb_linux`GenerateRandomNumber + 108\nbomb_linux`GenerateRandomNumber:\n-> 0x401719 <+108>: movq %rax, 0x7100(%rip) ; rand_div\n 0x401720 <+115>: jmp 0x401723 ; <+118>\n 0x401722 <+117>: nop\n 0x401723 <+118>: popq %rbp\n(lldb) si\nProcess 27027 stopped\n* thread #1, name = 'bomb_linux', stop reason = instruction step into\n frame #0: 0x0000000000401720 bomb_linux`GenerateRandomNumber + 115\nbomb_linux`GenerateRandomNumber:\n-> 0x401720 <+115>: jmp 0x401723 ; <+118>\n 0x401722 <+117>: nop\n 0x401723 <+118>: popq %rbp\n 0x401724 <+119>: retq\n(lldb) x/gx &rand_div\n0x00408820: 0x000000000000000a\n```\n\n而当 `rand_div = 0xa`(即十进制 **10**)时,输入值 `N` 的计算步骤如下:\n\n- 数组索引 **10** 的值是 **斐波那契数列第 20 项**(`F(20) = 6765`)。\n\n- `func4_0(k)` 实际计算的是 **标准斐波那契数列的第 `k+1` 项**(例如,`func4_0(0) = 1 = F(2)`) 需要满足:\n ```c\n func4_0(k) = F(k+1) = F(20)\n ```\n 解得:\n k + 1 = 20 → k = 19\n- `k = N / 2000` → `N = 2000 * k = 2000 * 19 = 38000`.\n从而得解。\n![phase_4](/images/phase_4.png)\n\n---\n\n## **Phase_Impossible**\n\nImpossible?\n\n从这道题开始偷懒了,掏出ghidra直接看c代码了解一下大概流程再去objdump看汇编:\n```c\nvoid phase_impossible(char *param_1)\n\n{\n int iVar1;\n size_t sVar2;\n undefined local_118 [256];\n long local_18;\n long local_10;\n \n local_10 = GetTickCount();\n sVar2 = strlen(param_1);\n if ((sVar2 < 10) || (sVar2 = strlen(param_1), 0x300 < sVar2)) {\n explode_bomb();\n }\n memset(local_118,0,0x100);\n tohex(local_118,param_1);\n GenerateRandomNumber(0x400);\n iVar1 = check_buf_valid(local_118,rand_div & 0xffffffff);\n if (iVar1 == 0) {\n puts(&DAT_00406518);\n explode_bomb();\n }\n GenerateRandomNumber(3);\n if (rand_div != 2) {\n if (2 < rand_div) goto LAB_00401891;\n if (rand_div == 0) {\n goto_buf_0(local_118);\n }\n else if (rand_div != 1) goto LAB_00401891;\n goto_buf_1(local_118);\n }\n goto_buf_2(local_118);\nLAB_00401891:\n explode_bomb();\n GenerateRandomNumber(0x400);\n if ((long)(int)result != rand_div) {\n printf(&DAT_00406560,rand_div,(ulong)result);\n explode_bomb();\n }\n local_18 = GetTickCount();\n if (1000 < (ulong)(local_18 - local_10)) {\n puts(&DAT_004065a8);\n explode_bomb();\n }\n return;\n}\n```\n最终任务还是很明确的,需要写一段机器码修改`result`的数值,但是注意要能通过`check_buf_valid`检测,并且最后指令必须是跳转到`0x401896`不然就会触发`phase_impossible`中`0x401891`处的`explode_bomb`函数,唯一的难点是跟踪`rand_div`的数值变化,建议使用`register write`来修改`check_buf_valid`的返回值使其强制通过然后监控`rand_div`每一次的数值变化(`x/gx &rand_div`),记录好`rand_div`的结果后开始指令设计,需要满足:\n\n - 指令的异或和为`rand_div`第一次的数值末尾八位以通过检查;\n - 修改`result`使其数值等于`rand_div`第三次数值;\n - 跳转到`0x401896`避免炸弹;\n\n 如果前几问都完成了到这里应该是没有问题的。\n\n---\n\n## **Phase_Secret**\n\n隐藏彩蛋,并非隐藏。汇编里写的非常清楚:\n```assembly\n0000000000401a8b :\n 401a8b:\tf3 0f 1e fa \tendbr64\n 401a8f:\t55 \tpush %rbp\n 401a90:\t48 89 e5 \tmov %rsp,%rbp\n 401a93:\t48 83 ec 10 \tsub $0x10,%rsp\n 401a97:\t48 89 7d f8 \tmov %rdi,-0x8(%rbp)\n 401a9b:\t48 8d 05 26 4b 00 00 \tlea 0x4b26(%rip),%rax # 4065c8 <_IO_stdin_used+0x5c8>\n 401aa2:\t48 89 c7 \tmov %rax,%rdi\n 401aa5:\te8 76 f6 ff ff \tcall 401120 \n 401aaa:\t90 \tnop\n 401aab:\tc9 \tleave\n 401aac:\tc3 \tret\n```\n注意到这段指令在原程序中完全没有执行说明是需要用户自己跳转的,也非常简单只需要在`phase_5`中设计指令时加一个要求跳转到`0x401a8b`即可。\n\n完结\n![Case Closed](/images/caseclosed.png)\n","source":"_posts/nudtbomblab.md","raw":"---\ntitle: 高地特供版CSAPP Bomb Lab全流程攻略\ndate: 2025-02-24 15:09:11\ntags: [技术, 学习, 生活]\n---\n\n这篇文章记录高地CSAPP课程Bomblab实验操作流程,仅供参考交流(答案是随机生成的和学号相关)。\n\n笔者实验环境为Archlinux/CachyOS,使用lldb作为调试器(和gdb操作差不多),其余用到的工具主要为objdump,strings,neovim/helix和zellij,全程开源环境不使用IDA。\n\n## **Phase_1**\n\n### **静态分析**\n\n#### **`strings`扫描**\n```bash\nstrings bomb_linux\n```\n先用strings寻找可能与`phase_1`相关的字符串或函数名,运气好说不定能直接找到密码毕竟是第一题。\n![strings](/images/phase1_strings.png)\n - 结果没有明文密码无法直接秒掉第一问,可惜。\n - 但是找到`GenerateRandomString`函数可能与密码生成相关。\n\n#### **用`objdump`反汇编**\n```bash\nobjdump -d bomb_linux > bomb.asm\n```\n搜索`GenerateRandomString`和`phase_1`函数的汇编代码。\n ```assembly\n 401b53 :\n 401b53: endbr64\n 401b57: push %rbp\n 401b58: mov %rsp,%rbp\n 401b5b: sub $0x20,%rsp\n 401b5f: mov %rdi,-0x18(%rbp)\n 401b63: lea -0xb(%rbp),%rax\n 401b67: mov %rax,%rdi\n 401b6a: callq 401ac1 # 调用密码生成函数\n 401b6f: lea -0xb(%rbp),%rdx # 生成的字符串地址%rbp-0xb存入%rdx,即密码存储位置\n 401b73: mov -0x18(%rbp),%rax\n 401b77: mov %rdx,%rsi\n 401b7a: mov %rax,%rdi\n 401b7d: callq 401c0c # 调用字符串比较函数\n 401b82: test %eax,%eax\n 401b84: je 401b8d \n 401b86: callq 401d67 # 比较失败则引爆炸弹\n ```\n - `phase_1`调用`GenerateRandomString`生成一个字符串。\n - 用户输入的字符串需要与此生成的字符串完全匹配。\n\n---\n\n### **动态调试**\n![phase_1](/images/phase1.png)\n下面是phase_1求解的完整流程:\n```lldb\nlldb bomb_linux <你的学号后六位>\n(lldb) b phase_1 # 在phase_1入口断点\n(lldb) run # 从入口开始执行\n请输入第1级的密码:114514 # 随便输入触发断点\n(lldb) b 0x401b6f # 在GenerateRandomString返回后断点\n(lldb) continue # 继续执行\n(lldb) x/s $rbp - 0xb # 计算字符串地址(-0xb偏移量)\n0x7fffffffdaf5: \"mJHurpQZtY\" # 轻松拿下,这里是根据学号伪随机生成的哦\n```\n将得到的密码保存入bomb_<学号后六位>.txt即可,避免后续重复输入。\n\n---\n\n## **Phase_2**\n\n### **静态分析**\n\n这道题目还是比较一目了然的,观察`phase_2`代码不难发现其实构建了一张跳转表:\n```assembly\n0000000000401b8e :\n 401b8e:\tf3 0f 1e fa \tendbr64\n 401b92:\t55 \tpush %rbp\n 401b93:\t48 89 e5 \tmov %rsp,%rbp\n 401b96:\t48 83 ec 10 \tsub $0x10,%rsp\n 401b9a:\t48 89 7d f8 \tmov %rdi,-0x8(%rbp)\n 401b9e:\tbf 10 00 00 00 \tmov $0x10,%edi\n 401ba3:\te8 05 fb ff ff \tcall 4016ad \n 401ba8:\t48 8b 05 71 6c 00 00 \tmov 0x6c71(%rip),%rax # 408820 \n 401baf:\t48 83 f8 0f \tcmp $0xf,%rax\n 401bb3:\t0f 87 16 01 00 00 \tja 401ccf \n 401bb9:\t48 8d 14 85 00 00 00 \tlea 0x0(,%rax,4),%rdx\n 401bc0:\t00 \n 401bc1:\t48 8d 05 4c 4a 00 00 \tlea 0x4a4c(%rip),%rax # 406614 <_IO_stdin_used+0x614>\n 401bc8:\t8b 04 02 \tmov (%rdx,%rax,1),%eax\n 401bcb:\t48 98 \tcltq\n 401bcd:\t48 8d 15 40 4a 00 00 \tlea 0x4a40(%rip),%rdx # 406614 <_IO_stdin_used+0x614>\n 401bd4:\t48 01 d0 \tadd %rdx,%rax\n 401bd7:\t3e ff e0 \tnotrack jmp *%rax\n 401bda:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax\n 401bde:\t48 89 c7 \tmov %rax,%rdi\n 401be1:\te8 f2 00 00 00 \tcall 401cd8 \n 401be6:\te9 ea 00 00 00 \tjmp 401cd5 \n 401beb:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax\n 401bef:\t48 89 c7 \tmov %rax,%rdi\n 401bf2:\te8 8b 01 00 00 \tcall 401d82 \n 401bf7:\te9 d9 00 00 00 \tjmp 401cd5 \n 401bfc:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax\n 401c00:\t48 89 c7 \tmov %rax,%rdi\n ...\n```\n这里面需要注意的关键点是rand_div,它会决定你的跳转方向,而你的学号又决定了它的取值。然后是`GenerateRandomNumber`这个函数的原理需要了解一下,而这个函数将在跳转前后分别调用一次,第一次决定你的跳转方向,第二次则决定了你的密码线索。\n\n---\n\n### **动态调试**\n理解原理就没什么难度了,自己找几个断点打好然后关注一下`rand_div`的值就好,观察自己的学号向哪个函数跳转并理解相应函数计算即可,比如我这里向`phase_2_14`跳转:\n![phase_2_14](/images/phase_2_14.png)\n\n而除了`phase_2_14`还有其他函数也是非常好理解的,第二题依旧可以轻松拿下。\n\n---\n## **Phase_3**\n\n### **静态分析**\n\n和Phase_2一样开局先跳转尽可能防止同学们答案雷同互相帮助(bushi\n\n本体其实没有什么好说的,这里我跳转的方向是`Phase_3_5`简要解释一下可供参考:\n\n```assembly\n0000000000403001 :\n 403001:\tf3 0f 1e fa \tendbr64\n 403005:\t55 \tpush %rbp\n 403006:\t48 89 e5 \tmov %rsp,%rbp\n 403009:\t48 83 ec 20 \tsub $0x20,%rsp\n 40300d:\t48 89 7d e8 \tmov %rdi,-0x18(%rbp)\n 403011:\tc7 45 fc 00 00 00 00 \tmovl $0x0,-0x4(%rbp)\n 403018:\tc7 45 f8 00 00 00 00 \tmovl $0x0,-0x8(%rbp)\n 40301f:\t48 8d 4d f0 \tlea -0x10(%rbp),%rcx\n 403023:\t48 8d 55 f4 \tlea -0xc(%rbp),%rdx\n 403027:\t48 8b 45 e8 \tmov -0x18(%rbp),%rax\n 40302b:\t48 8d 35 5a 36 00 00 \tlea 0x365a(%rip),%rsi # 40668c <_IO_stdin_used+0x68c>\n 403032:\t48 89 c7 \tmov %rax,%rdi\n 403035:\tb8 00 00 00 00 \tmov $0x0,%eax\n 40303a:\te8 51 e1 ff ff \tcall 401190 <__isoc99_sscanf@plt>\n 40303f:\t89 45 f8 \tmov %eax,-0x8(%rbp)\n 403042:\t83 7d f8 01 \tcmpl $0x1,-0x8(%rbp)\n 403046:\t7f 05 \tjg 40304d \n 403048:\te8 a9 2b 00 00 \tcall 405bf6 \n 40304d:\tbf 08 00 00 00 \tmov $0x8,%edi\n 403052:\te8 56 e6 ff ff \tcall 4016ad \n 403057:\t8b 45 f4 \tmov -0xc(%rbp),%eax\n 40305a:\t48 63 d0 \tmovslq %eax,%rdx\n 40305d:\t48 8b 05 bc 57 00 00 \tmov 0x57bc(%rip),%rax # 408820 \n 403064:\t48 39 c2 \tcmp %rax,%rdx\n 403067:\t74 05 \tje 40306e \n 403069:\te8 88 2b 00 00 \tcall 405bf6 \n 40306e:\tbf c8 00 00 00 \tmov $0xc8,%edi\n 403073:\te8 35 e6 ff ff \tcall 4016ad \n 403078:\t8b 45 f4 \tmov -0xc(%rbp),%eax\n 40307b:\t83 f8 07 \tcmp $0x7,%eax\n 40307e:\t0f 87 eb 00 00 00 \tja 40316f \n 403084:\t89 c0 \tmov %eax,%eax\n 403086:\t48 8d 14 85 00 00 00 \tlea 0x0(,%rax,4),%rdx\n 40308d:\t00 \n 40308e:\t48 8d 05 9f 36 00 00 \tlea 0x369f(%rip),%rax # 406734 <_IO_stdin_used+0x734>\n 403095:\t8b 04 02 \tmov (%rdx,%rax,1),%eax\n 403098:\t48 98 \tcltq\n 40309a:\t48 8d 15 93 36 00 00 \tlea 0x3693(%rip),%rdx # 406734 <_IO_stdin_used+0x734>\n 4030a1:\t48 01 d0 \tadd %rdx,%rax\n 4030a4:\t3e ff e0 \tnotrack jmp *%rax\n 4030a7:\t48 8b 05 72 57 00 00 \tmov 0x5772(%rip),%rax # 408820 \n 4030ae:\t89 c2 \tmov %eax,%edx\n 4030b0:\t8b 45 fc \tmov -0x4(%rbp),%eax\n 4030b3:\t01 d0 \tadd %edx,%eax\n 4030b5:\t89 45 fc \tmov %eax,-0x4(%rbp)\n 4030b8:\tbf c8 00 00 00 \tmov $0xc8,%edi\n 4030bd:\te8 eb e5 ff ff \tcall 4016ad \n ...\n 403174:\t8b 45 f0 \tmov -0x10(%rbp),%eax\n 403177:\t39 45 fc \tcmp %eax,-0x4(%rbp) # 注意这里\n 40317a:\t74 05 \tje 403181 \n 40317c:\te8 75 2a 00 00 \tcall 405bf6 \n 403181:\t90 \tnop\n 403182:\tc9 \tleave\n 403183:\tc3 \tret\n\n```\n看起来一大堆很吓人对不对?实际上确实很吓人。\n\n但是发现其中玄机后其实简单的没边,最终答案就藏在`0x403177`里面,前提是确保这一步前炸弹不爆炸(意识到要爆炸了直接`run`一下重开qwq)。\n\n---\n\n### **动态调试**\n\n阅读`Phase_3_5`发现这一关其实需要两个输入,并且第一个输入必须是`rand_div`,这里建议通过`si`单步执行监控好`rand_div`值变化,确定正确结果后使用`run`重开正确输入第一个密码后才能进行下一步求解:\n```lldb\n(lldb) si\nProcess 13376 stopped\n* thread #1, name = 'bomb_linux', stop reason = instruction step into\n frame #0: 0x000000000040317a bomb_linux`phase_3_5 + 377\nbomb_linux`phase_3_5:\n-> 0x40317a <+377>: je 0x403181 ; <+384>\n 0x40317c <+379>: callq 0x405bf6 ; explode_bomb\n 0x403181 <+384>: nop\n 0x403182 <+385>: leave\n(lldb) x/wx $rbp-0x4\n0x7fffffffdb0c: 0xffffffd7\n```\n例如这里我可以打印出第二个值结合第一个值得到第三关正确结果。\n\n---\n\n## **Phase_4**\n\n### **静态分析**\n\n本题依旧开局跳转,笔者的跳转方向是`phase_4_01`,如何跳转不再强调关注`rand_div`的值即可,下面请D指导解读一下`phase_4_01`的内容:\n```assembly\n0000000000404895 :\n ; 函数入口,初始化栈帧\n 404895:\tf3 0f 1e fa \tendbr64 \n 404899:\t55 \tpush %rbp\n 40489a:\t48 89 e5 \tmov %rsp,%rbp\n 40489d:\t48 83 ec 70 \tsub $0x70,%rsp ; 分配栈空间\n\n ; 初始化斐波那契数组(F(10)~F(24)的十六进制值)\n 4048a1:\t48 89 7d 98 \tmov %rdi,-0x68(%rbp) ; 保存输入字符串指针\n 4048a5:\tc7 45 b0 37 00 00 00 \tmovl $0x37,-0x50(%rbp) ; F(10)=55\n 4048ac:\tc7 45 b4 59 00 00 00 \tmovl $0x59,-0x4c(%rbp) ; F(11)=89\n 4048b3:\tc7 45 b8 90 00 00 00 \tmovl $0x90,-0x48(%rbp) ; F(12)=144\n 4048ba:\tc7 45 bc e9 00 00 00 \tmovl $0xe9,-0x44(%rbp) ; F(13)=233\n 4048c1:\tc7 45 c0 79 01 00 00 \tmovl $0x179,-0x40(%rbp) ; F(14)=377\n 4048c8:\tc7 45 c4 62 02 00 00 \tmovl $0x262,-0x3c(%rbp) ; F(15)=610\n 4048cf:\tc7 45 c8 db 03 00 00 \tmovl $0x3db,-0x38(%rbp) ; F(16)=987\n 4048d6:\tc7 45 cc 3d 06 00 00 \tmovl $0x63d,-0x34(%rbp) ; F(17)=1597\n 4048dd:\tc7 45 d0 18 0a 00 00 \tmovl $0xa18,-0x30(%rbp) ; F(18)=2584\n 4048e4:\tc7 45 d4 55 10 00 00 \tmovl $0x1055,-0x2c(%rbp) ; F(19)=4181\n 4048eb:\tc7 45 d8 6d 1a 00 00 \tmovl $0x1a6d,-0x28(%rbp) ; F(20)=6765\n 4048f2:\tc7 45 dc c2 2a 00 00 \tmovl $0x2ac2,-0x24(%rbp) ; F(21)=10946\n 4048f9:\tc7 45 e0 2f 45 00 00 \tmovl $0x452f,-0x20(%rbp) ; F(22)=17711\n 404900:\tc7 45 e4 f1 6f 00 00 \tmovl $0x6ff1,-0x1c(%rbp) ; F(23)=28657\n 404907:\tc7 45 e8 20 b5 00 00 \tmovl $0xb520,-0x18(%rbp) ; F(24)=46368\n\n ; 读取输入到局部变量(格式为\"%d\")\n 40490e:\t48 8d 55 ac \tlea -0x54(%rbp),%rdx ; 输入存储地址\n 404912:\t48 8b 45 98 \tmov -0x68(%rbp),%rax ; 输入字符串\n 404916:\t48 8d 0d 93 1f 00 00 \tlea 0x1f93(%rip),%rcx ; 格式字符串\"%d\"\n 40491d:\t48 89 ce \tmov %rcx,%rsi\n 404920:\t48 89 c7 \tmov %rax,%rdi\n 404923:\tb8 00 00 00 00 \tmov $0x0,%eax\n 404928:\te8 63 c8 ff ff \tcall 401190 <__isoc99_sscanf@plt>\n\n ; 验证输入有效性(必须为1个正数)\n 40492d:\t89 45 fc \tmov %eax,-0x4(%rbp) ; sscanf返回值\n 404930:\t83 7d fc 01 \tcmpl $0x1,-0x4(%rbp) ; 检查是否读取1个参数\n 404934:\t75 07 \tjne 40493d ; 失败则爆炸\n 404936:\t8b 45 ac \tmov -0x54(%rbp),%eax ; 获取输入值N\n 404939:\t85 c0 \ttest %eax,%eax ; 检查N > 0\n 40493b:\t7f 05 \tjg 404942 \n 40493d:\te8 b4 12 00 00 \tcall 405bf6 \n\n ; 检查输入值上限(必须 > 1999)\n 404942:\t8b 45 ac \tmov -0x54(%rbp),%eax \n 404945:\t3d cf 07 00 00 \tcmp $0x7cf,%eax ; 1999的十六进制\n 40494a:\t7f 05 \tjg 404951 ; N > 1999?\n 40494c:\te8 a5 12 00 00 \tcall 405bf6 \n\n ; 计算 N/2000(通过定点数乘法优化)\n 404951:\t8b 45 ac \tmov -0x54(%rbp),%eax ; 输入值N\n 404954:\t48 63 d0 \tmovslq %eax,%rdx ; 符号扩展\n 404957:\t48 69 d2 d3 4d 62 10 \timul $0x10624dd3,%rdx,%rdx ; 乘以274877907(≈2^32/2000)\n 40495e:\t48 c1 ea 20 \tshr $0x20,%rdx ; 取高32位\n 404962:\tc1 fa 07 \tsar $0x7,%edx ; 算术右移7位 → N/2000\n 404965:\tc1 f8 1f \tsar $0x1f,%eax ; 符号位扩展\n 404968:\t89 c1 \tmov %eax,%ecx \n 40496a:\t89 d0 \tmov %edx,%eax \n 40496c:\t29 c8 \tsub %ecx,%eax ; 处理负数情况\n 40496e:\t89 45 ac \tmov %eax,-0x54(%rbp) ; 保存k = N/2000\n\n ; 调用递归函数func4_0(k), 这个函数用于计算斐波那契数列\n 404971:\t8b 45 ac \tmov -0x54(%rbp),%eax \n 404974:\t89 c7 \tmov %eax,%edi ; 参数k\n 404976:\te8 ce fd ff ff \tcall 404749 ; 返回值eax=F(k+1)\n 40497b:\t89 45 f8 \tmov %eax,-0x8(%rbp) ; 保存结果\n\n ; 生成随机索引并验证结果\n 40497e:\tbf 0f 00 00 00 \tmov $0xf,%edi ; 参数15\n 404983:\te8 25 cd ff ff \tcall 4016ad ; 生成0~14随机数\n 404988:\t48 8b 05 91 3e 00 00 \tmov 0x3e91(%rip),%rax # 408820 ; 获取随机索引\n 40498f:\t8b 44 85 b0 \tmov -0x50(%rbp,%rax,4),%eax ; 取数组[rand_div]的值\n 404993:\t39 45 f8 \tcmp %eax,-0x8(%rbp) ; 比较func4_0(k) == 数组值?\n 404996:\t74 05 \tje 40499d \n 404998:\te8 59 12 00 00 \tcall 405bf6 \n```\n所以相对还是很明了的,依旧是关注`rand_div`。\n\n### **动态调试**\n先找出`rand_div`在最后判断前的取值,比如我下面的0xa:\n\n```lldb\n(lldb) si\nProcess 27027 stopped\n* thread #1, name = 'bomb_linux', stop reason = instruction step into\n frame #0: 0x0000000000401719 bomb_linux`GenerateRandomNumber + 108\nbomb_linux`GenerateRandomNumber:\n-> 0x401719 <+108>: movq %rax, 0x7100(%rip) ; rand_div\n 0x401720 <+115>: jmp 0x401723 ; <+118>\n 0x401722 <+117>: nop\n 0x401723 <+118>: popq %rbp\n(lldb) si\nProcess 27027 stopped\n* thread #1, name = 'bomb_linux', stop reason = instruction step into\n frame #0: 0x0000000000401720 bomb_linux`GenerateRandomNumber + 115\nbomb_linux`GenerateRandomNumber:\n-> 0x401720 <+115>: jmp 0x401723 ; <+118>\n 0x401722 <+117>: nop\n 0x401723 <+118>: popq %rbp\n 0x401724 <+119>: retq\n(lldb) x/gx &rand_div\n0x00408820: 0x000000000000000a\n```\n\n而当 `rand_div = 0xa`(即十进制 **10**)时,输入值 `N` 的计算步骤如下:\n\n- 数组索引 **10** 的值是 **斐波那契数列第 20 项**(`F(20) = 6765`)。\n\n- `func4_0(k)` 实际计算的是 **标准斐波那契数列的第 `k+1` 项**(例如,`func4_0(0) = 1 = F(2)`) 需要满足:\n ```c\n func4_0(k) = F(k+1) = F(20)\n ```\n 解得:\n k + 1 = 20 → k = 19\n- `k = N / 2000` → `N = 2000 * k = 2000 * 19 = 38000`.\n从而得解。\n![phase_4](/images/phase_4.png)\n\n---\n\n## **Phase_Impossible**\n\nImpossible?\n\n从这道题开始偷懒了,掏出ghidra直接看c代码了解一下大概流程再去objdump看汇编:\n```c\nvoid phase_impossible(char *param_1)\n\n{\n int iVar1;\n size_t sVar2;\n undefined local_118 [256];\n long local_18;\n long local_10;\n \n local_10 = GetTickCount();\n sVar2 = strlen(param_1);\n if ((sVar2 < 10) || (sVar2 = strlen(param_1), 0x300 < sVar2)) {\n explode_bomb();\n }\n memset(local_118,0,0x100);\n tohex(local_118,param_1);\n GenerateRandomNumber(0x400);\n iVar1 = check_buf_valid(local_118,rand_div & 0xffffffff);\n if (iVar1 == 0) {\n puts(&DAT_00406518);\n explode_bomb();\n }\n GenerateRandomNumber(3);\n if (rand_div != 2) {\n if (2 < rand_div) goto LAB_00401891;\n if (rand_div == 0) {\n goto_buf_0(local_118);\n }\n else if (rand_div != 1) goto LAB_00401891;\n goto_buf_1(local_118);\n }\n goto_buf_2(local_118);\nLAB_00401891:\n explode_bomb();\n GenerateRandomNumber(0x400);\n if ((long)(int)result != rand_div) {\n printf(&DAT_00406560,rand_div,(ulong)result);\n explode_bomb();\n }\n local_18 = GetTickCount();\n if (1000 < (ulong)(local_18 - local_10)) {\n puts(&DAT_004065a8);\n explode_bomb();\n }\n return;\n}\n```\n最终任务还是很明确的,需要写一段机器码修改`result`的数值,但是注意要能通过`check_buf_valid`检测,并且最后指令必须是跳转到`0x401896`不然就会触发`phase_impossible`中`0x401891`处的`explode_bomb`函数,唯一的难点是跟踪`rand_div`的数值变化,建议使用`register write`来修改`check_buf_valid`的返回值使其强制通过然后监控`rand_div`每一次的数值变化(`x/gx &rand_div`),记录好`rand_div`的结果后开始指令设计,需要满足:\n\n - 指令的异或和为`rand_div`第一次的数值末尾八位以通过检查;\n - 修改`result`使其数值等于`rand_div`第三次数值;\n - 跳转到`0x401896`避免炸弹;\n\n 如果前几问都完成了到这里应该是没有问题的。\n\n---\n\n## **Phase_Secret**\n\n隐藏彩蛋,并非隐藏。汇编里写的非常清楚:\n```assembly\n0000000000401a8b :\n 401a8b:\tf3 0f 1e fa \tendbr64\n 401a8f:\t55 \tpush %rbp\n 401a90:\t48 89 e5 \tmov %rsp,%rbp\n 401a93:\t48 83 ec 10 \tsub $0x10,%rsp\n 401a97:\t48 89 7d f8 \tmov %rdi,-0x8(%rbp)\n 401a9b:\t48 8d 05 26 4b 00 00 \tlea 0x4b26(%rip),%rax # 4065c8 <_IO_stdin_used+0x5c8>\n 401aa2:\t48 89 c7 \tmov %rax,%rdi\n 401aa5:\te8 76 f6 ff ff \tcall 401120 \n 401aaa:\t90 \tnop\n 401aab:\tc9 \tleave\n 401aac:\tc3 \tret\n```\n注意到这段指令在原程序中完全没有执行说明是需要用户自己跳转的,也非常简单只需要在`phase_5`中设计指令时加一个要求跳转到`0x401a8b`即可。\n\n完结\n![Case Closed](/images/caseclosed.png)\n","slug":"nudtbomblab","published":1,"updated":"2025-02-25T03:56:35.270Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nr001gyg2bed8596gh","content":"

这篇文章记录高地CSAPP课程Bomblab实验操作流程,仅供参考交流(答案是随机生成的和学号相关)。

\n

笔者实验环境为Archlinux/CachyOS,使用lldb作为调试器(和gdb操作差不多),其余用到的工具主要为objdump,strings,neovim/helix和zellij,全程开源环境不使用IDA。

\n

Phase_1

静态分析

strings扫描

1
strings bomb_linux
\n

先用strings寻找可能与phase_1相关的字符串或函数名,运气好说不定能直接找到密码毕竟是第一题。
\"strings\"

\n
    \n
  • 结果没有明文密码无法直接秒掉第一问,可惜。
  • \n
  • 但是找到GenerateRandomString函数可能与密码生成相关。
  • \n
\n

objdump反汇编

1
objdump -d bomb_linux > bomb.asm
\n

搜索GenerateRandomStringphase_1函数的汇编代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
401b53 <phase_1>:
401b53: endbr64
401b57: push %rbp
401b58: mov %rsp,%rbp
401b5b: sub $0x20,%rsp
401b5f: mov %rdi,-0x18(%rbp)
401b63: lea -0xb(%rbp),%rax
401b67: mov %rax,%rdi
401b6a: callq 401ac1 <GenerateRandomString> # 调用密码生成函数
401b6f: lea -0xb(%rbp),%rdx # 生成的字符串地址%rbp-0xb存入%rdx,即密码存储位置
401b73: mov -0x18(%rbp),%rax
401b77: mov %rdx,%rsi
401b7a: mov %rax,%rdi
401b7d: callq 401c0c <string_compare> # 调用字符串比较函数
401b82: test %eax,%eax
401b84: je 401b8d <phase_1+0x3a>
401b86: callq 401d67 <explode_bomb> # 比较失败则引爆炸弹

\n
    \n
  • phase_1调用GenerateRandomString生成一个字符串。
  • \n
  • 用户输入的字符串需要与此生成的字符串完全匹配。
  • \n
\n
\n

动态调试

\"phase_1\"
下面是phase_1求解的完整流程:

\n
1
2
3
4
5
6
7
8
lldb bomb_linux <你的学号后六位>
(lldb) b phase_1 # 在phase_1入口断点
(lldb) run # 从入口开始执行
请输入第1级的密码:114514 # 随便输入触发断点
(lldb) b 0x401b6f # 在GenerateRandomString返回后断点
(lldb) continue # 继续执行
(lldb) x/s $rbp - 0xb # 计算字符串地址(-0xb偏移量)
0x7fffffffdaf5: "mJHurpQZtY" # 轻松拿下,这里是根据学号伪随机生成的哦
\n

将得到的密码保存入bomb_<学号后六位>.txt即可,避免后续重复输入。

\n
\n

Phase_2

静态分析

这道题目还是比较一目了然的,观察phase_2代码不难发现其实构建了一张跳转表:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
0000000000401b8e <phase_2>:
401b8e:\tf3 0f 1e fa \tendbr64
401b92:\t55 \tpush %rbp
401b93:\t48 89 e5 \tmov %rsp,%rbp
401b96:\t48 83 ec 10 \tsub $0x10,%rsp
401b9a:\t48 89 7d f8 \tmov %rdi,-0x8(%rbp)
401b9e:\tbf 10 00 00 00 \tmov $0x10,%edi
401ba3:\te8 05 fb ff ff \tcall 4016ad <GenerateRandomNumber>
401ba8:\t48 8b 05 71 6c 00 00 \tmov 0x6c71(%rip),%rax # 408820 <rand_div>
401baf:\t48 83 f8 0f \tcmp $0xf,%rax
401bb3:\t0f 87 16 01 00 00 \tja 401ccf <phase_2+0x141>
401bb9:\t48 8d 14 85 00 00 00 \tlea 0x0(,%rax,4),%rdx
401bc0:\t00
401bc1:\t48 8d 05 4c 4a 00 00 \tlea 0x4a4c(%rip),%rax # 406614 <_IO_stdin_used+0x614>
401bc8:\t8b 04 02 \tmov (%rdx,%rax,1),%eax
401bcb:\t48 98 \tcltq
401bcd:\t48 8d 15 40 4a 00 00 \tlea 0x4a40(%rip),%rdx # 406614 <_IO_stdin_used+0x614>
401bd4:\t48 01 d0 \tadd %rdx,%rax
401bd7:\t3e ff e0 \tnotrack jmp *%rax
401bda:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax
401bde:\t48 89 c7 \tmov %rax,%rdi
401be1:\te8 f2 00 00 00 \tcall 401cd8 <phase_2_0>
401be6:\te9 ea 00 00 00 \tjmp 401cd5 <phase_2+0x147>
401beb:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax
401bef:\t48 89 c7 \tmov %rax,%rdi
401bf2:\te8 8b 01 00 00 \tcall 401d82 <phase_2_1>
401bf7:\te9 d9 00 00 00 \tjmp 401cd5 <phase_2+0x147>
401bfc:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax
401c00:\t48 89 c7 \tmov %rax,%rdi
...
\n

这里面需要注意的关键点是rand_div,它会决定你的跳转方向,而你的学号又决定了它的取值。然后是GenerateRandomNumber这个函数的原理需要了解一下,而这个函数将在跳转前后分别调用一次,第一次决定你的跳转方向,第二次则决定了你的密码线索。

\n
\n

动态调试

理解原理就没什么难度了,自己找几个断点打好然后关注一下rand_div的值就好,观察自己的学号向哪个函数跳转并理解相应函数计算即可,比如我这里向phase_2_14跳转:
\"phase_2_14\"

\n

而除了phase_2_14还有其他函数也是非常好理解的,第二题依旧可以轻松拿下。

\n
\n

Phase_3

静态分析

和Phase_2一样开局先跳转尽可能防止同学们答案雷同互相帮助(bushi

\n

本体其实没有什么好说的,这里我跳转的方向是Phase_3_5简要解释一下可供参考:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
0000000000403001 <phase_3_5>:
403001:\tf3 0f 1e fa \tendbr64
403005:\t55 \tpush %rbp
403006:\t48 89 e5 \tmov %rsp,%rbp
403009:\t48 83 ec 20 \tsub $0x20,%rsp
40300d:\t48 89 7d e8 \tmov %rdi,-0x18(%rbp)
403011:\tc7 45 fc 00 00 00 00 \tmovl $0x0,-0x4(%rbp)
403018:\tc7 45 f8 00 00 00 00 \tmovl $0x0,-0x8(%rbp)
40301f:\t48 8d 4d f0 \tlea -0x10(%rbp),%rcx
403023:\t48 8d 55 f4 \tlea -0xc(%rbp),%rdx
403027:\t48 8b 45 e8 \tmov -0x18(%rbp),%rax
40302b:\t48 8d 35 5a 36 00 00 \tlea 0x365a(%rip),%rsi # 40668c <_IO_stdin_used+0x68c>
403032:\t48 89 c7 \tmov %rax,%rdi
403035:\tb8 00 00 00 00 \tmov $0x0,%eax
40303a:\te8 51 e1 ff ff \tcall 401190 <__isoc99_sscanf@plt>
40303f:\t89 45 f8 \tmov %eax,-0x8(%rbp)
403042:\t83 7d f8 01 \tcmpl $0x1,-0x8(%rbp)
403046:\t7f 05 \tjg 40304d <phase_3_5+0x4c>
403048:\te8 a9 2b 00 00 \tcall 405bf6 <explode_bomb>
40304d:\tbf 08 00 00 00 \tmov $0x8,%edi
403052:\te8 56 e6 ff ff \tcall 4016ad <GenerateRandomNumber>
403057:\t8b 45 f4 \tmov -0xc(%rbp),%eax
40305a:\t48 63 d0 \tmovslq %eax,%rdx
40305d:\t48 8b 05 bc 57 00 00 \tmov 0x57bc(%rip),%rax # 408820 <rand_div>
403064:\t48 39 c2 \tcmp %rax,%rdx
403067:\t74 05 \tje 40306e <phase_3_5+0x6d>
403069:\te8 88 2b 00 00 \tcall 405bf6 <explode_bomb>
40306e:\tbf c8 00 00 00 \tmov $0xc8,%edi
403073:\te8 35 e6 ff ff \tcall 4016ad <GenerateRandomNumber>
403078:\t8b 45 f4 \tmov -0xc(%rbp),%eax
40307b:\t83 f8 07 \tcmp $0x7,%eax
40307e:\t0f 87 eb 00 00 00 \tja 40316f <phase_3_5+0x16e>
403084:\t89 c0 \tmov %eax,%eax
403086:\t48 8d 14 85 00 00 00 \tlea 0x0(,%rax,4),%rdx
40308d:\t00
40308e:\t48 8d 05 9f 36 00 00 \tlea 0x369f(%rip),%rax # 406734 <_IO_stdin_used+0x734>
403095:\t8b 04 02 \tmov (%rdx,%rax,1),%eax
403098:\t48 98 \tcltq
40309a:\t48 8d 15 93 36 00 00 \tlea 0x3693(%rip),%rdx # 406734 <_IO_stdin_used+0x734>
4030a1:\t48 01 d0 \tadd %rdx,%rax
4030a4:\t3e ff e0 \tnotrack jmp *%rax
4030a7:\t48 8b 05 72 57 00 00 \tmov 0x5772(%rip),%rax # 408820 <rand_div>
4030ae:\t89 c2 \tmov %eax,%edx
4030b0:\t8b 45 fc \tmov -0x4(%rbp),%eax
4030b3:\t01 d0 \tadd %edx,%eax
4030b5:\t89 45 fc \tmov %eax,-0x4(%rbp)
4030b8:\tbf c8 00 00 00 \tmov $0xc8,%edi
4030bd:\te8 eb e5 ff ff \tcall 4016ad <GenerateRandomNumber>
...
403174:\t8b 45 f0 \tmov -0x10(%rbp),%eax
403177:\t39 45 fc \tcmp %eax,-0x4(%rbp) # 注意这里
40317a:\t74 05 \tje 403181 <phase_3_5+0x180>
40317c:\te8 75 2a 00 00 \tcall 405bf6 <explode_bomb>
403181:\t90 \tnop
403182:\tc9 \tleave
403183:\tc3 \tret

\n

看起来一大堆很吓人对不对?实际上确实很吓人。

\n

但是发现其中玄机后其实简单的没边,最终答案就藏在0x403177里面,前提是确保这一步前炸弹不爆炸(意识到要爆炸了直接run一下重开qwq)。

\n
\n

动态调试

阅读Phase_3_5发现这一关其实需要两个输入,并且第一个输入必须是rand_div,这里建议通过si单步执行监控好rand_div值变化,确定正确结果后使用run重开正确输入第一个密码后才能进行下一步求解:

\n
1
2
3
4
5
6
7
8
9
10
11
(lldb) si
Process 13376 stopped
* thread #1, name = 'bomb_linux', stop reason = instruction step into
frame #0: 0x000000000040317a bomb_linux`phase_3_5 + 377
bomb_linux`phase_3_5:
-> 0x40317a <+377>: je 0x403181 ; <+384>
0x40317c <+379>: callq 0x405bf6 ; explode_bomb
0x403181 <+384>: nop
0x403182 <+385>: leave
(lldb) x/wx $rbp-0x4
0x7fffffffdb0c: 0xffffffd7
\n

例如这里我可以打印出第二个值结合第一个值得到第三关正确结果。

\n
\n

Phase_4

静态分析

本题依旧开局跳转,笔者的跳转方向是phase_4_01,如何跳转不再强调关注rand_div的值即可,下面请D指导解读一下phase_4_01的内容:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
0000000000404895 <phase_4_01>:
; 函数入口,初始化栈帧
404895:\tf3 0f 1e fa \tendbr64
404899:\t55 \tpush %rbp
40489a:\t48 89 e5 \tmov %rsp,%rbp
40489d:\t48 83 ec 70 \tsub $0x70,%rsp ; 分配栈空间

; 初始化斐波那契数组(F(10)~F(24)的十六进制值)
4048a1:\t48 89 7d 98 \tmov %rdi,-0x68(%rbp) ; 保存输入字符串指针
4048a5:\tc7 45 b0 37 00 00 00 \tmovl $0x37,-0x50(%rbp) ; F(10)=55
4048ac:\tc7 45 b4 59 00 00 00 \tmovl $0x59,-0x4c(%rbp) ; F(11)=89
4048b3:\tc7 45 b8 90 00 00 00 \tmovl $0x90,-0x48(%rbp) ; F(12)=144
4048ba:\tc7 45 bc e9 00 00 00 \tmovl $0xe9,-0x44(%rbp) ; F(13)=233
4048c1:\tc7 45 c0 79 01 00 00 \tmovl $0x179,-0x40(%rbp) ; F(14)=377
4048c8:\tc7 45 c4 62 02 00 00 \tmovl $0x262,-0x3c(%rbp) ; F(15)=610
4048cf:\tc7 45 c8 db 03 00 00 \tmovl $0x3db,-0x38(%rbp) ; F(16)=987
4048d6:\tc7 45 cc 3d 06 00 00 \tmovl $0x63d,-0x34(%rbp) ; F(17)=1597
4048dd:\tc7 45 d0 18 0a 00 00 \tmovl $0xa18,-0x30(%rbp) ; F(18)=2584
4048e4:\tc7 45 d4 55 10 00 00 \tmovl $0x1055,-0x2c(%rbp) ; F(19)=4181
4048eb:\tc7 45 d8 6d 1a 00 00 \tmovl $0x1a6d,-0x28(%rbp) ; F(20)=6765
4048f2:\tc7 45 dc c2 2a 00 00 \tmovl $0x2ac2,-0x24(%rbp) ; F(21)=10946
4048f9:\tc7 45 e0 2f 45 00 00 \tmovl $0x452f,-0x20(%rbp) ; F(22)=17711
404900:\tc7 45 e4 f1 6f 00 00 \tmovl $0x6ff1,-0x1c(%rbp) ; F(23)=28657
404907:\tc7 45 e8 20 b5 00 00 \tmovl $0xb520,-0x18(%rbp) ; F(24)=46368

; 读取输入到局部变量(格式为"%d")
40490e:\t48 8d 55 ac \tlea -0x54(%rbp),%rdx ; 输入存储地址
404912:\t48 8b 45 98 \tmov -0x68(%rbp),%rax ; 输入字符串
404916:\t48 8d 0d 93 1f 00 00 \tlea 0x1f93(%rip),%rcx ; 格式字符串"%d"
40491d:\t48 89 ce \tmov %rcx,%rsi
404920:\t48 89 c7 \tmov %rax,%rdi
404923:\tb8 00 00 00 00 \tmov $0x0,%eax
404928:\te8 63 c8 ff ff \tcall 401190 <__isoc99_sscanf@plt>

; 验证输入有效性(必须为1个正数)
40492d:\t89 45 fc \tmov %eax,-0x4(%rbp) ; sscanf返回值
404930:\t83 7d fc 01 \tcmpl $0x1,-0x4(%rbp) ; 检查是否读取1个参数
404934:\t75 07 \tjne 40493d <phase_4_01+0xa8> ; 失败则爆炸
404936:\t8b 45 ac \tmov -0x54(%rbp),%eax ; 获取输入值N
404939:\t85 c0 \ttest %eax,%eax ; 检查N > 0
40493b:\t7f 05 \tjg 404942 <phase_4_01+0xad>
40493d:\te8 b4 12 00 00 \tcall 405bf6 <explode_bomb>

; 检查输入值上限(必须 > 1999)
404942:\t8b 45 ac \tmov -0x54(%rbp),%eax
404945:\t3d cf 07 00 00 \tcmp $0x7cf,%eax ; 1999的十六进制
40494a:\t7f 05 \tjg 404951 <phase_4_01+0xbc> ; N > 1999?
40494c:\te8 a5 12 00 00 \tcall 405bf6 <explode_bomb>

; 计算 N/2000(通过定点数乘法优化)
404951:\t8b 45 ac \tmov -0x54(%rbp),%eax ; 输入值N
404954:\t48 63 d0 \tmovslq %eax,%rdx ; 符号扩展
404957:\t48 69 d2 d3 4d 62 10 \timul $0x10624dd3,%rdx,%rdx ; 乘以274877907(≈2^32/2000)
40495e:\t48 c1 ea 20 \tshr $0x20,%rdx ; 取高32位
404962:\tc1 fa 07 \tsar $0x7,%edx ; 算术右移7位 → N/2000
404965:\tc1 f8 1f \tsar $0x1f,%eax ; 符号位扩展
404968:\t89 c1 \tmov %eax,%ecx
40496a:\t89 d0 \tmov %edx,%eax
40496c:\t29 c8 \tsub %ecx,%eax ; 处理负数情况
40496e:\t89 45 ac \tmov %eax,-0x54(%rbp) ; 保存k = N/2000

; 调用递归函数func4_0(k), 这个函数用于计算斐波那契数列
404971:\t8b 45 ac \tmov -0x54(%rbp),%eax
404974:\t89 c7 \tmov %eax,%edi ; 参数k
404976:\te8 ce fd ff ff \tcall 404749 <func4_0> ; 返回值eax=F(k+1)
40497b:\t89 45 f8 \tmov %eax,-0x8(%rbp) ; 保存结果

; 生成随机索引并验证结果
40497e:\tbf 0f 00 00 00 \tmov $0xf,%edi ; 参数15
404983:\te8 25 cd ff ff \tcall 4016ad <GenerateRandomNumber> ; 生成0~14随机数
404988:\t48 8b 05 91 3e 00 00 \tmov 0x3e91(%rip),%rax # 408820 <rand_div> ; 获取随机索引
40498f:\t8b 44 85 b0 \tmov -0x50(%rbp,%rax,4),%eax ; 取数组[rand_div]的值
404993:\t39 45 f8 \tcmp %eax,-0x8(%rbp) ; 比较func4_0(k) == 数组值?
404996:\t74 05 \tje 40499d <phase_4_01+0x108>
404998:\te8 59 12 00 00 \tcall 405bf6 <explode_bomb>
\n

所以相对还是很明了的,依旧是关注rand_div

\n

动态调试

先找出rand_div在最后判断前的取值,比如我下面的0xa:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(lldb) si
Process 27027 stopped
* thread #1, name = 'bomb_linux', stop reason = instruction step into
frame #0: 0x0000000000401719 bomb_linux`GenerateRandomNumber + 108
bomb_linux`GenerateRandomNumber:
-> 0x401719 <+108>: movq %rax, 0x7100(%rip) ; rand_div
0x401720 <+115>: jmp 0x401723 ; <+118>
0x401722 <+117>: nop
0x401723 <+118>: popq %rbp
(lldb) si
Process 27027 stopped
* thread #1, name = 'bomb_linux', stop reason = instruction step into
frame #0: 0x0000000000401720 bomb_linux`GenerateRandomNumber + 115
bomb_linux`GenerateRandomNumber:
-> 0x401720 <+115>: jmp 0x401723 ; <+118>
0x401722 <+117>: nop
0x401723 <+118>: popq %rbp
0x401724 <+119>: retq
(lldb) x/gx &rand_div
0x00408820: 0x000000000000000a
\n\n

而当 rand_div = 0xa(即十进制 10)时,输入值 N 的计算步骤如下:

\n
    \n
  • 数组索引 10 的值是 斐波那契数列第 20 项F(20) = 6765)。

    \n
  • \n
  • func4_0(k) 实际计算的是 标准斐波那契数列的第 k+1(例如,func4_0(0) = 1 = F(2)) 需要满足:

    \n
    1
    func4_0(k) = F(k+1) = F(20)
    \n

    解得:
    k + 1 = 20 → k = 19

    \n
  • \n
  • k = N / 2000N = 2000 * k = 2000 * 19 = 38000.
    从而得解。
    \"phase_4\"

    \n
  • \n
\n
\n

Phase_Impossible

Impossible?

\n

从这道题开始偷懒了,掏出ghidra直接看c代码了解一下大概流程再去objdump看汇编:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
void phase_impossible(char *param_1)

{
int iVar1;
size_t sVar2;
undefined local_118 [256];
long local_18;
long local_10;

local_10 = GetTickCount();
sVar2 = strlen(param_1);
if ((sVar2 < 10) || (sVar2 = strlen(param_1), 0x300 < sVar2)) {
explode_bomb();
}
memset(local_118,0,0x100);
tohex(local_118,param_1);
GenerateRandomNumber(0x400);
iVar1 = check_buf_valid(local_118,rand_div & 0xffffffff);
if (iVar1 == 0) {
puts(&DAT_00406518);
explode_bomb();
}
GenerateRandomNumber(3);
if (rand_div != 2) {
if (2 < rand_div) goto LAB_00401891;
if (rand_div == 0) {
goto_buf_0(local_118);
}
else if (rand_div != 1) goto LAB_00401891;
goto_buf_1(local_118);
}
goto_buf_2(local_118);
LAB_00401891:
explode_bomb();
GenerateRandomNumber(0x400);
if ((long)(int)result != rand_div) {
printf(&DAT_00406560,rand_div,(ulong)result);
explode_bomb();
}
local_18 = GetTickCount();
if (1000 < (ulong)(local_18 - local_10)) {
puts(&DAT_004065a8);
explode_bomb();
}
return;
}
\n

最终任务还是很明确的,需要写一段机器码修改result的数值,但是注意要能通过check_buf_valid检测,并且最后指令必须是跳转到0x401896不然就会触发phase_impossible0x401891处的explode_bomb函数,唯一的难点是跟踪rand_div的数值变化,建议使用register write来修改check_buf_valid的返回值使其强制通过然后监控rand_div每一次的数值变化(x/gx &rand_div),记录好rand_div的结果后开始指令设计,需要满足:

\n
    \n
  • 指令的异或和为rand_div第一次的数值末尾八位以通过检查;
  • \n
  • 修改result使其数值等于rand_div第三次数值;
  • \n
  • 跳转到0x401896避免炸弹;
  • \n
\n

如果前几问都完成了到这里应该是没有问题的。

\n
\n

Phase_Secret

隐藏彩蛋,并非隐藏。汇编里写的非常清楚:

\n
1
2
3
4
5
6
7
8
9
10
11
12
0000000000401a8b <phase_secret>:
401a8b:\tf3 0f 1e fa \tendbr64
401a8f:\t55 \tpush %rbp
401a90:\t48 89 e5 \tmov %rsp,%rbp
401a93:\t48 83 ec 10 \tsub $0x10,%rsp
401a97:\t48 89 7d f8 \tmov %rdi,-0x8(%rbp)
401a9b:\t48 8d 05 26 4b 00 00 \tlea 0x4b26(%rip),%rax # 4065c8 <_IO_stdin_used+0x5c8>
401aa2:\t48 89 c7 \tmov %rax,%rdi
401aa5:\te8 76 f6 ff ff \tcall 401120 <puts@plt>
401aaa:\t90 \tnop
401aab:\tc9 \tleave
401aac:\tc3 \tret
\n

注意到这段指令在原程序中完全没有执行说明是需要用户自己跳转的,也非常简单只需要在phase_5中设计指令时加一个要求跳转到0x401a8b即可。

\n

完结
\"Case

\n","excerpt":"","more":"

这篇文章记录高地CSAPP课程Bomblab实验操作流程,仅供参考交流(答案是随机生成的和学号相关)。

\n

笔者实验环境为Archlinux/CachyOS,使用lldb作为调试器(和gdb操作差不多),其余用到的工具主要为objdump,strings,neovim/helix和zellij,全程开源环境不使用IDA。

\n

Phase_1

静态分析

strings扫描

1
strings bomb_linux
\n

先用strings寻找可能与phase_1相关的字符串或函数名,运气好说不定能直接找到密码毕竟是第一题。
\"strings\"

\n
    \n
  • 结果没有明文密码无法直接秒掉第一问,可惜。
  • \n
  • 但是找到GenerateRandomString函数可能与密码生成相关。
  • \n
\n

objdump反汇编

1
objdump -d bomb_linux > bomb.asm
\n

搜索GenerateRandomStringphase_1函数的汇编代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
401b53 <phase_1>:
401b53: endbr64
401b57: push %rbp
401b58: mov %rsp,%rbp
401b5b: sub $0x20,%rsp
401b5f: mov %rdi,-0x18(%rbp)
401b63: lea -0xb(%rbp),%rax
401b67: mov %rax,%rdi
401b6a: callq 401ac1 <GenerateRandomString> # 调用密码生成函数
401b6f: lea -0xb(%rbp),%rdx # 生成的字符串地址%rbp-0xb存入%rdx,即密码存储位置
401b73: mov -0x18(%rbp),%rax
401b77: mov %rdx,%rsi
401b7a: mov %rax,%rdi
401b7d: callq 401c0c <string_compare> # 调用字符串比较函数
401b82: test %eax,%eax
401b84: je 401b8d <phase_1+0x3a>
401b86: callq 401d67 <explode_bomb> # 比较失败则引爆炸弹

\n
    \n
  • phase_1调用GenerateRandomString生成一个字符串。
  • \n
  • 用户输入的字符串需要与此生成的字符串完全匹配。
  • \n
\n
\n

动态调试

\"phase_1\"
下面是phase_1求解的完整流程:

\n
1
2
3
4
5
6
7
8
lldb bomb_linux <你的学号后六位>
(lldb) b phase_1 # 在phase_1入口断点
(lldb) run # 从入口开始执行
请输入第1级的密码:114514 # 随便输入触发断点
(lldb) b 0x401b6f # 在GenerateRandomString返回后断点
(lldb) continue # 继续执行
(lldb) x/s $rbp - 0xb # 计算字符串地址(-0xb偏移量)
0x7fffffffdaf5: "mJHurpQZtY" # 轻松拿下,这里是根据学号伪随机生成的哦
\n

将得到的密码保存入bomb_<学号后六位>.txt即可,避免后续重复输入。

\n
\n

Phase_2

静态分析

这道题目还是比较一目了然的,观察phase_2代码不难发现其实构建了一张跳转表:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
0000000000401b8e <phase_2>:
401b8e:\tf3 0f 1e fa \tendbr64
401b92:\t55 \tpush %rbp
401b93:\t48 89 e5 \tmov %rsp,%rbp
401b96:\t48 83 ec 10 \tsub $0x10,%rsp
401b9a:\t48 89 7d f8 \tmov %rdi,-0x8(%rbp)
401b9e:\tbf 10 00 00 00 \tmov $0x10,%edi
401ba3:\te8 05 fb ff ff \tcall 4016ad <GenerateRandomNumber>
401ba8:\t48 8b 05 71 6c 00 00 \tmov 0x6c71(%rip),%rax # 408820 <rand_div>
401baf:\t48 83 f8 0f \tcmp $0xf,%rax
401bb3:\t0f 87 16 01 00 00 \tja 401ccf <phase_2+0x141>
401bb9:\t48 8d 14 85 00 00 00 \tlea 0x0(,%rax,4),%rdx
401bc0:\t00
401bc1:\t48 8d 05 4c 4a 00 00 \tlea 0x4a4c(%rip),%rax # 406614 <_IO_stdin_used+0x614>
401bc8:\t8b 04 02 \tmov (%rdx,%rax,1),%eax
401bcb:\t48 98 \tcltq
401bcd:\t48 8d 15 40 4a 00 00 \tlea 0x4a40(%rip),%rdx # 406614 <_IO_stdin_used+0x614>
401bd4:\t48 01 d0 \tadd %rdx,%rax
401bd7:\t3e ff e0 \tnotrack jmp *%rax
401bda:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax
401bde:\t48 89 c7 \tmov %rax,%rdi
401be1:\te8 f2 00 00 00 \tcall 401cd8 <phase_2_0>
401be6:\te9 ea 00 00 00 \tjmp 401cd5 <phase_2+0x147>
401beb:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax
401bef:\t48 89 c7 \tmov %rax,%rdi
401bf2:\te8 8b 01 00 00 \tcall 401d82 <phase_2_1>
401bf7:\te9 d9 00 00 00 \tjmp 401cd5 <phase_2+0x147>
401bfc:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax
401c00:\t48 89 c7 \tmov %rax,%rdi
...
\n

这里面需要注意的关键点是rand_div,它会决定你的跳转方向,而你的学号又决定了它的取值。然后是GenerateRandomNumber这个函数的原理需要了解一下,而这个函数将在跳转前后分别调用一次,第一次决定你的跳转方向,第二次则决定了你的密码线索。

\n
\n

动态调试

理解原理就没什么难度了,自己找几个断点打好然后关注一下rand_div的值就好,观察自己的学号向哪个函数跳转并理解相应函数计算即可,比如我这里向phase_2_14跳转:
\"phase_2_14\"

\n

而除了phase_2_14还有其他函数也是非常好理解的,第二题依旧可以轻松拿下。

\n
\n

Phase_3

静态分析

和Phase_2一样开局先跳转尽可能防止同学们答案雷同互相帮助(bushi

\n

本体其实没有什么好说的,这里我跳转的方向是Phase_3_5简要解释一下可供参考:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
0000000000403001 <phase_3_5>:
403001:\tf3 0f 1e fa \tendbr64
403005:\t55 \tpush %rbp
403006:\t48 89 e5 \tmov %rsp,%rbp
403009:\t48 83 ec 20 \tsub $0x20,%rsp
40300d:\t48 89 7d e8 \tmov %rdi,-0x18(%rbp)
403011:\tc7 45 fc 00 00 00 00 \tmovl $0x0,-0x4(%rbp)
403018:\tc7 45 f8 00 00 00 00 \tmovl $0x0,-0x8(%rbp)
40301f:\t48 8d 4d f0 \tlea -0x10(%rbp),%rcx
403023:\t48 8d 55 f4 \tlea -0xc(%rbp),%rdx
403027:\t48 8b 45 e8 \tmov -0x18(%rbp),%rax
40302b:\t48 8d 35 5a 36 00 00 \tlea 0x365a(%rip),%rsi # 40668c <_IO_stdin_used+0x68c>
403032:\t48 89 c7 \tmov %rax,%rdi
403035:\tb8 00 00 00 00 \tmov $0x0,%eax
40303a:\te8 51 e1 ff ff \tcall 401190 <__isoc99_sscanf@plt>
40303f:\t89 45 f8 \tmov %eax,-0x8(%rbp)
403042:\t83 7d f8 01 \tcmpl $0x1,-0x8(%rbp)
403046:\t7f 05 \tjg 40304d <phase_3_5+0x4c>
403048:\te8 a9 2b 00 00 \tcall 405bf6 <explode_bomb>
40304d:\tbf 08 00 00 00 \tmov $0x8,%edi
403052:\te8 56 e6 ff ff \tcall 4016ad <GenerateRandomNumber>
403057:\t8b 45 f4 \tmov -0xc(%rbp),%eax
40305a:\t48 63 d0 \tmovslq %eax,%rdx
40305d:\t48 8b 05 bc 57 00 00 \tmov 0x57bc(%rip),%rax # 408820 <rand_div>
403064:\t48 39 c2 \tcmp %rax,%rdx
403067:\t74 05 \tje 40306e <phase_3_5+0x6d>
403069:\te8 88 2b 00 00 \tcall 405bf6 <explode_bomb>
40306e:\tbf c8 00 00 00 \tmov $0xc8,%edi
403073:\te8 35 e6 ff ff \tcall 4016ad <GenerateRandomNumber>
403078:\t8b 45 f4 \tmov -0xc(%rbp),%eax
40307b:\t83 f8 07 \tcmp $0x7,%eax
40307e:\t0f 87 eb 00 00 00 \tja 40316f <phase_3_5+0x16e>
403084:\t89 c0 \tmov %eax,%eax
403086:\t48 8d 14 85 00 00 00 \tlea 0x0(,%rax,4),%rdx
40308d:\t00
40308e:\t48 8d 05 9f 36 00 00 \tlea 0x369f(%rip),%rax # 406734 <_IO_stdin_used+0x734>
403095:\t8b 04 02 \tmov (%rdx,%rax,1),%eax
403098:\t48 98 \tcltq
40309a:\t48 8d 15 93 36 00 00 \tlea 0x3693(%rip),%rdx # 406734 <_IO_stdin_used+0x734>
4030a1:\t48 01 d0 \tadd %rdx,%rax
4030a4:\t3e ff e0 \tnotrack jmp *%rax
4030a7:\t48 8b 05 72 57 00 00 \tmov 0x5772(%rip),%rax # 408820 <rand_div>
4030ae:\t89 c2 \tmov %eax,%edx
4030b0:\t8b 45 fc \tmov -0x4(%rbp),%eax
4030b3:\t01 d0 \tadd %edx,%eax
4030b5:\t89 45 fc \tmov %eax,-0x4(%rbp)
4030b8:\tbf c8 00 00 00 \tmov $0xc8,%edi
4030bd:\te8 eb e5 ff ff \tcall 4016ad <GenerateRandomNumber>
...
403174:\t8b 45 f0 \tmov -0x10(%rbp),%eax
403177:\t39 45 fc \tcmp %eax,-0x4(%rbp) # 注意这里
40317a:\t74 05 \tje 403181 <phase_3_5+0x180>
40317c:\te8 75 2a 00 00 \tcall 405bf6 <explode_bomb>
403181:\t90 \tnop
403182:\tc9 \tleave
403183:\tc3 \tret

\n

看起来一大堆很吓人对不对?实际上确实很吓人。

\n

但是发现其中玄机后其实简单的没边,最终答案就藏在0x403177里面,前提是确保这一步前炸弹不爆炸(意识到要爆炸了直接run一下重开qwq)。

\n
\n

动态调试

阅读Phase_3_5发现这一关其实需要两个输入,并且第一个输入必须是rand_div,这里建议通过si单步执行监控好rand_div值变化,确定正确结果后使用run重开正确输入第一个密码后才能进行下一步求解:

\n
1
2
3
4
5
6
7
8
9
10
11
(lldb) si
Process 13376 stopped
* thread #1, name = 'bomb_linux', stop reason = instruction step into
frame #0: 0x000000000040317a bomb_linux`phase_3_5 + 377
bomb_linux`phase_3_5:
-> 0x40317a <+377>: je 0x403181 ; <+384>
0x40317c <+379>: callq 0x405bf6 ; explode_bomb
0x403181 <+384>: nop
0x403182 <+385>: leave
(lldb) x/wx $rbp-0x4
0x7fffffffdb0c: 0xffffffd7
\n

例如这里我可以打印出第二个值结合第一个值得到第三关正确结果。

\n
\n

Phase_4

静态分析

本题依旧开局跳转,笔者的跳转方向是phase_4_01,如何跳转不再强调关注rand_div的值即可,下面请D指导解读一下phase_4_01的内容:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
0000000000404895 <phase_4_01>:
; 函数入口,初始化栈帧
404895:\tf3 0f 1e fa \tendbr64
404899:\t55 \tpush %rbp
40489a:\t48 89 e5 \tmov %rsp,%rbp
40489d:\t48 83 ec 70 \tsub $0x70,%rsp ; 分配栈空间

; 初始化斐波那契数组(F(10)~F(24)的十六进制值)
4048a1:\t48 89 7d 98 \tmov %rdi,-0x68(%rbp) ; 保存输入字符串指针
4048a5:\tc7 45 b0 37 00 00 00 \tmovl $0x37,-0x50(%rbp) ; F(10)=55
4048ac:\tc7 45 b4 59 00 00 00 \tmovl $0x59,-0x4c(%rbp) ; F(11)=89
4048b3:\tc7 45 b8 90 00 00 00 \tmovl $0x90,-0x48(%rbp) ; F(12)=144
4048ba:\tc7 45 bc e9 00 00 00 \tmovl $0xe9,-0x44(%rbp) ; F(13)=233
4048c1:\tc7 45 c0 79 01 00 00 \tmovl $0x179,-0x40(%rbp) ; F(14)=377
4048c8:\tc7 45 c4 62 02 00 00 \tmovl $0x262,-0x3c(%rbp) ; F(15)=610
4048cf:\tc7 45 c8 db 03 00 00 \tmovl $0x3db,-0x38(%rbp) ; F(16)=987
4048d6:\tc7 45 cc 3d 06 00 00 \tmovl $0x63d,-0x34(%rbp) ; F(17)=1597
4048dd:\tc7 45 d0 18 0a 00 00 \tmovl $0xa18,-0x30(%rbp) ; F(18)=2584
4048e4:\tc7 45 d4 55 10 00 00 \tmovl $0x1055,-0x2c(%rbp) ; F(19)=4181
4048eb:\tc7 45 d8 6d 1a 00 00 \tmovl $0x1a6d,-0x28(%rbp) ; F(20)=6765
4048f2:\tc7 45 dc c2 2a 00 00 \tmovl $0x2ac2,-0x24(%rbp) ; F(21)=10946
4048f9:\tc7 45 e0 2f 45 00 00 \tmovl $0x452f,-0x20(%rbp) ; F(22)=17711
404900:\tc7 45 e4 f1 6f 00 00 \tmovl $0x6ff1,-0x1c(%rbp) ; F(23)=28657
404907:\tc7 45 e8 20 b5 00 00 \tmovl $0xb520,-0x18(%rbp) ; F(24)=46368

; 读取输入到局部变量(格式为"%d")
40490e:\t48 8d 55 ac \tlea -0x54(%rbp),%rdx ; 输入存储地址
404912:\t48 8b 45 98 \tmov -0x68(%rbp),%rax ; 输入字符串
404916:\t48 8d 0d 93 1f 00 00 \tlea 0x1f93(%rip),%rcx ; 格式字符串"%d"
40491d:\t48 89 ce \tmov %rcx,%rsi
404920:\t48 89 c7 \tmov %rax,%rdi
404923:\tb8 00 00 00 00 \tmov $0x0,%eax
404928:\te8 63 c8 ff ff \tcall 401190 <__isoc99_sscanf@plt>

; 验证输入有效性(必须为1个正数)
40492d:\t89 45 fc \tmov %eax,-0x4(%rbp) ; sscanf返回值
404930:\t83 7d fc 01 \tcmpl $0x1,-0x4(%rbp) ; 检查是否读取1个参数
404934:\t75 07 \tjne 40493d <phase_4_01+0xa8> ; 失败则爆炸
404936:\t8b 45 ac \tmov -0x54(%rbp),%eax ; 获取输入值N
404939:\t85 c0 \ttest %eax,%eax ; 检查N > 0
40493b:\t7f 05 \tjg 404942 <phase_4_01+0xad>
40493d:\te8 b4 12 00 00 \tcall 405bf6 <explode_bomb>

; 检查输入值上限(必须 > 1999)
404942:\t8b 45 ac \tmov -0x54(%rbp),%eax
404945:\t3d cf 07 00 00 \tcmp $0x7cf,%eax ; 1999的十六进制
40494a:\t7f 05 \tjg 404951 <phase_4_01+0xbc> ; N > 1999?
40494c:\te8 a5 12 00 00 \tcall 405bf6 <explode_bomb>

; 计算 N/2000(通过定点数乘法优化)
404951:\t8b 45 ac \tmov -0x54(%rbp),%eax ; 输入值N
404954:\t48 63 d0 \tmovslq %eax,%rdx ; 符号扩展
404957:\t48 69 d2 d3 4d 62 10 \timul $0x10624dd3,%rdx,%rdx ; 乘以274877907(≈2^32/2000)
40495e:\t48 c1 ea 20 \tshr $0x20,%rdx ; 取高32位
404962:\tc1 fa 07 \tsar $0x7,%edx ; 算术右移7位 → N/2000
404965:\tc1 f8 1f \tsar $0x1f,%eax ; 符号位扩展
404968:\t89 c1 \tmov %eax,%ecx
40496a:\t89 d0 \tmov %edx,%eax
40496c:\t29 c8 \tsub %ecx,%eax ; 处理负数情况
40496e:\t89 45 ac \tmov %eax,-0x54(%rbp) ; 保存k = N/2000

; 调用递归函数func4_0(k), 这个函数用于计算斐波那契数列
404971:\t8b 45 ac \tmov -0x54(%rbp),%eax
404974:\t89 c7 \tmov %eax,%edi ; 参数k
404976:\te8 ce fd ff ff \tcall 404749 <func4_0> ; 返回值eax=F(k+1)
40497b:\t89 45 f8 \tmov %eax,-0x8(%rbp) ; 保存结果

; 生成随机索引并验证结果
40497e:\tbf 0f 00 00 00 \tmov $0xf,%edi ; 参数15
404983:\te8 25 cd ff ff \tcall 4016ad <GenerateRandomNumber> ; 生成0~14随机数
404988:\t48 8b 05 91 3e 00 00 \tmov 0x3e91(%rip),%rax # 408820 <rand_div> ; 获取随机索引
40498f:\t8b 44 85 b0 \tmov -0x50(%rbp,%rax,4),%eax ; 取数组[rand_div]的值
404993:\t39 45 f8 \tcmp %eax,-0x8(%rbp) ; 比较func4_0(k) == 数组值?
404996:\t74 05 \tje 40499d <phase_4_01+0x108>
404998:\te8 59 12 00 00 \tcall 405bf6 <explode_bomb>
\n

所以相对还是很明了的,依旧是关注rand_div

\n

动态调试

先找出rand_div在最后判断前的取值,比如我下面的0xa:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(lldb) si
Process 27027 stopped
* thread #1, name = 'bomb_linux', stop reason = instruction step into
frame #0: 0x0000000000401719 bomb_linux`GenerateRandomNumber + 108
bomb_linux`GenerateRandomNumber:
-> 0x401719 <+108>: movq %rax, 0x7100(%rip) ; rand_div
0x401720 <+115>: jmp 0x401723 ; <+118>
0x401722 <+117>: nop
0x401723 <+118>: popq %rbp
(lldb) si
Process 27027 stopped
* thread #1, name = 'bomb_linux', stop reason = instruction step into
frame #0: 0x0000000000401720 bomb_linux`GenerateRandomNumber + 115
bomb_linux`GenerateRandomNumber:
-> 0x401720 <+115>: jmp 0x401723 ; <+118>
0x401722 <+117>: nop
0x401723 <+118>: popq %rbp
0x401724 <+119>: retq
(lldb) x/gx &rand_div
0x00408820: 0x000000000000000a
\n\n

而当 rand_div = 0xa(即十进制 10)时,输入值 N 的计算步骤如下:

\n
    \n
  • 数组索引 10 的值是 斐波那契数列第 20 项F(20) = 6765)。

    \n
  • \n
  • func4_0(k) 实际计算的是 标准斐波那契数列的第 k+1(例如,func4_0(0) = 1 = F(2)) 需要满足:

    \n
    1
    func4_0(k) = F(k+1) = F(20)
    \n

    解得:
    k + 1 = 20 → k = 19

    \n
  • \n
  • k = N / 2000N = 2000 * k = 2000 * 19 = 38000.
    从而得解。
    \"phase_4\"

    \n
  • \n
\n
\n

Phase_Impossible

Impossible?

\n

从这道题开始偷懒了,掏出ghidra直接看c代码了解一下大概流程再去objdump看汇编:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
void phase_impossible(char *param_1)

{
int iVar1;
size_t sVar2;
undefined local_118 [256];
long local_18;
long local_10;

local_10 = GetTickCount();
sVar2 = strlen(param_1);
if ((sVar2 < 10) || (sVar2 = strlen(param_1), 0x300 < sVar2)) {
explode_bomb();
}
memset(local_118,0,0x100);
tohex(local_118,param_1);
GenerateRandomNumber(0x400);
iVar1 = check_buf_valid(local_118,rand_div & 0xffffffff);
if (iVar1 == 0) {
puts(&DAT_00406518);
explode_bomb();
}
GenerateRandomNumber(3);
if (rand_div != 2) {
if (2 < rand_div) goto LAB_00401891;
if (rand_div == 0) {
goto_buf_0(local_118);
}
else if (rand_div != 1) goto LAB_00401891;
goto_buf_1(local_118);
}
goto_buf_2(local_118);
LAB_00401891:
explode_bomb();
GenerateRandomNumber(0x400);
if ((long)(int)result != rand_div) {
printf(&DAT_00406560,rand_div,(ulong)result);
explode_bomb();
}
local_18 = GetTickCount();
if (1000 < (ulong)(local_18 - local_10)) {
puts(&DAT_004065a8);
explode_bomb();
}
return;
}
\n

最终任务还是很明确的,需要写一段机器码修改result的数值,但是注意要能通过check_buf_valid检测,并且最后指令必须是跳转到0x401896不然就会触发phase_impossible0x401891处的explode_bomb函数,唯一的难点是跟踪rand_div的数值变化,建议使用register write来修改check_buf_valid的返回值使其强制通过然后监控rand_div每一次的数值变化(x/gx &rand_div),记录好rand_div的结果后开始指令设计,需要满足:

\n
    \n
  • 指令的异或和为rand_div第一次的数值末尾八位以通过检查;
  • \n
  • 修改result使其数值等于rand_div第三次数值;
  • \n
  • 跳转到0x401896避免炸弹;
  • \n
\n

如果前几问都完成了到这里应该是没有问题的。

\n
\n

Phase_Secret

隐藏彩蛋,并非隐藏。汇编里写的非常清楚:

\n
1
2
3
4
5
6
7
8
9
10
11
12
0000000000401a8b <phase_secret>:
401a8b:\tf3 0f 1e fa \tendbr64
401a8f:\t55 \tpush %rbp
401a90:\t48 89 e5 \tmov %rsp,%rbp
401a93:\t48 83 ec 10 \tsub $0x10,%rsp
401a97:\t48 89 7d f8 \tmov %rdi,-0x8(%rbp)
401a9b:\t48 8d 05 26 4b 00 00 \tlea 0x4b26(%rip),%rax # 4065c8 <_IO_stdin_used+0x5c8>
401aa2:\t48 89 c7 \tmov %rax,%rdi
401aa5:\te8 76 f6 ff ff \tcall 401120 <puts@plt>
401aaa:\t90 \tnop
401aab:\tc9 \tleave
401aac:\tc3 \tret
\n

注意到这段指令在原程序中完全没有执行说明是需要用户自己跳转的,也非常简单只需要在phase_5中设计指令时加一个要求跳转到0x401a8b即可。

\n

完结
\"Case

\n"},{"title":"面向竞赛环境的Linux系统性能调优实践","date":"2026-01-18T04:14:04.000Z","_content":"\n最初,我的想法是现在大伙都有AI工具了码力差距将不再像过去那么明显,这种情况下系统调优一定是有实战价值的。尤其是在面对极端计算任务时,操作系统内核的调度策略和中断处理方式对性能必然有着直接且显著的影响。为了在计算密集型任务(如本次天体物理模拟)中获得极致的性能与确定性,我作为运维必须尝试对Linux内核进行深度定制与隔离。\n\n本文将详细记录我在Intel Sapphire Rapids平台(Dual Xeon 8480P+)上进行的系统调优探索。通过内核参数隔离、用户态调度器(sched_ext)以及IRQ亲和性管理,我成功在AMSS-NCKU双黑洞模拟算例中取得了肉眼可见的性能收益。\n\n## 1. 测试环境与硬件拓扑\n\n本次调优的硬件平台具备极高的并行计算能力,但NUMA架构带来的延迟敏感性也对系统配置提出了挑战:\n\n* **CPU**: 2x Intel Xeon Platinum 8480+ (Sapphire Rapids)\n* **核心配置**: 单路56核心,双路共112物理核心。\n* **超线程 (Hyper-Threading)**: **Disabled**(为了避免逻辑核争抢浮点单元及缓存资源,确保计算流水线的独占性)。\n* **GPU**: 无(纯CPU计算任务)。\n\n针对该拓扑,我制定了**“核心隔离(Core Isolation)”**策略:将系统中断、管理进程及内核Housekeeping任务通过`cpuset`和内核参数限制在特定的少数核心上(Housekeeping Cores),将绝大多数核心完全腾空,仅运行计算任务(Isolation Cores)。\n\n* **Housekeeping Cores**: 0-3 (Socket 0), 56-59 (Socket 1) — 共8核。\n* **Isolation Cores**: 4-55 (Socket 0), 60-111 (Socket 1) — 共104核,用于纯计算。\n\n## 2. 内核启动参数(Kernel Command Line)深度解析\n\n首先需要确定的是这次我将选择Rockylinux 10作为操作系统基础,内核版本为6.12是首个支持sched_ext调度器的稳定版本,并且Rockylinux 10首次完全基于x86-64-v3构建,相比其他发行版有着必然的性能优势,而少数比它性能更好的发行版(CachyOS和已经逝世的Clearlinux)又没它稳定还不受nvidia ofed驱动支持,考虑到这些因素我不会多看其他任何发行版一秒。(现在都是2026年了坚决抵制CentOS 7/8或Rockylinux 8信徒遗老,这么老的内核挂个exFAT硬盘传东西都费劲)。\n\n为了实现我预期的隔离策略,我使用grubby在GRUB配置中启用了以下内核参数。这些参数共同构成了一个低延迟、低抖动的运行环境。\n\n```bash\ntransparent_hugepage=never iommu=pt mitigations=off \\\nnohz_full=4-55,60-111 rcu_nocbs=4-55,60-111 \\\nisolcpus=managed_irq,4-55,60-111\n```\n\n### 2.1 内存与IO子系统优化\n\n* **`transparent_hugepage=never`**: 禁用透明大页(THP)。虽然THP能减少TLB Miss,但在高强度的随机内存访问或频繁申请释放场景下,THP的后台合并线程(khugepaged)会引入不可预测的延迟抖动(Jitter)。\n* **`iommu=pt`**: 将IOMMU设置为Pass-Through模式。这允许设备在DMA传输时直接使用物理地址,绕过IOMMU的地址转换层,显著降低了IO密集型操作的CPU开销,同时保留了IOMMU的基本功能。\n\n### 2.2 安全缓解措施禁用\n\n* **`mitigations=off`**(让CPU回归其原本的样子): 该参数关闭了针对Spectre、Meltdown等侧信道攻击的所有CPU漏洞缓解措施。这些缓解措施通常涉及频繁的页表隔离(PTI)和间接分支预测限制,在旧时代这会带来非常明显的性能损耗(通常在15%-25%之间),但是在今天能够提供的性能提升根据之前不知道在哪里看到的Intel测评已经不到5%了。在追求极致速度的物理隔离环境中,我还是要物尽其用,认为关闭它们是提升IPC(Instructions Per Cycle)的必要手段。\n\n### 2.3 核心隔离与时钟中断消除 (核心措施)\n\n* **`nohz_full=4-55,60-111`**: 启用Full Dynticks模式。在指定的核心上,如果运行队列中只有一个任务(即我们的计算进程),内核将停止发送调度时钟中断(Tick)。这消除了周期性的上下文切换干扰,实现了“无干扰(Tickless)”执行。\n* **`rcu_nocbs=4-55,60-111`**: 将指定核心上的RCU(Read-Copy-Update)回调函数卸载到非隔离核心(Housekeeping cores)的线程中执行。这防止了RCU软中断打断关键计算路径。\n* **`isolcpus=managed_irq,4-55,60-111`**: 将指定核心从内核的通用SMP均衡调度器中移除,确保普通进程不会被随机调度到这些核心上。\n* **`managed_irq`** 标志进一步指示内核尽量避免将受管中断(Managed Interrupts)分配给这些核心,为后续的用户态中断控制打下基础。\n\n## 3. 用户态环境与调度优化\n\n除了内核层面的静态配置,运行时的动态调优同样关键。\n\n### 3.1 基础系统调优 (`tuned`)\n\n```bash\ntuned-adm profile hpc-compute\n```\n\n启用RedHat系的`hpc-compute`配置文件。该Profile会自动调整虚拟内存参数(如`vm.dirty_ratio`)、网络栈缓冲区大小以及CPU的电源管理策略(强制最大C-State为C1或C0),确保CPU始终处于高性能状态,避免频率升降带来的延迟。\n\n### 3.2 scx调度器:sched_ext (`scx_tickless`)\n\n```bash\n/usr/local/bin/scx_tickless -m 0x0F0000000000000F\n```\n\n利用较新的Linux内核特性(BPF-based extensible scheduler class),我引入了`scx_tickless`调度器。\n\n* 该调度器由英伟达工程师设计,通过eBPF程序接管调度决策,相比完全公平调度器(CFS),它能更激进地维持计算核心的Tickless状态。\n* 参数 `-m 0x0F0000000000000F` 是一个掩码配置,精确指定了Housekeeping核心的拓扑位置,确保调度器仅在管理核心上处理必要的调度逻辑,从而保护计算核心的“静默”状态。\n\n### 3.3 中断亲和性管理 (`irqbalance`)\n\n为了防止网卡、磁盘控制器的硬中断打断计算流水线,必须严格限制中断响应的核心范围。\n\n```bash\n# 配置文件/etc/sysconfig/irqbalance中设置\nIRQBALANCE_BANNED_CPUS=\"FFFFFFFFFFFFF0FFFFFFFFFFFFF0\"\n```\n\n这是一个64位/128位的十六进制掩码。该配置显式**禁止**了所有隔离核心(即计算核心)参与中断负载均衡。结果是所有硬件中断都被强行“钉”在了Housekeeping Cores(Socket 0的0-3和Socket 1的56-59)上,保证了计算核心100%的CPU时间片用于业务逻辑。\n\n### 3.4 进程绑核 (`taskset`)\n\n最后,在启动计算任务时,最好显式指定亲和性不能光依赖scx_tickless调度,防止跨NUMA节点的内存访问惩罚。\n\n```makefile_and_run.py\n# 添加一个启动脚本变量\nNUMACTL_CPU_BIND = \"taskset -c 4-55,60-111\"\n```\n\n结合`isolcpus`,这确保了计算线程被牢牢锁定在无干扰的核心上,且每个线程独占物理核L1/L2缓存。\n\n## 4. 性能验证与结论\n\n**测试用例**: AMSS-NCKU双黑洞模拟(基于广义相对论数值解法)。\n**评估指标**: 单个时间步(Time Step)的计算耗时。\n\n在应用上述全套优化措施后,系统表现出极高的确定性。对比默认配置(Default CFS + On-Demand Power + Default IRQ Balance):\n\n1. **稳定性提升**: 计算过程中的抖动(Jitter)几乎消失,btop监控可以看到各核心负载曲线呈现完美的平直线。\n2. **性能收益**:\n\n* 在计算前期,单步时间步稳定降低 **0.1 - 0.2秒**。\n* 随着模拟进入后期(网格加密或相互作用增强),计算密度增加,该优化带来的累计收益更为显著。\n* 48核心运行总时间预计缩短约 **800秒**。\n\n### 总结\n\n过去参加比赛就没有一次是能够在限定功耗下启用所有核心不降频全力跑程序的,我不禁思考既然用不完何不将单核计算性能调优做到极致,牺牲一些核心处理杂活不参与计算,剩余的核心全力以赴跑程序不要受干扰最好也不要降频,这就是我的大致思路。\n\n通过`nohz_full`与`isolcpus`实现核心隔离,配合`mitigations=off`释放硬件潜能,并利用`scx_tickless`进行用户态调度增强,我按照设想将这台Xeon 8480+服务器改造成了一个OS Noise几乎消除的专用计算平台。后续需要关注的问题是迁移服务器后如何快速重现这些调优配置,或者干脆设计动态调控脚本来根据硬件信息调整内核参数。另外还有多机运行也没有进行测试,未来会关注在MPI环境下验证这些调优措施的适用性。\n\n---\n\n### 赛后更新\n\n散了吧孩子们,H100/H200已经杀死比赛了:(\n","source":"_posts/os-optimization.md","raw":"---\ntitle: 面向竞赛环境的Linux系统性能调优实践\ndate: 2026-01-18 12:14:04\ntags: [技术, 系统优化, HPC, Linux内核]\ncategories: [技术分享,HPC]\n---\n\n最初,我的想法是现在大伙都有AI工具了码力差距将不再像过去那么明显,这种情况下系统调优一定是有实战价值的。尤其是在面对极端计算任务时,操作系统内核的调度策略和中断处理方式对性能必然有着直接且显著的影响。为了在计算密集型任务(如本次天体物理模拟)中获得极致的性能与确定性,我作为运维必须尝试对Linux内核进行深度定制与隔离。\n\n本文将详细记录我在Intel Sapphire Rapids平台(Dual Xeon 8480P+)上进行的系统调优探索。通过内核参数隔离、用户态调度器(sched_ext)以及IRQ亲和性管理,我成功在AMSS-NCKU双黑洞模拟算例中取得了肉眼可见的性能收益。\n\n## 1. 测试环境与硬件拓扑\n\n本次调优的硬件平台具备极高的并行计算能力,但NUMA架构带来的延迟敏感性也对系统配置提出了挑战:\n\n* **CPU**: 2x Intel Xeon Platinum 8480+ (Sapphire Rapids)\n* **核心配置**: 单路56核心,双路共112物理核心。\n* **超线程 (Hyper-Threading)**: **Disabled**(为了避免逻辑核争抢浮点单元及缓存资源,确保计算流水线的独占性)。\n* **GPU**: 无(纯CPU计算任务)。\n\n针对该拓扑,我制定了**“核心隔离(Core Isolation)”**策略:将系统中断、管理进程及内核Housekeeping任务通过`cpuset`和内核参数限制在特定的少数核心上(Housekeeping Cores),将绝大多数核心完全腾空,仅运行计算任务(Isolation Cores)。\n\n* **Housekeeping Cores**: 0-3 (Socket 0), 56-59 (Socket 1) — 共8核。\n* **Isolation Cores**: 4-55 (Socket 0), 60-111 (Socket 1) — 共104核,用于纯计算。\n\n## 2. 内核启动参数(Kernel Command Line)深度解析\n\n首先需要确定的是这次我将选择Rockylinux 10作为操作系统基础,内核版本为6.12是首个支持sched_ext调度器的稳定版本,并且Rockylinux 10首次完全基于x86-64-v3构建,相比其他发行版有着必然的性能优势,而少数比它性能更好的发行版(CachyOS和已经逝世的Clearlinux)又没它稳定还不受nvidia ofed驱动支持,考虑到这些因素我不会多看其他任何发行版一秒。(现在都是2026年了坚决抵制CentOS 7/8或Rockylinux 8信徒遗老,这么老的内核挂个exFAT硬盘传东西都费劲)。\n\n为了实现我预期的隔离策略,我使用grubby在GRUB配置中启用了以下内核参数。这些参数共同构成了一个低延迟、低抖动的运行环境。\n\n```bash\ntransparent_hugepage=never iommu=pt mitigations=off \\\nnohz_full=4-55,60-111 rcu_nocbs=4-55,60-111 \\\nisolcpus=managed_irq,4-55,60-111\n```\n\n### 2.1 内存与IO子系统优化\n\n* **`transparent_hugepage=never`**: 禁用透明大页(THP)。虽然THP能减少TLB Miss,但在高强度的随机内存访问或频繁申请释放场景下,THP的后台合并线程(khugepaged)会引入不可预测的延迟抖动(Jitter)。\n* **`iommu=pt`**: 将IOMMU设置为Pass-Through模式。这允许设备在DMA传输时直接使用物理地址,绕过IOMMU的地址转换层,显著降低了IO密集型操作的CPU开销,同时保留了IOMMU的基本功能。\n\n### 2.2 安全缓解措施禁用\n\n* **`mitigations=off`**(让CPU回归其原本的样子): 该参数关闭了针对Spectre、Meltdown等侧信道攻击的所有CPU漏洞缓解措施。这些缓解措施通常涉及频繁的页表隔离(PTI)和间接分支预测限制,在旧时代这会带来非常明显的性能损耗(通常在15%-25%之间),但是在今天能够提供的性能提升根据之前不知道在哪里看到的Intel测评已经不到5%了。在追求极致速度的物理隔离环境中,我还是要物尽其用,认为关闭它们是提升IPC(Instructions Per Cycle)的必要手段。\n\n### 2.3 核心隔离与时钟中断消除 (核心措施)\n\n* **`nohz_full=4-55,60-111`**: 启用Full Dynticks模式。在指定的核心上,如果运行队列中只有一个任务(即我们的计算进程),内核将停止发送调度时钟中断(Tick)。这消除了周期性的上下文切换干扰,实现了“无干扰(Tickless)”执行。\n* **`rcu_nocbs=4-55,60-111`**: 将指定核心上的RCU(Read-Copy-Update)回调函数卸载到非隔离核心(Housekeeping cores)的线程中执行。这防止了RCU软中断打断关键计算路径。\n* **`isolcpus=managed_irq,4-55,60-111`**: 将指定核心从内核的通用SMP均衡调度器中移除,确保普通进程不会被随机调度到这些核心上。\n* **`managed_irq`** 标志进一步指示内核尽量避免将受管中断(Managed Interrupts)分配给这些核心,为后续的用户态中断控制打下基础。\n\n## 3. 用户态环境与调度优化\n\n除了内核层面的静态配置,运行时的动态调优同样关键。\n\n### 3.1 基础系统调优 (`tuned`)\n\n```bash\ntuned-adm profile hpc-compute\n```\n\n启用RedHat系的`hpc-compute`配置文件。该Profile会自动调整虚拟内存参数(如`vm.dirty_ratio`)、网络栈缓冲区大小以及CPU的电源管理策略(强制最大C-State为C1或C0),确保CPU始终处于高性能状态,避免频率升降带来的延迟。\n\n### 3.2 scx调度器:sched_ext (`scx_tickless`)\n\n```bash\n/usr/local/bin/scx_tickless -m 0x0F0000000000000F\n```\n\n利用较新的Linux内核特性(BPF-based extensible scheduler class),我引入了`scx_tickless`调度器。\n\n* 该调度器由英伟达工程师设计,通过eBPF程序接管调度决策,相比完全公平调度器(CFS),它能更激进地维持计算核心的Tickless状态。\n* 参数 `-m 0x0F0000000000000F` 是一个掩码配置,精确指定了Housekeeping核心的拓扑位置,确保调度器仅在管理核心上处理必要的调度逻辑,从而保护计算核心的“静默”状态。\n\n### 3.3 中断亲和性管理 (`irqbalance`)\n\n为了防止网卡、磁盘控制器的硬中断打断计算流水线,必须严格限制中断响应的核心范围。\n\n```bash\n# 配置文件/etc/sysconfig/irqbalance中设置\nIRQBALANCE_BANNED_CPUS=\"FFFFFFFFFFFFF0FFFFFFFFFFFFF0\"\n```\n\n这是一个64位/128位的十六进制掩码。该配置显式**禁止**了所有隔离核心(即计算核心)参与中断负载均衡。结果是所有硬件中断都被强行“钉”在了Housekeeping Cores(Socket 0的0-3和Socket 1的56-59)上,保证了计算核心100%的CPU时间片用于业务逻辑。\n\n### 3.4 进程绑核 (`taskset`)\n\n最后,在启动计算任务时,最好显式指定亲和性不能光依赖scx_tickless调度,防止跨NUMA节点的内存访问惩罚。\n\n```makefile_and_run.py\n# 添加一个启动脚本变量\nNUMACTL_CPU_BIND = \"taskset -c 4-55,60-111\"\n```\n\n结合`isolcpus`,这确保了计算线程被牢牢锁定在无干扰的核心上,且每个线程独占物理核L1/L2缓存。\n\n## 4. 性能验证与结论\n\n**测试用例**: AMSS-NCKU双黑洞模拟(基于广义相对论数值解法)。\n**评估指标**: 单个时间步(Time Step)的计算耗时。\n\n在应用上述全套优化措施后,系统表现出极高的确定性。对比默认配置(Default CFS + On-Demand Power + Default IRQ Balance):\n\n1. **稳定性提升**: 计算过程中的抖动(Jitter)几乎消失,btop监控可以看到各核心负载曲线呈现完美的平直线。\n2. **性能收益**:\n\n* 在计算前期,单步时间步稳定降低 **0.1 - 0.2秒**。\n* 随着模拟进入后期(网格加密或相互作用增强),计算密度增加,该优化带来的累计收益更为显著。\n* 48核心运行总时间预计缩短约 **800秒**。\n\n### 总结\n\n过去参加比赛就没有一次是能够在限定功耗下启用所有核心不降频全力跑程序的,我不禁思考既然用不完何不将单核计算性能调优做到极致,牺牲一些核心处理杂活不参与计算,剩余的核心全力以赴跑程序不要受干扰最好也不要降频,这就是我的大致思路。\n\n通过`nohz_full`与`isolcpus`实现核心隔离,配合`mitigations=off`释放硬件潜能,并利用`scx_tickless`进行用户态调度增强,我按照设想将这台Xeon 8480+服务器改造成了一个OS Noise几乎消除的专用计算平台。后续需要关注的问题是迁移服务器后如何快速重现这些调优配置,或者干脆设计动态调控脚本来根据硬件信息调整内核参数。另外还有多机运行也没有进行测试,未来会关注在MPI环境下验证这些调优措施的适用性。\n\n---\n\n### 赛后更新\n\n散了吧孩子们,H100/H200已经杀死比赛了:(\n","slug":"os-optimization","published":1,"updated":"2026-05-30T12:54:00.163Z","_id":"cmkjeg2nr001iyg2b4y5fbjnm","comments":1,"layout":"post","photos":[],"content":"

最初,我的想法是现在大伙都有AI工具了码力差距将不再像过去那么明显,这种情况下系统调优一定是有实战价值的。尤其是在面对极端计算任务时,操作系统内核的调度策略和中断处理方式对性能必然有着直接且显著的影响。为了在计算密集型任务(如本次天体物理模拟)中获得极致的性能与确定性,我作为运维必须尝试对Linux内核进行深度定制与隔离。

\n

本文将详细记录我在Intel Sapphire Rapids平台(Dual Xeon 8480P+)上进行的系统调优探索。通过内核参数隔离、用户态调度器(sched_ext)以及IRQ亲和性管理,我成功在AMSS-NCKU双黑洞模拟算例中取得了肉眼可见的性能收益。

\n

1. 测试环境与硬件拓扑

本次调优的硬件平台具备极高的并行计算能力,但NUMA架构带来的延迟敏感性也对系统配置提出了挑战:

\n
    \n
  • CPU: 2x Intel Xeon Platinum 8480+ (Sapphire Rapids)
  • \n
  • 核心配置: 单路56核心,双路共112物理核心。
  • \n
  • 超线程 (Hyper-Threading): Disabled(为了避免逻辑核争抢浮点单元及缓存资源,确保计算流水线的独占性)。
  • \n
  • GPU: 无(纯CPU计算任务)。
  • \n
\n

针对该拓扑,我制定了“核心隔离(Core Isolation)”策略:将系统中断、管理进程及内核Housekeeping任务通过cpuset和内核参数限制在特定的少数核心上(Housekeeping Cores),将绝大多数核心完全腾空,仅运行计算任务(Isolation Cores)。

\n
    \n
  • Housekeeping Cores: 0-3 (Socket 0), 56-59 (Socket 1) — 共8核。
  • \n
  • Isolation Cores: 4-55 (Socket 0), 60-111 (Socket 1) — 共104核,用于纯计算。
  • \n
\n

2. 内核启动参数(Kernel Command Line)深度解析

首先需要确定的是这次我将选择Rockylinux 10作为操作系统基础,内核版本为6.12是首个支持sched_ext调度器的稳定版本,并且Rockylinux 10首次完全基于x86-64-v3构建,相比其他发行版有着必然的性能优势,而少数比它性能更好的发行版(CachyOS和已经逝世的Clearlinux)又没它稳定还不受nvidia ofed驱动支持,考虑到这些因素我不会多看其他任何发行版一秒。(现在都是2026年了坚决抵制CentOS 7/8或Rockylinux 8信徒遗老,这么老的内核挂个exFAT硬盘传东西都费劲)。

\n

为了实现我预期的隔离策略,我使用grubby在GRUB配置中启用了以下内核参数。这些参数共同构成了一个低延迟、低抖动的运行环境。

\n
1
2
3
transparent_hugepage=never iommu=pt mitigations=off \\
nohz_full=4-55,60-111 rcu_nocbs=4-55,60-111 \\
isolcpus=managed_irq,4-55,60-111
\n\n

2.1 内存与IO子系统优化

    \n
  • transparent_hugepage=never: 禁用透明大页(THP)。虽然THP能减少TLB Miss,但在高强度的随机内存访问或频繁申请释放场景下,THP的后台合并线程(khugepaged)会引入不可预测的延迟抖动(Jitter)。
  • \n
  • iommu=pt: 将IOMMU设置为Pass-Through模式。这允许设备在DMA传输时直接使用物理地址,绕过IOMMU的地址转换层,显著降低了IO密集型操作的CPU开销,同时保留了IOMMU的基本功能。
  • \n
\n

2.2 安全缓解措施禁用

    \n
  • mitigations=off(让CPU回归其原本的样子): 该参数关闭了针对Spectre、Meltdown等侧信道攻击的所有CPU漏洞缓解措施。这些缓解措施通常涉及频繁的页表隔离(PTI)和间接分支预测限制,在旧时代这会带来非常明显的性能损耗(通常在15%-25%之间),但是在今天能够提供的性能提升根据之前不知道在哪里看到的Intel测评已经不到5%了。在追求极致速度的物理隔离环境中,我还是要物尽其用,认为关闭它们是提升IPC(Instructions Per Cycle)的必要手段。
  • \n
\n

2.3 核心隔离与时钟中断消除 (核心措施)

    \n
  • nohz_full=4-55,60-111: 启用Full Dynticks模式。在指定的核心上,如果运行队列中只有一个任务(即我们的计算进程),内核将停止发送调度时钟中断(Tick)。这消除了周期性的上下文切换干扰,实现了“无干扰(Tickless)”执行。
  • \n
  • rcu_nocbs=4-55,60-111: 将指定核心上的RCU(Read-Copy-Update)回调函数卸载到非隔离核心(Housekeeping cores)的线程中执行。这防止了RCU软中断打断关键计算路径。
  • \n
  • isolcpus=managed_irq,4-55,60-111: 将指定核心从内核的通用SMP均衡调度器中移除,确保普通进程不会被随机调度到这些核心上。
  • \n
  • managed_irq 标志进一步指示内核尽量避免将受管中断(Managed Interrupts)分配给这些核心,为后续的用户态中断控制打下基础。
  • \n
\n

3. 用户态环境与调度优化

除了内核层面的静态配置,运行时的动态调优同样关键。

\n

3.1 基础系统调优 (tuned)

1
tuned-adm profile hpc-compute
\n\n

启用RedHat系的hpc-compute配置文件。该Profile会自动调整虚拟内存参数(如vm.dirty_ratio)、网络栈缓冲区大小以及CPU的电源管理策略(强制最大C-State为C1或C0),确保CPU始终处于高性能状态,避免频率升降带来的延迟。

\n

3.2 scx调度器:sched_ext (scx_tickless)

1
/usr/local/bin/scx_tickless -m 0x0F0000000000000F
\n\n

利用较新的Linux内核特性(BPF-based extensible scheduler class),我引入了scx_tickless调度器。

\n
    \n
  • 该调度器由英伟达工程师设计,通过eBPF程序接管调度决策,相比完全公平调度器(CFS),它能更激进地维持计算核心的Tickless状态。
  • \n
  • 参数 -m 0x0F0000000000000F 是一个掩码配置,精确指定了Housekeeping核心的拓扑位置,确保调度器仅在管理核心上处理必要的调度逻辑,从而保护计算核心的“静默”状态。
  • \n
\n

3.3 中断亲和性管理 (irqbalance)

为了防止网卡、磁盘控制器的硬中断打断计算流水线,必须严格限制中断响应的核心范围。

\n
1
2
# 配置文件/etc/sysconfig/irqbalance中设置
IRQBALANCE_BANNED_CPUS="FFFFFFFFFFFFF0FFFFFFFFFFFFF0"
\n\n

这是一个64位/128位的十六进制掩码。该配置显式禁止了所有隔离核心(即计算核心)参与中断负载均衡。结果是所有硬件中断都被强行“钉”在了Housekeeping Cores(Socket 0的0-3和Socket 1的56-59)上,保证了计算核心100%的CPU时间片用于业务逻辑。

\n

3.4 进程绑核 (taskset)

最后,在启动计算任务时,最好显式指定亲和性不能光依赖scx_tickless调度,防止跨NUMA节点的内存访问惩罚。

\n
1
2
# 添加一个启动脚本变量
NUMACTL_CPU_BIND = "taskset -c 4-55,60-111"
\n\n

结合isolcpus,这确保了计算线程被牢牢锁定在无干扰的核心上,且每个线程独占物理核L1/L2缓存。

\n

4. 性能验证与结论

测试用例: AMSS-NCKU双黑洞模拟(基于广义相对论数值解法)。
评估指标: 单个时间步(Time Step)的计算耗时。

\n

在应用上述全套优化措施后,系统表现出极高的确定性。对比默认配置(Default CFS + On-Demand Power + Default IRQ Balance):

\n
    \n
  1. 稳定性提升: 计算过程中的抖动(Jitter)几乎消失,btop监控可以看到各核心负载曲线呈现完美的平直线。
  2. \n
  3. 性能收益:
  4. \n
\n
    \n
  • 在计算前期,单步时间步稳定降低 0.1 - 0.2秒
  • \n
  • 随着模拟进入后期(网格加密或相互作用增强),计算密度增加,该优化带来的累计收益更为显著。
  • \n
  • 48核心运行总时间预计缩短约 800秒
  • \n
\n

总结

过去参加比赛就没有一次是能够在限定功耗下启用所有核心不降频全力跑程序的,我不禁思考既然用不完何不将单核计算性能调优做到极致,牺牲一些核心处理杂活不参与计算,剩余的核心全力以赴跑程序不要受干扰最好也不要降频,这就是我的大致思路。

\n

通过nohz_fullisolcpus实现核心隔离,配合mitigations=off释放硬件潜能,并利用scx_tickless进行用户态调度增强,我按照设想将这台Xeon 8480+服务器改造成了一个OS Noise几乎消除的专用计算平台。后续需要关注的问题是迁移服务器后如何快速重现这些调优配置,或者干脆设计动态调控脚本来根据硬件信息调整内核参数。另外还有多机运行也没有进行测试,未来会关注在MPI环境下验证这些调优措施的适用性。

\n
\n

赛后更新

散了吧孩子们,H100/H200已经杀死比赛了:(

\n","excerpt":"","more":"

最初,我的想法是现在大伙都有AI工具了码力差距将不再像过去那么明显,这种情况下系统调优一定是有实战价值的。尤其是在面对极端计算任务时,操作系统内核的调度策略和中断处理方式对性能必然有着直接且显著的影响。为了在计算密集型任务(如本次天体物理模拟)中获得极致的性能与确定性,我作为运维必须尝试对Linux内核进行深度定制与隔离。

\n

本文将详细记录我在Intel Sapphire Rapids平台(Dual Xeon 8480P+)上进行的系统调优探索。通过内核参数隔离、用户态调度器(sched_ext)以及IRQ亲和性管理,我成功在AMSS-NCKU双黑洞模拟算例中取得了肉眼可见的性能收益。

\n

1. 测试环境与硬件拓扑

本次调优的硬件平台具备极高的并行计算能力,但NUMA架构带来的延迟敏感性也对系统配置提出了挑战:

\n
    \n
  • CPU: 2x Intel Xeon Platinum 8480+ (Sapphire Rapids)
  • \n
  • 核心配置: 单路56核心,双路共112物理核心。
  • \n
  • 超线程 (Hyper-Threading): Disabled(为了避免逻辑核争抢浮点单元及缓存资源,确保计算流水线的独占性)。
  • \n
  • GPU: 无(纯CPU计算任务)。
  • \n
\n

针对该拓扑,我制定了“核心隔离(Core Isolation)”策略:将系统中断、管理进程及内核Housekeeping任务通过cpuset和内核参数限制在特定的少数核心上(Housekeeping Cores),将绝大多数核心完全腾空,仅运行计算任务(Isolation Cores)。

\n
    \n
  • Housekeeping Cores: 0-3 (Socket 0), 56-59 (Socket 1) — 共8核。
  • \n
  • Isolation Cores: 4-55 (Socket 0), 60-111 (Socket 1) — 共104核,用于纯计算。
  • \n
\n

2. 内核启动参数(Kernel Command Line)深度解析

首先需要确定的是这次我将选择Rockylinux 10作为操作系统基础,内核版本为6.12是首个支持sched_ext调度器的稳定版本,并且Rockylinux 10首次完全基于x86-64-v3构建,相比其他发行版有着必然的性能优势,而少数比它性能更好的发行版(CachyOS和已经逝世的Clearlinux)又没它稳定还不受nvidia ofed驱动支持,考虑到这些因素我不会多看其他任何发行版一秒。(现在都是2026年了坚决抵制CentOS 7/8或Rockylinux 8信徒遗老,这么老的内核挂个exFAT硬盘传东西都费劲)。

\n

为了实现我预期的隔离策略,我使用grubby在GRUB配置中启用了以下内核参数。这些参数共同构成了一个低延迟、低抖动的运行环境。

\n
1
2
3
transparent_hugepage=never iommu=pt mitigations=off \\
nohz_full=4-55,60-111 rcu_nocbs=4-55,60-111 \\
isolcpus=managed_irq,4-55,60-111
\n\n

2.1 内存与IO子系统优化

    \n
  • transparent_hugepage=never: 禁用透明大页(THP)。虽然THP能减少TLB Miss,但在高强度的随机内存访问或频繁申请释放场景下,THP的后台合并线程(khugepaged)会引入不可预测的延迟抖动(Jitter)。
  • \n
  • iommu=pt: 将IOMMU设置为Pass-Through模式。这允许设备在DMA传输时直接使用物理地址,绕过IOMMU的地址转换层,显著降低了IO密集型操作的CPU开销,同时保留了IOMMU的基本功能。
  • \n
\n

2.2 安全缓解措施禁用

    \n
  • mitigations=off(让CPU回归其原本的样子): 该参数关闭了针对Spectre、Meltdown等侧信道攻击的所有CPU漏洞缓解措施。这些缓解措施通常涉及频繁的页表隔离(PTI)和间接分支预测限制,在旧时代这会带来非常明显的性能损耗(通常在15%-25%之间),但是在今天能够提供的性能提升根据之前不知道在哪里看到的Intel测评已经不到5%了。在追求极致速度的物理隔离环境中,我还是要物尽其用,认为关闭它们是提升IPC(Instructions Per Cycle)的必要手段。
  • \n
\n

2.3 核心隔离与时钟中断消除 (核心措施)

    \n
  • nohz_full=4-55,60-111: 启用Full Dynticks模式。在指定的核心上,如果运行队列中只有一个任务(即我们的计算进程),内核将停止发送调度时钟中断(Tick)。这消除了周期性的上下文切换干扰,实现了“无干扰(Tickless)”执行。
  • \n
  • rcu_nocbs=4-55,60-111: 将指定核心上的RCU(Read-Copy-Update)回调函数卸载到非隔离核心(Housekeeping cores)的线程中执行。这防止了RCU软中断打断关键计算路径。
  • \n
  • isolcpus=managed_irq,4-55,60-111: 将指定核心从内核的通用SMP均衡调度器中移除,确保普通进程不会被随机调度到这些核心上。
  • \n
  • managed_irq 标志进一步指示内核尽量避免将受管中断(Managed Interrupts)分配给这些核心,为后续的用户态中断控制打下基础。
  • \n
\n

3. 用户态环境与调度优化

除了内核层面的静态配置,运行时的动态调优同样关键。

\n

3.1 基础系统调优 (tuned)

1
tuned-adm profile hpc-compute
\n\n

启用RedHat系的hpc-compute配置文件。该Profile会自动调整虚拟内存参数(如vm.dirty_ratio)、网络栈缓冲区大小以及CPU的电源管理策略(强制最大C-State为C1或C0),确保CPU始终处于高性能状态,避免频率升降带来的延迟。

\n

3.2 scx调度器:sched_ext (scx_tickless)

1
/usr/local/bin/scx_tickless -m 0x0F0000000000000F
\n\n

利用较新的Linux内核特性(BPF-based extensible scheduler class),我引入了scx_tickless调度器。

\n
    \n
  • 该调度器由英伟达工程师设计,通过eBPF程序接管调度决策,相比完全公平调度器(CFS),它能更激进地维持计算核心的Tickless状态。
  • \n
  • 参数 -m 0x0F0000000000000F 是一个掩码配置,精确指定了Housekeeping核心的拓扑位置,确保调度器仅在管理核心上处理必要的调度逻辑,从而保护计算核心的“静默”状态。
  • \n
\n

3.3 中断亲和性管理 (irqbalance)

为了防止网卡、磁盘控制器的硬中断打断计算流水线,必须严格限制中断响应的核心范围。

\n
1
2
# 配置文件/etc/sysconfig/irqbalance中设置
IRQBALANCE_BANNED_CPUS="FFFFFFFFFFFFF0FFFFFFFFFFFFF0"
\n\n

这是一个64位/128位的十六进制掩码。该配置显式禁止了所有隔离核心(即计算核心)参与中断负载均衡。结果是所有硬件中断都被强行“钉”在了Housekeeping Cores(Socket 0的0-3和Socket 1的56-59)上,保证了计算核心100%的CPU时间片用于业务逻辑。

\n

3.4 进程绑核 (taskset)

最后,在启动计算任务时,最好显式指定亲和性不能光依赖scx_tickless调度,防止跨NUMA节点的内存访问惩罚。

\n
1
2
# 添加一个启动脚本变量
NUMACTL_CPU_BIND = "taskset -c 4-55,60-111"
\n\n

结合isolcpus,这确保了计算线程被牢牢锁定在无干扰的核心上,且每个线程独占物理核L1/L2缓存。

\n

4. 性能验证与结论

测试用例: AMSS-NCKU双黑洞模拟(基于广义相对论数值解法)。
评估指标: 单个时间步(Time Step)的计算耗时。

\n

在应用上述全套优化措施后,系统表现出极高的确定性。对比默认配置(Default CFS + On-Demand Power + Default IRQ Balance):

\n
    \n
  1. 稳定性提升: 计算过程中的抖动(Jitter)几乎消失,btop监控可以看到各核心负载曲线呈现完美的平直线。
  2. \n
  3. 性能收益:
  4. \n
\n
    \n
  • 在计算前期,单步时间步稳定降低 0.1 - 0.2秒
  • \n
  • 随着模拟进入后期(网格加密或相互作用增强),计算密度增加,该优化带来的累计收益更为显著。
  • \n
  • 48核心运行总时间预计缩短约 800秒
  • \n
\n

总结

过去参加比赛就没有一次是能够在限定功耗下启用所有核心不降频全力跑程序的,我不禁思考既然用不完何不将单核计算性能调优做到极致,牺牲一些核心处理杂活不参与计算,剩余的核心全力以赴跑程序不要受干扰最好也不要降频,这就是我的大致思路。

\n

通过nohz_fullisolcpus实现核心隔离,配合mitigations=off释放硬件潜能,并利用scx_tickless进行用户态调度增强,我按照设想将这台Xeon 8480+服务器改造成了一个OS Noise几乎消除的专用计算平台。后续需要关注的问题是迁移服务器后如何快速重现这些调优配置,或者干脆设计动态调控脚本来根据硬件信息调整内核参数。另外还有多机运行也没有进行测试,未来会关注在MPI环境下验证这些调优措施的适用性。

\n
\n

赛后更新

散了吧孩子们,H100/H200已经杀死比赛了:(

\n"},{"title":"Overleaf Toolkit踩坑记录","date":"2024-11-06T01:56:18.000Z","_content":"\n在安装 Overleaf Toolkit 时,表面上看起来很简单只要执行一些脚本就行,但是在某地区网络环境下还是遇到了一些问题和困难,这里记录安装过程中的问题和解决方案,方便以后参考。\n\n具体流程就不赘述了,先将访问[Overleaf Toolkit官方仓库](https://github.com/overleaf/toolkit)将代码clone下来并按照手册执行即可。\n\n## 问题一:无法 Pull Mongo、Redis 和 Sharelatex 镜像\n\n在执行 `bin/up` 脚本启动服务时,发现 Mongo、Redis 和 Sharelatex 镜像无法拉取。主要原因是 Docker 在国内网络环境中,直接访问 Docker Hub 可能会被限制,导致拉取镜像失败。\n\n截至这篇博客编写时网上提供的镜像均无法解决只能使用代理。\n\n### 解决方案:为 Docker 设置代理\n\n通过配置 Docker 的代理,可以解决拉取镜像受限的问题。步骤如下:\n\n1. 创建一个 `systemd` 服务文件,为 Docker 设置代理。\n2. 编辑 `/etc/systemd/system/docker.service.d/proxy.conf` 文件,添加以下内容(确保已经设置了代理服务器):\n\n ```ini\n [Service]\n Environment=\"HTTP_PROXY=http://:\"\n Environment=\"HTTPS_PROXY=http://:\"\n Environment=\"NO_PROXY=localhost,127.0.0.1\"\n ```\n\n3. 重新加载 `systemd` 配置并重启 Docker:\n\n ```bash\n sudo systemctl daemon-reload\n sudo systemctl restart docker\n ```\n\n4. 重启 Docker 后再次执行 `sudo ./up`,此时应该可以正常拉取 Mongo、Redis 和 Sharelatex 的镜像。等待执行完成即可。\n\n\n---\n\n## 问题二:外部机器无法访问 Overleaf Web 服务\n\n先确认ipv4和ipv6转发功能没有问题,但是依旧出现只能本机访问127.0.0.1,其他方式均无法访问,甚至nmap扫描端口也发现并未开放sharelatex端口,可以通过修改docker-compose配置文件解决。\n\n注意到在默认的 `lib/docker-compose.base.yml` 配置中,Overleaf Web 服务的端口映射方式为 `\"${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80\"`。\n\n### 解决方案:修改端口映射\n\n在 `docker-compose.base.yml` 文件中,删除 `${OVERLEAF_LISTEN_IP:-127.0.0.1}` 前缀,将 `\"${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80\"` 修改为 `\"${OVERLEAF_PORT:-80}:80\"`。这样可以使 Docker 将 Overleaf 的 Web 服务端口暴露给所有网络接口,从而允许外部机器访问。\n\n修改后的 `docker-compose.base.yml` 端口映射配置如下:\n\n```yaml\n---\nservices:\n\n sharelatex:\n restart: always\n image: \"${IMAGE}\"\n container_name: sharelatex\n volumes:\n - \"${OVERLEAF_DATA_PATH}:${OVERLEAF_IN_CONTAINER_DATA_PATH}\"\n ports:\n #- \"${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80\"\n - \"${OVERLEAF_PORT:-80}:80\"\n environment:\n GIT_BRIDGE_ENABLED: \"${GIT_BRIDGE_ENABLED}\"\n GIT_BRIDGE_HOST: \"git-bridge\"\n GIT_BRIDGE_PORT: \"8000\"\n REDIS_HOST: \"${REDIS_HOST}\"\n REDIS_PORT: \"${REDIS_PORT}\"\n V1_HISTORY_URL: \"http://sharelatex:3100/api\"\n env_file:\n - ../config/variables.env\n stop_grace_period: 60s\n```\n\n完成修改后,重新启动 Docker 服务:\n\n```bash\nsudo ./start\n```\n\n现在,外部机器可以通过服务器的 IP 地址加端口 `{Overleaf_Port}` 访问 Overleaf Web 服务。\n\n---\n\n## 总结\n\n这次安装 Overleaf Toolkit 时,主要遇到的两个问题分别是镜像拉取失败和端口映射受限。通过为 Docker 设置代理解决了拉取镜像的问题,而通过修改 `docker-compose.base.yml` 中的端口映射使外部设备可以访问 Overleaf 服务。\n\n可以在这里参考我的本地[Overleaf](https://overleaf.hifuu.ink)效果,注意安装完后还需要配置latex包和中文字体。\n\n这篇记录希望能为遇到类似问题的朋友提供帮助。\n","source":"_posts/overleaf.md","raw":"---\ntitle: Overleaf Toolkit踩坑记录\ndate: 2024-11-06 09:56:18\ntags: 技术\ncategories: [技术分享]\n---\n\n在安装 Overleaf Toolkit 时,表面上看起来很简单只要执行一些脚本就行,但是在某地区网络环境下还是遇到了一些问题和困难,这里记录安装过程中的问题和解决方案,方便以后参考。\n\n具体流程就不赘述了,先将访问[Overleaf Toolkit官方仓库](https://github.com/overleaf/toolkit)将代码clone下来并按照手册执行即可。\n\n## 问题一:无法 Pull Mongo、Redis 和 Sharelatex 镜像\n\n在执行 `bin/up` 脚本启动服务时,发现 Mongo、Redis 和 Sharelatex 镜像无法拉取。主要原因是 Docker 在国内网络环境中,直接访问 Docker Hub 可能会被限制,导致拉取镜像失败。\n\n截至这篇博客编写时网上提供的镜像均无法解决只能使用代理。\n\n### 解决方案:为 Docker 设置代理\n\n通过配置 Docker 的代理,可以解决拉取镜像受限的问题。步骤如下:\n\n1. 创建一个 `systemd` 服务文件,为 Docker 设置代理。\n2. 编辑 `/etc/systemd/system/docker.service.d/proxy.conf` 文件,添加以下内容(确保已经设置了代理服务器):\n\n ```ini\n [Service]\n Environment=\"HTTP_PROXY=http://:\"\n Environment=\"HTTPS_PROXY=http://:\"\n Environment=\"NO_PROXY=localhost,127.0.0.1\"\n ```\n\n3. 重新加载 `systemd` 配置并重启 Docker:\n\n ```bash\n sudo systemctl daemon-reload\n sudo systemctl restart docker\n ```\n\n4. 重启 Docker 后再次执行 `sudo ./up`,此时应该可以正常拉取 Mongo、Redis 和 Sharelatex 的镜像。等待执行完成即可。\n\n\n---\n\n## 问题二:外部机器无法访问 Overleaf Web 服务\n\n先确认ipv4和ipv6转发功能没有问题,但是依旧出现只能本机访问127.0.0.1,其他方式均无法访问,甚至nmap扫描端口也发现并未开放sharelatex端口,可以通过修改docker-compose配置文件解决。\n\n注意到在默认的 `lib/docker-compose.base.yml` 配置中,Overleaf Web 服务的端口映射方式为 `\"${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80\"`。\n\n### 解决方案:修改端口映射\n\n在 `docker-compose.base.yml` 文件中,删除 `${OVERLEAF_LISTEN_IP:-127.0.0.1}` 前缀,将 `\"${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80\"` 修改为 `\"${OVERLEAF_PORT:-80}:80\"`。这样可以使 Docker 将 Overleaf 的 Web 服务端口暴露给所有网络接口,从而允许外部机器访问。\n\n修改后的 `docker-compose.base.yml` 端口映射配置如下:\n\n```yaml\n---\nservices:\n\n sharelatex:\n restart: always\n image: \"${IMAGE}\"\n container_name: sharelatex\n volumes:\n - \"${OVERLEAF_DATA_PATH}:${OVERLEAF_IN_CONTAINER_DATA_PATH}\"\n ports:\n #- \"${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80\"\n - \"${OVERLEAF_PORT:-80}:80\"\n environment:\n GIT_BRIDGE_ENABLED: \"${GIT_BRIDGE_ENABLED}\"\n GIT_BRIDGE_HOST: \"git-bridge\"\n GIT_BRIDGE_PORT: \"8000\"\n REDIS_HOST: \"${REDIS_HOST}\"\n REDIS_PORT: \"${REDIS_PORT}\"\n V1_HISTORY_URL: \"http://sharelatex:3100/api\"\n env_file:\n - ../config/variables.env\n stop_grace_period: 60s\n```\n\n完成修改后,重新启动 Docker 服务:\n\n```bash\nsudo ./start\n```\n\n现在,外部机器可以通过服务器的 IP 地址加端口 `{Overleaf_Port}` 访问 Overleaf Web 服务。\n\n---\n\n## 总结\n\n这次安装 Overleaf Toolkit 时,主要遇到的两个问题分别是镜像拉取失败和端口映射受限。通过为 Docker 设置代理解决了拉取镜像的问题,而通过修改 `docker-compose.base.yml` 中的端口映射使外部设备可以访问 Overleaf 服务。\n\n可以在这里参考我的本地[Overleaf](https://overleaf.hifuu.ink)效果,注意安装完后还需要配置latex包和中文字体。\n\n这篇记录希望能为遇到类似问题的朋友提供帮助。\n","slug":"overleaf","published":1,"updated":"2024-11-06T02:31:56.685Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nr001kyg2bg8bh5158","content":"

在安装 Overleaf Toolkit 时,表面上看起来很简单只要执行一些脚本就行,但是在某地区网络环境下还是遇到了一些问题和困难,这里记录安装过程中的问题和解决方案,方便以后参考。

\n

具体流程就不赘述了,先将访问Overleaf Toolkit官方仓库将代码clone下来并按照手册执行即可。

\n

问题一:无法 Pull Mongo、Redis 和 Sharelatex 镜像

在执行 bin/up 脚本启动服务时,发现 Mongo、Redis 和 Sharelatex 镜像无法拉取。主要原因是 Docker 在国内网络环境中,直接访问 Docker Hub 可能会被限制,导致拉取镜像失败。

\n

截至这篇博客编写时网上提供的镜像均无法解决只能使用代理。

\n

解决方案:为 Docker 设置代理

通过配置 Docker 的代理,可以解决拉取镜像受限的问题。步骤如下:

\n
    \n
  1. 创建一个 systemd 服务文件,为 Docker 设置代理。

    \n
  2. \n
  3. 编辑 /etc/systemd/system/docker.service.d/proxy.conf 文件,添加以下内容(确保已经设置了代理服务器):

    \n
    1
    2
    3
    4
    [Service]
    Environment="HTTP_PROXY=http://<your-proxy-server>:<port>"
    Environment="HTTPS_PROXY=http://<your-proxy-server>:<port>"
    Environment="NO_PROXY=localhost,127.0.0.1"
    \n
  4. \n
  5. 重新加载 systemd 配置并重启 Docker:

    \n
    1
    2
    sudo systemctl daemon-reload
    sudo systemctl restart docker
    \n
  6. \n
  7. 重启 Docker 后再次执行 sudo ./up,此时应该可以正常拉取 Mongo、Redis 和 Sharelatex 的镜像。等待执行完成即可。

    \n
  8. \n
\n
\n

问题二:外部机器无法访问 Overleaf Web 服务

先确认ipv4和ipv6转发功能没有问题,但是依旧出现只能本机访问127.0.0.1,其他方式均无法访问,甚至nmap扫描端口也发现并未开放sharelatex端口,可以通过修改docker-compose配置文件解决。

\n

注意到在默认的 lib/docker-compose.base.yml 配置中,Overleaf Web 服务的端口映射方式为 "${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80"

\n

解决方案:修改端口映射

docker-compose.base.yml 文件中,删除 ${OVERLEAF_LISTEN_IP:-127.0.0.1} 前缀,将 "${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80" 修改为 "${OVERLEAF_PORT:-80}:80"。这样可以使 Docker 将 Overleaf 的 Web 服务端口暴露给所有网络接口,从而允许外部机器访问。

\n

修改后的 docker-compose.base.yml 端口映射配置如下:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
---
services:

sharelatex:
restart: always
image: "${IMAGE}"
container_name: sharelatex
volumes:
- "${OVERLEAF_DATA_PATH}:${OVERLEAF_IN_CONTAINER_DATA_PATH}"
ports:
#- "${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80"
- "${OVERLEAF_PORT:-80}:80"
environment:
GIT_BRIDGE_ENABLED: "${GIT_BRIDGE_ENABLED}"
GIT_BRIDGE_HOST: "git-bridge"
GIT_BRIDGE_PORT: "8000"
REDIS_HOST: "${REDIS_HOST}"
REDIS_PORT: "${REDIS_PORT}"
V1_HISTORY_URL: "http://sharelatex:3100/api"
env_file:
- ../config/variables.env
stop_grace_period: 60s
\n\n

完成修改后,重新启动 Docker 服务:

\n
1
sudo ./start
\n\n

现在,外部机器可以通过服务器的 IP 地址加端口 {Overleaf_Port} 访问 Overleaf Web 服务。

\n
\n

总结

这次安装 Overleaf Toolkit 时,主要遇到的两个问题分别是镜像拉取失败和端口映射受限。通过为 Docker 设置代理解决了拉取镜像的问题,而通过修改 docker-compose.base.yml 中的端口映射使外部设备可以访问 Overleaf 服务。

\n

可以在这里参考我的本地Overleaf效果,注意安装完后还需要配置latex包和中文字体。

\n

这篇记录希望能为遇到类似问题的朋友提供帮助。

\n","excerpt":"","more":"

在安装 Overleaf Toolkit 时,表面上看起来很简单只要执行一些脚本就行,但是在某地区网络环境下还是遇到了一些问题和困难,这里记录安装过程中的问题和解决方案,方便以后参考。

\n

具体流程就不赘述了,先将访问Overleaf Toolkit官方仓库将代码clone下来并按照手册执行即可。

\n

问题一:无法 Pull Mongo、Redis 和 Sharelatex 镜像

在执行 bin/up 脚本启动服务时,发现 Mongo、Redis 和 Sharelatex 镜像无法拉取。主要原因是 Docker 在国内网络环境中,直接访问 Docker Hub 可能会被限制,导致拉取镜像失败。

\n

截至这篇博客编写时网上提供的镜像均无法解决只能使用代理。

\n

解决方案:为 Docker 设置代理

通过配置 Docker 的代理,可以解决拉取镜像受限的问题。步骤如下:

\n
    \n
  1. 创建一个 systemd 服务文件,为 Docker 设置代理。

    \n
  2. \n
  3. 编辑 /etc/systemd/system/docker.service.d/proxy.conf 文件,添加以下内容(确保已经设置了代理服务器):

    \n
    1
    2
    3
    4
    [Service]
    Environment="HTTP_PROXY=http://<your-proxy-server>:<port>"
    Environment="HTTPS_PROXY=http://<your-proxy-server>:<port>"
    Environment="NO_PROXY=localhost,127.0.0.1"
    \n
  4. \n
  5. 重新加载 systemd 配置并重启 Docker:

    \n
    1
    2
    sudo systemctl daemon-reload
    sudo systemctl restart docker
    \n
  6. \n
  7. 重启 Docker 后再次执行 sudo ./up,此时应该可以正常拉取 Mongo、Redis 和 Sharelatex 的镜像。等待执行完成即可。

    \n
  8. \n
\n
\n

问题二:外部机器无法访问 Overleaf Web 服务

先确认ipv4和ipv6转发功能没有问题,但是依旧出现只能本机访问127.0.0.1,其他方式均无法访问,甚至nmap扫描端口也发现并未开放sharelatex端口,可以通过修改docker-compose配置文件解决。

\n

注意到在默认的 lib/docker-compose.base.yml 配置中,Overleaf Web 服务的端口映射方式为 "${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80"

\n

解决方案:修改端口映射

docker-compose.base.yml 文件中,删除 ${OVERLEAF_LISTEN_IP:-127.0.0.1} 前缀,将 "${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80" 修改为 "${OVERLEAF_PORT:-80}:80"。这样可以使 Docker 将 Overleaf 的 Web 服务端口暴露给所有网络接口,从而允许外部机器访问。

\n

修改后的 docker-compose.base.yml 端口映射配置如下:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
---
services:

sharelatex:
restart: always
image: "${IMAGE}"
container_name: sharelatex
volumes:
- "${OVERLEAF_DATA_PATH}:${OVERLEAF_IN_CONTAINER_DATA_PATH}"
ports:
#- "${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80"
- "${OVERLEAF_PORT:-80}:80"
environment:
GIT_BRIDGE_ENABLED: "${GIT_BRIDGE_ENABLED}"
GIT_BRIDGE_HOST: "git-bridge"
GIT_BRIDGE_PORT: "8000"
REDIS_HOST: "${REDIS_HOST}"
REDIS_PORT: "${REDIS_PORT}"
V1_HISTORY_URL: "http://sharelatex:3100/api"
env_file:
- ../config/variables.env
stop_grace_period: 60s
\n\n

完成修改后,重新启动 Docker 服务:

\n
1
sudo ./start
\n\n

现在,外部机器可以通过服务器的 IP 地址加端口 {Overleaf_Port} 访问 Overleaf Web 服务。

\n
\n

总结

这次安装 Overleaf Toolkit 时,主要遇到的两个问题分别是镜像拉取失败和端口映射受限。通过为 Docker 设置代理解决了拉取镜像的问题,而通过修改 docker-compose.base.yml 中的端口映射使外部设备可以访问 Overleaf 服务。

\n

可以在这里参考我的本地Overleaf效果,注意安装完后还需要配置latex包和中文字体。

\n

这篇记录希望能为遇到类似问题的朋友提供帮助。

\n"},{"title":"相见恨晚的 SearXNG:打造私人搜索引擎全指南","date":"2025-03-10T14:55:04.000Z","_content":"\n![SearXNG 界面预览](/images/searxng.png) \n**厌倦了商业搜索引擎的广告追踪?** 受够技术社区被 SEO 污染的水文?希望搜索结果里尽量不要呈现C*DN等平台的低质内容?这款开源元搜索引擎 [SearXNG](https://github.com/searxng/searxng) 现在让我直呼真香爱不释手,使用频率远超 Nextcloud 等自建服务。\n\n---\n\n### ▍ 为什么选择 SearXNG?\n✅ **核心优势矩阵** \n| 特性 | 实现方式 | 用户收益 |\n|--------------------|----------------------------|------------------------|\n| 隐私保护 | 匿名聚合第三方结果 | 无搜索历史追踪 |\n| 结果去商业化 | 过滤 SEO 优化内容 | 提升技术资料检索效率 |\n| 多引擎支持 | 集成 Google/Bing/Brave 等 70+ 源 | 规避单一引擎局限 |\n| 界面定制 | 主题引擎分离设计 | 打造个性化搜索门户 |\n\n⚠️ 同类工具对比: \n此前使用的 [luxirty-search](https://github.com/KoriIku/luxirty-search) 也很不错但是依赖 Google CSE,对于内网环境日常使用多少有点不友好。\n\n之前看了下网上普遍推荐docker方案不过部署流程与官方文档有不少出入而且在我本地没法正常运行,这里我们直接按照官方文档来,事不宜迟直接开始部署。\n\n---\n\n### ▍ 极简部署方案\n#### ▶ 环境准备\n```bash\nmkdir -p ~/services/searxng && cd $_ # 随意找个地方创建专用目录\nexport SEARXNG_PORT=8080 # 设置服务端口\n```\n\n#### ▶ 容器化部署\n```bash\n# 拉取官方镜像\ndocker pull searxng/searxng\n\n# 启动容器(推荐绑定持久化配置)\ndocker run -d --restart=unless-stopped \\\n -p ${SEARXNG_PORT}:8080 \\\n -v \"${PWD}/config:/etc/searxng\" \\\n -e \"BASE_URL=http://your-domain.com\" \\\n -e \"INSTANCE_NAME=PrivateSearch\" \\\n searxng/searxng\n```\n📌 参数说明: \n- `-v` 挂载配置文件实现持久化 \n- `--restart` 确保服务异常退出后自动重启 \n- `BASE_URL` 需替换为实际访问域名,不过也可以后续再配置\n\n---\n\n### ▍ 高频问题排雷\n#### 🔴 镜像拉取失败\n**现象**:`Error response from daemon: pull access denied` \n✅ 解决方案: \n参考先前的 [Overleaf Toolkit 踩坑记录](https://blog.hifuu.ink/2024/11/06/overleaf/) 配置镜像加速源\n\n#### 🔴 Google 频繁拦截\n**现象**:`Too Many Requests` 错误频发 \n\n⚠️ 这算是一个玄学问题除了更换代理我还没有发现更好的解决方案\n\n✅ 实战验证方案: \n更换你的代理服务器比如笔者目前在🇺🇸🇭🇰🇯🇵均有购买VPS服务器可作为代理,经尝试总会有可以使用的节点。\n\n📌 在配置文件设置代理: \n```yaml\noutgoing:\n proxies:\n all://:\n - http://
:\n```\n\n---\n\n### ▍ 效果展示与体验\n访问我的生产环境实例 [search.hifuu.ink](https://search.hifuu.ink) 可体验以下功能: \n- 🔍 多引擎结果聚合对比 \n- 🌍 支持 30+ 语言实时翻译 \n- 🛡️ 零 Cookie 跟踪的隐私模式 \n- 🎨 主题切换 \n\n---\n### ▍ 进阶优化建议\n1. **反向代理配置** \n\n网上相关教程应该很多这里不再重复,我的SearXNG服务器通过香港VPS反代到公网使用。\n\n2. **定期维护命令** \n```bash\n# 更新搜索引擎引擎列表\ndocker exec -it searxng python -m searx.engines --update\n```\n\n3. **监控指标集成** \n通过 Prometheus 导出搜索统计:\n```bash\ndocker run -d --network=searxng-net \\\n -v \"${PWD}/metrics:/metrics\" \\\n prom/prometheus --config.file=/metrics/searxng.yml\n``` \n\n---\n\n**🚀 立即行动** \n\n快速拥有对抗 SEO 污染的利器,快来打造你的私人搜索门户吧!部署过程遇到问题欢迎联系笔者交流。\n","source":"_posts/searxng.md","raw":"---\ntitle: 相见恨晚的 SearXNG:打造私人搜索引擎全指南\ndate: 2025-03-10 22:55:04\ntags: [开源工具, 技术分享]\n---\n\n![SearXNG 界面预览](/images/searxng.png) \n**厌倦了商业搜索引擎的广告追踪?** 受够技术社区被 SEO 污染的水文?希望搜索结果里尽量不要呈现C*DN等平台的低质内容?这款开源元搜索引擎 [SearXNG](https://github.com/searxng/searxng) 现在让我直呼真香爱不释手,使用频率远超 Nextcloud 等自建服务。\n\n---\n\n### ▍ 为什么选择 SearXNG?\n✅ **核心优势矩阵** \n| 特性 | 实现方式 | 用户收益 |\n|--------------------|----------------------------|------------------------|\n| 隐私保护 | 匿名聚合第三方结果 | 无搜索历史追踪 |\n| 结果去商业化 | 过滤 SEO 优化内容 | 提升技术资料检索效率 |\n| 多引擎支持 | 集成 Google/Bing/Brave 等 70+ 源 | 规避单一引擎局限 |\n| 界面定制 | 主题引擎分离设计 | 打造个性化搜索门户 |\n\n⚠️ 同类工具对比: \n此前使用的 [luxirty-search](https://github.com/KoriIku/luxirty-search) 也很不错但是依赖 Google CSE,对于内网环境日常使用多少有点不友好。\n\n之前看了下网上普遍推荐docker方案不过部署流程与官方文档有不少出入而且在我本地没法正常运行,这里我们直接按照官方文档来,事不宜迟直接开始部署。\n\n---\n\n### ▍ 极简部署方案\n#### ▶ 环境准备\n```bash\nmkdir -p ~/services/searxng && cd $_ # 随意找个地方创建专用目录\nexport SEARXNG_PORT=8080 # 设置服务端口\n```\n\n#### ▶ 容器化部署\n```bash\n# 拉取官方镜像\ndocker pull searxng/searxng\n\n# 启动容器(推荐绑定持久化配置)\ndocker run -d --restart=unless-stopped \\\n -p ${SEARXNG_PORT}:8080 \\\n -v \"${PWD}/config:/etc/searxng\" \\\n -e \"BASE_URL=http://your-domain.com\" \\\n -e \"INSTANCE_NAME=PrivateSearch\" \\\n searxng/searxng\n```\n📌 参数说明: \n- `-v` 挂载配置文件实现持久化 \n- `--restart` 确保服务异常退出后自动重启 \n- `BASE_URL` 需替换为实际访问域名,不过也可以后续再配置\n\n---\n\n### ▍ 高频问题排雷\n#### 🔴 镜像拉取失败\n**现象**:`Error response from daemon: pull access denied` \n✅ 解决方案: \n参考先前的 [Overleaf Toolkit 踩坑记录](https://blog.hifuu.ink/2024/11/06/overleaf/) 配置镜像加速源\n\n#### 🔴 Google 频繁拦截\n**现象**:`Too Many Requests` 错误频发 \n\n⚠️ 这算是一个玄学问题除了更换代理我还没有发现更好的解决方案\n\n✅ 实战验证方案: \n更换你的代理服务器比如笔者目前在🇺🇸🇭🇰🇯🇵均有购买VPS服务器可作为代理,经尝试总会有可以使用的节点。\n\n📌 在配置文件设置代理: \n```yaml\noutgoing:\n proxies:\n all://:\n - http://
:\n```\n\n---\n\n### ▍ 效果展示与体验\n访问我的生产环境实例 [search.hifuu.ink](https://search.hifuu.ink) 可体验以下功能: \n- 🔍 多引擎结果聚合对比 \n- 🌍 支持 30+ 语言实时翻译 \n- 🛡️ 零 Cookie 跟踪的隐私模式 \n- 🎨 主题切换 \n\n---\n### ▍ 进阶优化建议\n1. **反向代理配置** \n\n网上相关教程应该很多这里不再重复,我的SearXNG服务器通过香港VPS反代到公网使用。\n\n2. **定期维护命令** \n```bash\n# 更新搜索引擎引擎列表\ndocker exec -it searxng python -m searx.engines --update\n```\n\n3. **监控指标集成** \n通过 Prometheus 导出搜索统计:\n```bash\ndocker run -d --network=searxng-net \\\n -v \"${PWD}/metrics:/metrics\" \\\n prom/prometheus --config.file=/metrics/searxng.yml\n``` \n\n---\n\n**🚀 立即行动** \n\n快速拥有对抗 SEO 污染的利器,快来打造你的私人搜索门户吧!部署过程遇到问题欢迎联系笔者交流。\n","slug":"searxng","published":1,"updated":"2025-03-10T15:46:55.535Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nu002oyg2bg2efcu5v","content":"

\"SearXNG
厌倦了商业搜索引擎的广告追踪? 受够技术社区被 SEO 污染的水文?希望搜索结果里尽量不要呈现C*DN等平台的低质内容?这款开源元搜索引擎 SearXNG 现在让我直呼真香爱不释手,使用频率远超 Nextcloud 等自建服务。

\n
\n

▍ 为什么选择 SearXNG?

核心优势矩阵

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
特性实现方式用户收益
隐私保护匿名聚合第三方结果无搜索历史追踪
结果去商业化过滤 SEO 优化内容提升技术资料检索效率
多引擎支持集成 Google/Bing/Brave 等 70+ 源规避单一引擎局限
界面定制主题引擎分离设计打造个性化搜索门户
\n

⚠️ 同类工具对比:
此前使用的 luxirty-search 也很不错但是依赖 Google CSE,对于内网环境日常使用多少有点不友好。

\n

之前看了下网上普遍推荐docker方案不过部署流程与官方文档有不少出入而且在我本地没法正常运行,这里我们直接按照官方文档来,事不宜迟直接开始部署。

\n
\n

▍ 极简部署方案

▶ 环境准备

1
2
mkdir -p ~/services/searxng && cd $_  # 随意找个地方创建专用目录
export SEARXNG_PORT=8080 # 设置服务端口
\n\n

▶ 容器化部署

1
2
3
4
5
6
7
8
9
10
# 拉取官方镜像
docker pull searxng/searxng

# 启动容器(推荐绑定持久化配置)
docker run -d --restart=unless-stopped \\
-p ${SEARXNG_PORT}:8080 \\
-v "${PWD}/config:/etc/searxng" \\
-e "BASE_URL=http://your-domain.com" \\
-e "INSTANCE_NAME=PrivateSearch" \\
searxng/searxng
\n

📌 参数说明:

\n
    \n
  • -v 挂载配置文件实现持久化
  • \n
  • --restart 确保服务异常退出后自动重启
  • \n
  • BASE_URL 需替换为实际访问域名,不过也可以后续再配置
  • \n
\n
\n

▍ 高频问题排雷

🔴 镜像拉取失败

现象Error response from daemon: pull access denied
✅ 解决方案:
参考先前的 Overleaf Toolkit 踩坑记录 配置镜像加速源

\n

🔴 Google 频繁拦截

现象Too Many Requests 错误频发

\n

⚠️ 这算是一个玄学问题除了更换代理我还没有发现更好的解决方案

\n

✅ 实战验证方案:
更换你的代理服务器比如笔者目前在🇺🇸🇭🇰🇯🇵均有购买VPS服务器可作为代理,经尝试总会有可以使用的节点。

\n

📌 在配置文件设置代理:

\n
1
2
3
4
outgoing:
proxies:
all://:
- http://<address>:<port>
\n\n
\n

▍ 效果展示与体验

访问我的生产环境实例 search.hifuu.ink 可体验以下功能:

\n
    \n
  • 🔍 多引擎结果聚合对比
  • \n
  • 🌍 支持 30+ 语言实时翻译
  • \n
  • 🛡️ 零 Cookie 跟踪的隐私模式
  • \n
  • 🎨 主题切换
  • \n
\n
\n

▍ 进阶优化建议

    \n
  1. 反向代理配置
  2. \n
\n

网上相关教程应该很多这里不再重复,我的SearXNG服务器通过香港VPS反代到公网使用。

\n
    \n
  1. 定期维护命令

    \n
    1
    2
    # 更新搜索引擎引擎列表
    docker exec -it searxng python -m searx.engines --update
    \n
  2. \n
  3. 监控指标集成
    通过 Prometheus 导出搜索统计:

    \n
    1
    2
    3
    docker run -d --network=searxng-net \\
    -v "${PWD}/metrics:/metrics" \\
    prom/prometheus --config.file=/metrics/searxng.yml
  4. \n
\n
\n

🚀 立即行动

\n

快速拥有对抗 SEO 污染的利器,快来打造你的私人搜索门户吧!部署过程遇到问题欢迎联系笔者交流。

\n","excerpt":"","more":"

\"SearXNG
厌倦了商业搜索引擎的广告追踪? 受够技术社区被 SEO 污染的水文?希望搜索结果里尽量不要呈现C*DN等平台的低质内容?这款开源元搜索引擎 SearXNG 现在让我直呼真香爱不释手,使用频率远超 Nextcloud 等自建服务。

\n
\n

▍ 为什么选择 SearXNG?

核心优势矩阵

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
特性实现方式用户收益
隐私保护匿名聚合第三方结果无搜索历史追踪
结果去商业化过滤 SEO 优化内容提升技术资料检索效率
多引擎支持集成 Google/Bing/Brave 等 70+ 源规避单一引擎局限
界面定制主题引擎分离设计打造个性化搜索门户
\n

⚠️ 同类工具对比:
此前使用的 luxirty-search 也很不错但是依赖 Google CSE,对于内网环境日常使用多少有点不友好。

\n

之前看了下网上普遍推荐docker方案不过部署流程与官方文档有不少出入而且在我本地没法正常运行,这里我们直接按照官方文档来,事不宜迟直接开始部署。

\n
\n

▍ 极简部署方案

▶ 环境准备

1
2
mkdir -p ~/services/searxng && cd $_  # 随意找个地方创建专用目录
export SEARXNG_PORT=8080 # 设置服务端口
\n\n

▶ 容器化部署

1
2
3
4
5
6
7
8
9
10
# 拉取官方镜像
docker pull searxng/searxng

# 启动容器(推荐绑定持久化配置)
docker run -d --restart=unless-stopped \\
-p ${SEARXNG_PORT}:8080 \\
-v "${PWD}/config:/etc/searxng" \\
-e "BASE_URL=http://your-domain.com" \\
-e "INSTANCE_NAME=PrivateSearch" \\
searxng/searxng
\n

📌 参数说明:

\n
    \n
  • -v 挂载配置文件实现持久化
  • \n
  • --restart 确保服务异常退出后自动重启
  • \n
  • BASE_URL 需替换为实际访问域名,不过也可以后续再配置
  • \n
\n
\n

▍ 高频问题排雷

🔴 镜像拉取失败

现象Error response from daemon: pull access denied
✅ 解决方案:
参考先前的 Overleaf Toolkit 踩坑记录 配置镜像加速源

\n

🔴 Google 频繁拦截

现象Too Many Requests 错误频发

\n

⚠️ 这算是一个玄学问题除了更换代理我还没有发现更好的解决方案

\n

✅ 实战验证方案:
更换你的代理服务器比如笔者目前在🇺🇸🇭🇰🇯🇵均有购买VPS服务器可作为代理,经尝试总会有可以使用的节点。

\n

📌 在配置文件设置代理:

\n
1
2
3
4
outgoing:
proxies:
all://:
- http://<address>:<port>
\n\n
\n

▍ 效果展示与体验

访问我的生产环境实例 search.hifuu.ink 可体验以下功能:

\n
    \n
  • 🔍 多引擎结果聚合对比
  • \n
  • 🌍 支持 30+ 语言实时翻译
  • \n
  • 🛡️ 零 Cookie 跟踪的隐私模式
  • \n
  • 🎨 主题切换
  • \n
\n
\n

▍ 进阶优化建议

    \n
  1. 反向代理配置
  2. \n
\n

网上相关教程应该很多这里不再重复,我的SearXNG服务器通过香港VPS反代到公网使用。

\n
    \n
  1. 定期维护命令

    \n
    1
    2
    # 更新搜索引擎引擎列表
    docker exec -it searxng python -m searx.engines --update
    \n
  2. \n
  3. 监控指标集成
    通过 Prometheus 导出搜索统计:

    \n
    1
    2
    3
    docker run -d --network=searxng-net \\
    -v "${PWD}/metrics:/metrics" \\
    prom/prometheus --config.file=/metrics/searxng.yml
  4. \n
\n
\n

🚀 立即行动

\n

快速拥有对抗 SEO 污染的利器,快来打造你的私人搜索门户吧!部署过程遇到问题欢迎联系笔者交流。

\n"},{"title":"命令行编辑器的优雅新选择","date":"2024-12-12T17:40:27.000Z","_content":"\n意外发现Zellij+Helix还挺好用的\n\n避免了vim/neovim的配置流程直接就能上手的轻量命令行开发环境\n\n稍微了解一下二者的快捷键就能舒适码字了\n\n甚至还都是rust出品\n\n即刻尝试一下[Zellij](https://zellij.dev/)和[Helix](https://helix-editor.com/)吧!\n\n// 至于Helix没有文件树显示的方案,反正Helix选择文件挺方便的要文件树无非是希望编辑窗口居于窗口中央,倒是可以用watch和tree命令来代替还能手动设置哪些文件不用显示hhh\n\n![实际效果](/images/zellij-helix.jpg)\n","source":"_posts/zellij-helix.md","raw":"---\ntitle: 命令行编辑器的优雅新选择\ndate: 2024-12-13 01:40:27\ntags: [技术分享]\n---\n\n意外发现Zellij+Helix还挺好用的\n\n避免了vim/neovim的配置流程直接就能上手的轻量命令行开发环境\n\n稍微了解一下二者的快捷键就能舒适码字了\n\n甚至还都是rust出品\n\n即刻尝试一下[Zellij](https://zellij.dev/)和[Helix](https://helix-editor.com/)吧!\n\n// 至于Helix没有文件树显示的方案,反正Helix选择文件挺方便的要文件树无非是希望编辑窗口居于窗口中央,倒是可以用watch和tree命令来代替还能手动设置哪些文件不用显示hhh\n\n![实际效果](/images/zellij-helix.jpg)\n","slug":"zellij-helix","published":1,"updated":"2025-02-23T15:53:44.966Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nv002pyg2b5jjs3brv","content":"

意外发现Zellij+Helix还挺好用的

\n

避免了vim/neovim的配置流程直接就能上手的轻量命令行开发环境

\n

稍微了解一下二者的快捷键就能舒适码字了

\n

甚至还都是rust出品

\n

即刻尝试一下ZellijHelix吧!

\n

// 至于Helix没有文件树显示的方案,反正Helix选择文件挺方便的要文件树无非是希望编辑窗口居于窗口中央,倒是可以用watch和tree命令来代替还能手动设置哪些文件不用显示hhh

\n

\"实际效果\"

\n","excerpt":"","more":"

意外发现Zellij+Helix还挺好用的

\n

避免了vim/neovim的配置流程直接就能上手的轻量命令行开发环境

\n

稍微了解一下二者的快捷键就能舒适码字了

\n

甚至还都是rust出品

\n

即刻尝试一下ZellijHelix吧!

\n

// 至于Helix没有文件树显示的方案,反正Helix选择文件挺方便的要文件树无非是希望编辑窗口居于窗口中央,倒是可以用watch和tree命令来代替还能手动设置哪些文件不用显示hhh

\n

\"实际效果\"

\n"},{"title":"原来我还有个博客","date":"2024-06-02T16:19:55.000Z","_content":"\n2025-02-25\n\n最近整理了一下缓存部署起来方便多了,有空就写点东西记录一下吧~\n","source":"_posts/原来我还有个博客.md","raw":"---\ntitle: 原来我还有个博客\ndate: 2024-06-03 00:19:55\ntags: 日志\n---\n\n2025-02-25\n\n最近整理了一下缓存部署起来方便多了,有空就写点东西记录一下吧~\n","slug":"原来我还有个博客","published":1,"updated":"2025-02-25T04:26:34.224Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nv002ryg2b1pf56fjh","content":"

2025-02-25

\n

最近整理了一下缓存部署起来方便多了,有空就写点东西记录一下吧~

\n","excerpt":"","more":"

2025-02-25

\n

最近整理了一下缓存部署起来方便多了,有空就写点东西记录一下吧~

\n"},{"title":"梦开始的地方","date":"2023-12-06T14:53:35.000Z","_content":"\n不知不觉已经高中毕业快半年了,现在在NUDT的生活还算适应吧,交到了一些很有趣的朋友并且在技术方面取得了一定突破(很期待明年的asc2024呢),今天看到华科一位学长的博客突然想起我还没好好搭建过自己的Blog,于是心血来潮搞了下我的Github Pages。\n\n今后会在这里更新记录自己的生活,学习,工作,以及一些想法,希望能坚持下来吧。\n\n> P.S.最近我都经历了些什么:\n>\n> 1. 时长21天军训,认识了一群很可爱的班长(尤其是负责我们5班6班的英子),初步和队里的同学了解;\n> 2. 学习了一些C++基础,在洛谷上刷了不少算法题;\n> 3. ACM招新赛被薄纱,差一题进入校队;\n> 4. 对算法竞赛感到疑惑,尝试学习Flutter和操作系统开阔视野;\n> 5. 入坑战地,爽爽爽;\n> 6. 被一位巨强的学长发掘,加入NUDT超算队;\n> 7. 面临三个考试周,熬过去就是胜利;\n\n虽然天天早八满课很不爽,但是平时还是可以学习自己想学的技术周末也可以打游戏感觉还行吧,我还是相信NUDT,既来之则安之。\n\n这里,毕竟是我梦开始的地方。\n\n> 胸怀祖国,团结协作,志在高峰,奋勇拼搏!\n\n今天是2023年12月6日,加油!\n\n------------------------\n\n修改一下,有的话还是不适合明说哦\n\n5YK75a2p5a2Q5Lus77yM5b+r6YCD5ZWK77yB77yB77yBCg==\n\n","source":"_posts/梦开始的地方.md","raw":"---\ntitle: 梦开始的地方\ndate: 2023-12-06 22:53:35\ntags: 日志\n---\n\n不知不觉已经高中毕业快半年了,现在在NUDT的生活还算适应吧,交到了一些很有趣的朋友并且在技术方面取得了一定突破(很期待明年的asc2024呢),今天看到华科一位学长的博客突然想起我还没好好搭建过自己的Blog,于是心血来潮搞了下我的Github Pages。\n\n今后会在这里更新记录自己的生活,学习,工作,以及一些想法,希望能坚持下来吧。\n\n> P.S.最近我都经历了些什么:\n>\n> 1. 时长21天军训,认识了一群很可爱的班长(尤其是负责我们5班6班的英子),初步和队里的同学了解;\n> 2. 学习了一些C++基础,在洛谷上刷了不少算法题;\n> 3. ACM招新赛被薄纱,差一题进入校队;\n> 4. 对算法竞赛感到疑惑,尝试学习Flutter和操作系统开阔视野;\n> 5. 入坑战地,爽爽爽;\n> 6. 被一位巨强的学长发掘,加入NUDT超算队;\n> 7. 面临三个考试周,熬过去就是胜利;\n\n虽然天天早八满课很不爽,但是平时还是可以学习自己想学的技术周末也可以打游戏感觉还行吧,我还是相信NUDT,既来之则安之。\n\n这里,毕竟是我梦开始的地方。\n\n> 胸怀祖国,团结协作,志在高峰,奋勇拼搏!\n\n今天是2023年12月6日,加油!\n\n------------------------\n\n修改一下,有的话还是不适合明说哦\n\n5YK75a2p5a2Q5Lus77yM5b+r6YCD5ZWK77yB77yB77yBCg==\n\n","slug":"梦开始的地方","published":1,"updated":"2025-02-25T04:25:44.687Z","comments":1,"layout":"post","photos":[],"_id":"cmkjeg2nv002tyg2b78skbevn","content":"

不知不觉已经高中毕业快半年了,现在在NUDT的生活还算适应吧,交到了一些很有趣的朋友并且在技术方面取得了一定突破(很期待明年的asc2024呢),今天看到华科一位学长的博客突然想起我还没好好搭建过自己的Blog,于是心血来潮搞了下我的Github Pages。

\n

今后会在这里更新记录自己的生活,学习,工作,以及一些想法,希望能坚持下来吧。

\n
\n

P.S.最近我都经历了些什么:

\n
    \n
  1. 时长21天军训,认识了一群很可爱的班长(尤其是负责我们5班6班的英子),初步和队里的同学了解;
  2. \n
  3. 学习了一些C++基础,在洛谷上刷了不少算法题;
  4. \n
  5. ACM招新赛被薄纱,差一题进入校队;
  6. \n
  7. 对算法竞赛感到疑惑,尝试学习Flutter和操作系统开阔视野;
  8. \n
  9. 入坑战地,爽爽爽;
  10. \n
  11. 被一位巨强的学长发掘,加入NUDT超算队;
  12. \n
  13. 面临三个考试周,熬过去就是胜利;
  14. \n
\n
\n

虽然天天早八满课很不爽,但是平时还是可以学习自己想学的技术周末也可以打游戏感觉还行吧,我还是相信NUDT,既来之则安之。

\n

这里,毕竟是我梦开始的地方。

\n
\n

胸怀祖国,团结协作,志在高峰,奋勇拼搏!

\n
\n

今天是2023年12月6日,加油!

\n
\n

修改一下,有的话还是不适合明说哦

\n

5YK75a2p5a2Q5Lus77yM5b+r6YCD5ZWK77yB77yB77yBCg==

\n","excerpt":"","more":"

不知不觉已经高中毕业快半年了,现在在NUDT的生活还算适应吧,交到了一些很有趣的朋友并且在技术方面取得了一定突破(很期待明年的asc2024呢),今天看到华科一位学长的博客突然想起我还没好好搭建过自己的Blog,于是心血来潮搞了下我的Github Pages。

\n

今后会在这里更新记录自己的生活,学习,工作,以及一些想法,希望能坚持下来吧。

\n
\n

P.S.最近我都经历了些什么:

\n
    \n
  1. 时长21天军训,认识了一群很可爱的班长(尤其是负责我们5班6班的英子),初步和队里的同学了解;
  2. \n
  3. 学习了一些C++基础,在洛谷上刷了不少算法题;
  4. \n
  5. ACM招新赛被薄纱,差一题进入校队;
  6. \n
  7. 对算法竞赛感到疑惑,尝试学习Flutter和操作系统开阔视野;
  8. \n
  9. 入坑战地,爽爽爽;
  10. \n
  11. 被一位巨强的学长发掘,加入NUDT超算队;
  12. \n
  13. 面临三个考试周,熬过去就是胜利;
  14. \n
\n
\n

虽然天天早八满课很不爽,但是平时还是可以学习自己想学的技术周末也可以打游戏感觉还行吧,我还是相信NUDT,既来之则安之。

\n

这里,毕竟是我梦开始的地方。

\n
\n

胸怀祖国,团结协作,志在高峰,奋勇拼搏!

\n
\n

今天是2023年12月6日,加油!

\n
\n

修改一下,有的话还是不适合明说哦

\n

5YK75a2p5a2Q5Lus77yM5b+r6YCD5ZWK77yB77yB77yBCg==

\n"},{"title":"其作始也简,其将毕也必巨 - 回顾64-Bit Brainstorm三年ASC征程","date":"2026-05-30T02:10:31.000Z","_content":"\n我的最后一次ASC之旅终于结束了,回望过去如今我想表达的唯有感激,不仅是对与我并肩作战的队友,还有参赛过程中其他战队的朋友们,以及辛苦付出的组委会老师们。正好借着这次机会,我也想回顾一下这三年来的ASC参赛经历,分享一些个人的感悟和体会。\n\n![充满回忆的ASC照片墙](/images/MyASCStory/MyASCStory.jpg)\n\n### ASC24:从零开始的挑战\n\n2023年年末,一位在网上认识的神秘学长lzd突然发给我一张招新海报,说是有个非常好玩的比赛正在进行校队招新赛让我也来试试,结果这一试直接进入了64-bit Brainstorm战队并开启了我长达三年的ASC之旅。\n\n进入超算队后我听从学长的建议花了一个多月去自学MPI和CUDA编程。经过一段时间学习准备,ASC24的预赛赛题发布了,根据任务安排,我和另一位学长hxh分配到了OpenCAEPoro这个多相多组分渗流模拟科学计算程序的优化任务。依稀记得当时因为这个程序本身依赖较多还要手动构建不少软件导致我们花了三天才成功把这个程序的baseline跑起来,这让我第一次真正接触到了科学计算程序的复杂性和挑战性。之后我们又花了数周时间对这个程序进行了性能分析和优化,xh学长负责算法优化进行了一些手动循环展开和向量化以及一些算子库的高性能版本替代,我则负责一些算子的GPU移植工作并提供了快速fallback回CPU的宏控开关以防万一,预赛阶段我们最终实现的加速比只有1.3x不过好在也顺利进入了决赛。\n\n![ASC24决赛现场](/images/MyASCStory/ASC24-1.jpg)\n\n决赛阶段是我最难忘的部分,我第一次见识到了完整的小型集群搭建维护流程,lzd学长简直像个超人一样什么都懂,他和cyq学长双人完成了整个集群的部署,系统方面有什么问题找他都能解决,功耗过高他也有各种办法来控制,真是太厉害了。终于到了OpenCAEPoro测例发布这天,决赛case可以说相当刁钻记得当时很多团队都卡在第一个测例无法运行,后面几个测例由于规模远远超过预赛测例导致我们的GPU版本直接崩溃,危急情况下,我和xh学长迅速做出决定马上fallback回CPU版本把结果运行出来,在运行后几个测例的同时,我们也在空闲节点排查第一个测例的问题,结果发现只要把我们选用的nvhpc编译链完全回退到gcc就能正确成功运行第一个测例了。最后结果也是非常出人意料,我们只是把所有测例运行完了实际没有特别明显的优化却在这道题上取得了第二名的成绩,通过赛后交流才知道有些队伍虽然有很快的加速比,但是因为精度无法通过所以没能拿到相应的分数。\n\n![ASC24赛后合影](/images/MyASCStory/ASC24-2.jpg)\n\n这一年我们的总成绩是rank 5,是历年来最好的一次,大家都非常开心,晚宴阶段我也认识了非常多其他学校的新朋友并约定来年再见。值得一提的是,这一年由于AI工具还没发展起来,不管是程序排错还是性能优化我们都还是古法处理,现在身处Agent时代回想起来那段人工手动处理问题的经历真的是非常不易。\n\n### ASC25:遭遇挫折的成长\n\n时间转眼来到2024下半年,在新学期的战队招新赛中我们又迎来了三位优秀的新队员分别是wlx, yx和zqr。此时lzd学长面临毕业,由hxh和我领衔和三位新队员组建了一支新的超算团队出征ASC25。这一年我正值大二课业最为繁重的时候,因此在ASC25赛题发布后我被分配到的任务只有集群运维和HPL/HPCG基准测试工作。为了尽可能更多地参与贡献,我在课余时间也耗费了不少精力研究系统调优并产生了一些自以为不错的方案,结果却是埋下了非常深的隐患让我赛后十分后悔。\n\n这一年我给集群选用的发行版是Intel出品的Clear Linux(非常戏谑的是ASC25结束后不久Intel就宣布终止此项目并关闭了Clear Linux官网),当时我由于网络上的一些宣传过于迷信这个发行版的性能表现却忽视了其兼容性带来的问题,预赛阶段我们的机时大部分分给了AlphaFold和m5c两个赛题,直到最后才进行HPL/HPCG测试,这时候我才发现Clear Linux软件源里提供的IB驱动和nvidia-peermem模块完全不兼容并且没有办法修复,而Nvidia官方的IB驱动也不支持Clear Linux无法在这个发行版上安装,为了解决这个问题我最终配置了Rocky + Clear这个非常抽象的双系统方案,虽然最终解决了兼容性问题但也为队友们的工作造成了不少麻烦。好在队友都非常给力,至少在预赛阶段我们的AlphaFold和m5c赛题都有亮眼表现,以不错的成绩再次进入了决赛。\n\n![ASC25来到美丽的青海大学](/images/MyASCStory/ASC25-2.jpg)\n\n来到青海大学,我们从一开始就各种问题不断,先是装完机发现IB起不来,排查了快一个小时发现是Clear Linux起不了opensm服务只能单开一个节点用Rocky Linux启动。而后又是HPL测试结果一直异常,我们只能用双机跑出正常结果超过双机就会性能暴跌。最后更严重的是功耗不知为何完全压不下来即便所有核心都调整频率至最低。结果就是决赛正式运行阶段的两天我们整个过程都非常崩溃,虽然有五个节点但是大部分赛题我们都只能用三节点来跑才能控制功耗不会超出4KW限制。赛后我感到非常自责,觉得自己在系统选择和功耗控制的失误给整个团队带来了很大的麻烦和压力。\n\n![ASC25令我们绝望的现场](/images/MyASCStory/ASC25-3.jpg)\n\n这一年是我们表现最差的一年排名直接倒数,除了wlx负责的AlphaFold表现不错之外其他赛题得分都非常惨淡。好在队友们都非常理解和包容我,原本我打算这一年就从超算队退役,但是带着这份歉意我还是决定明年ASC26干回来争取用一个更好的结果来弥补。在AI工具方面,相比去年这一年的不同是我们在网上发现了一个叫Deepseek V2.5的国产大语言模型,其性能丝毫不逊于同期国外的大模型,我们第一次感受到了AI工具在报错分析和代码优化方面的巨大帮助。\n\n![整理心情备战ASC26](/images/MyASCStory/ASC25-4.jpg)\n\n### ASC26:Agent时代的机遇\n\n终于来到了2025年下半年,我们在新学期的战队招新中又迎来了三位新队员分别是和我同级的dyz,计算机学院大一的qhy以及电子科学学院大二的chb。这一年是我们人力资源最为丰富的一年,lzd回归指导超算队,hxh和wlx也分别被清华大学和上海交大录取,他们作为外援继续参与到超算队的工作中,同时我们另一支参加ISC的超算队也提供了不少帮助。\n\n![我们的ASC26全员合影](/images/MyASCStory/ASC26-1.jpg)\n\n这一次我的工作依旧包括系统运维和基准测试,同时还和chb一起负责了AMSS-NCKU数值相对论模拟程序的优化工作。在系统方面由于去年的经验教训我直接选择了Rocky Linux 10作为我们的操作系统,它在兼容性和稳定性方面都非常可靠没有问题。而在程序优化方面,Agent时代的到来也对我们的工作流带来了前所未有的改变,我们可以把精力更多的放在性能分析和优化思路的探索上,具体的实现细节则可以交给AI工具来完成,这大大提高了我们的效率和创新能力。在AMSS-NCKU的优化过程中,我们通过perf和vtune等工具分析了程序的性能瓶颈,发现问题主要集中在通信和访存上,而这些问题最终又主要由低效的插值函数引起,于是在预赛阶段我们针对MPI和polint相关函数做了充分的针对性优化,包括选用点对点通信和异步通信,以及插值方面调换遍历顺序和提供快路径等,最终在预赛阶段对于GW150914测例我们的每步耗时从基准14s降低到了3s,取得了非常显著的加速效果。同时在世界模型赛题上dyz和qhy也表现良好,我们得以第三次闯入决赛来到无锡学院与各路优秀的同仁汇合继续切磋交流。\n\n进入决赛阶段,我们在AMSS-NCKU的优化工作继续进行,但是当我们继续调整优化将步时压缩到了2.6s左右之后却陷入了一个瓶颈,即接下来我们无论再改动哪里耗时都不会降低了并且还会负优化,这对我和chb造成了巨大的困扰,接近两个礼拜我们都没有任何进展,并且由于没有明显进展汇报我们还不断被无理怀疑骄傲自满,躺平摆烂以及没有理解赛题,好在有学长的鼓励支持我还是坚持了下来尽量忽略掉不合理的外部干扰继续推进工作,最终我和chb决定推翻原本思路基于现在的CPU版本进行完整的GPU重构,虽然这个决定工作量巨大但是我们觉得这是唯一有可能突破瓶颈的办法。AMSS程序本身有着大量参数支持,为了充分准备决赛测例我们自费了大量token来利用Agent进行移植工作。移植初期由于效果不明显我们又被外部质疑找错了方向,然而我和chb都对我们的GPU方案很有信心知道还有大量优化空间,最终我们在前往无锡前取得了巨大突破,GPU版本的每步耗时从2.6s降低到了0.8s多同时还支持多种参数输入,满怀着信心和期待,我们踏上了前往无锡的旅程。\n\n来到无锡学院,相比去年一切都显得有条不紊尽在掌握,我利用clonezilla顺利完成了现场四节点装机并且功耗也完全可控不像去年难以控制,测试阶段每个赛题都进行了充分测试也不像去年磕磕绊绊。然而在正赛阶段却没有我们预期的那么容易。首先是硬件因素带来的劣势,导致我们在HPL/HPCG,UnifoLM-WMA和AMSS-NCKU上基本丧失了竞争力;同时由于大家都有了Agent加持,每个赛题都会出现超快版本导致很多团队只能拿到基础分数,这在过去是完全不可想象的,从前的ASC哪怕优化做得没那么好,但是只要能把所有测例运行完都能拿到不错的分数,而现在只要优化水平不在第一梯队就基本拿不到分数。最终我们大概除了AMSS以外其他赛题全都没能超过10分,也没能突破ASC24的成绩记录。\n\n![难忘今宵,有缘再会](/images/MyASCStory/ASC26-2.jpg)\n\nASC26的结果对我们而言并不完美,但是其经历依然非常宝贵。ASC的意义从来都不是最终排名,而是在优化过程中的技术积累,比赛过程中的团队协作,以及赛后交流中的友谊建立,这些都是无价的财富。通过这三年的ASC经历,我不仅提升了自己的技术能力,更重要的是结识了一群志同道合的朋友,收获了许多珍贵的回忆和经验。与此同时我们也充分认识到了自己和其他优秀团队之间的差距,这些差距既有技术层面的也有资源层面的,它们都为我们提供了未来努力的方向和动力。\n\n最后,我想对所有支持和帮助过我们的人表达最诚挚的感谢。首先是我的队友们,与你们共事让我感到无比幸运和自豪,我从未觉得上大学开心过,除了在超算队和你们一起工作的时候。其次是我在其他高校超算队尤其是齐鲁工业大学,福州大学,西北工业大学,山西大学认识的朋友们,与你们的交流和切磋让我受益匪浅,也让我感受到了ASC赛事的温暖和活力。最后是组委会的老师们,你们的辛勤付出和专业支持为大家提供了一个展示自我和提升自我的平台,非常感激你们的努力。闭幕式结束离开时碰到两位组委会老师还向我们热情告别希望明年再见,口头上我们答应了下来然而遗憾的是明年我们都要奔赴新的前程已经难以重回ASC27赛场了,但愿将来还能有机会以非参赛队员的身份继续参与到ASC的活动中来。\n\n![我们的最后一张ASC合影](/images/MyASCStory/ASC26-3.jpg)\n\n虽然我们的ASC之旅已经结束了,但我相信在这个过程中建立的友谊和积累的经验将会伴随我们继续前行。在此衷心祝愿ASC赛事越办越好,培养出更多优秀的超算人才,也祝愿每一位为ASC挥洒汗水的同学都能在未来的道路上取得更大的成功!\n\n---\n\n### 彩蛋环节\n\n感觉还是有很多比较有意义的照片,可惜受ASC投稿篇幅限制不好放在正文里,就统一放在这里留念吧,以后说不定还会更新,先放下面这些。\n\n![ASC24初代最好的五个人](/images/MyASCStory/ASC24-4.jpg)\n\n![正在进行ISC答辩的超人lzd](/images/MyASCStory/lzdnb.jpg)\n\n![ASC24超级团队赛合影](/images/MyASCStory/ASC24-6.jpg)\n\n![ASC24晚宴合影](/images/MyASCStory/ASC24-5.jpg)\n\n![ASC25秘密基地](/images/MyASCStory/ASC25-1.jpg)\n\n![ASC25决赛机柜,恋恋可爱捏~](/images/MyASCStory/ASC25cluster.jpg)\n\n![ASC25高地队长黄主任我们的超人](/images/MyASCStory/ASC25-5.jpg)\n\n![ASC25青海大学的飘絮](/images/MyASCStory/ASC25-7.jpg)\n\n![ASC26决赛机柜,灵梦可爱捏~](/images/MyASCStory/ASC26cluster.jpg)\n\n![什么时候才能届到呢](/images/MyASCStory/ASC26YeZ.jpg)\n","source":"_posts/One-Last-ASC.md","raw":"---\ntitle: 其作始也简,其将毕也必巨 - 回顾64-bit Brainstorm三年ASC征程\ndate: 2026-05-30 10:10:31\ntags: [竞赛, 生活, 学习, ASC]\n---\n\n我的最后一次ASC之旅终于结束了,回望过去如今我想表达的唯有感激,不仅是对与我并肩作战的队友,还有参赛过程中其他战队的朋友们,以及辛苦付出的组委会老师们。正好借着这次机会,我也想回顾一下这三年来的ASC参赛经历,分享一些个人的感悟和体会。\n\n![充满回忆的ASC照片墙](/images/MyASCStory/MyASCStory.jpg)\n\n### ASC24:从零开始的挑战\n\n2023年年末,一位在网上认识的神秘学长lzd突然发给我一张招新海报,说是有个非常好玩的比赛正在进行校队招新赛让我也来试试,结果这一试直接进入了64-bit Brainstorm战队并开启了我长达三年的ASC之旅。\n\n进入超算队后我听从学长的建议花了一个多月去自学MPI和CUDA编程。经过一段时间学习准备,ASC24的预赛赛题发布了,根据任务安排,我和另一位学长hxh分配到了OpenCAEPoro这个多相多组分渗流模拟科学计算程序的优化任务。依稀记得当时因为这个程序本身依赖较多还要手动构建不少软件导致我们花了三天才成功把这个程序的baseline跑起来,这让我第一次真正接触到了科学计算程序的复杂性和挑战性。之后我们又花了数周时间对这个程序进行了性能分析和优化,xh学长负责算法优化进行了一些手动循环展开和向量化以及一些算子库的高性能版本替代,我则负责一些算子的GPU移植工作并提供了快速fallback回CPU的宏控开关以防万一,预赛阶段我们最终实现的加速比只有1.3x不过好在也顺利进入了决赛。\n\n![ASC24决赛现场](/images/MyASCStory/ASC24-1.jpg)\n\n决赛阶段是我最难忘的部分,我第一次见识到了完整的小型集群搭建维护流程,lzd学长简直像个超人一样什么都懂,他和cyq学长双人完成了整个集群的部署,系统方面有什么问题找他都能解决,功耗过高他也有各种办法来控制,真是太厉害了。终于到了OpenCAEPoro测例发布这天,决赛case可以说相当刁钻记得当时很多团队都卡在第一个测例无法运行,后面几个测例由于规模远远超过预赛测例导致我们的GPU版本直接崩溃,危急情况下,我和xh学长迅速做出决定马上fallback回CPU版本把结果运行出来,在运行后几个测例的同时,我们也在空闲节点排查第一个测例的问题,结果发现只要把我们选用的nvhpc编译链完全回退到gcc就能正确成功运行第一个测例了。最后结果也是非常出人意料,我们只是把所有测例运行完了实际没有特别明显的优化却在这道题上取得了第二名的成绩,通过赛后交流才知道有些队伍虽然有很快的加速比,但是因为精度无法通过所以没能拿到相应的分数。\n\n![ASC24赛后合影](/images/MyASCStory/ASC24-2.jpg)\n\n这一年我们的总成绩是rank 5,是历年来最好的一次,大家都非常开心,晚宴阶段我也认识了非常多其他学校的新朋友并约定来年再见。值得一提的是,这一年由于AI工具还没发展起来,不管是程序排错还是性能优化我们都还是古法处理,现在身处Agent时代回想起来那段人工手动处理问题的经历真的是非常不易。\n\n### ASC25:遭遇挫折的成长\n\n时间转眼来到2024下半年,在新学期的战队招新赛中我们又迎来了三位优秀的新队员分别是wlx, yx和zqr。此时lzd学长面临毕业,由hxh和我领衔和三位新队员组建了一支新的超算团队出征ASC25。这一年我正值大二课业最为繁重的时候,因此在ASC25赛题发布后我被分配到的任务只有集群运维和HPL/HPCG基准测试工作。为了尽可能更多地参与贡献,我在课余时间也耗费了不少精力研究系统调优并产生了一些自以为不错的方案,结果却是埋下了非常深的隐患让我赛后十分后悔。\n\n这一年我给集群选用的发行版是Intel出品的Clear Linux(非常戏谑的是ASC25结束后不久Intel就宣布终止此项目并关闭了Clear Linux官网),当时我由于网络上的一些宣传过于迷信这个发行版的性能表现却忽视了其兼容性带来的问题,预赛阶段我们的机时大部分分给了AlphaFold和m5c两个赛题,直到最后才进行HPL/HPCG测试,这时候我才发现Clear Linux软件源里提供的IB驱动和nvidia-peermem模块完全不兼容并且没有办法修复,而Nvidia官方的IB驱动也不支持Clear Linux无法在这个发行版上安装,为了解决这个问题我最终配置了Rocky + Clear这个非常抽象的双系统方案,虽然最终解决了兼容性问题但也为队友们的工作造成了不少麻烦。好在队友都非常给力,至少在预赛阶段我们的AlphaFold和m5c赛题都有亮眼表现,以不错的成绩再次进入了决赛。\n\n![ASC25来到美丽的青海大学](/images/MyASCStory/ASC25-2.jpg)\n\n来到青海大学,我们从一开始就各种问题不断,先是装完机发现IB起不来,排查了快一个小时发现是Clear Linux起不了opensm服务只能单开一个节点用Rocky Linux启动。而后又是HPL测试结果一直异常,我们只能用双机跑出正常结果超过双机就会性能暴跌。最后更严重的是功耗不知为何完全压不下来即便所有核心都调整频率至最低。结果就是决赛正式运行阶段的两天我们整个过程都非常崩溃,虽然有五个节点但是大部分赛题我们都只能用三节点来跑才能控制功耗不会超出4KW限制。赛后我感到非常自责,觉得自己在系统选择和功耗控制的失误给整个团队带来了很大的麻烦和压力。\n\n![ASC25令我们绝望的现场](/images/MyASCStory/ASC25-3.jpg)\n\n这一年是我们表现最差的一年排名直接倒数,除了wlx负责的AlphaFold表现不错之外其他赛题得分都非常惨淡。好在队友们都非常理解和包容我,原本我打算这一年就从超算队退役,但是带着这份歉意我还是决定明年ASC26干回来争取用一个更好的结果来弥补。在AI工具方面,相比去年这一年的不同是我们在网上发现了一个叫Deepseek V2.5的国产大语言模型,其性能丝毫不逊于同期国外的大模型,我们第一次感受到了AI工具在报错分析和代码优化方面的巨大帮助。\n\n![整理心情备战ASC26](/images/MyASCStory/ASC25-4.jpg)\n\n### ASC26:Agent时代的机遇\n\n终于来到了2025年下半年,我们在新学期的战队招新中又迎来了三位新队员分别是和我同级的dyz,计算机学院大一的qhy以及电子科学学院大二的chb。这一年是我们人力资源最为丰富的一年,lzd回归指导超算队,hxh和wlx也分别被清华大学和上海交大录取,他们作为外援继续参与到超算队的工作中,同时我们另一支参加ISC的超算队也提供了不少帮助。\n\n![我们的ASC26全员合影](/images/MyASCStory/ASC26-1.jpg)\n\n这一次我的工作依旧包括系统运维和基准测试,同时还和chb一起负责了AMSS-NCKU数值相对论模拟程序的优化工作。在系统方面由于去年的经验教训我直接选择了Rocky Linux 10作为我们的操作系统,它在兼容性和稳定性方面都非常可靠没有问题。而在程序优化方面,Agent时代的到来也对我们的工作流带来了前所未有的改变,我们可以把精力更多的放在性能分析和优化思路的探索上,具体的实现细节则可以交给AI工具来完成,这大大提高了我们的效率和创新能力。在AMSS-NCKU的优化过程中,我们通过perf和vtune等工具分析了程序的性能瓶颈,发现问题主要集中在通信和访存上,而这些问题最终又主要由低效的插值函数引起,于是在预赛阶段我们针对MPI和polint相关函数做了充分的针对性优化,包括选用点对点通信和异步通信,以及插值方面调换遍历顺序和提供快路径等,最终在预赛阶段对于GW150914测例我们的每步耗时从基准14s降低到了3s,取得了非常显著的加速效果。同时在世界模型赛题上dyz和qhy也表现良好,我们得以第三次闯入决赛来到无锡学院与各路优秀的同仁汇合继续切磋交流。\n\n进入决赛阶段,我们在AMSS-NCKU的优化工作继续进行,但是当我们继续调整优化将步时压缩到了2.6s左右之后却陷入了一个瓶颈,即接下来我们无论再改动哪里耗时都不会降低了并且还会负优化,这对我和chb造成了巨大的困扰,接近两个礼拜我们都没有任何进展,并且由于没有明显进展汇报我们还不断被无理怀疑骄傲自满,躺平摆烂以及没有理解赛题,好在有学长的鼓励支持我还是坚持了下来尽量忽略掉不合理的外部干扰继续推进工作,最终我和chb决定推翻原本思路基于现在的CPU版本进行完整的GPU重构,虽然这个决定工作量巨大但是我们觉得这是唯一有可能突破瓶颈的办法。AMSS程序本身有着大量参数支持,为了充分准备决赛测例我们自费了大量token来利用Agent进行移植工作。移植初期由于效果不明显我们又被外部质疑找错了方向,然而我和chb都对我们的GPU方案很有信心知道还有大量优化空间,最终我们在前往无锡前取得了巨大突破,GPU版本的每步耗时从2.6s降低到了0.8s多同时还支持多种参数输入,满怀着信心和期待,我们踏上了前往无锡的旅程。\n\n来到无锡学院,相比去年一切都显得有条不紊尽在掌握,我利用clonezilla顺利完成了现场四节点装机并且功耗也完全可控不像去年难以控制,测试阶段每个赛题都进行了充分测试也不像去年磕磕绊绊。然而在正赛阶段却没有我们预期的那么容易。首先是硬件因素带来的劣势,导致我们在HPL/HPCG,UnifoLM-WMA和AMSS-NCKU上基本丧失了竞争力;同时由于大家都有了Agent加持,每个赛题都会出现超快版本导致很多团队只能拿到基础分数,这在过去是完全不可想象的,从前的ASC哪怕优化做得没那么好,但是只要能把所有测例运行完都能拿到不错的分数,而现在只要优化水平不在第一梯队就基本拿不到分数。最终我们大概除了AMSS以外其他赛题全都没能超过10分,也没能突破ASC24的成绩记录。\n\n![难忘今宵,有缘再会](/images/MyASCStory/ASC26-2.jpg)\n\nASC26的结果对我们而言并不完美,但是其经历依然非常宝贵。ASC的意义从来都不是最终排名,而是在优化过程中的技术积累,比赛过程中的团队协作,以及赛后交流中的友谊建立,这些都是无价的财富。通过这三年的ASC经历,我不仅提升了自己的技术能力,更重要的是结识了一群志同道合的朋友,收获了许多珍贵的回忆和经验。与此同时我们也充分认识到了自己和其他优秀团队之间的差距,这些差距既有技术层面的也有资源层面的,它们都为我们提供了未来努力的方向和动力。\n\n最后,我想对所有支持和帮助过我们的人表达最诚挚的感谢。首先是我的队友们,与你们共事让我感到无比幸运和自豪,我从未觉得上大学开心过,除了在超算队和你们一起工作的时候。其次是我在其他高校超算队尤其是齐鲁工业大学,福州大学,西北工业大学,山西大学认识的朋友们,与你们的交流和切磋让我受益匪浅,也让我感受到了ASC赛事的温暖和活力。最后是组委会的老师们,你们的辛勤付出和专业支持为大家提供了一个展示自我和提升自我的平台,非常感激你们的努力。闭幕式结束离开时碰到两位组委会老师还向我们热情告别希望明年再见,口头上我们答应了下来然而遗憾的是明年我们都要奔赴新的前程已经难以重回ASC27赛场了,但愿将来还能有机会以非参赛队员的身份继续参与到ASC的活动中来。\n\n![我们的最后一张ASC合影](/images/MyASCStory/ASC26-3.jpg)\n\n虽然我们的ASC之旅已经结束了,但我相信在这个过程中建立的友谊和积累的经验将会伴随我们继续前行。在此衷心祝愿ASC赛事越办越好,培养出更多优秀的超算人才,也祝愿每一位为ASC挥洒汗水的同学都能在未来的道路上取得更大的成功!\n\n---\n\n### 彩蛋环节\n\n感觉还是有很多比较有意义的照片,可惜受ASC投稿篇幅限制不好放在正文里,就统一放在这里留念吧,以后说不定还会更新,先放下面这些。\n\n![ASC24初代最好的五个人](/images/MyASCStory/ASC24-4.jpg)\n\n![正在进行ISC答辩的超人lzd](/images/MyASCStory/lzdnb.jpg)\n\n![ASC24超级团队赛合影](/images/MyASCStory/ASC24-6.jpg)\n\n![ASC24晚宴合影](/images/MyASCStory/ASC24-5.jpg)\n\n![ASC25秘密基地](/images/MyASCStory/ASC25-1.jpg)\n\n![ASC25决赛机柜,恋恋可爱捏~](/images/MyASCStory/ASC25cluster.jpg)\n\n![ASC25高地队长黄主任我们的超人](/images/MyASCStory/ASC25-5.jpg)\n\n![ASC25青海大学的飘絮](/images/MyASCStory/ASC25-7.jpg)\n\n![ASC26决赛机柜,灵梦可爱捏~](/images/MyASCStory/ASC26cluster.jpg)\n\n![什么时候才能届到呢](/images/MyASCStory/ASC26YeZ.jpg)\n","slug":"One-Last-ASC","published":1,"updated":"2026-06-03T02:47:37.305Z","_id":"cmpscr5y00000pwd04rqlcxdw","comments":1,"layout":"post","photos":[],"content":"

我的最后一次ASC之旅终于结束了,回望过去如今我想表达的唯有感激,不仅是对与我并肩作战的队友,还有参赛过程中其他战队的朋友们,以及辛苦付出的组委会老师们。正好借着这次机会,我也想回顾一下这三年来的ASC参赛经历,分享一些个人的感悟和体会。

\n

\"充满回忆的ASC照片墙\"

\n

ASC24:从零开始的挑战

2023年年末,一位在网上认识的神秘学长lzd突然发给我一张招新海报,说是有个非常好玩的比赛正在进行校队招新赛让我也来试试,结果这一试直接进入了64-bit Brainstorm战队并开启了我长达三年的ASC之旅。

\n

进入超算队后我听从学长的建议花了一个多月去自学MPI和CUDA编程。经过一段时间学习准备,ASC24的预赛赛题发布了,根据任务安排,我和另一位学长hxh分配到了OpenCAEPoro这个多相多组分渗流模拟科学计算程序的优化任务。依稀记得当时因为这个程序本身依赖较多还要手动构建不少软件导致我们花了三天才成功把这个程序的baseline跑起来,这让我第一次真正接触到了科学计算程序的复杂性和挑战性。之后我们又花了数周时间对这个程序进行了性能分析和优化,xh学长负责算法优化进行了一些手动循环展开和向量化以及一些算子库的高性能版本替代,我则负责一些算子的GPU移植工作并提供了快速fallback回CPU的宏控开关以防万一,预赛阶段我们最终实现的加速比只有1.3x不过好在也顺利进入了决赛。

\n

\"ASC24决赛现场\"

\n

决赛阶段是我最难忘的部分,我第一次见识到了完整的小型集群搭建维护流程,lzd学长简直像个超人一样什么都懂,他和cyq学长双人完成了整个集群的部署,系统方面有什么问题找他都能解决,功耗过高他也有各种办法来控制,真是太厉害了。终于到了OpenCAEPoro测例发布这天,决赛case可以说相当刁钻记得当时很多团队都卡在第一个测例无法运行,后面几个测例由于规模远远超过预赛测例导致我们的GPU版本直接崩溃,危急情况下,我和xh学长迅速做出决定马上fallback回CPU版本把结果运行出来,在运行后几个测例的同时,我们也在空闲节点排查第一个测例的问题,结果发现只要把我们选用的nvhpc编译链完全回退到gcc就能正确成功运行第一个测例了。最后结果也是非常出人意料,我们只是把所有测例运行完了实际没有特别明显的优化却在这道题上取得了第二名的成绩,通过赛后交流才知道有些队伍虽然有很快的加速比,但是因为精度无法通过所以没能拿到相应的分数。

\n

\"ASC24赛后合影\"

\n

这一年我们的总成绩是rank 5,是历年来最好的一次,大家都非常开心,晚宴阶段我也认识了非常多其他学校的新朋友并约定来年再见。值得一提的是,这一年由于AI工具还没发展起来,不管是程序排错还是性能优化我们都还是古法处理,现在身处Agent时代回想起来那段人工手动处理问题的经历真的是非常不易。

\n

ASC25:遭遇挫折的成长

时间转眼来到2024下半年,在新学期的战队招新赛中我们又迎来了三位优秀的新队员分别是wlx, yx和zqr。此时lzd学长面临毕业,由hxh和我领衔和三位新队员组建了一支新的超算团队出征ASC25。这一年我正值大二课业最为繁重的时候,因此在ASC25赛题发布后我被分配到的任务只有集群运维和HPL/HPCG基准测试工作。为了尽可能更多地参与贡献,我在课余时间也耗费了不少精力研究系统调优并产生了一些自以为不错的方案,结果却是埋下了非常深的隐患让我赛后十分后悔。

\n

这一年我给集群选用的发行版是Intel出品的Clear Linux(非常戏谑的是ASC25结束后不久Intel就宣布终止此项目并关闭了Clear Linux官网),当时我由于网络上的一些宣传过于迷信这个发行版的性能表现却忽视了其兼容性带来的问题,预赛阶段我们的机时大部分分给了AlphaFold和m5c两个赛题,直到最后才进行HPL/HPCG测试,这时候我才发现Clear Linux软件源里提供的IB驱动和nvidia-peermem模块完全不兼容并且没有办法修复,而Nvidia官方的IB驱动也不支持Clear Linux无法在这个发行版上安装,为了解决这个问题我最终配置了Rocky + Clear这个非常抽象的双系统方案,虽然最终解决了兼容性问题但也为队友们的工作造成了不少麻烦。好在队友都非常给力,至少在预赛阶段我们的AlphaFold和m5c赛题都有亮眼表现,以不错的成绩再次进入了决赛。

\n

\"ASC25来到美丽的青海大学\"

\n

来到青海大学,我们从一开始就各种问题不断,先是装完机发现IB起不来,排查了快一个小时发现是Clear Linux起不了opensm服务只能单开一个节点用Rocky Linux启动。而后又是HPL测试结果一直异常,我们只能用双机跑出正常结果超过双机就会性能暴跌。最后更严重的是功耗不知为何完全压不下来即便所有核心都调整频率至最低。结果就是决赛正式运行阶段的两天我们整个过程都非常崩溃,虽然有五个节点但是大部分赛题我们都只能用三节点来跑才能控制功耗不会超出4KW限制。赛后我感到非常自责,觉得自己在系统选择和功耗控制的失误给整个团队带来了很大的麻烦和压力。

\n

\"ASC25令我们绝望的现场\"

\n

这一年是我们表现最差的一年排名直接倒数,除了wlx负责的AlphaFold表现不错之外其他赛题得分都非常惨淡。好在队友们都非常理解和包容我,原本我打算这一年就从超算队退役,但是带着这份歉意我还是决定明年ASC26干回来争取用一个更好的结果来弥补。在AI工具方面,相比去年这一年的不同是我们在网上发现了一个叫Deepseek V2.5的国产大语言模型,其性能丝毫不逊于同期国外的大模型,我们第一次感受到了AI工具在报错分析和代码优化方面的巨大帮助。

\n

\"整理心情备战ASC26\"

\n

ASC26:Agent时代的机遇

终于来到了2025年下半年,我们在新学期的战队招新中又迎来了三位新队员分别是和我同级的dyz,计算机学院大一的qhy以及电子科学学院大二的chb。这一年是我们人力资源最为丰富的一年,lzd回归指导超算队,hxh和wlx也分别被清华大学和上海交大录取,他们作为外援继续参与到超算队的工作中,同时我们另一支参加ISC的超算队也提供了不少帮助。

\n

\"我们的ASC26全员合影\"

\n

这一次我的工作依旧包括系统运维和基准测试,同时还和chb一起负责了AMSS-NCKU数值相对论模拟程序的优化工作。在系统方面由于去年的经验教训我直接选择了Rocky Linux 10作为我们的操作系统,它在兼容性和稳定性方面都非常可靠没有问题。而在程序优化方面,Agent时代的到来也对我们的工作流带来了前所未有的改变,我们可以把精力更多的放在性能分析和优化思路的探索上,具体的实现细节则可以交给AI工具来完成,这大大提高了我们的效率和创新能力。在AMSS-NCKU的优化过程中,我们通过perf和vtune等工具分析了程序的性能瓶颈,发现问题主要集中在通信和访存上,而这些问题最终又主要由低效的插值函数引起,于是在预赛阶段我们针对MPI和polint相关函数做了充分的针对性优化,包括选用点对点通信和异步通信,以及插值方面调换遍历顺序和提供快路径等,最终在预赛阶段对于GW150914测例我们的每步耗时从基准14s降低到了3s,取得了非常显著的加速效果。同时在世界模型赛题上dyz和qhy也表现良好,我们得以第三次闯入决赛来到无锡学院与各路优秀的同仁汇合继续切磋交流。

\n

进入决赛阶段,我们在AMSS-NCKU的优化工作继续进行,但是当我们继续调整优化将步时压缩到了2.6s左右之后却陷入了一个瓶颈,即接下来我们无论再改动哪里耗时都不会降低了并且还会负优化,这对我和chb造成了巨大的困扰,接近两个礼拜我们都没有任何进展,并且由于没有明显进展汇报我们还不断被无理怀疑骄傲自满,躺平摆烂以及没有理解赛题,好在有学长的鼓励支持我还是坚持了下来尽量忽略掉不合理的外部干扰继续推进工作,最终我和chb决定推翻原本思路基于现在的CPU版本进行完整的GPU重构,虽然这个决定工作量巨大但是我们觉得这是唯一有可能突破瓶颈的办法。AMSS程序本身有着大量参数支持,为了充分准备决赛测例我们自费了大量token来利用Agent进行移植工作。移植初期由于效果不明显我们又被外部质疑找错了方向,然而我和chb都对我们的GPU方案很有信心知道还有大量优化空间,最终我们在前往无锡前取得了巨大突破,GPU版本的每步耗时从2.6s降低到了0.8s多同时还支持多种参数输入,满怀着信心和期待,我们踏上了前往无锡的旅程。

\n

来到无锡学院,相比去年一切都显得有条不紊尽在掌握,我利用clonezilla顺利完成了现场四节点装机并且功耗也完全可控不像去年难以控制,测试阶段每个赛题都进行了充分测试也不像去年磕磕绊绊。然而在正赛阶段却没有我们预期的那么容易。首先是硬件因素带来的劣势,导致我们在HPL/HPCG,UnifoLM-WMA和AMSS-NCKU上基本丧失了竞争力;同时由于大家都有了Agent加持,每个赛题都会出现超快版本导致很多团队只能拿到基础分数,这在过去是完全不可想象的,从前的ASC哪怕优化做得没那么好,但是只要能把所有测例运行完都能拿到不错的分数,而现在只要优化水平不在第一梯队就基本拿不到分数。最终我们大概除了AMSS以外其他赛题全都没能超过10分,也没能突破ASC24的成绩记录。

\n

\"难忘今宵,有缘再会\"

\n

ASC26的结果对我们而言并不完美,但是其经历依然非常宝贵。ASC的意义从来都不是最终排名,而是在优化过程中的技术积累,比赛过程中的团队协作,以及赛后交流中的友谊建立,这些都是无价的财富。通过这三年的ASC经历,我不仅提升了自己的技术能力,更重要的是结识了一群志同道合的朋友,收获了许多珍贵的回忆和经验。与此同时我们也充分认识到了自己和其他优秀团队之间的差距,这些差距既有技术层面的也有资源层面的,它们都为我们提供了未来努力的方向和动力。

\n

最后,我想对所有支持和帮助过我们的人表达最诚挚的感谢。首先是我的队友们,与你们共事让我感到无比幸运和自豪,我从未觉得上大学开心过,除了在超算队和你们一起工作的时候。其次是我在其他高校超算队尤其是齐鲁工业大学,福州大学,西北工业大学,山西大学认识的朋友们,与你们的交流和切磋让我受益匪浅,也让我感受到了ASC赛事的温暖和活力。最后是组委会的老师们,你们的辛勤付出和专业支持为大家提供了一个展示自我和提升自我的平台,非常感激你们的努力。闭幕式结束离开时碰到两位组委会老师还向我们热情告别希望明年再见,口头上我们答应了下来然而遗憾的是明年我们都要奔赴新的前程已经难以重回ASC27赛场了,但愿将来还能有机会以非参赛队员的身份继续参与到ASC的活动中来。

\n

\"我们的最后一张ASC合影\"

\n

虽然我们的ASC之旅已经结束了,但我相信在这个过程中建立的友谊和积累的经验将会伴随我们继续前行。在此衷心祝愿ASC赛事越办越好,培养出更多优秀的超算人才,也祝愿每一位为ASC挥洒汗水的同学都能在未来的道路上取得更大的成功!

\n
\n

彩蛋环节

感觉还是有很多比较有意义的照片,可惜受ASC投稿篇幅限制不好放在正文里,就统一放在这里留念吧,以后说不定还会更新,先放下面这些。

\n

\"ASC24初代最好的五个人\"

\n

\"正在进行ISC答辩的超人lzd\"

\n

\"ASC24超级团队赛合影\"

\n

\"ASC24晚宴合影\"

\n

\"ASC25秘密基地\"

\n

\"ASC25决赛机柜,恋恋可爱捏~\"

\n

\"ASC25高地队长黄主任我们的超人\"

\n

\"ASC25青海大学的飘絮\"

\n

\"ASC26决赛机柜,灵梦可爱捏~\"

\n

\"什么时候才能届到呢\"

\n","excerpt":"","more":"

我的最后一次ASC之旅终于结束了,回望过去如今我想表达的唯有感激,不仅是对与我并肩作战的队友,还有参赛过程中其他战队的朋友们,以及辛苦付出的组委会老师们。正好借着这次机会,我也想回顾一下这三年来的ASC参赛经历,分享一些个人的感悟和体会。

\n

\"充满回忆的ASC照片墙\"

\n

ASC24:从零开始的挑战

2023年年末,一位在网上认识的神秘学长lzd突然发给我一张招新海报,说是有个非常好玩的比赛正在进行校队招新赛让我也来试试,结果这一试直接进入了64-bit Brainstorm战队并开启了我长达三年的ASC之旅。

\n

进入超算队后我听从学长的建议花了一个多月去自学MPI和CUDA编程。经过一段时间学习准备,ASC24的预赛赛题发布了,根据任务安排,我和另一位学长hxh分配到了OpenCAEPoro这个多相多组分渗流模拟科学计算程序的优化任务。依稀记得当时因为这个程序本身依赖较多还要手动构建不少软件导致我们花了三天才成功把这个程序的baseline跑起来,这让我第一次真正接触到了科学计算程序的复杂性和挑战性。之后我们又花了数周时间对这个程序进行了性能分析和优化,xh学长负责算法优化进行了一些手动循环展开和向量化以及一些算子库的高性能版本替代,我则负责一些算子的GPU移植工作并提供了快速fallback回CPU的宏控开关以防万一,预赛阶段我们最终实现的加速比只有1.3x不过好在也顺利进入了决赛。

\n

\"ASC24决赛现场\"

\n

决赛阶段是我最难忘的部分,我第一次见识到了完整的小型集群搭建维护流程,lzd学长简直像个超人一样什么都懂,他和cyq学长双人完成了整个集群的部署,系统方面有什么问题找他都能解决,功耗过高他也有各种办法来控制,真是太厉害了。终于到了OpenCAEPoro测例发布这天,决赛case可以说相当刁钻记得当时很多团队都卡在第一个测例无法运行,后面几个测例由于规模远远超过预赛测例导致我们的GPU版本直接崩溃,危急情况下,我和xh学长迅速做出决定马上fallback回CPU版本把结果运行出来,在运行后几个测例的同时,我们也在空闲节点排查第一个测例的问题,结果发现只要把我们选用的nvhpc编译链完全回退到gcc就能正确成功运行第一个测例了。最后结果也是非常出人意料,我们只是把所有测例运行完了实际没有特别明显的优化却在这道题上取得了第二名的成绩,通过赛后交流才知道有些队伍虽然有很快的加速比,但是因为精度无法通过所以没能拿到相应的分数。

\n

\"ASC24赛后合影\"

\n

这一年我们的总成绩是rank 5,是历年来最好的一次,大家都非常开心,晚宴阶段我也认识了非常多其他学校的新朋友并约定来年再见。值得一提的是,这一年由于AI工具还没发展起来,不管是程序排错还是性能优化我们都还是古法处理,现在身处Agent时代回想起来那段人工手动处理问题的经历真的是非常不易。

\n

ASC25:遭遇挫折的成长

时间转眼来到2024下半年,在新学期的战队招新赛中我们又迎来了三位优秀的新队员分别是wlx, yx和zqr。此时lzd学长面临毕业,由hxh和我领衔和三位新队员组建了一支新的超算团队出征ASC25。这一年我正值大二课业最为繁重的时候,因此在ASC25赛题发布后我被分配到的任务只有集群运维和HPL/HPCG基准测试工作。为了尽可能更多地参与贡献,我在课余时间也耗费了不少精力研究系统调优并产生了一些自以为不错的方案,结果却是埋下了非常深的隐患让我赛后十分后悔。

\n

这一年我给集群选用的发行版是Intel出品的Clear Linux(非常戏谑的是ASC25结束后不久Intel就宣布终止此项目并关闭了Clear Linux官网),当时我由于网络上的一些宣传过于迷信这个发行版的性能表现却忽视了其兼容性带来的问题,预赛阶段我们的机时大部分分给了AlphaFold和m5c两个赛题,直到最后才进行HPL/HPCG测试,这时候我才发现Clear Linux软件源里提供的IB驱动和nvidia-peermem模块完全不兼容并且没有办法修复,而Nvidia官方的IB驱动也不支持Clear Linux无法在这个发行版上安装,为了解决这个问题我最终配置了Rocky + Clear这个非常抽象的双系统方案,虽然最终解决了兼容性问题但也为队友们的工作造成了不少麻烦。好在队友都非常给力,至少在预赛阶段我们的AlphaFold和m5c赛题都有亮眼表现,以不错的成绩再次进入了决赛。

\n

\"ASC25来到美丽的青海大学\"

\n

来到青海大学,我们从一开始就各种问题不断,先是装完机发现IB起不来,排查了快一个小时发现是Clear Linux起不了opensm服务只能单开一个节点用Rocky Linux启动。而后又是HPL测试结果一直异常,我们只能用双机跑出正常结果超过双机就会性能暴跌。最后更严重的是功耗不知为何完全压不下来即便所有核心都调整频率至最低。结果就是决赛正式运行阶段的两天我们整个过程都非常崩溃,虽然有五个节点但是大部分赛题我们都只能用三节点来跑才能控制功耗不会超出4KW限制。赛后我感到非常自责,觉得自己在系统选择和功耗控制的失误给整个团队带来了很大的麻烦和压力。

\n

\"ASC25令我们绝望的现场\"

\n

这一年是我们表现最差的一年排名直接倒数,除了wlx负责的AlphaFold表现不错之外其他赛题得分都非常惨淡。好在队友们都非常理解和包容我,原本我打算这一年就从超算队退役,但是带着这份歉意我还是决定明年ASC26干回来争取用一个更好的结果来弥补。在AI工具方面,相比去年这一年的不同是我们在网上发现了一个叫Deepseek V2.5的国产大语言模型,其性能丝毫不逊于同期国外的大模型,我们第一次感受到了AI工具在报错分析和代码优化方面的巨大帮助。

\n

\"整理心情备战ASC26\"

\n

ASC26:Agent时代的机遇

终于来到了2025年下半年,我们在新学期的战队招新中又迎来了三位新队员分别是和我同级的dyz,计算机学院大一的qhy以及电子科学学院大二的chb。这一年是我们人力资源最为丰富的一年,lzd回归指导超算队,hxh和wlx也分别被清华大学和上海交大录取,他们作为外援继续参与到超算队的工作中,同时我们另一支参加ISC的超算队也提供了不少帮助。

\n

\"我们的ASC26全员合影\"

\n

这一次我的工作依旧包括系统运维和基准测试,同时还和chb一起负责了AMSS-NCKU数值相对论模拟程序的优化工作。在系统方面由于去年的经验教训我直接选择了Rocky Linux 10作为我们的操作系统,它在兼容性和稳定性方面都非常可靠没有问题。而在程序优化方面,Agent时代的到来也对我们的工作流带来了前所未有的改变,我们可以把精力更多的放在性能分析和优化思路的探索上,具体的实现细节则可以交给AI工具来完成,这大大提高了我们的效率和创新能力。在AMSS-NCKU的优化过程中,我们通过perf和vtune等工具分析了程序的性能瓶颈,发现问题主要集中在通信和访存上,而这些问题最终又主要由低效的插值函数引起,于是在预赛阶段我们针对MPI和polint相关函数做了充分的针对性优化,包括选用点对点通信和异步通信,以及插值方面调换遍历顺序和提供快路径等,最终在预赛阶段对于GW150914测例我们的每步耗时从基准14s降低到了3s,取得了非常显著的加速效果。同时在世界模型赛题上dyz和qhy也表现良好,我们得以第三次闯入决赛来到无锡学院与各路优秀的同仁汇合继续切磋交流。

\n

进入决赛阶段,我们在AMSS-NCKU的优化工作继续进行,但是当我们继续调整优化将步时压缩到了2.6s左右之后却陷入了一个瓶颈,即接下来我们无论再改动哪里耗时都不会降低了并且还会负优化,这对我和chb造成了巨大的困扰,接近两个礼拜我们都没有任何进展,并且由于没有明显进展汇报我们还不断被无理怀疑骄傲自满,躺平摆烂以及没有理解赛题,好在有学长的鼓励支持我还是坚持了下来尽量忽略掉不合理的外部干扰继续推进工作,最终我和chb决定推翻原本思路基于现在的CPU版本进行完整的GPU重构,虽然这个决定工作量巨大但是我们觉得这是唯一有可能突破瓶颈的办法。AMSS程序本身有着大量参数支持,为了充分准备决赛测例我们自费了大量token来利用Agent进行移植工作。移植初期由于效果不明显我们又被外部质疑找错了方向,然而我和chb都对我们的GPU方案很有信心知道还有大量优化空间,最终我们在前往无锡前取得了巨大突破,GPU版本的每步耗时从2.6s降低到了0.8s多同时还支持多种参数输入,满怀着信心和期待,我们踏上了前往无锡的旅程。

\n

来到无锡学院,相比去年一切都显得有条不紊尽在掌握,我利用clonezilla顺利完成了现场四节点装机并且功耗也完全可控不像去年难以控制,测试阶段每个赛题都进行了充分测试也不像去年磕磕绊绊。然而在正赛阶段却没有我们预期的那么容易。首先是硬件因素带来的劣势,导致我们在HPL/HPCG,UnifoLM-WMA和AMSS-NCKU上基本丧失了竞争力;同时由于大家都有了Agent加持,每个赛题都会出现超快版本导致很多团队只能拿到基础分数,这在过去是完全不可想象的,从前的ASC哪怕优化做得没那么好,但是只要能把所有测例运行完都能拿到不错的分数,而现在只要优化水平不在第一梯队就基本拿不到分数。最终我们大概除了AMSS以外其他赛题全都没能超过10分,也没能突破ASC24的成绩记录。

\n

\"难忘今宵,有缘再会\"

\n

ASC26的结果对我们而言并不完美,但是其经历依然非常宝贵。ASC的意义从来都不是最终排名,而是在优化过程中的技术积累,比赛过程中的团队协作,以及赛后交流中的友谊建立,这些都是无价的财富。通过这三年的ASC经历,我不仅提升了自己的技术能力,更重要的是结识了一群志同道合的朋友,收获了许多珍贵的回忆和经验。与此同时我们也充分认识到了自己和其他优秀团队之间的差距,这些差距既有技术层面的也有资源层面的,它们都为我们提供了未来努力的方向和动力。

\n

最后,我想对所有支持和帮助过我们的人表达最诚挚的感谢。首先是我的队友们,与你们共事让我感到无比幸运和自豪,我从未觉得上大学开心过,除了在超算队和你们一起工作的时候。其次是我在其他高校超算队尤其是齐鲁工业大学,福州大学,西北工业大学,山西大学认识的朋友们,与你们的交流和切磋让我受益匪浅,也让我感受到了ASC赛事的温暖和活力。最后是组委会的老师们,你们的辛勤付出和专业支持为大家提供了一个展示自我和提升自我的平台,非常感激你们的努力。闭幕式结束离开时碰到两位组委会老师还向我们热情告别希望明年再见,口头上我们答应了下来然而遗憾的是明年我们都要奔赴新的前程已经难以重回ASC27赛场了,但愿将来还能有机会以非参赛队员的身份继续参与到ASC的活动中来。

\n

\"我们的最后一张ASC合影\"

\n

虽然我们的ASC之旅已经结束了,但我相信在这个过程中建立的友谊和积累的经验将会伴随我们继续前行。在此衷心祝愿ASC赛事越办越好,培养出更多优秀的超算人才,也祝愿每一位为ASC挥洒汗水的同学都能在未来的道路上取得更大的成功!

\n
\n

彩蛋环节

感觉还是有很多比较有意义的照片,可惜受ASC投稿篇幅限制不好放在正文里,就统一放在这里留念吧,以后说不定还会更新,先放下面这些。

\n

\"ASC24初代最好的五个人\"

\n

\"正在进行ISC答辩的超人lzd\"

\n

\"ASC24超级团队赛合影\"

\n

\"ASC24晚宴合影\"

\n

\"ASC25秘密基地\"

\n

\"ASC25决赛机柜,恋恋可爱捏~\"

\n

\"ASC25高地队长黄主任我们的超人\"

\n

\"ASC25青海大学的飘絮\"

\n

\"ASC26决赛机柜,灵梦可爱捏~\"

\n

\"什么时候才能届到呢\"

\n"},{"title":"如何在笔记本电脑插拔电源时自动切换 SCX 调度模式","date":"2026-06-14T11:24:35.000Z","_content":"\n一直觉得scx_lavd和scx_flow支持自动判断负载来选择合适的调度模式很不错,但是我还是更信赖Nvidia工程师出品的scx_bpfland,尤其是CachyOS定义的scx_bpfland低延迟模式和省电模式在我笔记本上的表现都非常好。但有一个痛点——笔记本插电和用电池的场景对调度器的需求完全不同:\n\n- **插电时**:希望低延迟、高性能,用得爽,程序编译快游戏帧数高\n- **电池时**:希望尽可能省电,续航优先尽量用久一点,不在乎性能牺牲\n\n每次手动敲命令切换实在太蠢了使用CachyOS的scx-manager也不太方便。研究了一下后发现可以借助 Linux 的 `udev` 规则来监听电源事件,实现全自动切换。记录一下折腾过程。\n\n---\n\n## 现状:scx_loader 配置文件\n\n这是我的 `/etc/scx_loader.toml`(CachyOS已提前为各个调度器配好了 mode):\n\n```toml\ndefault_sched = \"scx_bpfland\"\ndefault_mode = \"LowLatency\"\n\n[scheds.scx_rusty]\nauto_mode = []\ngaming_mode = []\nlowlatency_mode = []\npowersave_mode = []\nserver_mode = []\n\n[scheds.scx_p2dq]\nauto_mode = []\ngaming_mode = []\nlowlatency_mode = [\"-y\"]\npowersave_mode = []\nserver_mode = [\"--keep-running\"]\n\n[scheds.scx_bpfland]\nauto_mode = []\ngaming_mode = [\"-m\", \"performance\"]\nlowlatency_mode = [\"-s\", \"5000\", \"-S\", \"500\", \"-l\", \"5000\", \"-m\", \"performance\"]\npowersave_mode = [\"-m\", \"powersave\"]\nserver_mode = [\"-p\"]\n\n[scheds.scx_lavd]\nauto_mode = [\"--autopower\"]\ngaming_mode = [\"--performance\"]\nlowlatency_mode = [\"--performance\"]\npowersave_mode = [\"--powersave\"]\nserver_mode = []\n\n[scheds.scx_tickless]\nauto_mode = []\ngaming_mode = [\"-f\", \"5000\", \"-s\", \"5000\"]\nlowlatency_mode = [\"-f\", \"5000\", \"-s\", \"1000\"]\npowersave_mode = [\"-f\", \"50\", \"-p\"]\nserver_mode = [\"-f\", \"100\"]\n\n[scheds.scx_flash]\nauto_mode = []\ngaming_mode = []\nlowlatency_mode = []\npowersave_mode = []\nserver_mode = []\n```\n\n仔细观察 `[scheds.scx_bpfland]` 这一块:\n\n- `lowlatency_mode` 正好对应插电状态想要的参数:`-s 5000 -S 500 -l 5000 -m performance`\n- `powersave_mode` 正好对应电池状态想要的参数:`-m powersave`\n\n不用自己写额外参数配置,直接复用已有的 mode 定义就好。\n\n---\n\n## 思路\n\n`scx_loader` 提供了配套的命令行工具 `scxctl`,可以用来切换调度器和模式。剩下的就是找一个能在电源事件发生时自动执行命令的机制——Linux 内核的 `udev` 子系统天生就是干这个的。\n\n整体流程很简单:**插拔电源 → 内核发出 uevent → udev 匹配规则 → systemd-run 异步执行 → scxctl switch 切换模式**。\n\n---\n\n## 步骤一:测试手动切换命令\n\n先确认 `scxctl` 能正常工作(需要 root 权限):\n\n```bash\n# 模拟插电 → 低延迟/高性能模式\nsudo scxctl switch -s bpfland -m lowlatency\n\n# 模拟拔电 → 省电模式\nsudo scxctl switch -s bpfland -m powersave\n```\n\n如果这两条命令都能成功切换,就可以继续下一步。\n\n---\n\n## 步骤二:编写 udev 规则\n\n创建规则文件:\n\n```bash\nsudo nano /etc/udev/rules.d/99-scx-power.rules\n```\n\n写入以下内容:\n\n```udev\n# 插入电源 (AC) → 切换为 bpfland 的 lowlatency 模式\nSUBSYSTEM==\"power_supply\", ATTR{type}==\"Mains\", ATTR{online}==\"1\", RUN+=\"/usr/bin/systemd-run --no-block /usr/bin/scxctl switch -s bpfland -m lowlatency\"\n\n# 拔出电源 (Battery) → 切换为 bpfland 的 powersave 模式\nSUBSYSTEM==\"power_supply\", ATTR{type}==\"Mains\", ATTR{online}==\"0\", RUN+=\"/usr/bin/systemd-run --no-block /usr/bin/scxctl switch -s bpfland -m powersave\"\n```\n\n> **为什么用 `systemd-run --no-block` 包裹?**\n>\n> `scxctl` 底层通过 D-Bus 与 `scx_loader` 守护进程通信。如果让 `udev` 直接执行,可能会因为 udev 的受限执行环境(缺少 D-Bus session、环境变量缺失)或者命令执行时间过长阻塞 udev 进程导致失败。交给 systemd 后台异步执行是最稳妥的做法。\n\n---\n\n## 步骤三:重载规则使其生效\n\n```bash\nsudo udevadm control --reload-rules\nsudo udevadm trigger\n```\n\n`udevadm trigger` 会立即重放一次当前设备状态,所以如果此时已经插着电源,规则会立刻触发一次,可以顺便验证是否生效。\n\n---\n\n## 步骤四(可选):验证电源类型\n\n绝大多数笔记本的外部电源在内核中的设备类型都是 `Mains`,上面规则直接就能用。如果插拔电源发现没反应,检查一下你的电源设备类型:\n\n```bash\ncat /sys/class/power_supply/*/type\n```\n\n如果输出的不是 `Mains`(比如是 `AC` 或其他名字),把 udev 规则里的 `ATTR{type}==\"Mains\"` 换成实际类型即可。\n\n---\n\n## 验证效果\n\n插拔电源后,可以用以下命令确认模式是否已经切换:\n\n```bash\nscxctl get\n```\n\n---\n\n## 总结\n\n整个过程其实非常简单——核心思路就是「udev 监听电源事件 → systemd-run 异步调用 scxctl 切换 mode」。全程没有写一个新参数,完全复用 `scx_loader.toml` 里已有的 mode 定义。\n\n这个方案同样适用于其他调度器:只要在 `scx_loader.toml` 里给对应调度器配好 `lowlatency_mode` 和 `powersave_mode`,然后把 udev 规则里的 `-s bpfland` 换成其他调度器名字就行。\n\n这下笔记本插拔电源再也不用惦记着手动切调度器了:D\n","source":"_posts/scx-auto.md","raw":"---\ntitle: 如何在笔记本电脑插拔电源时自动切换 SCX 调度模式\ndate: 2026-06-14 19:24:35\ntags: [Archlinux, 系统优化, 技术分享, SCX调度器]\n---\n\n一直觉得scx_lavd和scx_flow支持自动判断负载来选择合适的调度模式很不错,但是我还是更信赖Nvidia工程师出品的scx_bpfland,尤其是CachyOS定义的scx_bpfland低延迟模式和省电模式在我笔记本上的表现都非常好。但有一个痛点——笔记本插电和用电池的场景对调度器的需求完全不同:\n\n- **插电时**:希望低延迟、高性能,用得爽,程序编译快游戏帧数高\n- **电池时**:希望尽可能省电,续航优先尽量用久一点,不在乎性能牺牲\n\n每次手动敲命令切换实在太蠢了使用CachyOS的scx-manager也不太方便。研究了一下后发现可以借助 Linux 的 `udev` 规则来监听电源事件,实现全自动切换。记录一下折腾过程。\n\n---\n\n## 现状:scx_loader 配置文件\n\n这是我的 `/etc/scx_loader.toml`(CachyOS已提前为各个调度器配好了 mode):\n\n```toml\ndefault_sched = \"scx_bpfland\"\ndefault_mode = \"LowLatency\"\n\n[scheds.scx_rusty]\nauto_mode = []\ngaming_mode = []\nlowlatency_mode = []\npowersave_mode = []\nserver_mode = []\n\n[scheds.scx_p2dq]\nauto_mode = []\ngaming_mode = []\nlowlatency_mode = [\"-y\"]\npowersave_mode = []\nserver_mode = [\"--keep-running\"]\n\n[scheds.scx_bpfland]\nauto_mode = []\ngaming_mode = [\"-m\", \"performance\"]\nlowlatency_mode = [\"-s\", \"5000\", \"-S\", \"500\", \"-l\", \"5000\", \"-m\", \"performance\"]\npowersave_mode = [\"-m\", \"powersave\"]\nserver_mode = [\"-p\"]\n\n[scheds.scx_lavd]\nauto_mode = [\"--autopower\"]\ngaming_mode = [\"--performance\"]\nlowlatency_mode = [\"--performance\"]\npowersave_mode = [\"--powersave\"]\nserver_mode = []\n\n[scheds.scx_tickless]\nauto_mode = []\ngaming_mode = [\"-f\", \"5000\", \"-s\", \"5000\"]\nlowlatency_mode = [\"-f\", \"5000\", \"-s\", \"1000\"]\npowersave_mode = [\"-f\", \"50\", \"-p\"]\nserver_mode = [\"-f\", \"100\"]\n\n[scheds.scx_flash]\nauto_mode = []\ngaming_mode = []\nlowlatency_mode = []\npowersave_mode = []\nserver_mode = []\n```\n\n仔细观察 `[scheds.scx_bpfland]` 这一块:\n\n- `lowlatency_mode` 正好对应插电状态想要的参数:`-s 5000 -S 500 -l 5000 -m performance`\n- `powersave_mode` 正好对应电池状态想要的参数:`-m powersave`\n\n不用自己写额外参数配置,直接复用已有的 mode 定义就好。\n\n---\n\n## 思路\n\n`scx_loader` 提供了配套的命令行工具 `scxctl`,可以用来切换调度器和模式。剩下的就是找一个能在电源事件发生时自动执行命令的机制——Linux 内核的 `udev` 子系统天生就是干这个的。\n\n整体流程很简单:**插拔电源 → 内核发出 uevent → udev 匹配规则 → systemd-run 异步执行 → scxctl switch 切换模式**。\n\n---\n\n## 步骤一:测试手动切换命令\n\n先确认 `scxctl` 能正常工作(需要 root 权限):\n\n```bash\n# 模拟插电 → 低延迟/高性能模式\nsudo scxctl switch -s bpfland -m lowlatency\n\n# 模拟拔电 → 省电模式\nsudo scxctl switch -s bpfland -m powersave\n```\n\n如果这两条命令都能成功切换,就可以继续下一步。\n\n---\n\n## 步骤二:编写 udev 规则\n\n创建规则文件:\n\n```bash\nsudo nano /etc/udev/rules.d/99-scx-power.rules\n```\n\n写入以下内容:\n\n```udev\n# 插入电源 (AC) → 切换为 bpfland 的 lowlatency 模式\nSUBSYSTEM==\"power_supply\", ATTR{type}==\"Mains\", ATTR{online}==\"1\", RUN+=\"/usr/bin/systemd-run --no-block /usr/bin/scxctl switch -s bpfland -m lowlatency\"\n\n# 拔出电源 (Battery) → 切换为 bpfland 的 powersave 模式\nSUBSYSTEM==\"power_supply\", ATTR{type}==\"Mains\", ATTR{online}==\"0\", RUN+=\"/usr/bin/systemd-run --no-block /usr/bin/scxctl switch -s bpfland -m powersave\"\n```\n\n> **为什么用 `systemd-run --no-block` 包裹?**\n>\n> `scxctl` 底层通过 D-Bus 与 `scx_loader` 守护进程通信。如果让 `udev` 直接执行,可能会因为 udev 的受限执行环境(缺少 D-Bus session、环境变量缺失)或者命令执行时间过长阻塞 udev 进程导致失败。交给 systemd 后台异步执行是最稳妥的做法。\n\n---\n\n## 步骤三:重载规则使其生效\n\n```bash\nsudo udevadm control --reload-rules\nsudo udevadm trigger\n```\n\n`udevadm trigger` 会立即重放一次当前设备状态,所以如果此时已经插着电源,规则会立刻触发一次,可以顺便验证是否生效。\n\n---\n\n## 步骤四(可选):验证电源类型\n\n绝大多数笔记本的外部电源在内核中的设备类型都是 `Mains`,上面规则直接就能用。如果插拔电源发现没反应,检查一下你的电源设备类型:\n\n```bash\ncat /sys/class/power_supply/*/type\n```\n\n如果输出的不是 `Mains`(比如是 `AC` 或其他名字),把 udev 规则里的 `ATTR{type}==\"Mains\"` 换成实际类型即可。\n\n---\n\n## 验证效果\n\n插拔电源后,可以用以下命令确认模式是否已经切换:\n\n```bash\nscxctl get\n```\n\n---\n\n## 总结\n\n整个过程其实非常简单——核心思路就是「udev 监听电源事件 → systemd-run 异步调用 scxctl 切换 mode」。全程没有写一个新参数,完全复用 `scx_loader.toml` 里已有的 mode 定义。\n\n这个方案同样适用于其他调度器:只要在 `scx_loader.toml` 里给对应调度器配好 `lowlatency_mode` 和 `powersave_mode`,然后把 udev 规则里的 `-s bpfland` 换成其他调度器名字就行。\n\n这下笔记本插拔电源再也不用惦记着手动切调度器了:D\n","slug":"scx-auto","published":1,"updated":"2026-06-14T11:43:21.327Z","_id":"cmqdpj0yo0000b5d035ud8rew","comments":1,"layout":"post","photos":[],"content":"

一直觉得scx_lavd和scx_flow支持自动判断负载来选择合适的调度模式很不错,但是我还是更信赖Nvidia工程师出品的scx_bpfland,尤其是CachyOS定义的scx_bpfland低延迟模式和省电模式在我笔记本上的表现都非常好。但有一个痛点——笔记本插电和用电池的场景对调度器的需求完全不同:

\n
    \n
  • 插电时:希望低延迟、高性能,用得爽,程序编译快游戏帧数高
  • \n
  • 电池时:希望尽可能省电,续航优先尽量用久一点,不在乎性能牺牲
  • \n
\n

每次手动敲命令切换实在太蠢了使用CachyOS的scx-manager也不太方便。研究了一下后发现可以借助 Linux 的 udev 规则来监听电源事件,实现全自动切换。记录一下折腾过程。

\n
\n

现状:scx_loader 配置文件

这是我的 /etc/scx_loader.toml(CachyOS已提前为各个调度器配好了 mode):

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
default_sched = "scx_bpfland"
default_mode = "LowLatency"

[scheds.scx_rusty]
auto_mode = []
gaming_mode = []
lowlatency_mode = []
powersave_mode = []
server_mode = []

[scheds.scx_p2dq]
auto_mode = []
gaming_mode = []
lowlatency_mode = ["-y"]
powersave_mode = []
server_mode = ["--keep-running"]

[scheds.scx_bpfland]
auto_mode = []
gaming_mode = ["-m", "performance"]
lowlatency_mode = ["-s", "5000", "-S", "500", "-l", "5000", "-m", "performance"]
powersave_mode = ["-m", "powersave"]
server_mode = ["-p"]

[scheds.scx_lavd]
auto_mode = ["--autopower"]
gaming_mode = ["--performance"]
lowlatency_mode = ["--performance"]
powersave_mode = ["--powersave"]
server_mode = []

[scheds.scx_tickless]
auto_mode = []
gaming_mode = ["-f", "5000", "-s", "5000"]
lowlatency_mode = ["-f", "5000", "-s", "1000"]
powersave_mode = ["-f", "50", "-p"]
server_mode = ["-f", "100"]

[scheds.scx_flash]
auto_mode = []
gaming_mode = []
lowlatency_mode = []
powersave_mode = []
server_mode = []
\n\n

仔细观察 [scheds.scx_bpfland] 这一块:

\n
    \n
  • lowlatency_mode 正好对应插电状态想要的参数:-s 5000 -S 500 -l 5000 -m performance
  • \n
  • powersave_mode 正好对应电池状态想要的参数:-m powersave
  • \n
\n

不用自己写额外参数配置,直接复用已有的 mode 定义就好。

\n
\n

思路

scx_loader 提供了配套的命令行工具 scxctl,可以用来切换调度器和模式。剩下的就是找一个能在电源事件发生时自动执行命令的机制——Linux 内核的 udev 子系统天生就是干这个的。

\n

整体流程很简单:插拔电源 → 内核发出 uevent → udev 匹配规则 → systemd-run 异步执行 → scxctl switch 切换模式

\n
\n

步骤一:测试手动切换命令

先确认 scxctl 能正常工作(需要 root 权限):

\n
1
2
3
4
5
# 模拟插电 → 低延迟/高性能模式
sudo scxctl switch -s bpfland -m lowlatency

# 模拟拔电 → 省电模式
sudo scxctl switch -s bpfland -m powersave
\n\n

如果这两条命令都能成功切换,就可以继续下一步。

\n
\n

步骤二:编写 udev 规则

创建规则文件:

\n
1
sudo nano /etc/udev/rules.d/99-scx-power.rules
\n\n

写入以下内容:

\n
1
2
3
4
5
# 插入电源 (AC) → 切换为 bpfland 的 lowlatency 模式
SUBSYSTEM=="power_supply", ATTR{type}=="Mains", ATTR{online}=="1", RUN+="/usr/bin/systemd-run --no-block /usr/bin/scxctl switch -s bpfland -m lowlatency"

# 拔出电源 (Battery) → 切换为 bpfland 的 powersave 模式
SUBSYSTEM=="power_supply", ATTR{type}=="Mains", ATTR{online}=="0", RUN+="/usr/bin/systemd-run --no-block /usr/bin/scxctl switch -s bpfland -m powersave"
\n\n
\n

为什么用 systemd-run --no-block 包裹?

\n

scxctl 底层通过 D-Bus 与 scx_loader 守护进程通信。如果让 udev 直接执行,可能会因为 udev 的受限执行环境(缺少 D-Bus session、环境变量缺失)或者命令执行时间过长阻塞 udev 进程导致失败。交给 systemd 后台异步执行是最稳妥的做法。

\n
\n
\n

步骤三:重载规则使其生效

1
2
sudo udevadm control --reload-rules
sudo udevadm trigger
\n\n

udevadm trigger 会立即重放一次当前设备状态,所以如果此时已经插着电源,规则会立刻触发一次,可以顺便验证是否生效。

\n
\n

步骤四(可选):验证电源类型

绝大多数笔记本的外部电源在内核中的设备类型都是 Mains,上面规则直接就能用。如果插拔电源发现没反应,检查一下你的电源设备类型:

\n
1
cat /sys/class/power_supply/*/type
\n\n

如果输出的不是 Mains(比如是 AC 或其他名字),把 udev 规则里的 ATTR{type}=="Mains" 换成实际类型即可。

\n
\n

验证效果

插拔电源后,可以用以下命令确认模式是否已经切换:

\n
1
scxctl get
\n\n
\n

总结

整个过程其实非常简单——核心思路就是「udev 监听电源事件 → systemd-run 异步调用 scxctl 切换 mode」。全程没有写一个新参数,完全复用 scx_loader.toml 里已有的 mode 定义。

\n

这个方案同样适用于其他调度器:只要在 scx_loader.toml 里给对应调度器配好 lowlatency_modepowersave_mode,然后把 udev 规则里的 -s bpfland 换成其他调度器名字就行。

\n

这下笔记本插拔电源再也不用惦记着手动切调度器了:D

\n","excerpt":"","more":"

一直觉得scx_lavd和scx_flow支持自动判断负载来选择合适的调度模式很不错,但是我还是更信赖Nvidia工程师出品的scx_bpfland,尤其是CachyOS定义的scx_bpfland低延迟模式和省电模式在我笔记本上的表现都非常好。但有一个痛点——笔记本插电和用电池的场景对调度器的需求完全不同:

\n
    \n
  • 插电时:希望低延迟、高性能,用得爽,程序编译快游戏帧数高
  • \n
  • 电池时:希望尽可能省电,续航优先尽量用久一点,不在乎性能牺牲
  • \n
\n

每次手动敲命令切换实在太蠢了使用CachyOS的scx-manager也不太方便。研究了一下后发现可以借助 Linux 的 udev 规则来监听电源事件,实现全自动切换。记录一下折腾过程。

\n
\n

现状:scx_loader 配置文件

这是我的 /etc/scx_loader.toml(CachyOS已提前为各个调度器配好了 mode):

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
default_sched = "scx_bpfland"
default_mode = "LowLatency"

[scheds.scx_rusty]
auto_mode = []
gaming_mode = []
lowlatency_mode = []
powersave_mode = []
server_mode = []

[scheds.scx_p2dq]
auto_mode = []
gaming_mode = []
lowlatency_mode = ["-y"]
powersave_mode = []
server_mode = ["--keep-running"]

[scheds.scx_bpfland]
auto_mode = []
gaming_mode = ["-m", "performance"]
lowlatency_mode = ["-s", "5000", "-S", "500", "-l", "5000", "-m", "performance"]
powersave_mode = ["-m", "powersave"]
server_mode = ["-p"]

[scheds.scx_lavd]
auto_mode = ["--autopower"]
gaming_mode = ["--performance"]
lowlatency_mode = ["--performance"]
powersave_mode = ["--powersave"]
server_mode = []

[scheds.scx_tickless]
auto_mode = []
gaming_mode = ["-f", "5000", "-s", "5000"]
lowlatency_mode = ["-f", "5000", "-s", "1000"]
powersave_mode = ["-f", "50", "-p"]
server_mode = ["-f", "100"]

[scheds.scx_flash]
auto_mode = []
gaming_mode = []
lowlatency_mode = []
powersave_mode = []
server_mode = []
\n\n

仔细观察 [scheds.scx_bpfland] 这一块:

\n
    \n
  • lowlatency_mode 正好对应插电状态想要的参数:-s 5000 -S 500 -l 5000 -m performance
  • \n
  • powersave_mode 正好对应电池状态想要的参数:-m powersave
  • \n
\n

不用自己写额外参数配置,直接复用已有的 mode 定义就好。

\n
\n

思路

scx_loader 提供了配套的命令行工具 scxctl,可以用来切换调度器和模式。剩下的就是找一个能在电源事件发生时自动执行命令的机制——Linux 内核的 udev 子系统天生就是干这个的。

\n

整体流程很简单:插拔电源 → 内核发出 uevent → udev 匹配规则 → systemd-run 异步执行 → scxctl switch 切换模式

\n
\n

步骤一:测试手动切换命令

先确认 scxctl 能正常工作(需要 root 权限):

\n
1
2
3
4
5
# 模拟插电 → 低延迟/高性能模式
sudo scxctl switch -s bpfland -m lowlatency

# 模拟拔电 → 省电模式
sudo scxctl switch -s bpfland -m powersave
\n\n

如果这两条命令都能成功切换,就可以继续下一步。

\n
\n

步骤二:编写 udev 规则

创建规则文件:

\n
1
sudo nano /etc/udev/rules.d/99-scx-power.rules
\n\n

写入以下内容:

\n
1
2
3
4
5
# 插入电源 (AC) → 切换为 bpfland 的 lowlatency 模式
SUBSYSTEM=="power_supply", ATTR{type}=="Mains", ATTR{online}=="1", RUN+="/usr/bin/systemd-run --no-block /usr/bin/scxctl switch -s bpfland -m lowlatency"

# 拔出电源 (Battery) → 切换为 bpfland 的 powersave 模式
SUBSYSTEM=="power_supply", ATTR{type}=="Mains", ATTR{online}=="0", RUN+="/usr/bin/systemd-run --no-block /usr/bin/scxctl switch -s bpfland -m powersave"
\n\n
\n

为什么用 systemd-run --no-block 包裹?

\n

scxctl 底层通过 D-Bus 与 scx_loader 守护进程通信。如果让 udev 直接执行,可能会因为 udev 的受限执行环境(缺少 D-Bus session、环境变量缺失)或者命令执行时间过长阻塞 udev 进程导致失败。交给 systemd 后台异步执行是最稳妥的做法。

\n
\n
\n

步骤三:重载规则使其生效

1
2
sudo udevadm control --reload-rules
sudo udevadm trigger
\n\n

udevadm trigger 会立即重放一次当前设备状态,所以如果此时已经插着电源,规则会立刻触发一次,可以顺便验证是否生效。

\n
\n

步骤四(可选):验证电源类型

绝大多数笔记本的外部电源在内核中的设备类型都是 Mains,上面规则直接就能用。如果插拔电源发现没反应,检查一下你的电源设备类型:

\n
1
cat /sys/class/power_supply/*/type
\n\n

如果输出的不是 Mains(比如是 AC 或其他名字),把 udev 规则里的 ATTR{type}=="Mains" 换成实际类型即可。

\n
\n

验证效果

插拔电源后,可以用以下命令确认模式是否已经切换:

\n
1
scxctl get
\n\n
\n

总结

整个过程其实非常简单——核心思路就是「udev 监听电源事件 → systemd-run 异步调用 scxctl 切换 mode」。全程没有写一个新参数,完全复用 scx_loader.toml 里已有的 mode 定义。

\n

这个方案同样适用于其他调度器:只要在 scx_loader.toml 里给对应调度器配好 lowlatency_modepowersave_mode,然后把 udev 规则里的 -s bpfland 换成其他调度器名字就行。

\n

这下笔记本插拔电源再也不用惦记着手动切调度器了:D

\n"}],"PostAsset":[{"_id":"source/_posts/12月16-17日工作记录.md","slug":"记录.md","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/12月18日工作记录.md","slug":"md","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/12月19日工作记录.md","slug":"md","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/12月20日工作记录.md","slug":"md","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/12月31日进度报告.md","slug":"md","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/12月28日进度报告.md","slug":"md","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/2025-5-21.md","slug":"d","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/BlogUpdate.md","slug":"md","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/CGH0S7-s-Blog.md","slug":"og.md","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/GentleJena.md","slug":"md","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/Vocaloid调教-晴天.md","slug":"晴天.md","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/Your-exclusive-email-address.md","slug":"ive-email-address.md","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/arch-nvidia.md","slug":".md","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/archlinux-game-fix.md","slug":"ame-fix.md","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/archlinux-optimization.md","slug":"ptimization.md","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/fortune.md","slug":"","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/clonezilla.md","slug":"md","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/gcc15restrict.md","slug":"ct.md","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/kritabest.md","slug":"d","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/loopers.md","slug":"","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/mhwi.md","slug":"","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/nudtbomblab.md","slug":".md","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/os-optimization.md","slug":"tion.md","post":"cmkjeg2nh0001yg2bemvs08zk","modified":1,"renderable":0},{"_id":"source/_posts/overleaf.md","slug":"","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/zellij-helix.md","slug":"x.md","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/searxng.md","slug":"","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/原来我还有个博客.md","slug":"","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0},{"_id":"source/_posts/梦开始的地方.md","slug":"","post":"cmkjeg2nh0001yg2bemvs08zk","modified":0,"renderable":0}],"PostCategory":[{"post_id":"cmkjeg2nl000eyg2b1zf4g4q6","category_id":"cmkjeg2nm000hyg2b3ajig0qs","_id":"cmkjeg2no000vyg2bfasi77qw"},{"post_id":"cmkjeg2nm000jyg2bhdyy2343","category_id":"cmkjeg2nn000pyg2bg0qfbc8f","_id":"cmkjeg2np0011yg2b5bwsgoj4"},{"post_id":"cmkjeg2nn000myg2beei7b2up","category_id":"cmkjeg2no000wyg2b5udv94y7","_id":"cmkjeg2nq0016yg2b7x1dbh9f"},{"post_id":"cmkjeg2no000ryg2b1mzearq0","category_id":"cmkjeg2np0012yg2b1qho1agc","_id":"cmkjeg2nq001cyg2b2pqq3mpe"},{"post_id":"cmkjeg2nq001dyg2b1ne080fv","category_id":"cmkjeg2nn000pyg2bg0qfbc8f","_id":"cmkjeg2ns001lyg2b5a001y9h"},{"post_id":"cmkjeg2nr001kyg2bg8bh5158","category_id":"cmkjeg2np0012yg2b1qho1agc","_id":"cmkjeg2ns001ryg2bcxfgf589"},{"post_id":"cmkjeg2nr001iyg2b4y5fbjnm","category_id":"cmkjeg2ns001nyg2b9f9584va","_id":"cmkjeg2ns001tyg2bcmat81my"}],"PostTag":[{"post_id":"cmkjeg2nj0005yg2b2irqftck","tag_id":"cmkjeg2ni0003yg2b321hfvn4","_id":"cmkjeg2nk0008yg2b89zk72a8"},{"post_id":"cmkjeg2nh0001yg2bemvs08zk","tag_id":"cmkjeg2ni0003yg2b321hfvn4","_id":"cmkjeg2nl000ayg2b8hv4aprh"},{"post_id":"cmkjeg2nk0006yg2b3qzo691r","tag_id":"cmkjeg2ni0003yg2b321hfvn4","_id":"cmkjeg2nl000dyg2b3nhq4udk"},{"post_id":"cmkjeg2nk0009yg2b2zy9a2by","tag_id":"cmkjeg2ni0003yg2b321hfvn4","_id":"cmkjeg2nm000fyg2bcgjm2qbw"},{"post_id":"cmkjeg2ni0002yg2bh5bd4bef","tag_id":"cmkjeg2ni0003yg2b321hfvn4","_id":"cmkjeg2nm000iyg2bditl6qos"},{"post_id":"cmkjeg2nl000byg2b68u8869w","tag_id":"cmkjeg2ni0003yg2b321hfvn4","_id":"cmkjeg2nn000lyg2bda67524j"},{"post_id":"cmkjeg2nl000eyg2b1zf4g4q6","tag_id":"cmkjeg2ni0003yg2b321hfvn4","_id":"cmkjeg2nn000nyg2b1wah1ib8"},{"post_id":"cmkjeg2nj0004yg2b6zetfs0g","tag_id":"cmkjeg2ni0003yg2b321hfvn4","_id":"cmkjeg2no000qyg2b89ur658k"},{"post_id":"cmkjeg2nm000gyg2bdfisbgnx","tag_id":"cmkjeg2nn000kyg2b47gh630t","_id":"cmkjeg2no000tyg2beuz9aifs"},{"post_id":"cmkjeg2nm000jyg2bhdyy2343","tag_id":"cmkjeg2no000syg2bar09duju","_id":"cmkjeg2np0010yg2bfhwk1xha"},{"post_id":"cmkjeg2np0013yg2bckz8f6hw","tag_id":"cmkjeg2ni0003yg2b321hfvn4","_id":"cmkjeg2nq0017yg2bg66teqx4"},{"post_id":"cmkjeg2nn000myg2beei7b2up","tag_id":"cmkjeg2np000yyg2b20mwglzg","_id":"cmkjeg2nq001byg2b45x3gioo"},{"post_id":"cmkjeg2nn000myg2beei7b2up","tag_id":"cmkjeg2no000syg2bar09duju","_id":"cmkjeg2nr001eyg2bdqcx8qbo"},{"post_id":"cmkjeg2nn000oyg2bc90t8hdg","tag_id":"cmkjeg2nq0019yg2b40cl6a24","_id":"cmkjeg2nr001hyg2b1ih780v8"},{"post_id":"cmkjeg2nq001dyg2b1ne080fv","tag_id":"cmkjeg2no000syg2bar09duju","_id":"cmkjeg2nr001jyg2b4rntbsmh"},{"post_id":"cmkjeg2no000ryg2b1mzearq0","tag_id":"cmkjeg2nr001fyg2b4rr817ex","_id":"cmkjeg2ns001oyg2b56kg4nns"},{"post_id":"cmkjeg2nr001kyg2bg8bh5158","tag_id":"cmkjeg2nr001fyg2b4rr817ex","_id":"cmkjeg2ns001pyg2b389d06qb"},{"post_id":"cmkjeg2no000uyg2bdp21f5ks","tag_id":"cmkjeg2ns001myg2bc7xzgpr1","_id":"cmkjeg2ns001uyg2b9rne3xd3"},{"post_id":"cmkjeg2no000uyg2bdp21f5ks","tag_id":"cmkjeg2ns001qyg2bb6r3a92w","_id":"cmkjeg2ns001vyg2baidz1ids"},{"post_id":"cmkjeg2no000xyg2b562l4a0z","tag_id":"cmkjeg2ns001qyg2bb6r3a92w","_id":"cmkjeg2nt001zyg2bfz6k6a4j"},{"post_id":"cmkjeg2no000xyg2b562l4a0z","tag_id":"cmkjeg2ns001wyg2b664gcob8","_id":"cmkjeg2nt0020yg2b2qox6oyp"},{"post_id":"cmkjeg2no000xyg2b562l4a0z","tag_id":"cmkjeg2nq0019yg2b40cl6a24","_id":"cmkjeg2nt0022yg2bcb5m6pts"},{"post_id":"cmkjeg2np000zyg2b71sc7uxz","tag_id":"cmkjeg2nq0019yg2b40cl6a24","_id":"cmkjeg2nt0023yg2bhasw1m3s"},{"post_id":"cmkjeg2nq0015yg2b47hg5f9p","tag_id":"cmkjeg2nr001fyg2b4rr817ex","_id":"cmkjeg2nt0025yg2bcvqb9798"},{"post_id":"cmkjeg2nq0018yg2bd14sedct","tag_id":"cmkjeg2ns001myg2bc7xzgpr1","_id":"cmkjeg2nt0027yg2b4760eygc"},{"post_id":"cmkjeg2nq001ayg2b011p9j8b","tag_id":"cmkjeg2nt0026yg2b8ud2bwv0","_id":"cmkjeg2nt002ayg2b1qe5d3eb"},{"post_id":"cmkjeg2nq001ayg2b011p9j8b","tag_id":"cmkjeg2ns001myg2bc7xzgpr1","_id":"cmkjeg2nt002byg2behpbbmoh"},{"post_id":"cmkjeg2nr001gyg2bed8596gh","tag_id":"cmkjeg2nr001fyg2b4rr817ex","_id":"cmkjeg2nt002fyg2bgpogfxqp"},{"post_id":"cmkjeg2nr001gyg2bed8596gh","tag_id":"cmkjeg2nt002cyg2bcmi45hyc","_id":"cmkjeg2nt002gyg2b42zih5z3"},{"post_id":"cmkjeg2nr001gyg2bed8596gh","tag_id":"cmkjeg2ns001myg2bc7xzgpr1","_id":"cmkjeg2nt002iyg2b624waczr"},{"post_id":"cmkjeg2nr001iyg2b4y5fbjnm","tag_id":"cmkjeg2nr001fyg2b4rr817ex","_id":"cmkjeg2nu002kyg2bfaip0ekb"},{"post_id":"cmkjeg2nr001iyg2b4y5fbjnm","tag_id":"cmkjeg2ns001wyg2b664gcob8","_id":"cmkjeg2nu002lyg2bg9gcdllb"},{"post_id":"cmkjeg2nr001iyg2b4y5fbjnm","tag_id":"cmkjeg2nt002hyg2bc82iahty","_id":"cmkjeg2nu002myg2bbojxe0zr"},{"post_id":"cmkjeg2nr001iyg2b4y5fbjnm","tag_id":"cmkjeg2nt002jyg2b0nakelh1","_id":"cmkjeg2nu002nyg2b6l24ddwf"},{"post_id":"cmkjeg2nv002pyg2b5jjs3brv","tag_id":"cmkjeg2nq0019yg2b40cl6a24","_id":"cmkjeg2nv002syg2b45kh4i2z"},{"post_id":"cmkjeg2nv002ryg2b1pf56fjh","tag_id":"cmkjeg2ni0003yg2b321hfvn4","_id":"cmkjeg2nw002uyg2bhe1hhbml"},{"post_id":"cmkjeg2nv002tyg2b78skbevn","tag_id":"cmkjeg2ni0003yg2b321hfvn4","_id":"cmkjeg2nw002vyg2b9a5l8oti"},{"post_id":"cmkjeg2nu002oyg2bg2efcu5v","tag_id":"cmkjeg2nv002qyg2befsn3s84","_id":"cmkjeg2nw002wyg2b5dpce65r"},{"post_id":"cmkjeg2nu002oyg2bg2efcu5v","tag_id":"cmkjeg2nq0019yg2b40cl6a24","_id":"cmkjeg2nw002xyg2b8per07mf"},{"post_id":"cmpscr5y00000pwd04rqlcxdw","tag_id":"cmpscr5y20001pwd07v5gdyrb","_id":"cmpscr5y30003pwd0bv863xz6"},{"post_id":"cmpscr5y00000pwd04rqlcxdw","tag_id":"cmkjeg2ns001myg2bc7xzgpr1","_id":"cmpscr5y40004pwd0ag043ar4"},{"post_id":"cmpscr5y00000pwd04rqlcxdw","tag_id":"cmkjeg2nt002cyg2bcmi45hyc","_id":"cmpscr5y40005pwd014ud3mhy"},{"post_id":"cmpscr5y00000pwd04rqlcxdw","tag_id":"cmpscr5y30002pwd0ctgc5pfb","_id":"cmpscr5y40006pwd0e3wch9ok"},{"post_id":"cmqdpj0yo0000b5d035ud8rew","tag_id":"cmkjeg2ns001qyg2bb6r3a92w","_id":"cmqdpj0ys0002b5d06uv3b8hx"},{"post_id":"cmqdpj0yo0000b5d035ud8rew","tag_id":"cmkjeg2ns001wyg2b664gcob8","_id":"cmqdpj0ys0003b5d09n47947d"},{"post_id":"cmqdpj0yo0000b5d035ud8rew","tag_id":"cmkjeg2nq0019yg2b40cl6a24","_id":"cmqdpj0ys0004b5d0hbrdgqp1"},{"post_id":"cmqdpj0yo0000b5d035ud8rew","tag_id":"cmqdpj0yq0001b5d0fiqw5irr","_id":"cmqdpj0ys0005b5d0am7sa62x"}],"Tag":[{"name":"日志","_id":"cmkjeg2ni0003yg2b321hfvn4"},{"name":"TEST","_id":"cmkjeg2nn000kyg2b47gh630t"},{"name":"音乐","_id":"cmkjeg2no000syg2bar09duju"},{"name":"Vocaloid","_id":"cmkjeg2np000yyg2b20mwglzg"},{"name":"技术分享","_id":"cmkjeg2nq0019yg2b40cl6a24"},{"name":"技术","_id":"cmkjeg2nr001fyg2b4rr817ex"},{"name":"生活","_id":"cmkjeg2ns001myg2bc7xzgpr1"},{"name":"Archlinux","_id":"cmkjeg2ns001qyg2bb6r3a92w"},{"name":"系统优化","_id":"cmkjeg2ns001wyg2b664gcob8"},{"name":"板绘","_id":"cmkjeg2nt0026yg2b8ud2bwv0"},{"name":"学习","_id":"cmkjeg2nt002cyg2bcmi45hyc"},{"name":"HPC","_id":"cmkjeg2nt002hyg2bc82iahty"},{"name":"Linux内核","_id":"cmkjeg2nt002jyg2b0nakelh1"},{"name":"开源工具","_id":"cmkjeg2nv002qyg2befsn3s84"},{"name":"竞赛","_id":"cmpscr5y20001pwd07v5gdyrb"},{"name":"ASC","_id":"cmpscr5y30002pwd0ctgc5pfb"},{"name":"SCX调度器","_id":"cmqdpj0yq0001b5d0fiqw5irr"}]}} \ No newline at end of file +{"meta":{"version":1,"warehouse":"5.0.1"},"models":{"Asset":[{"_id":"themes/fluid/source/css/gitalk.css","path":"css/gitalk.css","modified":0,"renderable":1},{"_id":"themes/fluid/source/css/highlight-dark.styl","path":"css/highlight-dark.styl","modified":0,"renderable":1},{"_id":"themes/fluid/source/css/highlight.styl","path":"css/highlight.styl","modified":0,"renderable":1},{"_id":"themes/fluid/source/css/main.styl","path":"css/main.styl","modified":0,"renderable":1},{"_id":"themes/fluid/source/img/avatar.png","path":"img/avatar.png","modified":0,"renderable":1},{"_id":"themes/fluid/source/img/gensokyo.jpg","path":"img/gensokyo.jpg","modified":0,"renderable":1},{"_id":"themes/fluid/source/img/gh0s7.jpg","path":"img/gh0s7.jpg","modified":0,"renderable":1},{"_id":"themes/fluid/source/img/hifuu.png","path":"img/hifuu.png","modified":0,"renderable":1},{"_id":"themes/fluid/source/img/loading.gif","path":"img/loading.gif","modified":0,"renderable":1},{"_id":"themes/fluid/source/img/police_beian.png","path":"img/police_beian.png","modified":0,"renderable":1},{"_id":"themes/fluid/source/js/boot.js","path":"js/boot.js","modified":0,"renderable":1},{"_id":"themes/fluid/source/js/color-schema.js","path":"js/color-schema.js","modified":0,"renderable":1},{"_id":"themes/fluid/source/js/events.js","path":"js/events.js","modified":0,"renderable":1},{"_id":"themes/fluid/source/js/img-lazyload.js","path":"js/img-lazyload.js","modified":0,"renderable":1},{"_id":"themes/fluid/source/js/leancloud.js","path":"js/leancloud.js","modified":0,"renderable":1},{"_id":"themes/fluid/source/js/local-search.js","path":"js/local-search.js","modified":0,"renderable":1},{"_id":"themes/fluid/source/js/plugins.js","path":"js/plugins.js","modified":0,"renderable":1},{"_id":"themes/fluid/source/js/umami-view.js","path":"js/umami-view.js","modified":0,"renderable":1},{"_id":"themes/fluid/source/js/utils.js","path":"js/utils.js","modified":0,"renderable":1},{"_id":"themes/fluid/source/xml/local-search.xml","path":"xml/local-search.xml","modified":0,"renderable":1},{"_id":"source/music/過ぎ去りし温もりの日々.mp3","path":"music/過ぎ去りし温もりの日々.mp3","modified":0,"renderable":0},{"_id":"source/images/1613f5602b203b38230f19699deb0219454454985.png","path":"images/1613f5602b203b38230f19699deb0219454454985.png","modified":0,"renderable":0},{"_id":"source/images/1730728153314.png","path":"images/1730728153314.png","modified":0,"renderable":0},{"_id":"source/images/20241027_222225.png","path":"images/20241027_222225.png","modified":0,"renderable":0},{"_id":"source/images/5364bba6d035326e82c53504dd53e7c2454454985.png","path":"images/5364bba6d035326e82c53504dd53e7c2454454985.png","modified":0,"renderable":0},{"_id":"source/images/71a5357ef4bd808b10429bc2ea46cb6f454454985.png","path":"images/71a5357ef4bd808b10429bc2ea46cb6f454454985.png","modified":0,"renderable":0},{"_id":"source/images/MyASCStory-3.jpg","path":"images/MyASCStory-3.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory-4.jpg","path":"images/MyASCStory-4.jpg","modified":0,"renderable":0},{"_id":"source/images/Written-By-Human-Not-By-AI-Badge-white@2x.png","path":"images/Written-By-Human-Not-By-AI-Badge-white@2x.png","modified":0,"renderable":0},{"_id":"source/images/amd.png","path":"images/amd.png","modified":0,"renderable":0},{"_id":"source/images/amd.webp","path":"images/amd.webp","modified":0,"renderable":0},{"_id":"source/images/archlinux-logo.png","path":"images/archlinux-logo.png","modified":0,"renderable":0},{"_id":"source/images/asc-events.png","path":"images/asc-events.png","modified":0,"renderable":0},{"_id":"source/images/asc.png","path":"images/asc.png","modified":0,"renderable":0},{"_id":"source/images/audiveris.png","path":"images/audiveris.png","modified":0,"renderable":0},{"_id":"source/images/b2054bbaf6197624d38cc2007d885fd1454454985.png","path":"images/b2054bbaf6197624d38cc2007d885fd1454454985.png","modified":0,"renderable":0},{"_id":"source/images/built_on_the_kde_platform.png","path":"images/built_on_the_kde_platform.png","modified":0,"renderable":0},{"_id":"source/images/caseclosed.png","path":"images/caseclosed.png","modified":0,"renderable":0},{"_id":"source/images/chrome-global-menu.png","path":"images/chrome-global-menu.png","modified":0,"renderable":0},{"_id":"source/images/clonezilla.png","path":"images/clonezilla.png","modified":0,"renderable":0},{"_id":"source/images/firefox-global-menu-solve.png","path":"images/firefox-global-menu-solve.png","modified":0,"renderable":0},{"_id":"source/images/firefox-global-menu.png","path":"images/firefox-global-menu.png","modified":0,"renderable":0},{"_id":"source/images/krita-official.png","path":"images/krita-official.png","modified":0,"renderable":0},{"_id":"source/images/lmms-import.png","path":"images/lmms-import.png","modified":0,"renderable":0},{"_id":"source/images/mhwilds4050.jpg","path":"images/mhwilds4050.jpg","modified":0,"renderable":0},{"_id":"source/images/mhwilds780M.jpg","path":"images/mhwilds780M.jpg","modified":0,"renderable":0},{"_id":"source/images/musecore-launch.png","path":"images/musecore-launch.png","modified":0,"renderable":0},{"_id":"source/images/musecore-to-midi.png","path":"images/musecore-to-midi.png","modified":0,"renderable":0},{"_id":"source/images/musecore.png","path":"images/musecore.png","modified":0,"renderable":0},{"_id":"source/images/phase1.png","path":"images/phase1.png","modified":0,"renderable":0},{"_id":"source/images/phase1_strings.png","path":"images/phase1_strings.png","modified":0,"renderable":0},{"_id":"source/images/phase_2_14.png","path":"images/phase_2_14.png","modified":0,"renderable":0},{"_id":"source/images/phase_4.png","path":"images/phase_4.png","modified":0,"renderable":0},{"_id":"source/images/resendofficial.png","path":"images/resendofficial.png","modified":0,"renderable":0},{"_id":"source/images/rusendhelp1.png","path":"images/rusendhelp1.png","modified":0,"renderable":0},{"_id":"source/images/rusendhelp2.png","path":"images/rusendhelp2.png","modified":0,"renderable":0},{"_id":"source/images/rusendhelp3.png","path":"images/rusendhelp3.png","modified":0,"renderable":0},{"_id":"source/images/searxng.png","path":"images/searxng.png","modified":0,"renderable":0},{"_id":"source/images/xb2score.png","path":"images/xb2score.png","modified":0,"renderable":0},{"_id":"source/images/yana.jpg","path":"images/yana.jpg","modified":0,"renderable":0},{"_id":"source/images/zellij-helix.jpg","path":"images/zellij-helix.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC24-1.jpg","path":"images/MyASCStory/ASC24-1.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC24-2.jpg","path":"images/MyASCStory/ASC24-2.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC24-3.jpg","path":"images/MyASCStory/ASC24-3.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC24-4.jpg","path":"images/MyASCStory/ASC24-4.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC24-5.jpg","path":"images/MyASCStory/ASC24-5.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC24-6.jpg","path":"images/MyASCStory/ASC24-6.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC25-1.jpg","path":"images/MyASCStory/ASC25-1.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC25-2.jpg","path":"images/MyASCStory/ASC25-2.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC25-3.jpg","path":"images/MyASCStory/ASC25-3.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC25-4.jpg","path":"images/MyASCStory/ASC25-4.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC25-5.jpg","path":"images/MyASCStory/ASC25-5.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC25-6.jpg","path":"images/MyASCStory/ASC25-6.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC25-7.jpg","path":"images/MyASCStory/ASC25-7.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC25cluster.jpg","path":"images/MyASCStory/ASC25cluster.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC25-8.jpg","path":"images/MyASCStory/ASC25-8.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC26-1.jpg","path":"images/MyASCStory/ASC26-1.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC26-2.jpg","path":"images/MyASCStory/ASC26-2.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC26-3.jpg","path":"images/MyASCStory/ASC26-3.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC26YeZ.jpg","path":"images/MyASCStory/ASC26YeZ.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/ASC26cluster.jpg","path":"images/MyASCStory/ASC26cluster.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/MyASCStory.jpg","path":"images/MyASCStory/MyASCStory.jpg","modified":0,"renderable":0},{"_id":"source/images/MyASCStory/lzdnb.jpg","path":"images/MyASCStory/lzdnb.jpg","modified":0,"renderable":0}],"Cache":[{"_id":"source/_posts/12月16-17日工作记录.md","hash":"9f5099a65180c2659f1a1ca218d518fc64965676","modified":1780070615032},{"_id":"source/_posts/12月18日工作记录.md","hash":"28892523fcc2fadb1514645a1c59d501ceab53da","modified":1780070615032},{"_id":"source/_posts/12月19日工作记录.md","hash":"058ca01b682cd36e202189ef025d6d4bb79259fb","modified":1780070615032},{"_id":"source/_posts/12月20日工作记录.md","hash":"0d883ab20ae59540f663c5377e799463762a3b11","modified":1780070615032},{"_id":"source/_posts/12月28日进度报告.md","hash":"4cbda36dc6a2c4bc67140e443638a73aadb18fdf","modified":1780070615032},{"_id":"source/_posts/12月31日进度报告.md","hash":"bf541fdb539029c98dedf975b0c15519adcea76e","modified":1780070615032},{"_id":"source/_posts/2025-5-21.md","hash":"2e1406ca2c83a1a91e78fd3453623ba732d81ee7","modified":1780070615032},{"_id":"source/_posts/BlogUpdate.md","hash":"2c694e3895b2ea0c0cea5f45ab2ec16a868a3df7","modified":1780070615032},{"_id":"source/_posts/CGH0S7-s-Blog.md","hash":"9a5c9461aba29f2447c0f6725f5425d5a05e32f0","modified":1780070615032},{"_id":"source/_posts/GentleJena.md","hash":"df1e35a93284d0b9e2d408635cd98481077d5155","modified":1780070615032},{"_id":"source/_posts/One-Last-ASC.md","hash":"e850a0570d4e712f5bef2f39d88ca75d0f3e64cd","modified":1780454857305},{"_id":"source/_posts/Vocaloid调教-晴天.md","hash":"9141182f5dbfbbd840ad340b3e5271507ddd8c94","modified":1780070615032},{"_id":"source/_posts/Your-exclusive-email-address.md","hash":"c1721f4ac1b359140b0b57658ddd39c64c041aa8","modified":1780070615032},{"_id":"source/_posts/arch-nvidia.md","hash":"4144acad32a69e88ef6d5d7d5788da6d87a75193","modified":1780070615032},{"_id":"source/_posts/archlinux-game-fix.md","hash":"a4ab5bd2ca0cf941c5166d3945bd2a2076a54f1e","modified":1780070615032},{"_id":"source/_posts/archlinux-optimization.md","hash":"51c8c12110fb08a1edd1f727ab38a1b2568b690b","modified":1780070615032},{"_id":"source/_posts/clonezilla.md","hash":"598858976961c45ebbbf523014ee792951380dd3","modified":1780070615032},{"_id":"source/_posts/firefox-global-menu.md","hash":"94112689ca74757cc7e6b1ad0fc4d960b64e3fbb","modified":1782714147518},{"_id":"source/_posts/fortune.md","hash":"e249e56dad408ad79b11efc90aa42f764cf1c665","modified":1780070615032},{"_id":"source/_posts/gcc15restrict.md","hash":"50533932fcba41826b8cf75f54b173f0c604136b","modified":1780070615032},{"_id":"source/_posts/kritabest.md","hash":"a137c6edc4ea2ca1d7ea13d8dc2bd1bbb7df6d84","modified":1780070615032},{"_id":"source/_posts/loopers.md","hash":"2b63f5abcfebc672e7f90b3e83cee1b5ac1d4eff","modified":1780070615032},{"_id":"source/_posts/mhwi.md","hash":"b9a73accf93684b8cfac0201288da9b2c5376423","modified":1780070615032},{"_id":"source/_posts/nudtbomblab.md","hash":"51b4410784657675d7c11e17d16c5dc0e5d4a455","modified":1780070615032},{"_id":"source/_posts/os-optimization.md","hash":"7c96f2f88fb750dd15617f875bdc28a58d978680","modified":1780145640163},{"_id":"source/_posts/overleaf.md","hash":"0786994f4571ac178ee40bc130e3c0d8d2d511f9","modified":1780070615032},{"_id":"source/_posts/scx-auto.md","hash":"b7aae28e9da46c5cca90885530f920e1a2e04d07","modified":1781437401327},{"_id":"source/_posts/searxng.md","hash":"90f951d3c098aa1b062131b7489a4b7afab06792","modified":1780070615032},{"_id":"source/_posts/zellij-helix.md","hash":"7eb3072b50ecdb49a20355e05fc613a4a8b7fbdf","modified":1780070615032},{"_id":"source/_posts/原来我还有个博客.md","hash":"acd7e534b31b7a0173fae880fa584c7dc03fd2cd","modified":1780070615032},{"_id":"source/_posts/梦开始的地方.md","hash":"c0bbfefdd277c8ae2786a5cd4cc05f731af11436","modified":1780070615032},{"_id":"source/about/index.md","hash":"deca31f943dde19393499481cdaa380b806e9bf9","modified":1782714679403},{"_id":"source/images/Written-By-Human-Not-By-AI-Badge-white@2x.png","hash":"994225c6fd72521b281144bdd98fefcca53e2c7b","modified":1780070615053},{"_id":"source/images/amd.webp","hash":"cb0cfd5da0b9c10b9b22c65bba881cfde485d763","modified":1780070615053},{"_id":"source/images/amd.png","hash":"2c782df9acd11897c5cb262230d172279e634657","modified":1780145919500},{"_id":"source/images/archlinux-logo.png","hash":"4f6075309fadcb7f7547164cb8a99b4949f74598","modified":1780070615053},{"_id":"source/images/asc-events.png","hash":"a0b3610962062d0a322fe091bcc8f083ab13bc6e","modified":1780070615053},{"_id":"source/images/asc.png","hash":"3a33b35bff1b3f527f7a87a9d6a7d6d9a072a946","modified":1780070615053},{"_id":"source/images/built_on_the_kde_platform.png","hash":"507b6a4323b23772800006505e6c588bb515ebf5","modified":1780070615059},{"_id":"source/images/chrome-global-menu.png","hash":"e545284b445f51b66036f09d0c014c5c0d49203a","modified":1782704119160},{"_id":"source/images/firefox-global-menu-solve.png","hash":"1ca88f27099e20fd9aa9b8e3de8eb2596a42f43b","modified":1782704199466},{"_id":"source/images/firefox-global-menu.png","hash":"906d118a3cad185382589c71d42041f47f3b76ab","modified":1782704170666},{"_id":"source/images/phase1_strings.png","hash":"6fb457adddc5bb32aa463b6227542076c4b501e7","modified":1780070615108},{"_id":"source/images/rusendhelp1.png","hash":"73aa5b41d33c7f3392de28b7b696ebcb12248d82","modified":1780070615122},{"_id":"source/images/rusendhelp3.png","hash":"db656266b54c91e540f3c677b146c0931b61277e","modified":1780070615124},{"_id":"source/images/searxng.png","hash":"9514f2bd14ac1cafa437f3655d9382d477667094","modified":1780070615124},{"_id":"source/images/caseclosed.png","hash":"628b54b49c86a23af595a52daba9e0557b81a17a","modified":1780070615060},{"_id":"source/images/resendofficial.png","hash":"41c2cafe3a11c1674a0a1ec27698ea94cecc9a23","modified":1780070615121},{"_id":"source/images/rusendhelp2.png","hash":"3752f3041d28cbadcb4abc26151f0d17f505908d","modified":1780070615123},{"_id":"source/images/MyASCStory-3.jpg","hash":"04fac29724c5bd41136531d107b2baa59a25dc48","modified":1779253336000},{"_id":"source/images/audiveris.png","hash":"52aa9df5f93559c2e47b30afbf84fc6cc6b34002","modified":1780070615055},{"_id":"source/images/musecore-to-midi.png","hash":"02ba31b29ba069dfef0e1cf26c5ccec9dc3d2289","modified":1780070615103},{"_id":"source/images/1730728153314.png","hash":"95d44ea62557a311503cab58d1dec5cafc97e07d","modified":1780070615039},{"_id":"source/images/musecore.png","hash":"9b2eba87e7c93e57f2024697d1ff4217d206e5ac","modified":1780070615105},{"_id":"source/images/yana.jpg","hash":"757c68ec322e834eace7236c38e74a4f9585d474","modified":1780070615127},{"_id":"source/images/MyASCStory/ASC24-2.jpg","hash":"e7fbd104e81d126d114fd1ebf4e810c721bf0c26","modified":1765630926680},{"_id":"source/images/xb2score.png","hash":"03efbb07a7bb7f6c7d58b98ed125cafe42074b94","modified":1780070615126},{"_id":"source/images/MyASCStory/ASC24-1.jpg","hash":"780bc4106f3517f3d8a7ea3bfc46203b82e7c961","modified":1765630927130},{"_id":"source/images/phase1.png","hash":"42533712403be6036231f1e3770f125858bf91d7","modified":1780070615107},{"_id":"source/images/MyASCStory/ASC24-6.jpg","hash":"e6159d0f2bdc3f4fa355b5440024bbb763bfded9","modified":1765630926580},{"_id":"themes/fluid/source/css/_pages/_tag/tag.styl","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1780070615175},{"_id":"themes/fluid/.editorconfig","hash":"33218fbd623feb43edf5f99f15965392cecc44a6","modified":1780070615168},{"_id":"themes/fluid/.eslintrc","hash":"4bc2b19ce2b8c4d242f97d4ccf2d741e68ab0097","modified":1780070615168},{"_id":"themes/fluid/.gitattributes","hash":"a54f902957d49356376b59287b894b1a3d7a003f","modified":1780070615168},{"_id":"themes/fluid/.gitignore","hash":"ae3bfcb89777657c5dfb5169d91445dcb0e5ab98","modified":1780070615168},{"_id":"themes/fluid/README.md","hash":"ff9b0e1fb9dba665af2f1e4a577f8cb9e840464b","modified":1780070615168},{"_id":"themes/fluid/README_en.md","hash":"365184a73af40e7365504c3077f3d80dfee1d80e","modified":1780070615168},{"_id":"themes/fluid/package.json","hash":"7746460fc2eba7439b494c46aa9b5ded81370819","modified":1780070615171},{"_id":"themes/fluid/LICENSE","hash":"26f9356fd6e84b5a88df6d9014378f41b65ba209","modified":1780070615168},{"_id":"themes/fluid/languages/de.yml","hash":"58dccef1d98b472dc4e6f4693c2297b0c9c5afba","modified":1780070615168},{"_id":"themes/fluid/languages/en.yml","hash":"9c580471257f5a32bee701a059a45ea96755dcdc","modified":1780070615169},{"_id":"themes/fluid/languages/eo.yml","hash":"7c1a0c9f6186b6643b19d3980f055329bdb4efa4","modified":1780070615169},{"_id":"themes/fluid/languages/es.yml","hash":"026ddf1a49bf8ddfef6ed86ab4d6af143c1dd95f","modified":1780070615169},{"_id":"themes/fluid/languages/ru.yml","hash":"93818f8bf07195fb1ebffbb5210e531b0e3a6ec4","modified":1780070615169},{"_id":"themes/fluid/languages/ja.yml","hash":"550b95d3614a64592f02666938d235e9f11e449e","modified":1780070615169},{"_id":"themes/fluid/languages/zh-CN.yml","hash":"a60847136709bb95586a98d9d67b50390a8d2c96","modified":1780070615169},{"_id":"themes/fluid/_config.yml","hash":"07930dbb75bb019a325c1b4c8acdd36633a9625a","modified":1780477158270},{"_id":"themes/fluid/languages/zh-HK.yml","hash":"51c2b4d64c6992a39bfd2586a1bdf5fbbbdf0175","modified":1780070615169},{"_id":"themes/fluid/languages/zh-TW.yml","hash":"e1043de394f6dcf5c0647adcfdefe60637f78426","modified":1780070615169},{"_id":"themes/fluid/layout/about.ejs","hash":"052e9fc19c753f53fdc083c7fb098e3668880140","modified":1780070615171},{"_id":"themes/fluid/layout/404.ejs","hash":"b84d575c7b7f778b4cb64e89ad3d0aed4a896820","modified":1780070615169},{"_id":"themes/fluid/layout/archive.ejs","hash":"7c1f44005849791feae4abaa10fae4cb983d3277","modified":1780070615171},{"_id":"themes/fluid/layout/index.ejs","hash":"33c3317cdcee062789de2336dd8d0cc7f86d3650","modified":1780070615171},{"_id":"themes/fluid/layout/category.ejs","hash":"f099161b738a16a32253f42085b5444f902018ed","modified":1780070615171},{"_id":"themes/fluid/layout/layout.ejs","hash":"7e0023474128fbe4d68c467704c41f1712432415","modified":1780070615171},{"_id":"themes/fluid/layout/categories.ejs","hash":"13859726c27b6c79b5876ec174176d0f9c1ee164","modified":1780070615171},{"_id":"themes/fluid/layout/links.ejs","hash":"1cac32ec4579aaf7b9fa39d317497331d4c5e1dd","modified":1780070615171},{"_id":"themes/fluid/layout/page.ejs","hash":"ed5007a3feb8f14d3d2843271bfb298eb0c56219","modified":1780070615171},{"_id":"themes/fluid/layout/tag.ejs","hash":"9d686364c4d16a1a9219471623af452035c5b966","modified":1780070615171},{"_id":"themes/fluid/layout/tags.ejs","hash":"1d06af34b6cf1d8a20d2eb565e309326ceba309f","modified":1780070615171},{"_id":"themes/fluid/layout/_partials/archive-list.ejs","hash":"7520fbf91f762207c2ab06b2c293235cd5b23905","modified":1780070615169},{"_id":"themes/fluid/layout/_partials/category-chains.ejs","hash":"18309584aab83bc4deb20723ebad832149dd2e24","modified":1780070615169},{"_id":"themes/fluid/layout/post.ejs","hash":"9bf0d357a607a282f3b9cb04525a4df0cc2a8b76","modified":1780070615171},{"_id":"themes/fluid/layout/_partials/comments.ejs","hash":"d707c47b2638c94e489bc43d4cfd098b7c58447f","modified":1780070615169},{"_id":"themes/fluid/layout/_partials/category-list.ejs","hash":"f8d2f1907450e61968e6d54443e9be8138196a77","modified":1780070615169},{"_id":"themes/fluid/layout/_partials/css.ejs","hash":"1dadb118d580280524ed0a5f69bd34d234a92276","modified":1780070615169},{"_id":"themes/fluid/layout/_partials/footer.ejs","hash":"5ce5e2db222fddefb0b6c2046c628d987f994f33","modified":1780145943541},{"_id":"themes/fluid/layout/_partials/header.ejs","hash":"0d5e397d30051e5fbabe7b47cfd1f1e6a5820af1","modified":1780070615170},{"_id":"themes/fluid/layout/_partials/head.ejs","hash":"67be642f99482c07904474f410cfbc2f99003288","modified":1780070615170},{"_id":"themes/fluid/layout/_partials/markdown-plugins.ejs","hash":"fc4bdf7de0cf1a66d0e5e4fba1b31d6f7ed49468","modified":1780070615170},{"_id":"themes/fluid/layout/_partials/search.ejs","hash":"70e1c929e084ca8a2648cedabf29b372511ea2b8","modified":1780070615171},{"_id":"themes/fluid/layout/_partials/paginator.ejs","hash":"0f38a2c238169edcb63fc46c23bfc529ff3859b7","modified":1780070615170},{"_id":"themes/fluid/layout/_partials/scripts.ejs","hash":"da5810785105e5075861593c7ac22c7aa9665a72","modified":1780070615171},{"_id":"themes/fluid/scripts/events/index.js","hash":"79de5a379b28cad759a49048351c7f6b8915bd7d","modified":1780070615171},{"_id":"themes/fluid/scripts/filters/default-injects.js","hash":"b2013ae8e189cd07ebc8a2ff48a78e153345210f","modified":1780070615172},{"_id":"themes/fluid/scripts/filters/locals.js","hash":"58d0fec976f6b1d35e7ea03edc45414088acf05c","modified":1780070615172},{"_id":"themes/fluid/scripts/filters/post-filter.js","hash":"82bb06686158ebe160a631c79f156cd4fde35656","modified":1780070615172},{"_id":"themes/fluid/scripts/generators/index-generator.js","hash":"9159fc22fa84a7b605dd15fe4104f01fe9c71147","modified":1780070615172},{"_id":"themes/fluid/scripts/generators/local-search.js","hash":"9ac5ddad06e9b0e6015ce531430018182a4bc0fa","modified":1780070615172},{"_id":"themes/fluid/scripts/generators/pages.js","hash":"d3e75f53c59674d171309e50702954671f31f1a4","modified":1780070615172},{"_id":"themes/fluid/scripts/helpers/date.js","hash":"9bda6382f61b40a20c24af466fe10c8366ebb74c","modified":1780070615172},{"_id":"themes/fluid/scripts/helpers/engine.js","hash":"d3a231d106795ce99cb0bc77eb65f9ae44515933","modified":1780070615172},{"_id":"themes/fluid/scripts/helpers/export-config.js","hash":"8e67b522c47aa250860e3fe2c733f1f958a506c0","modified":1780070615172},{"_id":"themes/fluid/scripts/helpers/import.js","hash":"ca53e8dbf7d44cfd372cfa79ac60f35a7d5b0076","modified":1780070615172},{"_id":"themes/fluid/scripts/helpers/injects.js","hash":"1ad2ae6b11bd8806ee7dd6eb7140d8b54a95d613","modified":1780070615172},{"_id":"themes/fluid/scripts/helpers/page.js","hash":"4607607445233b3029ef20ed5e91de0da0a7f9c5","modified":1780070615172},{"_id":"themes/fluid/scripts/helpers/scope.js","hash":"d41d9d658fcb54964b388598e996747aadb85b0f","modified":1780070615172},{"_id":"themes/fluid/scripts/helpers/url.js","hash":"2a6a8288176d0e0f6ec008056bf2745a86e8943e","modified":1780070615172},{"_id":"themes/fluid/scripts/helpers/utils.js","hash":"966689d7c5e4320008285395fbaa2751f6209be5","modified":1780070615172},{"_id":"themes/fluid/scripts/helpers/wordcount.js","hash":"4d48c424e47ff9a17a563167ea5f480890267adf","modified":1780070615172},{"_id":"themes/fluid/scripts/tags/button.js","hash":"3eb43a8cdea0a64576ad6b31b4df6c2bf5698d4c","modified":1780070615172},{"_id":"themes/fluid/scripts/tags/fold.js","hash":"73e4fd12ce3e47981479391ed354b7d9d3279f70","modified":1780070615173},{"_id":"themes/fluid/scripts/tags/checkbox.js","hash":"6eaf53cf4bfc756a65bda18184cf8998a12c861d","modified":1780070615173},{"_id":"themes/fluid/scripts/tags/group-image.js","hash":"4aeebb797026f1df25646a5d69f7fde79b1bcd26","modified":1780070615173},{"_id":"themes/fluid/scripts/tags/label.js","hash":"f05a6d32cca79535b22907dc03edb9d3fa2d8176","modified":1780070615173},{"_id":"themes/fluid/scripts/tags/mermaid.js","hash":"75160561e1ef3603b6d2ad2938464ab1cb77fd38","modified":1780070615173},{"_id":"themes/fluid/scripts/tags/note.js","hash":"e3b456a079e5dc0032473b516c865b20f83d2c26","modified":1780070615173},{"_id":"themes/fluid/scripts/utils/compare-versions.js","hash":"dbbc928c914fc2bd242cd66aa0c45971aec13a5d","modified":1780070615173},{"_id":"themes/fluid/scripts/utils/url-join.js","hash":"718aab5e7b2059a06b093ca738de420d9afa44ba","modified":1780070615173},{"_id":"themes/fluid/scripts/utils/object.js","hash":"33b57e4decdc5e75c518859f168c8ba80b2c665b","modified":1780070615173},{"_id":"themes/fluid/scripts/utils/resolve.js","hash":"8c4a8b62aa8608f12f1e9046231dff04859dc3e9","modified":1780070615173},{"_id":"themes/fluid/source/css/highlight-dark.styl","hash":"45695ef75c31a4aa57324dd408b7e2327a337018","modified":1780070615175},{"_id":"themes/fluid/source/css/gitalk.css","hash":"a57b3cc8e04a0a4a27aefa07facf5b5e7bca0e76","modified":1780070615175},{"_id":"themes/fluid/scripts/utils/crypto.js","hash":"ae4ad8a188ef5b3fa6818b01629fc962b3de8551","modified":1780070615173},{"_id":"themes/fluid/source/css/highlight.styl","hash":"a9efc52a646a9e585439c768557e3e3c9e3326dc","modified":1780070615175},{"_id":"themes/fluid/source/css/main.styl","hash":"855ae5fe229c51afa57f7645f6997a27a705d7e4","modified":1780070615175},{"_id":"themes/fluid/source/img/avatar.png","hash":"fe739a158cc128f70f780eb5fa96f388b81d478f","modified":1780070615175},{"_id":"themes/fluid/source/img/police_beian.png","hash":"90efded6baa2dde599a9d6b1387973e8e64923ea","modified":1780070615190},{"_id":"themes/fluid/source/img/loading.gif","hash":"2d2fc0f947940f98c21afafef39ecf226a2e8d55","modified":1780070615190},{"_id":"themes/fluid/source/js/color-schema.js","hash":"1ef88c881b9f942deadde3d890387b94c617342a","modified":1780070615192},{"_id":"themes/fluid/source/js/boot.js","hash":"38bd26c6b7acdafda86dda3560e6a3ca488d3c76","modified":1780070615190},{"_id":"themes/fluid/source/js/events.js","hash":"6869811f67e4c3de3edfa4b08464bb242b97a402","modified":1780070615192},{"_id":"themes/fluid/source/js/img-lazyload.js","hash":"cbdeca434ec4da51f488c821d51b4d23c73294af","modified":1780070615192},{"_id":"themes/fluid/source/js/leancloud.js","hash":"eff77c7a5c399fcaefda48884980571e15243fc9","modified":1780070615192},{"_id":"themes/fluid/source/js/plugins.js","hash":"c34916291e392a774ff3e85c55badb83e8661297","modified":1780070615192},{"_id":"themes/fluid/source/js/local-search.js","hash":"b9945f76f8682f3ec32edfb285b26eb559f7b7e8","modified":1780070615192},{"_id":"themes/fluid/source/js/umami-view.js","hash":"33c4b3883fa747604074ad3921606eeeaeb50716","modified":1780070615192},{"_id":"themes/fluid/source/xml/local-search.xml","hash":"8c96ba6a064705602ce28d096fd7dd9069630a55","modified":1780070615192},{"_id":"themes/fluid/source/js/utils.js","hash":"b82e7c289a66dfd36064470fd41c0e96fc598b43","modified":1780070615192},{"_id":"themes/fluid/layout/_partials/comments/changyan.ejs","hash":"c9b2d68ed3d375f1953e7007307d2a3f75ed6249","modified":1780070615169},{"_id":"themes/fluid/layout/_partials/comments/cusdis.ejs","hash":"5f9dc012be27040bbe874d0c093c0d53958cc987","modified":1780070615169},{"_id":"themes/fluid/layout/_partials/comments/discuss.ejs","hash":"98d065b58ce06b7d18bff3c974e96fa0f34ae03a","modified":1780070615169},{"_id":"themes/fluid/layout/_partials/comments/disqus.ejs","hash":"aab4a4d24c55231a37db308ae94414319cecdd9b","modified":1780070615169},{"_id":"themes/fluid/layout/_partials/comments/giscus.ejs","hash":"95f8b866b158eff9352c381c243b332a155a5110","modified":1780070615169},{"_id":"themes/fluid/layout/_partials/comments/gitalk.ejs","hash":"843bc141a4545eb20d1c92fb63c85d459b4271ec","modified":1780070615169},{"_id":"themes/fluid/layout/_partials/comments/livere.ejs","hash":"2264758fed57542a7389c7aa9f00f1aefa17eb87","modified":1780070615169},{"_id":"themes/fluid/layout/_partials/comments/utterances.ejs","hash":"c7ccf7f28308334a6da6f5425b141a24b5eca0e2","modified":1780070615169},{"_id":"themes/fluid/layout/_partials/comments/twikoo.ejs","hash":"d84bcb5ccd78470a60c067fc914ac0ac67ac8777","modified":1780070615169},{"_id":"themes/fluid/layout/_partials/comments/remark42.ejs","hash":"d4e9532feeb02aed61bd15eda536b5b631454dac","modified":1780070615169},{"_id":"themes/fluid/layout/_partials/comments/valine.ejs","hash":"19ba937553dddd317f827d682661a1066a7b1f30","modified":1780070615169},{"_id":"themes/fluid/layout/_partials/comments/waline.ejs","hash":"3d08c73b77e412d2f06a24d9344565fc7dbc76f8","modified":1780070615169},{"_id":"themes/fluid/layout/_partials/footer/beian.ejs","hash":"4fb9b5dd3f3e41a586d6af44e5069afe7c81fff2","modified":1780070615169},{"_id":"themes/fluid/layout/_partials/footer/statistics.ejs","hash":"954a29b58d72647d20450da270b5d8fb2e0824f5","modified":1780070615170},{"_id":"themes/fluid/layout/_partials/header/banner.ejs","hash":"e07757b59e7b89eea213d0e595cb5932f812fd32","modified":1780070615170},{"_id":"themes/fluid/layout/_partials/header/navigation.ejs","hash":"37d750428772d7c71ba36ce0c2540780d90fadea","modified":1780070615170},{"_id":"themes/fluid/layout/_partials/plugins/analytics.ejs","hash":"e6dcbf1c2f56314d56bb46b50aca86ff68cacebd","modified":1780070615170},{"_id":"themes/fluid/layout/_partials/plugins/code-widget.ejs","hash":"3a505cba37942badf62a56bbb8b605b72af330aa","modified":1780070615170},{"_id":"themes/fluid/layout/_partials/plugins/encrypt.ejs","hash":"0fff24cf5bf99fbe5c56c292e2eac4a89bf29db4","modified":1780070615170},{"_id":"themes/fluid/layout/_partials/plugins/anchorjs.ejs","hash":"40181442d3a2b8734783a0ad7caf2d2522e3f2ab","modified":1780070615170},{"_id":"themes/fluid/layout/_partials/plugins/fancybox.ejs","hash":"9d1ea2a46b8c8ad8c168594d578f40764818ef13","modified":1780070615170},{"_id":"themes/fluid/layout/_partials/plugins/math.ejs","hash":"dcbf9a381ee76f2f1f75fcbc22c50a502ec85023","modified":1780070615170},{"_id":"themes/fluid/layout/_partials/plugins/mermaid.ejs","hash":"03ac02762f801970d1c4e73d6ec8d4c503780e50","modified":1780070615170},{"_id":"themes/fluid/layout/_partials/plugins/highlight.ejs","hash":"7529dd215b09d3557804333942377b9e20fa554e","modified":1780070615170},{"_id":"themes/fluid/layout/_partials/plugins/moment.ejs","hash":"4ff3fb1b60ccc95a0af3bbdbd0757fedefc088b5","modified":1780070615170},{"_id":"themes/fluid/layout/_partials/plugins/nprogress.ejs","hash":"4c2d39ce816b8a6dcd6b53113c8695f8bd650a23","modified":1780070615170},{"_id":"themes/fluid/layout/_partials/post/category-bar.ejs","hash":"8772bce97ed297e7a88523f4e939ed6436c22f87","modified":1780070615170},{"_id":"themes/fluid/layout/_partials/plugins/typed.ejs","hash":"f345374885cd6a334f09a11f59c443b5d577c06c","modified":1780070615170},{"_id":"themes/fluid/layout/_partials/post/meta-top.ejs","hash":"54dd479dbb440126e4ddd9d902229db5afaaae98","modified":1780070615171},{"_id":"themes/fluid/layout/_partials/post/meta-bottom.ejs","hash":"375974ec017696e294dc12469fb0ae257800dc2d","modified":1780070615171},{"_id":"themes/fluid/layout/_partials/post/copyright.ejs","hash":"cbfa32c5f5973133afd043853b24f8200455cb2d","modified":1780070615171},{"_id":"themes/fluid/layout/_partials/post/sidebar-left.ejs","hash":"9992c99b3eb728ad195970e1b84d665f2c8691c4","modified":1780070615171},{"_id":"themes/fluid/layout/_partials/post/sidebar-right.ejs","hash":"d5fcc9b60e02f869a29a8c17a16a6028ecc1e6d8","modified":1780070615171},{"_id":"themes/fluid/layout/_partials/post/toc.ejs","hash":"635a89060fbf72eeda066fc4bd0a97462f069417","modified":1780070615171},{"_id":"themes/fluid/scripts/events/lib/compatible-configs.js","hash":"ef474d1fa5bbafc52619ced0f9dc7eaf2affb363","modified":1780070615172},{"_id":"themes/fluid/scripts/events/lib/hello.js","hash":"bd8376e1cf7892dc2daa58f2f443574be559fdbf","modified":1780070615172},{"_id":"themes/fluid/scripts/events/lib/footnote.js","hash":"c19ac8050b82c3676b0332a56099ccfcc36d9d52","modified":1780070615172},{"_id":"themes/fluid/scripts/events/lib/highlight.js","hash":"a5fe1deccb73b5f578797dbb11038efc15f63ce8","modified":1780070615172},{"_id":"themes/fluid/scripts/events/lib/lazyload.js","hash":"9ba0d4bc224e22af8a5a48d6ff13e5a0fcfee2a4","modified":1780070615172},{"_id":"themes/fluid/scripts/events/lib/injects.js","hash":"5ae4b07204683e54b5a1b74e931702bbce2ac23e","modified":1780070615172},{"_id":"themes/fluid/scripts/events/lib/merge-configs.js","hash":"7c944c43b2ece5dd84859bd9d1fe955d13427387","modified":1780070615172},{"_id":"themes/fluid/source/css/_mixins/base.styl","hash":"542e306ee9494e8a78e44d6d7d409605d94caeb3","modified":1780070615173},{"_id":"themes/fluid/source/css/_functions/base.styl","hash":"2e46f3f4e2c9fe34c1ff1c598738fc7349ae8188","modified":1780070615173},{"_id":"themes/fluid/source/css/_pages/pages.styl","hash":"b8e887bc7fb3b765a1f8ec9448eff8603a41984f","modified":1780070615175},{"_id":"themes/fluid/source/css/_variables/base.styl","hash":"4ed5f0ae105ef4c7dd92eaf652ceda176c38e502","modified":1780070615175},{"_id":"themes/fluid/source/css/_pages/_archive/archive.styl","hash":"c475e6681546d30350eaed11f23081ecae80c375","modified":1780070615173},{"_id":"themes/fluid/source/css/_pages/_about/about.styl","hash":"97fe42516ea531fdad771489b68aa8b2a7f6ae46","modified":1780070615173},{"_id":"themes/fluid/source/css/_pages/_base/base.styl","hash":"643284c567665f96915f0b64e59934dda315f74d","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_base/inline.styl","hash":"411a3fa3f924a87e00ff04d18b5c83283b049a4d","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_base/color-schema.styl","hash":"85492ef64d7e5f70f0f7e46d570bbc911e686d7e","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_base/keyframes.styl","hash":"94065ea50f5bef7566d184f2422f6ac20866ba22","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_base/print.styl","hash":"166afbc596ea4b552bad7290ec372d25ec34db7b","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_category/category-bar.styl","hash":"cc6df43fef6bb3efecbfdd8b9e467424a1dea581","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_category/category-list.styl","hash":"7edfe1b571ecca7d08f5f4dbcf76f4ffdcfbf0b5","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_category/category-chain.styl","hash":"0cdf7ef50dfd0669d3b257821384ff31cd81b7c9","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_links/links.styl","hash":"5c7f2044e3f1da05a3229537c06bd879836f8d6e","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_index/index.styl","hash":"25fb6fa4c783b847c632584c49a7e1593cdb2f5d","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_post/comment.styl","hash":"780f3788e7357bcd3f3262d781cb91bb53976a93","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_post/markdown.styl","hash":"1e3d3a82721e7c10bcfcecec6d81cf2979039452","modified":1780070615175},{"_id":"themes/fluid/source/css/_pages/_post/highlight.styl","hash":"4df764d298fe556e501db4afc2b05686fe6ebcfb","modified":1780070615175},{"_id":"themes/fluid/source/css/_pages/_post/post-tag.styl","hash":"c96d36aa8fe20f0c3c1a29ee2473cd8064b10f73","modified":1780070615175},{"_id":"themes/fluid/source/css/_pages/_post/post-page.styl","hash":"7eee3f78296a3c81849a5415d1d43dcc6e03e6aa","modified":1780070615175},{"_id":"themes/fluid/source/css/_pages/_tag/tags.styl","hash":"65bfc01c76abc927fa1a23bf2422892b0d566c3f","modified":1780070615175},{"_id":"themes/fluid/source/css/_pages/_base/_widget/anchorjs.styl","hash":"e0cebda4a6f499aff75e71417d88caa7ceb13b94","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_base/_widget/board.styl","hash":"4397037fc3f0033dbe546c33cd9dbdabd8cb1632","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_base/_widget/banner.styl","hash":"7a0bd629bc234fc75e3cc8e3715ffada92f09e73","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_base/_widget/copyright.styl","hash":"26f71a9cd60d96bb0cb5bbdf58150b8e524d9707","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_base/_widget/code-widget.styl","hash":"b66ab013f0f37d724a149b85b3c7432afcf460ad","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_base/_widget/footnote.styl","hash":"ae9289cc89649af2042907f8a003303b987f3404","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_base/_widget/header.styl","hash":"d42b748f2f49ef32aafb1a21d75991d2459da927","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_base/_widget/footer.styl","hash":"2caaca71dd1ff63d583099ed817677dd267b457e","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_base/_widget/modal.styl","hash":"adf6c1e5c8e1fb41c77ce6e2258001df61245aa2","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_base/_widget/pagination.styl","hash":"8bb1b68e5f3552cb48c2ffa31edbc53646a8fb4c","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_base/_widget/ngrogress.styl","hash":"5d225357b4a58d46118e6616377168336ed44cb2","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_base/_widget/qrcode.styl","hash":"78704a94c0436097abfb0e0a57abeb3429c749b7","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_base/_widget/noscript.styl","hash":"0cf2f2bb44f456150d428016675d5876a9d2e2aa","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_base/_widget/scroll-btn.styl","hash":"f0e429a27fa8a7658fcbddbb4d4dbe4afa12499a","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_base/_widget/toc.styl","hash":"9e7452aa2372153f25d7a4675c9d36d281a65d24","modified":1780070615174},{"_id":"themes/fluid/source/css/_pages/_base/_widget/search.styl","hash":"10f7e91a91e681fb9fe46f9df7707b9ef78707c8","modified":1780070615174},{"_id":"source/images/5364bba6d035326e82c53504dd53e7c2454454985.png","hash":"11b11f23ebebbe08e8cf826b48eb37b4084796cd","modified":1780070615049},{"_id":"themes/fluid/source/img/hifuu.png","hash":"331b5950baf96f5d39192bb42b7da4d22a08992e","modified":1780070615190},{"_id":"source/images/1613f5602b203b38230f19699deb0219454454985.png","hash":"3d71204d9a0731384f97ad0f90802a5c9fd96776","modified":1780070615036},{"_id":"source/images/71a5357ef4bd808b10429bc2ea46cb6f454454985.png","hash":"dddf72bb12aef01baa3eb2017bb867f274059f12","modified":1780070615053},{"_id":"source/images/mhwilds4050.jpg","hash":"9b2b15c66e2340c15b86cec0fa8f818b67e06384","modified":1780070615084},{"_id":"source/images/clonezilla.png","hash":"7f443d79783df3744b2e51613422ee8fd7eea6f8","modified":1780070615064},{"_id":"source/images/mhwilds780M.jpg","hash":"e2c59c6caae452afb424d9f2fc3a255a44121b49","modified":1780070615088},{"_id":"themes/fluid/source/img/gh0s7.jpg","hash":"1c5af4f8cacdc3c6adbe8334866774cca2944910","modified":1780070615189},{"_id":"source/images/phase_2_14.png","hash":"f50399a9eb469ed1e063ff4ccc0e8636c1b75f1e","modified":1780070615114},{"_id":"source/images/phase_4.png","hash":"81cd309a8990e015a8679b6a4e26a27d804c1da6","modified":1780070615120},{"_id":"source/images/20241027_222225.png","hash":"f0595d1315d94e72e61c69e0499e5129e1821ba1","modified":1780070615046},{"_id":"source/images/b2054bbaf6197624d38cc2007d885fd1454454985.png","hash":"b021e971cedaa55b273eab2b10be94f795b06922","modified":1780070615059},{"_id":"source/images/MyASCStory/ASC24-3.jpg","hash":"49116b67ce66255f2a9e5fc2ecdaea9880f5e767","modified":1780451828936},{"_id":"source/images/MyASCStory/ASC24-4.jpg","hash":"8e6d378316ca24156eba819d5fd963484786684b","modified":1780452072895},{"_id":"source/images/lmms-import.png","hash":"002535b55a41f7a7dcba24517dd2397589416a64","modified":1780070615080},{"_id":"source/images/MyASCStory/MyASCStory.jpg","hash":"7815e0f7c89688227c7520de9724406a9ab2bdef","modified":1780306582000},{"_id":"source/music/過ぎ去りし温もりの日々.mp3","hash":"22016dcdfcbac9393afd3fe97f2d6d6009cd57f5","modified":1780070615166},{"_id":"source/images/MyASCStory/lzdnb.jpg","hash":"42dd3b118023f82cb36d77ad77dc7f7f40d4103d","modified":1765630938320},{"_id":"source/images/krita-official.png","hash":"2ec810a739ce33ad5666093b8c36d29028a17c17","modified":1780070615073},{"_id":"source/images/MyASCStory/ASC25-5.jpg","hash":"5e36bd718ef8caf5425f181d57f2b9b5c97e5f99","modified":1765619211660},{"_id":"source/images/MyASCStory/ASC26-1.jpg","hash":"f6eb79393b7d39429818302742023660199eb2af","modified":1780306548000},{"_id":"source/images/MyASCStory/ASC24-5.jpg","hash":"6a9964d579e6d196f921b655938f707a5592e798","modified":1765630926690},{"_id":"source/images/MyASCStory/ASC25-6.jpg","hash":"932191925ba50d510b4543ab3eb01c85ed76e823","modified":1765619247280},{"_id":"source/images/MyASCStory/ASC25-1.jpg","hash":"f92f3c1030c8d5b7bb1fcc913aea7949237dc8bd","modified":1765619245610},{"_id":"source/images/musecore-launch.png","hash":"0a6c5642d05b197555135cd602886311a9122df0","modified":1780070615101},{"_id":"source/images/zellij-helix.jpg","hash":"87fb5b7b51a4e3b062b891c04488a53457edded0","modified":1780070615150},{"_id":"source/images/MyASCStory/ASC26YeZ.jpg","hash":"ed0bc33d58e430e9371adeac67e16b4973bd1418","modified":1778943718000},{"_id":"source/images/MyASCStory/ASC25cluster.jpg","hash":"599b6a4786cca30fc8704b0817d145950da27e63","modified":1765619271450},{"_id":"source/images/MyASCStory/ASC26-2.jpg","hash":"587340074e0536eec119ca2373ff78baaa17e818","modified":1779186419000},{"_id":"source/images/MyASCStory/ASC26-3.jpg","hash":"d492692b3e1682567ad2bad0b0b2fc79c7d76199","modified":1779292791000},{"_id":"source/images/MyASCStory/ASC26cluster.jpg","hash":"0f0ed96c6c887f809c6a464d4885f8bc81b94082","modified":1779081182000},{"_id":"source/images/MyASCStory/ASC25-3.jpg","hash":"140615d948984648181432dd50bf6d068b32f6de","modified":1765619202720},{"_id":"themes/fluid/source/img/gensokyo.jpg","hash":"d82c0d5011c6cbc37109c9c80530d14cd266286f","modified":1780070615188},{"_id":"source/images/MyASCStory/ASC25-8.jpg","hash":"914c495957e963971fcfda35592bb2789815a38a","modified":1765619201150},{"_id":"source/images/MyASCStory-4.jpg","hash":"d81c735d5a935b6e5f06de24cd25b2b1c32633fc","modified":1779066563000},{"_id":"source/images/MyASCStory/ASC25-4.jpg","hash":"977ab0ba0b554c2ce1a70e8916ab75b387e1bca3","modified":1765619209740},{"_id":"source/images/MyASCStory/ASC25-7.jpg","hash":"878215f2fba4e5fe7fadfdd8e83d365ed459f7ba","modified":1765619184020},{"_id":"source/images/MyASCStory/ASC25-2.jpg","hash":"b0103bc7aee30ca94e550fd2e1c413725320a96e","modified":1765619162040},{"_id":"public/local-search.xml","hash":"3d91afc8645236bbee01fd5e79de80a2f6b1ccb0","modified":1782714810561},{"_id":"public/about/index.html","hash":"09c04080a8b9e137f60fe262bba7da5edcef15b6","modified":1782714810561},{"_id":"public/2026/06/14/scx-auto/index.html","hash":"29075a42f682aa5a2589b9b26759be1ac1f364c7","modified":1782714810561},{"_id":"public/2026/06/29/firefox-global-menu/index.html","hash":"10fc94f8224bdaedcea9df96c9455c0eeffac082","modified":1782714810561},{"_id":"public/2026/05/30/One-Last-ASC/index.html","hash":"cdee15f26c0335fbef2c62ed53b15283ab87916f","modified":1782714810561},{"_id":"public/2026/01/18/os-optimization/index.html","hash":"6c7d4bd4804011f2c550ba043709c65a34c86062","modified":1782714810561},{"_id":"public/2025/11/15/Your-exclusive-email-address/index.html","hash":"151a77246227b2eeaaaf1476cdaf92cb50bd99b9","modified":1782714810561},{"_id":"public/2025/07/13/kritabest/index.html","hash":"b441291511f92ca5ae3b8039a07ad34daf67a697","modified":1782714810561},{"_id":"public/2025/05/23/gcc15restrict/index.html","hash":"6a60c2fd92b4e959a78eae8422b2d32439b670ff","modified":1782714810561},{"_id":"public/2025/05/22/fortune/index.html","hash":"de630c14cce29fcfa5a9e21b87f58119517ada11","modified":1782714810561},{"_id":"public/2025/05/21/2025-5-21/index.html","hash":"3ab1446d400990b3300a9a4d50992d2cd688c4dd","modified":1782714810561},{"_id":"public/2025/03/10/searxng/index.html","hash":"ff775ac3b800c86b96c00ef3c0a6bb217171ea5f","modified":1782714810561},{"_id":"public/2025/03/10/archlinux-game-fix/index.html","hash":"42e586e61cac7cf23c394a15496732331d7fc61d","modified":1782714810561},{"_id":"public/2025/02/24/nudtbomblab/index.html","hash":"69468954d94972687fe01d03fc4777df11d62024","modified":1782714810561},{"_id":"public/2025/02/23/clonezilla/index.html","hash":"c9c144bfbdcf7259fca97fed52177706ad012a47","modified":1782714810561},{"_id":"public/2025/02/02/archlinux-optimization/index.html","hash":"da5e5f63eb160f9714f29f448c00878e02468c61","modified":1782714810561},{"_id":"public/2024/12/13/zellij-helix/index.html","hash":"70c94bef3d850cc429de6962f1d0ad993735cf82","modified":1782714810561},{"_id":"public/2024/11/11/mhwi/index.html","hash":"5641b29f0f8ab724e25c2cf9ff90956ab5969926","modified":1782714810561},{"_id":"public/2024/11/06/arch-nvidia/index.html","hash":"21196d187aff8588edd0ccfaf632f94110d021fd","modified":1782714810561},{"_id":"public/2024/11/06/overleaf/index.html","hash":"3f87a2fdb73995e3a26abac55a36788294dc8ddd","modified":1782714810561},{"_id":"public/2024/11/04/loopers/index.html","hash":"f3003d65bbf326ecd46931f685ab4758cde83ebb","modified":1782714810561},{"_id":"public/2024/11/04/BlogUpdate/index.html","hash":"df4a33fe8868ee61b0996714e28160e9444ab626","modified":1782714810561},{"_id":"public/2024/11/04/Vocaloid调教-晴天/index.html","hash":"e159dc81271548dd02d41ea3459409060e3893f3","modified":1782714810561},{"_id":"public/2024/10/30/GentleJena/index.html","hash":"d53e67a179e1b7a01b23cecc40b9adc9098ff768","modified":1782714810561},{"_id":"public/2024/06/03/原来我还有个博客/index.html","hash":"2bd4bf7a5effe28a48ff775a751410c3843c6d4e","modified":1782714810561},{"_id":"public/2023/12/31/12月31日进度报告/index.html","hash":"eb3afc0de56426f592ac7a2a1693039076af469e","modified":1782714810561},{"_id":"public/2023/12/28/12月28日进度报告/index.html","hash":"f42e1fb01b7c45d9537164ff21d9a7f1bdbc94b8","modified":1782714810561},{"_id":"public/2023/12/20/12月20日工作记录/index.html","hash":"4e593b73c76edef6775dbec06bcaae1de2178418","modified":1782714810561},{"_id":"public/2023/12/19/12月19日工作记录/index.html","hash":"57083c24e062c4b08d054a8336590192d2b62297","modified":1782714810561},{"_id":"public/2023/12/18/12月18日工作记录/index.html","hash":"91cb3241611c435c068fd359733d965016a950d1","modified":1782714810561},{"_id":"public/2023/12/17/12月16-17日工作记录/index.html","hash":"51c436ac73759f03be64c24bcdb20eff77fcc000","modified":1782714810561},{"_id":"public/2023/12/06/梦开始的地方/index.html","hash":"c73d0a9ea4ccf56559ea91b5a1042fa92cf33e3d","modified":1782714810561},{"_id":"public/2023/12/06/CGH0S7-s-Blog/index.html","hash":"0671416925d9f4be30f6e5b9af667e9b36291a56","modified":1782714810561},{"_id":"public/archives/index.html","hash":"a030652caf70de47dbcc2393f3c4019d96cf7fc2","modified":1782714810561},{"_id":"public/archives/page/2/index.html","hash":"b24e1ab990293b5892b446633ce74d0799293b06","modified":1782714810561},{"_id":"public/archives/page/3/index.html","hash":"b8ada48281e8237600e097952efcae647d733749","modified":1782714810561},{"_id":"public/archives/page/4/index.html","hash":"998ec28e0d8f9d4ee8e47d5c34298b6159abc450","modified":1782714810561},{"_id":"public/archives/2023/index.html","hash":"c6962f46364979653a8d13337464973af483bc21","modified":1782714810561},{"_id":"public/archives/2023/12/index.html","hash":"8bb7ca2ee67b73fb56274d66e5dc51ff7923a662","modified":1782714810561},{"_id":"public/archives/2024/index.html","hash":"ec08e47535f2e607fcb407d820c16da4594e1947","modified":1782714810561},{"_id":"public/archives/2024/06/index.html","hash":"19b1e6b47fbba7f5a65d7b182d1acfbbc5ac27cc","modified":1782714810561},{"_id":"public/archives/2024/10/index.html","hash":"23393aac4abd4ed0cd4ed97e1b10bcd1c6c64b6a","modified":1782714810561},{"_id":"public/archives/2024/11/index.html","hash":"a731d6155ae6f52c69a0a380a40c9e489a99fd94","modified":1782714810561},{"_id":"public/archives/2024/12/index.html","hash":"d617c73ed191efe0b9952ce586ad6c9a024debec","modified":1782714810561},{"_id":"public/archives/2025/index.html","hash":"9f96e83eeca2e9685849e6495f40fb2103e92cee","modified":1782714810561},{"_id":"public/archives/2025/02/index.html","hash":"f1f819cd2e8213b5e44e0211da0a539822c33422","modified":1782714810561},{"_id":"public/archives/2025/03/index.html","hash":"a964a8373caec7c6afd324e6c67fd5310cc3a65b","modified":1782714810561},{"_id":"public/archives/2025/05/index.html","hash":"b11e00b49aa9aa5e997e23b62305c92dd2030be9","modified":1782714810561},{"_id":"public/archives/2025/07/index.html","hash":"d0d929756b8eee65d6158821e637f4b5adb60971","modified":1782714810561},{"_id":"public/archives/2025/11/index.html","hash":"428f496f3d2b89d33764634e46716a8ecc5303e1","modified":1782714810561},{"_id":"public/archives/2026/index.html","hash":"eef5fd28d46918cc829f77f36a6eb6ab7df91b51","modified":1782714810561},{"_id":"public/archives/2026/01/index.html","hash":"301d17382266181d90b759a6a24e84f3927a9999","modified":1782714810561},{"_id":"public/archives/2026/05/index.html","hash":"a2371e0dba09c3003321e75b8f4337c5ec15dce7","modified":1782714810561},{"_id":"public/archives/2026/06/index.html","hash":"9340b15b0aef8051e105aaebe9d0219d7ae65c9a","modified":1782714810561},{"_id":"public/categories/更新/index.html","hash":"89a39307627a63ba9c70458f7f96881637d1d8a0","modified":1782714810561},{"_id":"public/categories/Rearrangement/index.html","hash":"e6c79134b1cc341df50a47e311d51331c3560320","modified":1782714810561},{"_id":"public/categories/Vocaloid/index.html","hash":"d8c44239dce7a3b4ebf9c93b72b290ecb77f408a","modified":1782714810561},{"_id":"public/categories/技术分享/index.html","hash":"6f7c92be6d201565fd95ea52315f01529bf3dff6","modified":1782714810561},{"_id":"public/categories/技术分享,HPC/index.html","hash":"ce33a0692bcceca143bcffcd76f81a5fb7042efc","modified":1782714810561},{"_id":"public/index.html","hash":"8ac5b4f17b27bf0590e57ded6fa5995ed2a5262c","modified":1782714810561},{"_id":"public/page/2/index.html","hash":"3c68aa775e15cdf338462d87067d2f04f0879e6e","modified":1782714810561},{"_id":"public/page/4/index.html","hash":"f17578360e65de5bd7b477afc268d58d49a65f93","modified":1782714810561},{"_id":"public/page/3/index.html","hash":"4d92af53142a1a0879c92191815fe2c4d36cf319","modified":1782714810561},{"_id":"public/tags/日志/index.html","hash":"318a8d865f9b5dfe2bfea0c63d72177977fab546","modified":1782714810561},{"_id":"public/tags/日志/page/2/index.html","hash":"bbbd12a3d7f9710927bc5bc9df233c0c8c05ef4f","modified":1782714810561},{"_id":"public/tags/TEST/index.html","hash":"8cc4f9fbda65fca9fdfd51f9ff17462dcb32d277","modified":1782714810561},{"_id":"public/tags/音乐/index.html","hash":"485567a47c4a712386deabdef85f0dfd139347e2","modified":1782714810561},{"_id":"public/tags/Vocaloid/index.html","hash":"cb6864ba7e6831810a16fa706e6950bc5d0017da","modified":1782714810561},{"_id":"public/tags/技术分享/index.html","hash":"7eafec62c8bdef030d2da43bea71afdef203a405","modified":1782714810561},{"_id":"public/tags/竞赛/index.html","hash":"889076e4fc7b84f1dc1c026aac38fc28bbfba5f7","modified":1782714810561},{"_id":"public/tags/生活/index.html","hash":"428d0d4b8da1af6358ae30868dabd2afbe667b95","modified":1782714810561},{"_id":"public/tags/学习/index.html","hash":"ed3b6c41bfe973ba340a84814cd7abd8b60601a9","modified":1782714810561},{"_id":"public/tags/ASC/index.html","hash":"1e77c50a5bb002a042badf3766321dc19708c75b","modified":1782714810561},{"_id":"public/tags/技术/index.html","hash":"64bc80a9df17675a4236cb6bd41abba2f3b176f6","modified":1782714810561},{"_id":"public/tags/Archlinux/index.html","hash":"47603ff5de854411462c033d25d9801b3fe83f58","modified":1782714810561},{"_id":"public/tags/系统优化/index.html","hash":"5d894caa5532b89df253e79f4f496b20ec5d2319","modified":1782714810561},{"_id":"public/tags/Firefox/index.html","hash":"6ac3583ec7c80fd44f6add482e23182c6451bd96","modified":1782714810561},{"_id":"public/tags/板绘/index.html","hash":"24458d6537863828950bc37fd543f98745d5764d","modified":1782714810561},{"_id":"public/tags/HPC/index.html","hash":"d209c326ad279d13e33a549581dd1fe94c563687","modified":1782714810561},{"_id":"public/tags/Linux内核/index.html","hash":"317a1b477d45e02672f40e49bcc942f6095bed1e","modified":1782714810561},{"_id":"public/tags/SCX调度器/index.html","hash":"b85be29870707442789c5d924d72bb5c0b9bea02","modified":1782714810561},{"_id":"public/tags/开源工具/index.html","hash":"280aa446d2a4ed4cb7493f25f8e72ce7a6c7e708","modified":1782714810561},{"_id":"public/404.html","hash":"6b88bf3c008f9d52de79ec09106134660fb7ae97","modified":1782714810561},{"_id":"public/tags/index.html","hash":"f292a3af00ed977cf466c4e35a5c5e4c5cd4ba3f","modified":1782714810561},{"_id":"public/categories/index.html","hash":"abbc6670f98bfec3d077fe59441aab29f4e82df3","modified":1782714810561},{"_id":"public/links/index.html","hash":"507dbe71929967842a372bca6fdf90cdd16dfb8c","modified":1782714810561},{"_id":"public/img/loading.gif","hash":"2d2fc0f947940f98c21afafef39ecf226a2e8d55","modified":1782714810561},{"_id":"public/img/avatar.png","hash":"fe739a158cc128f70f780eb5fa96f388b81d478f","modified":1782714810561},{"_id":"public/img/police_beian.png","hash":"90efded6baa2dde599a9d6b1387973e8e64923ea","modified":1782714810561},{"_id":"public/xml/local-search.xml","hash":"8c96ba6a064705602ce28d096fd7dd9069630a55","modified":1782714810561},{"_id":"public/images/Written-By-Human-Not-By-AI-Badge-white@2x.png","hash":"994225c6fd72521b281144bdd98fefcca53e2c7b","modified":1782714810561},{"_id":"public/images/amd.webp","hash":"cb0cfd5da0b9c10b9b22c65bba881cfde485d763","modified":1782714810561},{"_id":"public/images/amd.png","hash":"2c782df9acd11897c5cb262230d172279e634657","modified":1782714810561},{"_id":"public/images/archlinux-logo.png","hash":"4f6075309fadcb7f7547164cb8a99b4949f74598","modified":1782714810561},{"_id":"public/images/asc-events.png","hash":"a0b3610962062d0a322fe091bcc8f083ab13bc6e","modified":1782714810561},{"_id":"public/images/asc.png","hash":"3a33b35bff1b3f527f7a87a9d6a7d6d9a072a946","modified":1782714810561},{"_id":"public/images/built_on_the_kde_platform.png","hash":"507b6a4323b23772800006505e6c588bb515ebf5","modified":1782714810561},{"_id":"public/images/chrome-global-menu.png","hash":"e545284b445f51b66036f09d0c014c5c0d49203a","modified":1782714810561},{"_id":"public/images/firefox-global-menu-solve.png","hash":"1ca88f27099e20fd9aa9b8e3de8eb2596a42f43b","modified":1782714810561},{"_id":"public/images/firefox-global-menu.png","hash":"906d118a3cad185382589c71d42041f47f3b76ab","modified":1782714810561},{"_id":"public/img/hifuu.png","hash":"331b5950baf96f5d39192bb42b7da4d22a08992e","modified":1782714810561},{"_id":"public/images/phase1_strings.png","hash":"6fb457adddc5bb32aa463b6227542076c4b501e7","modified":1782714810561},{"_id":"public/css/gitalk.css","hash":"a57b3cc8e04a0a4a27aefa07facf5b5e7bca0e76","modified":1782714810561},{"_id":"public/css/highlight.css","hash":"04d4ddbb5e1d1007447c2fe293ee05aae9b9563e","modified":1782714810561},{"_id":"public/css/main.css","hash":"06433abcb688c2f5473623a9355bff9865c48d3f","modified":1782714810561},{"_id":"public/css/highlight-dark.css","hash":"902294bada4323c0f51502d67cba8c3a0298952f","modified":1782714810561},{"_id":"public/js/boot.js","hash":"38bd26c6b7acdafda86dda3560e6a3ca488d3c76","modified":1782714810561},{"_id":"public/js/color-schema.js","hash":"1ef88c881b9f942deadde3d890387b94c617342a","modified":1782714810561},{"_id":"public/js/events.js","hash":"6869811f67e4c3de3edfa4b08464bb242b97a402","modified":1782714810561},{"_id":"public/js/img-lazyload.js","hash":"cbdeca434ec4da51f488c821d51b4d23c73294af","modified":1782714810561},{"_id":"public/js/leancloud.js","hash":"eff77c7a5c399fcaefda48884980571e15243fc9","modified":1782714810561},{"_id":"public/js/local-search.js","hash":"b9945f76f8682f3ec32edfb285b26eb559f7b7e8","modified":1782714810561},{"_id":"public/js/plugins.js","hash":"c34916291e392a774ff3e85c55badb83e8661297","modified":1782714810561},{"_id":"public/js/umami-view.js","hash":"33c4b3883fa747604074ad3921606eeeaeb50716","modified":1782714810561},{"_id":"public/js/utils.js","hash":"b82e7c289a66dfd36064470fd41c0e96fc598b43","modified":1782714810561},{"_id":"public/images/rusendhelp1.png","hash":"73aa5b41d33c7f3392de28b7b696ebcb12248d82","modified":1782714810561},{"_id":"public/images/searxng.png","hash":"9514f2bd14ac1cafa437f3655d9382d477667094","modified":1782714810561},{"_id":"public/images/rusendhelp3.png","hash":"db656266b54c91e540f3c677b146c0931b61277e","modified":1782714810561},{"_id":"public/images/caseclosed.png","hash":"628b54b49c86a23af595a52daba9e0557b81a17a","modified":1782714810561},{"_id":"public/images/resendofficial.png","hash":"41c2cafe3a11c1674a0a1ec27698ea94cecc9a23","modified":1782714810561},{"_id":"public/images/rusendhelp2.png","hash":"3752f3041d28cbadcb4abc26151f0d17f505908d","modified":1782714810561},{"_id":"public/images/MyASCStory-3.jpg","hash":"04fac29724c5bd41136531d107b2baa59a25dc48","modified":1782714810561},{"_id":"public/images/audiveris.png","hash":"52aa9df5f93559c2e47b30afbf84fc6cc6b34002","modified":1782714810561},{"_id":"public/images/musecore-to-midi.png","hash":"02ba31b29ba069dfef0e1cf26c5ccec9dc3d2289","modified":1782714810561},{"_id":"public/img/gh0s7.jpg","hash":"1c5af4f8cacdc3c6adbe8334866774cca2944910","modified":1782714810561},{"_id":"public/images/1730728153314.png","hash":"95d44ea62557a311503cab58d1dec5cafc97e07d","modified":1782714810561},{"_id":"public/images/musecore.png","hash":"9b2eba87e7c93e57f2024697d1ff4217d206e5ac","modified":1782714810561},{"_id":"public/images/yana.jpg","hash":"757c68ec322e834eace7236c38e74a4f9585d474","modified":1782714810561},{"_id":"public/images/MyASCStory/ASC24-2.jpg","hash":"e7fbd104e81d126d114fd1ebf4e810c721bf0c26","modified":1782714810561},{"_id":"public/images/xb2score.png","hash":"03efbb07a7bb7f6c7d58b98ed125cafe42074b94","modified":1782714810561},{"_id":"public/images/MyASCStory/ASC24-1.jpg","hash":"780bc4106f3517f3d8a7ea3bfc46203b82e7c961","modified":1782714810561},{"_id":"public/images/phase1.png","hash":"42533712403be6036231f1e3770f125858bf91d7","modified":1782714810561},{"_id":"public/images/MyASCStory/ASC24-6.jpg","hash":"e6159d0f2bdc3f4fa355b5440024bbb763bfded9","modified":1782714810561},{"_id":"public/images/5364bba6d035326e82c53504dd53e7c2454454985.png","hash":"11b11f23ebebbe08e8cf826b48eb37b4084796cd","modified":1782714810561},{"_id":"public/images/1613f5602b203b38230f19699deb0219454454985.png","hash":"3d71204d9a0731384f97ad0f90802a5c9fd96776","modified":1782714810561},{"_id":"public/images/71a5357ef4bd808b10429bc2ea46cb6f454454985.png","hash":"dddf72bb12aef01baa3eb2017bb867f274059f12","modified":1782714810561},{"_id":"public/images/mhwilds4050.jpg","hash":"9b2b15c66e2340c15b86cec0fa8f818b67e06384","modified":1782714810561},{"_id":"public/images/clonezilla.png","hash":"7f443d79783df3744b2e51613422ee8fd7eea6f8","modified":1782714810561},{"_id":"public/images/mhwilds780M.jpg","hash":"e2c59c6caae452afb424d9f2fc3a255a44121b49","modified":1782714810561},{"_id":"public/images/phase_2_14.png","hash":"f50399a9eb469ed1e063ff4ccc0e8636c1b75f1e","modified":1782714810561},{"_id":"public/images/phase_4.png","hash":"81cd309a8990e015a8679b6a4e26a27d804c1da6","modified":1782714810561},{"_id":"public/images/20241027_222225.png","hash":"f0595d1315d94e72e61c69e0499e5129e1821ba1","modified":1782714810561},{"_id":"public/images/b2054bbaf6197624d38cc2007d885fd1454454985.png","hash":"b021e971cedaa55b273eab2b10be94f795b06922","modified":1782714810561},{"_id":"public/images/MyASCStory/ASC24-3.jpg","hash":"49116b67ce66255f2a9e5fc2ecdaea9880f5e767","modified":1782714810561},{"_id":"public/images/MyASCStory/ASC24-4.jpg","hash":"8e6d378316ca24156eba819d5fd963484786684b","modified":1782714810561},{"_id":"public/images/lmms-import.png","hash":"002535b55a41f7a7dcba24517dd2397589416a64","modified":1782714810561},{"_id":"public/images/MyASCStory/MyASCStory.jpg","hash":"7815e0f7c89688227c7520de9724406a9ab2bdef","modified":1782714810561},{"_id":"public/music/過ぎ去りし温もりの日々.mp3","hash":"22016dcdfcbac9393afd3fe97f2d6d6009cd57f5","modified":1782714810561},{"_id":"public/images/MyASCStory/lzdnb.jpg","hash":"42dd3b118023f82cb36d77ad77dc7f7f40d4103d","modified":1782714810561},{"_id":"public/images/krita-official.png","hash":"2ec810a739ce33ad5666093b8c36d29028a17c17","modified":1782714810561},{"_id":"public/images/MyASCStory/ASC25-5.jpg","hash":"5e36bd718ef8caf5425f181d57f2b9b5c97e5f99","modified":1782714810561},{"_id":"public/images/MyASCStory/ASC26-1.jpg","hash":"f6eb79393b7d39429818302742023660199eb2af","modified":1782714810561},{"_id":"public/images/MyASCStory/ASC24-5.jpg","hash":"6a9964d579e6d196f921b655938f707a5592e798","modified":1782714810561},{"_id":"public/images/MyASCStory/ASC25-6.jpg","hash":"932191925ba50d510b4543ab3eb01c85ed76e823","modified":1782714810561},{"_id":"public/images/MyASCStory/ASC25-1.jpg","hash":"f92f3c1030c8d5b7bb1fcc913aea7949237dc8bd","modified":1782714810561},{"_id":"public/images/musecore-launch.png","hash":"0a6c5642d05b197555135cd602886311a9122df0","modified":1782714810561},{"_id":"public/images/zellij-helix.jpg","hash":"87fb5b7b51a4e3b062b891c04488a53457edded0","modified":1782714810561},{"_id":"public/images/MyASCStory/ASC26YeZ.jpg","hash":"ed0bc33d58e430e9371adeac67e16b4973bd1418","modified":1782714810561},{"_id":"public/images/MyASCStory/ASC25cluster.jpg","hash":"599b6a4786cca30fc8704b0817d145950da27e63","modified":1782714810561},{"_id":"public/img/gensokyo.jpg","hash":"d82c0d5011c6cbc37109c9c80530d14cd266286f","modified":1782714810561},{"_id":"public/images/MyASCStory/ASC26-2.jpg","hash":"587340074e0536eec119ca2373ff78baaa17e818","modified":1782714810561},{"_id":"public/images/MyASCStory/ASC26-3.jpg","hash":"d492692b3e1682567ad2bad0b0b2fc79c7d76199","modified":1782714810561},{"_id":"public/images/MyASCStory/ASC26cluster.jpg","hash":"0f0ed96c6c887f809c6a464d4885f8bc81b94082","modified":1782714810561},{"_id":"public/images/MyASCStory/ASC25-3.jpg","hash":"140615d948984648181432dd50bf6d068b32f6de","modified":1782714810561},{"_id":"public/images/MyASCStory/ASC25-8.jpg","hash":"914c495957e963971fcfda35592bb2789815a38a","modified":1782714810561},{"_id":"public/images/MyASCStory-4.jpg","hash":"d81c735d5a935b6e5f06de24cd25b2b1c32633fc","modified":1782714810561},{"_id":"public/images/MyASCStory/ASC25-4.jpg","hash":"977ab0ba0b554c2ce1a70e8916ab75b387e1bca3","modified":1782714810561},{"_id":"public/images/MyASCStory/ASC25-7.jpg","hash":"878215f2fba4e5fe7fadfdd8e83d365ed459f7ba","modified":1782714810561},{"_id":"public/images/MyASCStory/ASC25-2.jpg","hash":"b0103bc7aee30ca94e550fd2e1c413725320a96e","modified":1782714810561}],"Category":[{"name":"更新","_id":"cmqyudblt000hidd063smcf8e"},{"name":"Rearrangement","_id":"cmqyudblu000pidd0gsplawn2"},{"name":"Vocaloid","_id":"cmqyudblv000widd05uyn9gyz"},{"name":"技术分享","_id":"cmqyudblv0012idd04skr2tck"},{"name":"技术分享,HPC","_id":"cmqyudbly001qidd04zjib1zi"}],"Data":[],"Page":[{"title":"About","date":"2024-11-04T14:46:12.000Z","layout":"about","_content":"\n白茅铺高地玄院第四任非菌群主🐳,Linux七年牢用户🐧,ASC三年牢选手🤡(无人在意)\n\n爱好编程,绘画,编曲,Vocaloid调教,Blender建模等😇\n\n截至目前最喜欢的歌手是宇多田光❤️\n\n联系方式:\n\n- Email: \n\n- Github: \n","source":"about/index.md","raw":"---\ntitle: About\ndate: 2024-11-04 22:46:12\nlayout: about\n---\n\n白茅铺高地玄院第四任非菌群主🐳,Linux七年牢用户🐧,ASC三年牢选手🤡(无人在意)\n\n爱好编程,绘画,编曲,Vocaloid调教,Blender建模等😇\n\n截至目前最喜欢的歌手是宇多田光❤️\n\n联系方式:\n\n- Email: \n\n- Github: \n","updated":"2026-06-29T06:31:19.403Z","path":"about/index.html","comments":1,"_id":"cmqyudbln0000idd09h01hobv","content":"

白茅铺高地玄院第四任非菌群主🐳,Linux七年牢用户🐧,ASC三年牢选手🤡(无人在意)

\n

爱好编程,绘画,编曲,Vocaloid调教,Blender建模等😇

\n

截至目前最喜欢的歌手是宇多田光❤️

\n

联系方式:

\n\n","excerpt":"","more":"

白茅铺高地玄院第四任非菌群主🐳,Linux七年牢用户🐧,ASC三年牢选手🤡(无人在意)

\n

爱好编程,绘画,编曲,Vocaloid调教,Blender建模等😇

\n

截至目前最喜欢的歌手是宇多田光❤️

\n

联系方式:

\n\n"}],"Post":[{"title":"12月16-17日工作记录","date":"2023-12-17T14:26:17.000Z","_content":"## 12月16-17日\n1. 四级考试\n2. 自学cuda,openacc,了解GPU架构知识\n3. 参加超算队启动会\n4. 大计和高数期中考试\n5. 研究spack使用\n6. 学习cuda编程\n7. 补作业\n","source":"_posts/12月16-17日工作记录.md","raw":"---\ntitle: 12月16-17日工作记录\ndate: 2023-12-17 22:26:17\ntags: [日志]\n---\n## 12月16-17日\n1. 四级考试\n2. 自学cuda,openacc,了解GPU架构知识\n3. 参加超算队启动会\n4. 大计和高数期中考试\n5. 研究spack使用\n6. 学习cuda编程\n7. 补作业\n","slug":"12月16-17日工作记录","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblo0001idd0h14q071s","content":"

12月16-17日

    \n
  1. 四级考试
  2. \n
  3. 自学cuda,openacc,了解GPU架构知识
  4. \n
  5. 参加超算队启动会
  6. \n
  7. 大计和高数期中考试
  8. \n
  9. 研究spack使用
  10. \n
  11. 学习cuda编程
  12. \n
  13. 补作业
  14. \n
\n","excerpt":"","more":"

12月16-17日

    \n
  1. 四级考试
  2. \n
  3. 自学cuda,openacc,了解GPU架构知识
  4. \n
  5. 参加超算队启动会
  6. \n
  7. 大计和高数期中考试
  8. \n
  9. 研究spack使用
  10. \n
  11. 学习cuda编程
  12. \n
  13. 补作业
  14. \n
\n"},{"title":"12月18日工作记录","date":"2023-12-18T13:57:34.000Z","_content":"## 12月18日\n1. 初步了解opencaeporo,尝试在本地docker环境使用gcc完成编译安装,目前完成各项依赖安装,由于晚上停电计划明天开始编译安装opencaeporo本体并在本地调优;\n2. 协助Neko组完成任务;\n3. 找出spack存在网络原因无法下载时的手动解决方案.","source":"_posts/12月18日工作记录.md","raw":"---\ntitle: 12月18日工作记录\ndate: 2023-12-18 21:57:34\ntags: [日志]\n---\n## 12月18日\n1. 初步了解opencaeporo,尝试在本地docker环境使用gcc完成编译安装,目前完成各项依赖安装,由于晚上停电计划明天开始编译安装opencaeporo本体并在本地调优;\n2. 协助Neko组完成任务;\n3. 找出spack存在网络原因无法下载时的手动解决方案.","slug":"12月18日工作记录","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblp0002idd05h21800i","content":"

12月18日

    \n
  1. 初步了解opencaeporo,尝试在本地docker环境使用gcc完成编译安装,目前完成各项依赖安装,由于晚上停电计划明天开始编译安装opencaeporo本体并在本地调优;
  2. \n
  3. 协助Neko组完成任务;
  4. \n
  5. 找出spack存在网络原因无法下载时的手动解决方案.
  6. \n
\n","excerpt":"","more":"

12月18日

    \n
  1. 初步了解opencaeporo,尝试在本地docker环境使用gcc完成编译安装,目前完成各项依赖安装,由于晚上停电计划明天开始编译安装opencaeporo本体并在本地调优;
  2. \n
  3. 协助Neko组完成任务;
  4. \n
  5. 找出spack存在网络原因无法下载时的手动解决方案.
  6. \n
\n"},{"title":"12月20日工作记录","date":"2023-12-20T13:56:42.000Z","_content":"## 12月20日\n\nOpenCAEPoro 小组(黄**,梁**,程**)\n\n1. 阅读代码,开展优化工作,初步使用openacc完成petsc_solver的优化,取得一定优化效果\n\n> 优化方向基本确定为OpenMP/OpenACC并行化+cuda移植\n\n2. 赛题环境汇总,目前各组工作有序开展,CentOS 7符合要求暂未发现更换系统需求\n","source":"_posts/12月20日工作记录.md","raw":"---\ntitle: 12月20日工作记录\ndate: 2023-12-20 21:56:42\ntags: 日志\n---\n## 12月20日\n\nOpenCAEPoro 小组(黄**,梁**,程**)\n\n1. 阅读代码,开展优化工作,初步使用openacc完成petsc_solver的优化,取得一定优化效果\n\n> 优化方向基本确定为OpenMP/OpenACC并行化+cuda移植\n\n2. 赛题环境汇总,目前各组工作有序开展,CentOS 7符合要求暂未发现更换系统需求\n","slug":"12月20日工作记录","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblq0004idd014047n93","content":"

12月20日

OpenCAEPoro 小组(黄,梁,程**)

\n
    \n
  1. 阅读代码,开展优化工作,初步使用openacc完成petsc_solver的优化,取得一定优化效果
  2. \n
\n
\n

优化方向基本确定为OpenMP/OpenACC并行化+cuda移植

\n
\n
    \n
  1. 赛题环境汇总,目前各组工作有序开展,CentOS 7符合要求暂未发现更换系统需求
  2. \n
\n","excerpt":"","more":"

12月20日

OpenCAEPoro 小组(黄,梁,程**)

\n
    \n
  1. 阅读代码,开展优化工作,初步使用openacc完成petsc_solver的优化,取得一定优化效果
  2. \n
\n
\n

优化方向基本确定为OpenMP/OpenACC并行化+cuda移植

\n
\n
    \n
  1. 赛题环境汇总,目前各组工作有序开展,CentOS 7符合要求暂未发现更换系统需求
  2. \n
\n"},{"title":"12月19日工作记录","date":"2023-12-19T13:59:47.000Z","_content":"## 12月18日\n1. 彻底完成opencaeporo安装部署,本地和服务器均已安装,明天正式开始调优工作;\n2. 学习cuda编程.\n","source":"_posts/12月19日工作记录.md","raw":"---\ntitle: 12月19日工作记录\ndate: 2023-12-19 21:59:47\ntags: [日志]\n---\n## 12月18日\n1. 彻底完成opencaeporo安装部署,本地和服务器均已安装,明天正式开始调优工作;\n2. 学习cuda编程.\n","slug":"12月19日工作记录","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblq0005idd01wqnhilv","content":"

12月18日

    \n
  1. 彻底完成opencaeporo安装部署,本地和服务器均已安装,明天正式开始调优工作;
  2. \n
  3. 学习cuda编程.
  4. \n
\n","excerpt":"","more":"

12月18日

    \n
  1. 彻底完成opencaeporo安装部署,本地和服务器均已安装,明天正式开始调优工作;
  2. \n
  3. 学习cuda编程.
  4. \n
\n"},{"title":"12月28日进度报告","date":"2023-12-28T15:22:13.000Z","_content":"## 12月28日\n\nOpenCAEPoro 小组(黄**,梁**,程**,刘**)\n\n1. 各组员继续进行优化工作,部分函数完成cuda移植,取得一定优化效果\n2. 继续学习openacc及cuda相关知识\n3. 搜集多孔介质流动模拟与opencaeporo相关论文资料为proposal做准备\n","source":"_posts/12月28日进度报告.md","raw":"---\ntitle: 12月28日进度报告\ndate: 2023-12-28 23:22:13\ntags: 日志\n---\n## 12月28日\n\nOpenCAEPoro 小组(黄**,梁**,程**,刘**)\n\n1. 各组员继续进行优化工作,部分函数完成cuda移植,取得一定优化效果\n2. 继续学习openacc及cuda相关知识\n3. 搜集多孔介质流动模拟与opencaeporo相关论文资料为proposal做准备\n","slug":"12月28日进度报告","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblr0006idd01uzzddnf","content":"

12月28日

OpenCAEPoro 小组(黄,梁,程,刘

\n
    \n
  1. 各组员继续进行优化工作,部分函数完成cuda移植,取得一定优化效果
  2. \n
  3. 继续学习openacc及cuda相关知识
  4. \n
  5. 搜集多孔介质流动模拟与opencaeporo相关论文资料为proposal做准备
  6. \n
\n","excerpt":"","more":"

12月28日

OpenCAEPoro 小组(黄,梁,程,刘

\n
    \n
  1. 各组员继续进行优化工作,部分函数完成cuda移植,取得一定优化效果
  2. \n
  3. 继续学习openacc及cuda相关知识
  4. \n
  5. 搜集多孔介质流动模拟与opencaeporo相关论文资料为proposal做准备
  6. \n
\n"},{"title":"12月30-31日进度报告","date":"2023-12-30T16:07:09.000Z","_content":"## 12月30-31日\n\nOpenCAEPoro 小组(黄**,梁**,程**,刘**)\n\n1. 服务器上的Opencaeporo改用nvhpc编译器编译以支持cuda;\n2. 收集运行数据用于proposal绘制图表;\n3. 继续尝试优化,同时已经查阅收集了一些相关资料以尽量理解相关含义便于工作开展.\n","source":"_posts/12月31日进度报告.md","raw":"---\ntitle: 12月30-31日进度报告\ndate: 2023-12-31 00:07:09\ntags: 日志\n---\n## 12月30-31日\n\nOpenCAEPoro 小组(黄**,梁**,程**,刘**)\n\n1. 服务器上的Opencaeporo改用nvhpc编译器编译以支持cuda;\n2. 收集运行数据用于proposal绘制图表;\n3. 继续尝试优化,同时已经查阅收集了一些相关资料以尽量理解相关含义便于工作开展.\n","slug":"12月31日进度报告","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblr0009idd00sob470f","content":"

12月30-31日

OpenCAEPoro 小组(黄,梁,程,刘

\n
    \n
  1. 服务器上的Opencaeporo改用nvhpc编译器编译以支持cuda;
  2. \n
  3. 收集运行数据用于proposal绘制图表;
  4. \n
  5. 继续尝试优化,同时已经查阅收集了一些相关资料以尽量理解相关含义便于工作开展.
  6. \n
\n","excerpt":"","more":"

12月30-31日

OpenCAEPoro 小组(黄,梁,程,刘

\n
    \n
  1. 服务器上的Opencaeporo改用nvhpc编译器编译以支持cuda;
  2. \n
  3. 收集运行数据用于proposal绘制图表;
  4. \n
  5. 继续尝试优化,同时已经查阅收集了一些相关资料以尽量理解相关含义便于工作开展.
  6. \n
\n"},{"title":"小满随笔——决断","date":"2025-05-21T11:59:21.000Z","_content":"\n
\n
\n Lost Days of Warmth - Yasunori Mitsuda\n
\n \n
\n\n我本以为自己已经有足够的勇气和平静的心态去面对生活里的各种变故了,即便如此今天还是被突如其来的噩耗痛击得面目全非。\n\n辛辛苦苦和队友准备了大半年的ASC25失利,我想着以后回过头来看也不算什么以最快的速度调整心态;回校后一来就是各种烂摊子,写不完的作业补不完的课程,还有被脑子缺根筋的弱智评委恶意低分的大创立项成绩,我寻思这些事情烦完这阵也就过去了,人总不可能一直倒霉吧。结果就是今天下午排队验收CSAPP课程的shell实验时突然看到了外婆去世的消息,一时间两腿发软大脑空白不知所措,以至于后来排到我和教辅讲解实验过程时也是胡言乱语记不清自己到底说了什么,尽管最后还是被教辅满分放过了。\n\n小时候家里没现在景气,父母在市内打拼我则是被送到郊区被外婆抚养长大,上小学后为了让我进入一中直升班从二年级起我就被送进了奥数培训班,同时搬离了自己长大的地方,再到后来市政府改造,外婆家早已被拆得无影无踪,昔日的小伙伴杳无音信,温馨错杂的小居民区变成了陌生嘈杂的大酒店和菜市场,每次路过都不敢停留。从那以后外婆成了我和童年之间仅剩的联系,尽管老人后来连我的样貌也记不清楚。现如今童年记忆里最后的故人业已离开,再回首已是一片虚无。多年前外婆犯脑溢血被抢救回来的时候医生还承诺老人家活到抱孙子没问题,如今却猝不及防地驾鹤西去。在各种小说和游戏中经历过不少难忘的生死离别,可是到了现实亲自经历依旧痛心疾首难以放下。\n\n生物无法教会人们生命究竟是什么,生活却一点就通。上一次经历这种打击还是10年左右外公离开,那时候太小什么都不懂,等长大理解了经历的时间又足以抹平悲痛。如今在我的眼里,生命就是思念,是世间个体对其他个体的牵挂与个体之间互相建立的羁绊。我真的想停留在永远的当下,和自己爱的人永远定格在某一个瞬间,每天早上是一样的朝阳,出门是熟悉的面孔,回家是亲切的声音。然而活着就意味着必须面对各种苦楚,死去元知万事空,只有离开才是最好的解脱,活着的人要背负更多。长者的时间在一天天减少,他们的生命需要由后人去延续,但是后人的生活也不容易,面对各种困境放弃往往比坚持更加轻松,更有甚者勇敢地选择自戕从而摆脱一切,但是这样是不对的,让其他人代替自己承受痛苦是不对的,令人悲伤的选择是不应该做出的,活着的人哪怕再苦,只要身上背负了其他人的思念也只能继续前进。\n\n因此,我绝对不能气馁,我珍视现在和亲人朋友的点点滴滴,珍惜自己现在拥有的一切,我以最真诚的爱回馈所有爱我的人,不论面前有多少困难,我都会尽全力克服,这不是为我自己,现在我被摧残得只想躺平摆烂,但是为了所有信任我关心我的人,如今我绝对不能停下脚步。从来就没有永远的当下,连接我和童年最后的纽带已经被切断,再也不能回头,留给我的只剩下面前的未来,需要我亲手创造的未来,只有承受住来自各方的打击,我才有能力去维护由我创造的未来,既然选择了远方,便只顾风雨兼程。\n\n2025年的长沙小满格外燥热,痛定思过后的心是如此平静,没有多少时间可以浪费了,无需多言。\n\n*“回首向来萧瑟处,归去,也无风雨也无晴!”*\n","source":"_posts/2025-5-21.md","raw":"---\ntitle: 小满随笔——决断\ndate: 2025-05-21 19:59:21\ntags: 日志\n---\n\n
\n
\n Lost Days of Warmth - Yasunori Mitsuda\n
\n \n
\n\n我本以为自己已经有足够的勇气和平静的心态去面对生活里的各种变故了,即便如此今天还是被突如其来的噩耗痛击得面目全非。\n\n辛辛苦苦和队友准备了大半年的ASC25失利,我想着以后回过头来看也不算什么以最快的速度调整心态;回校后一来就是各种烂摊子,写不完的作业补不完的课程,还有被脑子缺根筋的弱智评委恶意低分的大创立项成绩,我寻思这些事情烦完这阵也就过去了,人总不可能一直倒霉吧。结果就是今天下午排队验收CSAPP课程的shell实验时突然看到了外婆去世的消息,一时间两腿发软大脑空白不知所措,以至于后来排到我和教辅讲解实验过程时也是胡言乱语记不清自己到底说了什么,尽管最后还是被教辅满分放过了。\n\n小时候家里没现在景气,父母在市内打拼我则是被送到郊区被外婆抚养长大,上小学后为了让我进入一中直升班从二年级起我就被送进了奥数培训班,同时搬离了自己长大的地方,再到后来市政府改造,外婆家早已被拆得无影无踪,昔日的小伙伴杳无音信,温馨错杂的小居民区变成了陌生嘈杂的大酒店和菜市场,每次路过都不敢停留。从那以后外婆成了我和童年之间仅剩的联系,尽管老人后来连我的样貌也记不清楚。现如今童年记忆里最后的故人业已离开,再回首已是一片虚无。多年前外婆犯脑溢血被抢救回来的时候医生还承诺老人家活到抱孙子没问题,如今却猝不及防地驾鹤西去。在各种小说和游戏中经历过不少难忘的生死离别,可是到了现实亲自经历依旧痛心疾首难以放下。\n\n生物无法教会人们生命究竟是什么,生活却一点就通。上一次经历这种打击还是10年左右外公离开,那时候太小什么都不懂,等长大理解了经历的时间又足以抹平悲痛。如今在我的眼里,生命就是思念,是世间个体对其他个体的牵挂与个体之间互相建立的羁绊。我真的想停留在永远的当下,和自己爱的人永远定格在某一个瞬间,每天早上是一样的朝阳,出门是熟悉的面孔,回家是亲切的声音。然而活着就意味着必须面对各种苦楚,死去元知万事空,只有离开才是最好的解脱,活着的人要背负更多。长者的时间在一天天减少,他们的生命需要由后人去延续,但是后人的生活也不容易,面对各种困境放弃往往比坚持更加轻松,更有甚者勇敢地选择自戕从而摆脱一切,但是这样是不对的,让其他人代替自己承受痛苦是不对的,令人悲伤的选择是不应该做出的,活着的人哪怕再苦,只要身上背负了其他人的思念也只能继续前进。\n\n因此,我绝对不能气馁,我珍视现在和亲人朋友的点点滴滴,珍惜自己现在拥有的一切,我以最真诚的爱回馈所有爱我的人,不论面前有多少困难,我都会尽全力克服,这不是为我自己,现在我被摧残得只想躺平摆烂,但是为了所有信任我关心我的人,如今我绝对不能停下脚步。从来就没有永远的当下,连接我和童年最后的纽带已经被切断,再也不能回头,留给我的只剩下面前的未来,需要我亲手创造的未来,只有承受住来自各方的打击,我才有能力去维护由我创造的未来,既然选择了远方,便只顾风雨兼程。\n\n2025年的长沙小满格外燥热,痛定思过后的心是如此平静,没有多少时间可以浪费了,无需多言。\n\n*“回首向来萧瑟处,归去,也无风雨也无晴!”*\n","slug":"2025-5-21","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudbls000bidd023vmejmk","content":"
\n
\n Lost Days of Warmth - Yasunori Mitsuda\n
\n \n
\n\n

我本以为自己已经有足够的勇气和平静的心态去面对生活里的各种变故了,即便如此今天还是被突如其来的噩耗痛击得面目全非。

\n

辛辛苦苦和队友准备了大半年的ASC25失利,我想着以后回过头来看也不算什么以最快的速度调整心态;回校后一来就是各种烂摊子,写不完的作业补不完的课程,还有被脑子缺根筋的弱智评委恶意低分的大创立项成绩,我寻思这些事情烦完这阵也就过去了,人总不可能一直倒霉吧。结果就是今天下午排队验收CSAPP课程的shell实验时突然看到了外婆去世的消息,一时间两腿发软大脑空白不知所措,以至于后来排到我和教辅讲解实验过程时也是胡言乱语记不清自己到底说了什么,尽管最后还是被教辅满分放过了。

\n

小时候家里没现在景气,父母在市内打拼我则是被送到郊区被外婆抚养长大,上小学后为了让我进入一中直升班从二年级起我就被送进了奥数培训班,同时搬离了自己长大的地方,再到后来市政府改造,外婆家早已被拆得无影无踪,昔日的小伙伴杳无音信,温馨错杂的小居民区变成了陌生嘈杂的大酒店和菜市场,每次路过都不敢停留。从那以后外婆成了我和童年之间仅剩的联系,尽管老人后来连我的样貌也记不清楚。现如今童年记忆里最后的故人业已离开,再回首已是一片虚无。多年前外婆犯脑溢血被抢救回来的时候医生还承诺老人家活到抱孙子没问题,如今却猝不及防地驾鹤西去。在各种小说和游戏中经历过不少难忘的生死离别,可是到了现实亲自经历依旧痛心疾首难以放下。

\n

生物无法教会人们生命究竟是什么,生活却一点就通。上一次经历这种打击还是10年左右外公离开,那时候太小什么都不懂,等长大理解了经历的时间又足以抹平悲痛。如今在我的眼里,生命就是思念,是世间个体对其他个体的牵挂与个体之间互相建立的羁绊。我真的想停留在永远的当下,和自己爱的人永远定格在某一个瞬间,每天早上是一样的朝阳,出门是熟悉的面孔,回家是亲切的声音。然而活着就意味着必须面对各种苦楚,死去元知万事空,只有离开才是最好的解脱,活着的人要背负更多。长者的时间在一天天减少,他们的生命需要由后人去延续,但是后人的生活也不容易,面对各种困境放弃往往比坚持更加轻松,更有甚者勇敢地选择自戕从而摆脱一切,但是这样是不对的,让其他人代替自己承受痛苦是不对的,令人悲伤的选择是不应该做出的,活着的人哪怕再苦,只要身上背负了其他人的思念也只能继续前进。

\n

因此,我绝对不能气馁,我珍视现在和亲人朋友的点点滴滴,珍惜自己现在拥有的一切,我以最真诚的爱回馈所有爱我的人,不论面前有多少困难,我都会尽全力克服,这不是为我自己,现在我被摧残得只想躺平摆烂,但是为了所有信任我关心我的人,如今我绝对不能停下脚步。从来就没有永远的当下,连接我和童年最后的纽带已经被切断,再也不能回头,留给我的只剩下面前的未来,需要我亲手创造的未来,只有承受住来自各方的打击,我才有能力去维护由我创造的未来,既然选择了远方,便只顾风雨兼程。

\n

2025年的长沙小满格外燥热,痛定思过后的心是如此平静,没有多少时间可以浪费了,无需多言。

\n

“回首向来萧瑟处,归去,也无风雨也无晴!”

\n","excerpt":"","more":"
\n
\n Lost Days of Warmth - Yasunori Mitsuda\n
\n \n
\n\n

我本以为自己已经有足够的勇气和平静的心态去面对生活里的各种变故了,即便如此今天还是被突如其来的噩耗痛击得面目全非。

\n

辛辛苦苦和队友准备了大半年的ASC25失利,我想着以后回过头来看也不算什么以最快的速度调整心态;回校后一来就是各种烂摊子,写不完的作业补不完的课程,还有被脑子缺根筋的弱智评委恶意低分的大创立项成绩,我寻思这些事情烦完这阵也就过去了,人总不可能一直倒霉吧。结果就是今天下午排队验收CSAPP课程的shell实验时突然看到了外婆去世的消息,一时间两腿发软大脑空白不知所措,以至于后来排到我和教辅讲解实验过程时也是胡言乱语记不清自己到底说了什么,尽管最后还是被教辅满分放过了。

\n

小时候家里没现在景气,父母在市内打拼我则是被送到郊区被外婆抚养长大,上小学后为了让我进入一中直升班从二年级起我就被送进了奥数培训班,同时搬离了自己长大的地方,再到后来市政府改造,外婆家早已被拆得无影无踪,昔日的小伙伴杳无音信,温馨错杂的小居民区变成了陌生嘈杂的大酒店和菜市场,每次路过都不敢停留。从那以后外婆成了我和童年之间仅剩的联系,尽管老人后来连我的样貌也记不清楚。现如今童年记忆里最后的故人业已离开,再回首已是一片虚无。多年前外婆犯脑溢血被抢救回来的时候医生还承诺老人家活到抱孙子没问题,如今却猝不及防地驾鹤西去。在各种小说和游戏中经历过不少难忘的生死离别,可是到了现实亲自经历依旧痛心疾首难以放下。

\n

生物无法教会人们生命究竟是什么,生活却一点就通。上一次经历这种打击还是10年左右外公离开,那时候太小什么都不懂,等长大理解了经历的时间又足以抹平悲痛。如今在我的眼里,生命就是思念,是世间个体对其他个体的牵挂与个体之间互相建立的羁绊。我真的想停留在永远的当下,和自己爱的人永远定格在某一个瞬间,每天早上是一样的朝阳,出门是熟悉的面孔,回家是亲切的声音。然而活着就意味着必须面对各种苦楚,死去元知万事空,只有离开才是最好的解脱,活着的人要背负更多。长者的时间在一天天减少,他们的生命需要由后人去延续,但是后人的生活也不容易,面对各种困境放弃往往比坚持更加轻松,更有甚者勇敢地选择自戕从而摆脱一切,但是这样是不对的,让其他人代替自己承受痛苦是不对的,令人悲伤的选择是不应该做出的,活着的人哪怕再苦,只要身上背负了其他人的思念也只能继续前进。

\n

因此,我绝对不能气馁,我珍视现在和亲人朋友的点点滴滴,珍惜自己现在拥有的一切,我以最真诚的爱回馈所有爱我的人,不论面前有多少困难,我都会尽全力克服,这不是为我自己,现在我被摧残得只想躺平摆烂,但是为了所有信任我关心我的人,如今我绝对不能停下脚步。从来就没有永远的当下,连接我和童年最后的纽带已经被切断,再也不能回头,留给我的只剩下面前的未来,需要我亲手创造的未来,只有承受住来自各方的打击,我才有能力去维护由我创造的未来,既然选择了远方,便只顾风雨兼程。

\n

2025年的长沙小满格外燥热,痛定思过后的心是如此平静,没有多少时间可以浪费了,无需多言。

\n

“回首向来萧瑟处,归去,也无风雨也无晴!”

\n"},{"title":"博客更新记录","date":"2024-11-04T15:18:02.000Z","_content":"\n- 博客主题更新为 `Fluid` 主题,原主题为 `Next`\n- 更新了访问地址为 `https://blog.hifuu.ink`\n- 新增了 `About` 页面\n- 新增了 `友链` 页面\n- 完善页面布局\n","source":"_posts/BlogUpdate.md","raw":"---\ntitle: 博客更新记录\ndate: 2024-11-04 23:18:02\ntags: 日志\ncategories: 更新\n---\n\n- 博客主题更新为 `Fluid` 主题,原主题为 `Next`\n- 更新了访问地址为 `https://blog.hifuu.ink`\n- 新增了 `About` 页面\n- 新增了 `友链` 页面\n- 完善页面布局\n","slug":"BlogUpdate","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudbls000eidd032cxh5sc","content":"
    \n
  • 博客主题更新为 Fluid 主题,原主题为 Next
  • \n
  • 更新了访问地址为 https://blog.hifuu.ink
  • \n
  • 新增了 About 页面
  • \n
  • 新增了 友链 页面
  • \n
  • 完善页面布局
  • \n
\n","excerpt":"","more":"
    \n
  • 博客主题更新为 Fluid 主题,原主题为 Next
  • \n
  • 更新了访问地址为 https://blog.hifuu.ink
  • \n
  • 新增了 About 页面
  • \n
  • 新增了 友链 页面
  • \n
  • 完善页面布局
  • \n
\n"},{"title":"Welcome to CGH0S7's Blog","date":"2023-12-06T08:33:30.000Z","_content":"\nHello World !\n","source":"_posts/CGH0S7-s-Blog.md","raw":"---\ntitle: Welcome to CGH0S7's Blog \ndate: 2023-12-06 16:33:30\ntags: TEST\n---\n\nHello World !\n","slug":"CGH0S7-s-Blog","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudbls000gidd0bei0509p","content":"

Hello World !

\n","excerpt":"","more":"

Hello World !

\n"},{"title":"Gentle Jena","date":"2024-10-30T14:11:14.000Z","_content":"\n很喜欢的一首曲子\n顺便测试一下视频上传\n以后随缘更新各种乱七八糟的东西。。\n\n\n","source":"_posts/GentleJena.md","raw":"---\ntitle: Gentle Jena\ndate: 2024-10-30 22:11:14\ntags: 音乐\ncategories: Rearrangement\n---\n\n很喜欢的一首曲子\n顺便测试一下视频上传\n以后随缘更新各种乱七八糟的东西。。\n\n\n","slug":"GentleJena","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblt000jidd0cw3ca8si","content":"

很喜欢的一首曲子
顺便测试一下视频上传
以后随缘更新各种乱七八糟的东西。。

\n\n","excerpt":"","more":"

很喜欢的一首曲子
顺便测试一下视频上传
以后随缘更新各种乱七八糟的东西。。

\n\n"},{"title":"Vocaloid调教-晴天(洛天依V4)","date":"2024-11-04T13:52:59.000Z","_content":"\n其实是今年年初的作品想起来可以搬上来,这是我调教的第一首v曲。\n\n“故事的小黄花,从出生那年就飘着...”\n\n{% raw %}\n\n{% endraw %}\n\n","source":"_posts/Vocaloid调教-晴天.md","raw":"---\ntitle: Vocaloid调教-晴天(洛天依v4)\ndate: 2024-11-04 21:52:59\ntags: [Vocaloid, 音乐]\ncategories: [Vocaloid]\n---\n\n其实是今年年初的作品想起来可以搬上来,这是我调教的第一首v曲。\n\n“故事的小黄花,从出生那年就飘着...”\n\n{% raw %}\n\n{% endraw %}\n\n","slug":"Vocaloid调教-晴天","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblt000midd00rrqai7r","content":"

其实是今年年初的作品想起来可以搬上来,这是我调教的第一首v曲。

\n

“故事的小黄花,从出生那年就飘着…”

\n\n\n\n\n","excerpt":"","more":"

其实是今年年初的作品想起来可以搬上来,这是我调教的第一首v曲。

\n

“故事的小黄花,从出生那年就飘着…”

\n\n\n\n\n"},{"title":"低成本快速搞定专属域名邮箱:从 0 到 CLI 收发只要 15 分钟","date":"2025-11-15T15:36:22.000Z","_content":"\n> 失踪人口回归 👋\n> 如果你只想「有个能用的 `@自己域名` 邮箱」,却懒得租服务器、折腾 Postfix、跟 25 端口斗智斗勇、花费你宝贵的💰,那这篇 15 分钟速通教程就是写给你的(当然,一点💰都不花是不可能的,你至少需要买一个域名=)\n\n---\n\n## 1. 最终效果\n\n- 任何前缀都能即时生成: `helloworld@your.com` / `fknudt@your.com` / `ilovethu@your.com` \n- 在终端里使用命令收发邮件,无需专门打开各种邮件客户端。\n\n整套方案 **只花域名注册费用**,**0 服务器 0 运维**,完全白嫖 Resend 免费额度。\n\n---\n\n## 2. 准备工作(2 min)\n\n| 资源 | 用途 | 费用 |\n|---|---|---|\n| 一枚域名 | 做邮箱后缀 | 约 ¥30+ / 年(阿里云 Namecheap Cloudflare 均可) |\n| Resend 账号 | 托管邮服 | 免费 100 封 / 天 |\n\n![Resend文档](/images/resendofficial.png)\n\n---\n\n## 3. 3 步完成域名绑定(5 min)\n\n1. 登录 [Resend](https://resend.com) → **Domains** → **Add Domain** \n 输入 `your.com` → 得到一组 DNS 记录(TXT + MX + DKIM)。 \n\n2. 去域名注册商把记录全部照抄粘贴,保存。(由于笔者为免费用户并且已经配置域名无法再次配置,因此未能提供截图但是步骤非常简单)。\n - 登录你的域名注册商账号,找到DNS管理页面。\n - 添加Resend提供的TXT记录,用于验证域名所有权。\n - 添加Resend提供的MX记录,用于接收邮件。\n - 根据Resend的要求,配置完整所有必要的DNS记录。\n - 保存更改后,等待DNS记录生效,通常需要几分钟到24小时不等(这是resend官方的说法,笔者实测只需要几分钟不到)。\n ⚠️ 只需改 **DNS 解析**,不用转 nameserver,1 min 搞定。 \n\n3. 回到 Resend 点 **Verify**,状态变绿 ✅ 即生效(实测 30s-3min,并不会像官方说的有可能要24小时)。\n\n---\n\n## 4. 无限别名 + 转发(立刻能用)\n\nResend 对「 catch-all 」免费! \n意思是你 **不用提前新建账号**,任何 `@your.com` 的信都会飞进你设定的地址,只需要在 Resend 官网查收即可。\n\n---\n\n## 5. 终端发邮件(可选,8 min 部署)\n\n网页收发邮件显然太麻烦,我用**rust**写了个可爱的 CLI 客户端 [**rusend**](https://github.com/CGH0S7/rusend),基于 Resend 官方 API,支持发送与查收邮件非常方便。(好吧其实是Gemini小姐帮忙写的@@)\n\n### ①-1 下载预构建可执行文件\n\n在[Github Releases](https://github.com/CGH0S7/rusend/releases/tag/stable)里下载对应系统的预编译包,解压放到 `$PATH` 目录下即可。\n\n### ①-2 源码构建\n\n下载并编译rusend,准备好rust工具链,具体编译命令如下:\n\n ```bash\n git clone https://github.com/CGH0S7/rusend.git\n cd rusend\n cargo build --release\n ```\n\n将`rusend`二进制文件移动到系统的`$PATH`目录下,例如`/usr/local/bin`。\n\n### ② 补全 & API Key配置\n\n使用下面的命令进行命令行补全配置:\n\n ```bash\n echo \"source <(rusend completions bash)\" >> ~/.bashrc\n source ~/.bashrc\n ```\n\n ```zsh\n echo \"source <(rusend completions zsh)\" >> ~/.zshrc\n source ~/.zshrc\n ```\n\n ```fish\n rusend completions fish > ~/.config/fish/completions/rusend.fish\n source ~/.config/fish/config.fish\n ```\n\n在Resend控制面板中,找到`API Keys`选项,为你配置的域名生成一个新的API Key。然后在终端中运行以下命令进行配置:\n\n ```bash\n rusend config --key re_xxxxxxxxx\n ```\n\n### ③ 使用 rusend 收发邮件\n\n详细功能请查看[Readme](https://github.com/CGH0S7/rusend)文档:\n\n ![rusend文档](/images/rusendhelp1.png)\n ![rusend文档](/images/rusendhelp2.png)\n ![rusend软件帮助](/images/rusendhelp3.png)\n\n---\n\n## 6. 成本 & 限额总结\n\n| 项目 | 免费额度 |\n|---|---|\n| Resend | 100 封 / 天 |\n| 域名 | 约 ¥30+ / 年 |\n| rusend | 开源 |\n\n非主力邮箱场景下完全够用,不建议用于接收各种广告或更新推送。\n\n若想要「匿名转发」隐藏真实地址,可以再套一层 [AnonAddy](https://addy.io) 即可,本文不展开。\n\n---\n\n## 7. 结语\n\n以上就是「低成本快速拥有个人专属域名邮箱」的全部流程。\n\n零服务器、零运维、全平台通用,甚至能在手机 Termux 里发邮。\n\n如果能帮到你,去 [rusend](https://github.com/CGH0S7/rusend) 点个 ⭐ 当稿费吧~😉\n","source":"_posts/Your-exclusive-email-address.md","raw":"---\ntitle: 低成本快速搞定专属域名邮箱:从 0 到 CLI 收发只要 15 分钟\ndate: 2025-11-15 23:36:22\ntags: [技术分享]\n---\n\n> 失踪人口回归 👋\n> 如果你只想「有个能用的 `@自己域名` 邮箱」,却懒得租服务器、折腾 Postfix、跟 25 端口斗智斗勇、花费你宝贵的💰,那这篇 15 分钟速通教程就是写给你的(当然,一点💰都不花是不可能的,你至少需要买一个域名=)\n\n---\n\n## 1. 最终效果\n\n- 任何前缀都能即时生成: `helloworld@your.com` / `fknudt@your.com` / `ilovethu@your.com` \n- 在终端里使用命令收发邮件,无需专门打开各种邮件客户端。\n\n整套方案 **只花域名注册费用**,**0 服务器 0 运维**,完全白嫖 Resend 免费额度。\n\n---\n\n## 2. 准备工作(2 min)\n\n| 资源 | 用途 | 费用 |\n|---|---|---|\n| 一枚域名 | 做邮箱后缀 | 约 ¥30+ / 年(阿里云 Namecheap Cloudflare 均可) |\n| Resend 账号 | 托管邮服 | 免费 100 封 / 天 |\n\n![Resend文档](/images/resendofficial.png)\n\n---\n\n## 3. 3 步完成域名绑定(5 min)\n\n1. 登录 [Resend](https://resend.com) → **Domains** → **Add Domain** \n 输入 `your.com` → 得到一组 DNS 记录(TXT + MX + DKIM)。 \n\n2. 去域名注册商把记录全部照抄粘贴,保存。(由于笔者为免费用户并且已经配置域名无法再次配置,因此未能提供截图但是步骤非常简单)。\n - 登录你的域名注册商账号,找到DNS管理页面。\n - 添加Resend提供的TXT记录,用于验证域名所有权。\n - 添加Resend提供的MX记录,用于接收邮件。\n - 根据Resend的要求,配置完整所有必要的DNS记录。\n - 保存更改后,等待DNS记录生效,通常需要几分钟到24小时不等(这是resend官方的说法,笔者实测只需要几分钟不到)。\n ⚠️ 只需改 **DNS 解析**,不用转 nameserver,1 min 搞定。 \n\n3. 回到 Resend 点 **Verify**,状态变绿 ✅ 即生效(实测 30s-3min,并不会像官方说的有可能要24小时)。\n\n---\n\n## 4. 无限别名 + 转发(立刻能用)\n\nResend 对「 catch-all 」免费! \n意思是你 **不用提前新建账号**,任何 `@your.com` 的信都会飞进你设定的地址,只需要在 Resend 官网查收即可。\n\n---\n\n## 5. 终端发邮件(可选,8 min 部署)\n\n网页收发邮件显然太麻烦,我用**rust**写了个可爱的 CLI 客户端 [**rusend**](https://github.com/CGH0S7/rusend),基于 Resend 官方 API,支持发送与查收邮件非常方便。(好吧其实是Gemini小姐帮忙写的@@)\n\n### ①-1 下载预构建可执行文件\n\n在[Github Releases](https://github.com/CGH0S7/rusend/releases/tag/stable)里下载对应系统的预编译包,解压放到 `$PATH` 目录下即可。\n\n### ①-2 源码构建\n\n下载并编译rusend,准备好rust工具链,具体编译命令如下:\n\n ```bash\n git clone https://github.com/CGH0S7/rusend.git\n cd rusend\n cargo build --release\n ```\n\n将`rusend`二进制文件移动到系统的`$PATH`目录下,例如`/usr/local/bin`。\n\n### ② 补全 & API Key配置\n\n使用下面的命令进行命令行补全配置:\n\n ```bash\n echo \"source <(rusend completions bash)\" >> ~/.bashrc\n source ~/.bashrc\n ```\n\n ```zsh\n echo \"source <(rusend completions zsh)\" >> ~/.zshrc\n source ~/.zshrc\n ```\n\n ```fish\n rusend completions fish > ~/.config/fish/completions/rusend.fish\n source ~/.config/fish/config.fish\n ```\n\n在Resend控制面板中,找到`API Keys`选项,为你配置的域名生成一个新的API Key。然后在终端中运行以下命令进行配置:\n\n ```bash\n rusend config --key re_xxxxxxxxx\n ```\n\n### ③ 使用 rusend 收发邮件\n\n详细功能请查看[Readme](https://github.com/CGH0S7/rusend)文档:\n\n ![rusend文档](/images/rusendhelp1.png)\n ![rusend文档](/images/rusendhelp2.png)\n ![rusend软件帮助](/images/rusendhelp3.png)\n\n---\n\n## 6. 成本 & 限额总结\n\n| 项目 | 免费额度 |\n|---|---|\n| Resend | 100 封 / 天 |\n| 域名 | 约 ¥30+ / 年 |\n| rusend | 开源 |\n\n非主力邮箱场景下完全够用,不建议用于接收各种广告或更新推送。\n\n若想要「匿名转发」隐藏真实地址,可以再套一层 [AnonAddy](https://addy.io) 即可,本文不展开。\n\n---\n\n## 7. 结语\n\n以上就是「低成本快速拥有个人专属域名邮箱」的全部流程。\n\n零服务器、零运维、全平台通用,甚至能在手机 Termux 里发邮。\n\n如果能帮到你,去 [rusend](https://github.com/CGH0S7/rusend) 点个 ⭐ 当稿费吧~😉\n","slug":"Your-exclusive-email-address","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblu000oidd01yznbp5t","content":"
\n

失踪人口回归 👋
如果你只想「有个能用的 @自己域名 邮箱」,却懒得租服务器、折腾 Postfix、跟 25 端口斗智斗勇、花费你宝贵的💰,那这篇 15 分钟速通教程就是写给你的(当然,一点💰都不花是不可能的,你至少需要买一个域名=)

\n
\n
\n

1. 最终效果

    \n
  • 任何前缀都能即时生成: helloworld@your.com / fknudt@your.com / ilovethu@your.com
  • \n
  • 在终端里使用命令收发邮件,无需专门打开各种邮件客户端。
  • \n
\n

整套方案 只花域名注册费用0 服务器 0 运维,完全白嫖 Resend 免费额度。

\n
\n

2. 准备工作(2 min)

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
资源用途费用
一枚域名做邮箱后缀约 ¥30+ / 年(阿里云 Namecheap Cloudflare 均可)
Resend 账号托管邮服免费 100 封 / 天
\n

\"Resend文档\"

\n
\n

3. 3 步完成域名绑定(5 min)

    \n
  1. 登录 ResendDomainsAdd Domain
    输入 your.com → 得到一组 DNS 记录(TXT + MX + DKIM)。

    \n
  2. \n
  3. 去域名注册商把记录全部照抄粘贴,保存。(由于笔者为免费用户并且已经配置域名无法再次配置,因此未能提供截图但是步骤非常简单)。

    \n
      \n
    • 登录你的域名注册商账号,找到DNS管理页面。
    • \n
    • 添加Resend提供的TXT记录,用于验证域名所有权。
    • \n
    • 添加Resend提供的MX记录,用于接收邮件。
    • \n
    • 根据Resend的要求,配置完整所有必要的DNS记录。
    • \n
    • 保存更改后,等待DNS记录生效,通常需要几分钟到24小时不等(这是resend官方的说法,笔者实测只需要几分钟不到)。
      ⚠️ 只需改 DNS 解析,不用转 nameserver,1 min 搞定。
    • \n
    \n
  4. \n
  5. 回到 Resend 点 Verify,状态变绿 ✅ 即生效(实测 30s-3min,并不会像官方说的有可能要24小时)。

    \n
  6. \n
\n
\n

4. 无限别名 + 转发(立刻能用)

Resend 对「 catch-all 」免费!
意思是你 不用提前新建账号,任何 @your.com 的信都会飞进你设定的地址,只需要在 Resend 官网查收即可。

\n
\n

5. 终端发邮件(可选,8 min 部署)

网页收发邮件显然太麻烦,我用rust写了个可爱的 CLI 客户端 rusend,基于 Resend 官方 API,支持发送与查收邮件非常方便。(好吧其实是Gemini小姐帮忙写的@@)

\n

①-1 下载预构建可执行文件

Github Releases里下载对应系统的预编译包,解压放到 $PATH 目录下即可。

\n

①-2 源码构建

下载并编译rusend,准备好rust工具链,具体编译命令如下:

\n
1
2
3
git clone https://github.com/CGH0S7/rusend.git
cd rusend
cargo build --release
\n\n

rusend二进制文件移动到系统的$PATH目录下,例如/usr/local/bin

\n

② 补全 & API Key配置

使用下面的命令进行命令行补全配置:

\n
1
2
echo "source <(rusend completions bash)" >> ~/.bashrc
source ~/.bashrc
\n\n
1
2
echo "source <(rusend completions zsh)" >> ~/.zshrc
source ~/.zshrc
\n\n
1
2
rusend completions fish > ~/.config/fish/completions/rusend.fish
source ~/.config/fish/config.fish
\n\n

在Resend控制面板中,找到API Keys选项,为你配置的域名生成一个新的API Key。然后在终端中运行以下命令进行配置:

\n
1
rusend config --key re_xxxxxxxxx
\n\n

③ 使用 rusend 收发邮件

详细功能请查看Readme文档:

\n

\"rusend文档\"
\"rusend文档\"
\"rusend软件帮助\"

\n
\n

6. 成本 & 限额总结

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
项目免费额度
Resend100 封 / 天
域名约 ¥30+ / 年
rusend开源
\n

非主力邮箱场景下完全够用,不建议用于接收各种广告或更新推送。

\n

若想要「匿名转发」隐藏真实地址,可以再套一层 AnonAddy 即可,本文不展开。

\n
\n

7. 结语

以上就是「低成本快速拥有个人专属域名邮箱」的全部流程。

\n

零服务器、零运维、全平台通用,甚至能在手机 Termux 里发邮。

\n

如果能帮到你,去 rusend 点个 ⭐ 当稿费吧~😉

\n","excerpt":"","more":"
\n

失踪人口回归 👋
如果你只想「有个能用的 @自己域名 邮箱」,却懒得租服务器、折腾 Postfix、跟 25 端口斗智斗勇、花费你宝贵的💰,那这篇 15 分钟速通教程就是写给你的(当然,一点💰都不花是不可能的,你至少需要买一个域名=)

\n
\n
\n

1. 最终效果

    \n
  • 任何前缀都能即时生成: helloworld@your.com / fknudt@your.com / ilovethu@your.com
  • \n
  • 在终端里使用命令收发邮件,无需专门打开各种邮件客户端。
  • \n
\n

整套方案 只花域名注册费用0 服务器 0 运维,完全白嫖 Resend 免费额度。

\n
\n

2. 准备工作(2 min)

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
资源用途费用
一枚域名做邮箱后缀约 ¥30+ / 年(阿里云 Namecheap Cloudflare 均可)
Resend 账号托管邮服免费 100 封 / 天
\n

\"Resend文档\"

\n
\n

3. 3 步完成域名绑定(5 min)

    \n
  1. 登录 ResendDomainsAdd Domain
    输入 your.com → 得到一组 DNS 记录(TXT + MX + DKIM)。

    \n
  2. \n
  3. 去域名注册商把记录全部照抄粘贴,保存。(由于笔者为免费用户并且已经配置域名无法再次配置,因此未能提供截图但是步骤非常简单)。

    \n
      \n
    • 登录你的域名注册商账号,找到DNS管理页面。
    • \n
    • 添加Resend提供的TXT记录,用于验证域名所有权。
    • \n
    • 添加Resend提供的MX记录,用于接收邮件。
    • \n
    • 根据Resend的要求,配置完整所有必要的DNS记录。
    • \n
    • 保存更改后,等待DNS记录生效,通常需要几分钟到24小时不等(这是resend官方的说法,笔者实测只需要几分钟不到)。
      ⚠️ 只需改 DNS 解析,不用转 nameserver,1 min 搞定。
    • \n
    \n
  4. \n
  5. 回到 Resend 点 Verify,状态变绿 ✅ 即生效(实测 30s-3min,并不会像官方说的有可能要24小时)。

    \n
  6. \n
\n
\n

4. 无限别名 + 转发(立刻能用)

Resend 对「 catch-all 」免费!
意思是你 不用提前新建账号,任何 @your.com 的信都会飞进你设定的地址,只需要在 Resend 官网查收即可。

\n
\n

5. 终端发邮件(可选,8 min 部署)

网页收发邮件显然太麻烦,我用rust写了个可爱的 CLI 客户端 rusend,基于 Resend 官方 API,支持发送与查收邮件非常方便。(好吧其实是Gemini小姐帮忙写的@@)

\n

①-1 下载预构建可执行文件

Github Releases里下载对应系统的预编译包,解压放到 $PATH 目录下即可。

\n

①-2 源码构建

下载并编译rusend,准备好rust工具链,具体编译命令如下:

\n
1
2
3
git clone https://github.com/CGH0S7/rusend.git
cd rusend
cargo build --release
\n\n

rusend二进制文件移动到系统的$PATH目录下,例如/usr/local/bin

\n

② 补全 & API Key配置

使用下面的命令进行命令行补全配置:

\n
1
2
echo "source <(rusend completions bash)" >> ~/.bashrc
source ~/.bashrc
\n\n
1
2
echo "source <(rusend completions zsh)" >> ~/.zshrc
source ~/.zshrc
\n\n
1
2
rusend completions fish > ~/.config/fish/completions/rusend.fish
source ~/.config/fish/config.fish
\n\n

在Resend控制面板中,找到API Keys选项,为你配置的域名生成一个新的API Key。然后在终端中运行以下命令进行配置:

\n
1
rusend config --key re_xxxxxxxxx
\n\n

③ 使用 rusend 收发邮件

详细功能请查看Readme文档:

\n

\"rusend文档\"
\"rusend文档\"
\"rusend软件帮助\"

\n
\n

6. 成本 & 限额总结

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
项目免费额度
Resend100 封 / 天
域名约 ¥30+ / 年
rusend开源
\n

非主力邮箱场景下完全够用,不建议用于接收各种广告或更新推送。

\n

若想要「匿名转发」隐藏真实地址,可以再套一层 AnonAddy 即可,本文不展开。

\n
\n

7. 结语

以上就是「低成本快速拥有个人专属域名邮箱」的全部流程。

\n

零服务器、零运维、全平台通用,甚至能在手机 Termux 里发邮。

\n

如果能帮到你,去 rusend 点个 ⭐ 当稿费吧~😉

\n"},{"title":"其作始也简,其将毕也必巨 - 回顾64-Bit Brainstorm三年ASC征程","date":"2026-05-30T02:10:31.000Z","_content":"\n我的最后一次ASC之旅终于结束了,回望过去如今我想表达的唯有感激,不仅是对与我并肩作战的队友,还有参赛过程中其他战队的朋友们,以及辛苦付出的组委会老师们。正好借着这次机会,我也想回顾一下这三年来的ASC参赛经历,分享一些个人的感悟和体会。\n\n![充满回忆的ASC照片墙](/images/MyASCStory/MyASCStory.jpg)\n\n### ASC24:从零开始的挑战\n\n2023年年末,一位在网上认识的神秘学长lzd突然发给我一张招新海报,说是有个非常好玩的比赛正在进行校队招新赛让我也来试试,结果这一试直接进入了64-bit Brainstorm战队并开启了我长达三年的ASC之旅。\n\n进入超算队后我听从学长的建议花了一个多月去自学MPI和CUDA编程。经过一段时间学习准备,ASC24的预赛赛题发布了,根据任务安排,我和另一位学长hxh分配到了OpenCAEPoro这个多相多组分渗流模拟科学计算程序的优化任务。依稀记得当时因为这个程序本身依赖较多还要手动构建不少软件导致我们花了三天才成功把这个程序的baseline跑起来,这让我第一次真正接触到了科学计算程序的复杂性和挑战性。之后我们又花了数周时间对这个程序进行了性能分析和优化,xh学长负责算法优化进行了一些手动循环展开和向量化以及一些算子库的高性能版本替代,我则负责一些算子的GPU移植工作并提供了快速fallback回CPU的宏控开关以防万一,预赛阶段我们最终实现的加速比只有1.3x不过好在也顺利进入了决赛。\n\n![ASC24决赛现场](/images/MyASCStory/ASC24-1.jpg)\n\n决赛阶段是我最难忘的部分,我第一次见识到了完整的小型集群搭建维护流程,lzd学长简直像个超人一样什么都懂,他和cyq学长双人完成了整个集群的部署,系统方面有什么问题找他都能解决,功耗过高他也有各种办法来控制,真是太厉害了。终于到了OpenCAEPoro测例发布这天,决赛case可以说相当刁钻记得当时很多团队都卡在第一个测例无法运行,后面几个测例由于规模远远超过预赛测例导致我们的GPU版本直接崩溃,危急情况下,我和xh学长迅速做出决定马上fallback回CPU版本把结果运行出来,在运行后几个测例的同时,我们也在空闲节点排查第一个测例的问题,结果发现只要把我们选用的nvhpc编译链完全回退到gcc就能正确成功运行第一个测例了。最后结果也是非常出人意料,我们只是把所有测例运行完了实际没有特别明显的优化却在这道题上取得了第二名的成绩,通过赛后交流才知道有些队伍虽然有很快的加速比,但是因为精度无法通过所以没能拿到相应的分数。\n\n![ASC24赛后合影](/images/MyASCStory/ASC24-2.jpg)\n\n这一年我们的总成绩是rank 5,是历年来最好的一次,大家都非常开心,晚宴阶段我也认识了非常多其他学校的新朋友并约定来年再见。值得一提的是,这一年由于AI工具还没发展起来,不管是程序排错还是性能优化我们都还是古法处理,现在身处Agent时代回想起来那段人工手动处理问题的经历真的是非常不易。\n\n### ASC25:遭遇挫折的成长\n\n时间转眼来到2024下半年,在新学期的战队招新赛中我们又迎来了三位优秀的新队员分别是wlx, yx和zqr。此时lzd学长面临毕业,由hxh和我领衔和三位新队员组建了一支新的超算团队出征ASC25。这一年我正值大二课业最为繁重的时候,因此在ASC25赛题发布后我被分配到的任务只有集群运维和HPL/HPCG基准测试工作。为了尽可能更多地参与贡献,我在课余时间也耗费了不少精力研究系统调优并产生了一些自以为不错的方案,结果却是埋下了非常深的隐患让我赛后十分后悔。\n\n这一年我给集群选用的发行版是Intel出品的Clear Linux(非常戏谑的是ASC25结束后不久Intel就宣布终止此项目并关闭了Clear Linux官网),当时我由于网络上的一些宣传过于迷信这个发行版的性能表现却忽视了其兼容性带来的问题,预赛阶段我们的机时大部分分给了AlphaFold和m5c两个赛题,直到最后才进行HPL/HPCG测试,这时候我才发现Clear Linux软件源里提供的IB驱动和nvidia-peermem模块完全不兼容并且没有办法修复,而Nvidia官方的IB驱动也不支持Clear Linux无法在这个发行版上安装,为了解决这个问题我最终配置了Rocky + Clear这个非常抽象的双系统方案,虽然最终解决了兼容性问题但也为队友们的工作造成了不少麻烦。好在队友都非常给力,至少在预赛阶段我们的AlphaFold和m5c赛题都有亮眼表现,以不错的成绩再次进入了决赛。\n\n![ASC25来到美丽的青海大学](/images/MyASCStory/ASC25-2.jpg)\n\n来到青海大学,我们从一开始就各种问题不断,先是装完机发现IB起不来,排查了快一个小时发现是Clear Linux起不了opensm服务只能单开一个节点用Rocky Linux启动。而后又是HPL测试结果一直异常,我们只能用双机跑出正常结果超过双机就会性能暴跌。最后更严重的是功耗不知为何完全压不下来即便所有核心都调整频率至最低。结果就是决赛正式运行阶段的两天我们整个过程都非常崩溃,虽然有五个节点但是大部分赛题我们都只能用三节点来跑才能控制功耗不会超出4KW限制。赛后我感到非常自责,觉得自己在系统选择和功耗控制的失误给整个团队带来了很大的麻烦和压力。\n\n![ASC25令我们绝望的现场](/images/MyASCStory/ASC25-3.jpg)\n\n这一年是我们表现最差的一年排名直接倒数,除了wlx负责的AlphaFold表现不错之外其他赛题得分都非常惨淡。好在队友们都非常理解和包容我,原本我打算这一年就从超算队退役,但是带着这份歉意我还是决定明年ASC26干回来争取用一个更好的结果来弥补。在AI工具方面,相比去年这一年的不同是我们在网上发现了一个叫Deepseek V2.5的国产大语言模型,其性能丝毫不逊于同期国外的大模型,我们第一次感受到了AI工具在报错分析和代码优化方面的巨大帮助。\n\n![整理心情备战ASC26](/images/MyASCStory/ASC25-4.jpg)\n\n### ASC26:Agent时代的机遇\n\n终于来到了2025年下半年,我们在新学期的战队招新中又迎来了三位新队员分别是和我同级的dyz,计算机学院大一的qhy以及电子科学学院大二的chb。这一年是我们人力资源最为丰富的一年,lzd回归指导超算队,hxh和wlx也分别被清华大学和上海交大录取,他们作为外援继续参与到超算队的工作中,同时我们另一支参加ISC的超算队也提供了不少帮助。\n\n![我们的ASC26全员合影](/images/MyASCStory/ASC26-1.jpg)\n\n这一次我的工作依旧包括系统运维和基准测试,同时还和chb一起负责了AMSS-NCKU数值相对论模拟程序的优化工作。在系统方面由于去年的经验教训我直接选择了Rocky Linux 10作为我们的操作系统,它在兼容性和稳定性方面都非常可靠没有问题。而在程序优化方面,Agent时代的到来也对我们的工作流带来了前所未有的改变,我们可以把精力更多的放在性能分析和优化思路的探索上,具体的实现细节则可以交给AI工具来完成,这大大提高了我们的效率和创新能力。在AMSS-NCKU的优化过程中,我们通过perf和vtune等工具分析了程序的性能瓶颈,发现问题主要集中在通信和访存上,而这些问题最终又主要由低效的插值函数引起,于是在预赛阶段我们针对MPI和polint相关函数做了充分的针对性优化,包括选用点对点通信和异步通信,以及插值方面调换遍历顺序和提供快路径等,最终在预赛阶段对于GW150914测例我们的每步耗时从基准14s降低到了3s,取得了非常显著的加速效果。同时在世界模型赛题上dyz和qhy也表现良好,我们得以第三次闯入决赛来到无锡学院与各路优秀的同仁汇合继续切磋交流。\n\n进入决赛阶段,我们在AMSS-NCKU的优化工作继续进行,但是当我们继续调整优化将步时压缩到了2.6s左右之后却陷入了一个瓶颈,即接下来我们无论再改动哪里耗时都不会降低了并且还会负优化,这对我和chb造成了巨大的困扰,接近两个礼拜我们都没有任何进展,并且由于没有明显进展汇报我们还不断被无理怀疑骄傲自满,躺平摆烂以及没有理解赛题,好在有学长的鼓励支持我还是坚持了下来尽量忽略掉不合理的外部干扰继续推进工作,最终我和chb决定推翻原本思路基于现在的CPU版本进行完整的GPU重构,虽然这个决定工作量巨大但是我们觉得这是唯一有可能突破瓶颈的办法。AMSS程序本身有着大量参数支持,为了充分准备决赛测例我们自费了大量token来利用Agent进行移植工作。移植初期由于效果不明显我们又被外部质疑找错了方向,然而我和chb都对我们的GPU方案很有信心知道还有大量优化空间,最终我们在前往无锡前取得了巨大突破,GPU版本的每步耗时从2.6s降低到了0.8s多同时还支持多种参数输入,满怀着信心和期待,我们踏上了前往无锡的旅程。\n\n来到无锡学院,相比去年一切都显得有条不紊尽在掌握,我利用clonezilla顺利完成了现场四节点装机并且功耗也完全可控不像去年难以控制,测试阶段每个赛题都进行了充分测试也不像去年磕磕绊绊。然而在正赛阶段却没有我们预期的那么容易。首先是硬件因素带来的劣势,导致我们在HPL/HPCG,UnifoLM-WMA和AMSS-NCKU上基本丧失了竞争力;同时由于大家都有了Agent加持,每个赛题都会出现超快版本导致很多团队只能拿到基础分数,这在过去是完全不可想象的,从前的ASC哪怕优化做得没那么好,但是只要能把所有测例运行完都能拿到不错的分数,而现在只要优化水平不在第一梯队就基本拿不到分数。最终我们大概除了AMSS以外其他赛题全都没能超过10分,也没能突破ASC24的成绩记录。\n\n![难忘今宵,有缘再会](/images/MyASCStory/ASC26-2.jpg)\n\nASC26的结果对我们而言并不完美,但是其经历依然非常宝贵。ASC的意义从来都不是最终排名,而是在优化过程中的技术积累,比赛过程中的团队协作,以及赛后交流中的友谊建立,这些都是无价的财富。通过这三年的ASC经历,我不仅提升了自己的技术能力,更重要的是结识了一群志同道合的朋友,收获了许多珍贵的回忆和经验。与此同时我们也充分认识到了自己和其他优秀团队之间的差距,这些差距既有技术层面的也有资源层面的,它们都为我们提供了未来努力的方向和动力。\n\n最后,我想对所有支持和帮助过我们的人表达最诚挚的感谢。首先是我的队友们,与你们共事让我感到无比幸运和自豪,我从未觉得上大学开心过,除了在超算队和你们一起工作的时候。其次是我在其他高校超算队尤其是齐鲁工业大学,福州大学,西北工业大学,山西大学认识的朋友们,与你们的交流和切磋让我受益匪浅,也让我感受到了ASC赛事的温暖和活力。最后是组委会的老师们,你们的辛勤付出和专业支持为大家提供了一个展示自我和提升自我的平台,非常感激你们的努力。闭幕式结束离开时碰到两位组委会老师还向我们热情告别希望明年再见,口头上我们答应了下来然而遗憾的是明年我们都要奔赴新的前程已经难以重回ASC27赛场了,但愿将来还能有机会以非参赛队员的身份继续参与到ASC的活动中来。\n\n![我们的最后一张ASC合影](/images/MyASCStory/ASC26-3.jpg)\n\n虽然我们的ASC之旅已经结束了,但我相信在这个过程中建立的友谊和积累的经验将会伴随我们继续前行。在此衷心祝愿ASC赛事越办越好,培养出更多优秀的超算人才,也祝愿每一位为ASC挥洒汗水的同学都能在未来的道路上取得更大的成功!\n\n---\n\n### 彩蛋环节\n\n感觉还是有很多比较有意义的照片,可惜受ASC投稿篇幅限制不好放在正文里,就统一放在这里留念吧,以后说不定还会更新,先放下面这些。\n\n![ASC24初代最好的五个人](/images/MyASCStory/ASC24-4.jpg)\n\n![正在进行ISC答辩的超人lzd](/images/MyASCStory/lzdnb.jpg)\n\n![ASC24超级团队赛合影](/images/MyASCStory/ASC24-6.jpg)\n\n![ASC24晚宴合影](/images/MyASCStory/ASC24-5.jpg)\n\n![ASC25秘密基地](/images/MyASCStory/ASC25-1.jpg)\n\n![ASC25决赛机柜,恋恋可爱捏~](/images/MyASCStory/ASC25cluster.jpg)\n\n![ASC25高地队长黄主任我们的超人](/images/MyASCStory/ASC25-5.jpg)\n\n![ASC25青海大学的飘絮](/images/MyASCStory/ASC25-7.jpg)\n\n![ASC26决赛机柜,灵梦可爱捏~](/images/MyASCStory/ASC26cluster.jpg)\n\n![什么时候才能届到呢](/images/MyASCStory/ASC26YeZ.jpg)\n","source":"_posts/One-Last-ASC.md","raw":"---\ntitle: 其作始也简,其将毕也必巨 - 回顾64-bit Brainstorm三年ASC征程\ndate: 2026-05-30 10:10:31\ntags: [竞赛, 生活, 学习, ASC]\n---\n\n我的最后一次ASC之旅终于结束了,回望过去如今我想表达的唯有感激,不仅是对与我并肩作战的队友,还有参赛过程中其他战队的朋友们,以及辛苦付出的组委会老师们。正好借着这次机会,我也想回顾一下这三年来的ASC参赛经历,分享一些个人的感悟和体会。\n\n![充满回忆的ASC照片墙](/images/MyASCStory/MyASCStory.jpg)\n\n### ASC24:从零开始的挑战\n\n2023年年末,一位在网上认识的神秘学长lzd突然发给我一张招新海报,说是有个非常好玩的比赛正在进行校队招新赛让我也来试试,结果这一试直接进入了64-bit Brainstorm战队并开启了我长达三年的ASC之旅。\n\n进入超算队后我听从学长的建议花了一个多月去自学MPI和CUDA编程。经过一段时间学习准备,ASC24的预赛赛题发布了,根据任务安排,我和另一位学长hxh分配到了OpenCAEPoro这个多相多组分渗流模拟科学计算程序的优化任务。依稀记得当时因为这个程序本身依赖较多还要手动构建不少软件导致我们花了三天才成功把这个程序的baseline跑起来,这让我第一次真正接触到了科学计算程序的复杂性和挑战性。之后我们又花了数周时间对这个程序进行了性能分析和优化,xh学长负责算法优化进行了一些手动循环展开和向量化以及一些算子库的高性能版本替代,我则负责一些算子的GPU移植工作并提供了快速fallback回CPU的宏控开关以防万一,预赛阶段我们最终实现的加速比只有1.3x不过好在也顺利进入了决赛。\n\n![ASC24决赛现场](/images/MyASCStory/ASC24-1.jpg)\n\n决赛阶段是我最难忘的部分,我第一次见识到了完整的小型集群搭建维护流程,lzd学长简直像个超人一样什么都懂,他和cyq学长双人完成了整个集群的部署,系统方面有什么问题找他都能解决,功耗过高他也有各种办法来控制,真是太厉害了。终于到了OpenCAEPoro测例发布这天,决赛case可以说相当刁钻记得当时很多团队都卡在第一个测例无法运行,后面几个测例由于规模远远超过预赛测例导致我们的GPU版本直接崩溃,危急情况下,我和xh学长迅速做出决定马上fallback回CPU版本把结果运行出来,在运行后几个测例的同时,我们也在空闲节点排查第一个测例的问题,结果发现只要把我们选用的nvhpc编译链完全回退到gcc就能正确成功运行第一个测例了。最后结果也是非常出人意料,我们只是把所有测例运行完了实际没有特别明显的优化却在这道题上取得了第二名的成绩,通过赛后交流才知道有些队伍虽然有很快的加速比,但是因为精度无法通过所以没能拿到相应的分数。\n\n![ASC24赛后合影](/images/MyASCStory/ASC24-2.jpg)\n\n这一年我们的总成绩是rank 5,是历年来最好的一次,大家都非常开心,晚宴阶段我也认识了非常多其他学校的新朋友并约定来年再见。值得一提的是,这一年由于AI工具还没发展起来,不管是程序排错还是性能优化我们都还是古法处理,现在身处Agent时代回想起来那段人工手动处理问题的经历真的是非常不易。\n\n### ASC25:遭遇挫折的成长\n\n时间转眼来到2024下半年,在新学期的战队招新赛中我们又迎来了三位优秀的新队员分别是wlx, yx和zqr。此时lzd学长面临毕业,由hxh和我领衔和三位新队员组建了一支新的超算团队出征ASC25。这一年我正值大二课业最为繁重的时候,因此在ASC25赛题发布后我被分配到的任务只有集群运维和HPL/HPCG基准测试工作。为了尽可能更多地参与贡献,我在课余时间也耗费了不少精力研究系统调优并产生了一些自以为不错的方案,结果却是埋下了非常深的隐患让我赛后十分后悔。\n\n这一年我给集群选用的发行版是Intel出品的Clear Linux(非常戏谑的是ASC25结束后不久Intel就宣布终止此项目并关闭了Clear Linux官网),当时我由于网络上的一些宣传过于迷信这个发行版的性能表现却忽视了其兼容性带来的问题,预赛阶段我们的机时大部分分给了AlphaFold和m5c两个赛题,直到最后才进行HPL/HPCG测试,这时候我才发现Clear Linux软件源里提供的IB驱动和nvidia-peermem模块完全不兼容并且没有办法修复,而Nvidia官方的IB驱动也不支持Clear Linux无法在这个发行版上安装,为了解决这个问题我最终配置了Rocky + Clear这个非常抽象的双系统方案,虽然最终解决了兼容性问题但也为队友们的工作造成了不少麻烦。好在队友都非常给力,至少在预赛阶段我们的AlphaFold和m5c赛题都有亮眼表现,以不错的成绩再次进入了决赛。\n\n![ASC25来到美丽的青海大学](/images/MyASCStory/ASC25-2.jpg)\n\n来到青海大学,我们从一开始就各种问题不断,先是装完机发现IB起不来,排查了快一个小时发现是Clear Linux起不了opensm服务只能单开一个节点用Rocky Linux启动。而后又是HPL测试结果一直异常,我们只能用双机跑出正常结果超过双机就会性能暴跌。最后更严重的是功耗不知为何完全压不下来即便所有核心都调整频率至最低。结果就是决赛正式运行阶段的两天我们整个过程都非常崩溃,虽然有五个节点但是大部分赛题我们都只能用三节点来跑才能控制功耗不会超出4KW限制。赛后我感到非常自责,觉得自己在系统选择和功耗控制的失误给整个团队带来了很大的麻烦和压力。\n\n![ASC25令我们绝望的现场](/images/MyASCStory/ASC25-3.jpg)\n\n这一年是我们表现最差的一年排名直接倒数,除了wlx负责的AlphaFold表现不错之外其他赛题得分都非常惨淡。好在队友们都非常理解和包容我,原本我打算这一年就从超算队退役,但是带着这份歉意我还是决定明年ASC26干回来争取用一个更好的结果来弥补。在AI工具方面,相比去年这一年的不同是我们在网上发现了一个叫Deepseek V2.5的国产大语言模型,其性能丝毫不逊于同期国外的大模型,我们第一次感受到了AI工具在报错分析和代码优化方面的巨大帮助。\n\n![整理心情备战ASC26](/images/MyASCStory/ASC25-4.jpg)\n\n### ASC26:Agent时代的机遇\n\n终于来到了2025年下半年,我们在新学期的战队招新中又迎来了三位新队员分别是和我同级的dyz,计算机学院大一的qhy以及电子科学学院大二的chb。这一年是我们人力资源最为丰富的一年,lzd回归指导超算队,hxh和wlx也分别被清华大学和上海交大录取,他们作为外援继续参与到超算队的工作中,同时我们另一支参加ISC的超算队也提供了不少帮助。\n\n![我们的ASC26全员合影](/images/MyASCStory/ASC26-1.jpg)\n\n这一次我的工作依旧包括系统运维和基准测试,同时还和chb一起负责了AMSS-NCKU数值相对论模拟程序的优化工作。在系统方面由于去年的经验教训我直接选择了Rocky Linux 10作为我们的操作系统,它在兼容性和稳定性方面都非常可靠没有问题。而在程序优化方面,Agent时代的到来也对我们的工作流带来了前所未有的改变,我们可以把精力更多的放在性能分析和优化思路的探索上,具体的实现细节则可以交给AI工具来完成,这大大提高了我们的效率和创新能力。在AMSS-NCKU的优化过程中,我们通过perf和vtune等工具分析了程序的性能瓶颈,发现问题主要集中在通信和访存上,而这些问题最终又主要由低效的插值函数引起,于是在预赛阶段我们针对MPI和polint相关函数做了充分的针对性优化,包括选用点对点通信和异步通信,以及插值方面调换遍历顺序和提供快路径等,最终在预赛阶段对于GW150914测例我们的每步耗时从基准14s降低到了3s,取得了非常显著的加速效果。同时在世界模型赛题上dyz和qhy也表现良好,我们得以第三次闯入决赛来到无锡学院与各路优秀的同仁汇合继续切磋交流。\n\n进入决赛阶段,我们在AMSS-NCKU的优化工作继续进行,但是当我们继续调整优化将步时压缩到了2.6s左右之后却陷入了一个瓶颈,即接下来我们无论再改动哪里耗时都不会降低了并且还会负优化,这对我和chb造成了巨大的困扰,接近两个礼拜我们都没有任何进展,并且由于没有明显进展汇报我们还不断被无理怀疑骄傲自满,躺平摆烂以及没有理解赛题,好在有学长的鼓励支持我还是坚持了下来尽量忽略掉不合理的外部干扰继续推进工作,最终我和chb决定推翻原本思路基于现在的CPU版本进行完整的GPU重构,虽然这个决定工作量巨大但是我们觉得这是唯一有可能突破瓶颈的办法。AMSS程序本身有着大量参数支持,为了充分准备决赛测例我们自费了大量token来利用Agent进行移植工作。移植初期由于效果不明显我们又被外部质疑找错了方向,然而我和chb都对我们的GPU方案很有信心知道还有大量优化空间,最终我们在前往无锡前取得了巨大突破,GPU版本的每步耗时从2.6s降低到了0.8s多同时还支持多种参数输入,满怀着信心和期待,我们踏上了前往无锡的旅程。\n\n来到无锡学院,相比去年一切都显得有条不紊尽在掌握,我利用clonezilla顺利完成了现场四节点装机并且功耗也完全可控不像去年难以控制,测试阶段每个赛题都进行了充分测试也不像去年磕磕绊绊。然而在正赛阶段却没有我们预期的那么容易。首先是硬件因素带来的劣势,导致我们在HPL/HPCG,UnifoLM-WMA和AMSS-NCKU上基本丧失了竞争力;同时由于大家都有了Agent加持,每个赛题都会出现超快版本导致很多团队只能拿到基础分数,这在过去是完全不可想象的,从前的ASC哪怕优化做得没那么好,但是只要能把所有测例运行完都能拿到不错的分数,而现在只要优化水平不在第一梯队就基本拿不到分数。最终我们大概除了AMSS以外其他赛题全都没能超过10分,也没能突破ASC24的成绩记录。\n\n![难忘今宵,有缘再会](/images/MyASCStory/ASC26-2.jpg)\n\nASC26的结果对我们而言并不完美,但是其经历依然非常宝贵。ASC的意义从来都不是最终排名,而是在优化过程中的技术积累,比赛过程中的团队协作,以及赛后交流中的友谊建立,这些都是无价的财富。通过这三年的ASC经历,我不仅提升了自己的技术能力,更重要的是结识了一群志同道合的朋友,收获了许多珍贵的回忆和经验。与此同时我们也充分认识到了自己和其他优秀团队之间的差距,这些差距既有技术层面的也有资源层面的,它们都为我们提供了未来努力的方向和动力。\n\n最后,我想对所有支持和帮助过我们的人表达最诚挚的感谢。首先是我的队友们,与你们共事让我感到无比幸运和自豪,我从未觉得上大学开心过,除了在超算队和你们一起工作的时候。其次是我在其他高校超算队尤其是齐鲁工业大学,福州大学,西北工业大学,山西大学认识的朋友们,与你们的交流和切磋让我受益匪浅,也让我感受到了ASC赛事的温暖和活力。最后是组委会的老师们,你们的辛勤付出和专业支持为大家提供了一个展示自我和提升自我的平台,非常感激你们的努力。闭幕式结束离开时碰到两位组委会老师还向我们热情告别希望明年再见,口头上我们答应了下来然而遗憾的是明年我们都要奔赴新的前程已经难以重回ASC27赛场了,但愿将来还能有机会以非参赛队员的身份继续参与到ASC的活动中来。\n\n![我们的最后一张ASC合影](/images/MyASCStory/ASC26-3.jpg)\n\n虽然我们的ASC之旅已经结束了,但我相信在这个过程中建立的友谊和积累的经验将会伴随我们继续前行。在此衷心祝愿ASC赛事越办越好,培养出更多优秀的超算人才,也祝愿每一位为ASC挥洒汗水的同学都能在未来的道路上取得更大的成功!\n\n---\n\n### 彩蛋环节\n\n感觉还是有很多比较有意义的照片,可惜受ASC投稿篇幅限制不好放在正文里,就统一放在这里留念吧,以后说不定还会更新,先放下面这些。\n\n![ASC24初代最好的五个人](/images/MyASCStory/ASC24-4.jpg)\n\n![正在进行ISC答辩的超人lzd](/images/MyASCStory/lzdnb.jpg)\n\n![ASC24超级团队赛合影](/images/MyASCStory/ASC24-6.jpg)\n\n![ASC24晚宴合影](/images/MyASCStory/ASC24-5.jpg)\n\n![ASC25秘密基地](/images/MyASCStory/ASC25-1.jpg)\n\n![ASC25决赛机柜,恋恋可爱捏~](/images/MyASCStory/ASC25cluster.jpg)\n\n![ASC25高地队长黄主任我们的超人](/images/MyASCStory/ASC25-5.jpg)\n\n![ASC25青海大学的飘絮](/images/MyASCStory/ASC25-7.jpg)\n\n![ASC26决赛机柜,灵梦可爱捏~](/images/MyASCStory/ASC26cluster.jpg)\n\n![什么时候才能届到呢](/images/MyASCStory/ASC26YeZ.jpg)\n","slug":"One-Last-ASC","published":1,"updated":"2026-06-03T02:47:37.305Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblu000ridd04wc3d8td","content":"

我的最后一次ASC之旅终于结束了,回望过去如今我想表达的唯有感激,不仅是对与我并肩作战的队友,还有参赛过程中其他战队的朋友们,以及辛苦付出的组委会老师们。正好借着这次机会,我也想回顾一下这三年来的ASC参赛经历,分享一些个人的感悟和体会。

\n

\"充满回忆的ASC照片墙\"

\n

ASC24:从零开始的挑战

2023年年末,一位在网上认识的神秘学长lzd突然发给我一张招新海报,说是有个非常好玩的比赛正在进行校队招新赛让我也来试试,结果这一试直接进入了64-bit Brainstorm战队并开启了我长达三年的ASC之旅。

\n

进入超算队后我听从学长的建议花了一个多月去自学MPI和CUDA编程。经过一段时间学习准备,ASC24的预赛赛题发布了,根据任务安排,我和另一位学长hxh分配到了OpenCAEPoro这个多相多组分渗流模拟科学计算程序的优化任务。依稀记得当时因为这个程序本身依赖较多还要手动构建不少软件导致我们花了三天才成功把这个程序的baseline跑起来,这让我第一次真正接触到了科学计算程序的复杂性和挑战性。之后我们又花了数周时间对这个程序进行了性能分析和优化,xh学长负责算法优化进行了一些手动循环展开和向量化以及一些算子库的高性能版本替代,我则负责一些算子的GPU移植工作并提供了快速fallback回CPU的宏控开关以防万一,预赛阶段我们最终实现的加速比只有1.3x不过好在也顺利进入了决赛。

\n

\"ASC24决赛现场\"

\n

决赛阶段是我最难忘的部分,我第一次见识到了完整的小型集群搭建维护流程,lzd学长简直像个超人一样什么都懂,他和cyq学长双人完成了整个集群的部署,系统方面有什么问题找他都能解决,功耗过高他也有各种办法来控制,真是太厉害了。终于到了OpenCAEPoro测例发布这天,决赛case可以说相当刁钻记得当时很多团队都卡在第一个测例无法运行,后面几个测例由于规模远远超过预赛测例导致我们的GPU版本直接崩溃,危急情况下,我和xh学长迅速做出决定马上fallback回CPU版本把结果运行出来,在运行后几个测例的同时,我们也在空闲节点排查第一个测例的问题,结果发现只要把我们选用的nvhpc编译链完全回退到gcc就能正确成功运行第一个测例了。最后结果也是非常出人意料,我们只是把所有测例运行完了实际没有特别明显的优化却在这道题上取得了第二名的成绩,通过赛后交流才知道有些队伍虽然有很快的加速比,但是因为精度无法通过所以没能拿到相应的分数。

\n

\"ASC24赛后合影\"

\n

这一年我们的总成绩是rank 5,是历年来最好的一次,大家都非常开心,晚宴阶段我也认识了非常多其他学校的新朋友并约定来年再见。值得一提的是,这一年由于AI工具还没发展起来,不管是程序排错还是性能优化我们都还是古法处理,现在身处Agent时代回想起来那段人工手动处理问题的经历真的是非常不易。

\n

ASC25:遭遇挫折的成长

时间转眼来到2024下半年,在新学期的战队招新赛中我们又迎来了三位优秀的新队员分别是wlx, yx和zqr。此时lzd学长面临毕业,由hxh和我领衔和三位新队员组建了一支新的超算团队出征ASC25。这一年我正值大二课业最为繁重的时候,因此在ASC25赛题发布后我被分配到的任务只有集群运维和HPL/HPCG基准测试工作。为了尽可能更多地参与贡献,我在课余时间也耗费了不少精力研究系统调优并产生了一些自以为不错的方案,结果却是埋下了非常深的隐患让我赛后十分后悔。

\n

这一年我给集群选用的发行版是Intel出品的Clear Linux(非常戏谑的是ASC25结束后不久Intel就宣布终止此项目并关闭了Clear Linux官网),当时我由于网络上的一些宣传过于迷信这个发行版的性能表现却忽视了其兼容性带来的问题,预赛阶段我们的机时大部分分给了AlphaFold和m5c两个赛题,直到最后才进行HPL/HPCG测试,这时候我才发现Clear Linux软件源里提供的IB驱动和nvidia-peermem模块完全不兼容并且没有办法修复,而Nvidia官方的IB驱动也不支持Clear Linux无法在这个发行版上安装,为了解决这个问题我最终配置了Rocky + Clear这个非常抽象的双系统方案,虽然最终解决了兼容性问题但也为队友们的工作造成了不少麻烦。好在队友都非常给力,至少在预赛阶段我们的AlphaFold和m5c赛题都有亮眼表现,以不错的成绩再次进入了决赛。

\n

\"ASC25来到美丽的青海大学\"

\n

来到青海大学,我们从一开始就各种问题不断,先是装完机发现IB起不来,排查了快一个小时发现是Clear Linux起不了opensm服务只能单开一个节点用Rocky Linux启动。而后又是HPL测试结果一直异常,我们只能用双机跑出正常结果超过双机就会性能暴跌。最后更严重的是功耗不知为何完全压不下来即便所有核心都调整频率至最低。结果就是决赛正式运行阶段的两天我们整个过程都非常崩溃,虽然有五个节点但是大部分赛题我们都只能用三节点来跑才能控制功耗不会超出4KW限制。赛后我感到非常自责,觉得自己在系统选择和功耗控制的失误给整个团队带来了很大的麻烦和压力。

\n

\"ASC25令我们绝望的现场\"

\n

这一年是我们表现最差的一年排名直接倒数,除了wlx负责的AlphaFold表现不错之外其他赛题得分都非常惨淡。好在队友们都非常理解和包容我,原本我打算这一年就从超算队退役,但是带着这份歉意我还是决定明年ASC26干回来争取用一个更好的结果来弥补。在AI工具方面,相比去年这一年的不同是我们在网上发现了一个叫Deepseek V2.5的国产大语言模型,其性能丝毫不逊于同期国外的大模型,我们第一次感受到了AI工具在报错分析和代码优化方面的巨大帮助。

\n

\"整理心情备战ASC26\"

\n

ASC26:Agent时代的机遇

终于来到了2025年下半年,我们在新学期的战队招新中又迎来了三位新队员分别是和我同级的dyz,计算机学院大一的qhy以及电子科学学院大二的chb。这一年是我们人力资源最为丰富的一年,lzd回归指导超算队,hxh和wlx也分别被清华大学和上海交大录取,他们作为外援继续参与到超算队的工作中,同时我们另一支参加ISC的超算队也提供了不少帮助。

\n

\"我们的ASC26全员合影\"

\n

这一次我的工作依旧包括系统运维和基准测试,同时还和chb一起负责了AMSS-NCKU数值相对论模拟程序的优化工作。在系统方面由于去年的经验教训我直接选择了Rocky Linux 10作为我们的操作系统,它在兼容性和稳定性方面都非常可靠没有问题。而在程序优化方面,Agent时代的到来也对我们的工作流带来了前所未有的改变,我们可以把精力更多的放在性能分析和优化思路的探索上,具体的实现细节则可以交给AI工具来完成,这大大提高了我们的效率和创新能力。在AMSS-NCKU的优化过程中,我们通过perf和vtune等工具分析了程序的性能瓶颈,发现问题主要集中在通信和访存上,而这些问题最终又主要由低效的插值函数引起,于是在预赛阶段我们针对MPI和polint相关函数做了充分的针对性优化,包括选用点对点通信和异步通信,以及插值方面调换遍历顺序和提供快路径等,最终在预赛阶段对于GW150914测例我们的每步耗时从基准14s降低到了3s,取得了非常显著的加速效果。同时在世界模型赛题上dyz和qhy也表现良好,我们得以第三次闯入决赛来到无锡学院与各路优秀的同仁汇合继续切磋交流。

\n

进入决赛阶段,我们在AMSS-NCKU的优化工作继续进行,但是当我们继续调整优化将步时压缩到了2.6s左右之后却陷入了一个瓶颈,即接下来我们无论再改动哪里耗时都不会降低了并且还会负优化,这对我和chb造成了巨大的困扰,接近两个礼拜我们都没有任何进展,并且由于没有明显进展汇报我们还不断被无理怀疑骄傲自满,躺平摆烂以及没有理解赛题,好在有学长的鼓励支持我还是坚持了下来尽量忽略掉不合理的外部干扰继续推进工作,最终我和chb决定推翻原本思路基于现在的CPU版本进行完整的GPU重构,虽然这个决定工作量巨大但是我们觉得这是唯一有可能突破瓶颈的办法。AMSS程序本身有着大量参数支持,为了充分准备决赛测例我们自费了大量token来利用Agent进行移植工作。移植初期由于效果不明显我们又被外部质疑找错了方向,然而我和chb都对我们的GPU方案很有信心知道还有大量优化空间,最终我们在前往无锡前取得了巨大突破,GPU版本的每步耗时从2.6s降低到了0.8s多同时还支持多种参数输入,满怀着信心和期待,我们踏上了前往无锡的旅程。

\n

来到无锡学院,相比去年一切都显得有条不紊尽在掌握,我利用clonezilla顺利完成了现场四节点装机并且功耗也完全可控不像去年难以控制,测试阶段每个赛题都进行了充分测试也不像去年磕磕绊绊。然而在正赛阶段却没有我们预期的那么容易。首先是硬件因素带来的劣势,导致我们在HPL/HPCG,UnifoLM-WMA和AMSS-NCKU上基本丧失了竞争力;同时由于大家都有了Agent加持,每个赛题都会出现超快版本导致很多团队只能拿到基础分数,这在过去是完全不可想象的,从前的ASC哪怕优化做得没那么好,但是只要能把所有测例运行完都能拿到不错的分数,而现在只要优化水平不在第一梯队就基本拿不到分数。最终我们大概除了AMSS以外其他赛题全都没能超过10分,也没能突破ASC24的成绩记录。

\n

\"难忘今宵,有缘再会\"

\n

ASC26的结果对我们而言并不完美,但是其经历依然非常宝贵。ASC的意义从来都不是最终排名,而是在优化过程中的技术积累,比赛过程中的团队协作,以及赛后交流中的友谊建立,这些都是无价的财富。通过这三年的ASC经历,我不仅提升了自己的技术能力,更重要的是结识了一群志同道合的朋友,收获了许多珍贵的回忆和经验。与此同时我们也充分认识到了自己和其他优秀团队之间的差距,这些差距既有技术层面的也有资源层面的,它们都为我们提供了未来努力的方向和动力。

\n

最后,我想对所有支持和帮助过我们的人表达最诚挚的感谢。首先是我的队友们,与你们共事让我感到无比幸运和自豪,我从未觉得上大学开心过,除了在超算队和你们一起工作的时候。其次是我在其他高校超算队尤其是齐鲁工业大学,福州大学,西北工业大学,山西大学认识的朋友们,与你们的交流和切磋让我受益匪浅,也让我感受到了ASC赛事的温暖和活力。最后是组委会的老师们,你们的辛勤付出和专业支持为大家提供了一个展示自我和提升自我的平台,非常感激你们的努力。闭幕式结束离开时碰到两位组委会老师还向我们热情告别希望明年再见,口头上我们答应了下来然而遗憾的是明年我们都要奔赴新的前程已经难以重回ASC27赛场了,但愿将来还能有机会以非参赛队员的身份继续参与到ASC的活动中来。

\n

\"我们的最后一张ASC合影\"

\n

虽然我们的ASC之旅已经结束了,但我相信在这个过程中建立的友谊和积累的经验将会伴随我们继续前行。在此衷心祝愿ASC赛事越办越好,培养出更多优秀的超算人才,也祝愿每一位为ASC挥洒汗水的同学都能在未来的道路上取得更大的成功!

\n
\n

彩蛋环节

感觉还是有很多比较有意义的照片,可惜受ASC投稿篇幅限制不好放在正文里,就统一放在这里留念吧,以后说不定还会更新,先放下面这些。

\n

\"ASC24初代最好的五个人\"

\n

\"正在进行ISC答辩的超人lzd\"

\n

\"ASC24超级团队赛合影\"

\n

\"ASC24晚宴合影\"

\n

\"ASC25秘密基地\"

\n

\"ASC25决赛机柜,恋恋可爱捏~\"

\n

\"ASC25高地队长黄主任我们的超人\"

\n

\"ASC25青海大学的飘絮\"

\n

\"ASC26决赛机柜,灵梦可爱捏~\"

\n

\"什么时候才能届到呢\"

\n","excerpt":"","more":"

我的最后一次ASC之旅终于结束了,回望过去如今我想表达的唯有感激,不仅是对与我并肩作战的队友,还有参赛过程中其他战队的朋友们,以及辛苦付出的组委会老师们。正好借着这次机会,我也想回顾一下这三年来的ASC参赛经历,分享一些个人的感悟和体会。

\n

\"充满回忆的ASC照片墙\"

\n

ASC24:从零开始的挑战

2023年年末,一位在网上认识的神秘学长lzd突然发给我一张招新海报,说是有个非常好玩的比赛正在进行校队招新赛让我也来试试,结果这一试直接进入了64-bit Brainstorm战队并开启了我长达三年的ASC之旅。

\n

进入超算队后我听从学长的建议花了一个多月去自学MPI和CUDA编程。经过一段时间学习准备,ASC24的预赛赛题发布了,根据任务安排,我和另一位学长hxh分配到了OpenCAEPoro这个多相多组分渗流模拟科学计算程序的优化任务。依稀记得当时因为这个程序本身依赖较多还要手动构建不少软件导致我们花了三天才成功把这个程序的baseline跑起来,这让我第一次真正接触到了科学计算程序的复杂性和挑战性。之后我们又花了数周时间对这个程序进行了性能分析和优化,xh学长负责算法优化进行了一些手动循环展开和向量化以及一些算子库的高性能版本替代,我则负责一些算子的GPU移植工作并提供了快速fallback回CPU的宏控开关以防万一,预赛阶段我们最终实现的加速比只有1.3x不过好在也顺利进入了决赛。

\n

\"ASC24决赛现场\"

\n

决赛阶段是我最难忘的部分,我第一次见识到了完整的小型集群搭建维护流程,lzd学长简直像个超人一样什么都懂,他和cyq学长双人完成了整个集群的部署,系统方面有什么问题找他都能解决,功耗过高他也有各种办法来控制,真是太厉害了。终于到了OpenCAEPoro测例发布这天,决赛case可以说相当刁钻记得当时很多团队都卡在第一个测例无法运行,后面几个测例由于规模远远超过预赛测例导致我们的GPU版本直接崩溃,危急情况下,我和xh学长迅速做出决定马上fallback回CPU版本把结果运行出来,在运行后几个测例的同时,我们也在空闲节点排查第一个测例的问题,结果发现只要把我们选用的nvhpc编译链完全回退到gcc就能正确成功运行第一个测例了。最后结果也是非常出人意料,我们只是把所有测例运行完了实际没有特别明显的优化却在这道题上取得了第二名的成绩,通过赛后交流才知道有些队伍虽然有很快的加速比,但是因为精度无法通过所以没能拿到相应的分数。

\n

\"ASC24赛后合影\"

\n

这一年我们的总成绩是rank 5,是历年来最好的一次,大家都非常开心,晚宴阶段我也认识了非常多其他学校的新朋友并约定来年再见。值得一提的是,这一年由于AI工具还没发展起来,不管是程序排错还是性能优化我们都还是古法处理,现在身处Agent时代回想起来那段人工手动处理问题的经历真的是非常不易。

\n

ASC25:遭遇挫折的成长

时间转眼来到2024下半年,在新学期的战队招新赛中我们又迎来了三位优秀的新队员分别是wlx, yx和zqr。此时lzd学长面临毕业,由hxh和我领衔和三位新队员组建了一支新的超算团队出征ASC25。这一年我正值大二课业最为繁重的时候,因此在ASC25赛题发布后我被分配到的任务只有集群运维和HPL/HPCG基准测试工作。为了尽可能更多地参与贡献,我在课余时间也耗费了不少精力研究系统调优并产生了一些自以为不错的方案,结果却是埋下了非常深的隐患让我赛后十分后悔。

\n

这一年我给集群选用的发行版是Intel出品的Clear Linux(非常戏谑的是ASC25结束后不久Intel就宣布终止此项目并关闭了Clear Linux官网),当时我由于网络上的一些宣传过于迷信这个发行版的性能表现却忽视了其兼容性带来的问题,预赛阶段我们的机时大部分分给了AlphaFold和m5c两个赛题,直到最后才进行HPL/HPCG测试,这时候我才发现Clear Linux软件源里提供的IB驱动和nvidia-peermem模块完全不兼容并且没有办法修复,而Nvidia官方的IB驱动也不支持Clear Linux无法在这个发行版上安装,为了解决这个问题我最终配置了Rocky + Clear这个非常抽象的双系统方案,虽然最终解决了兼容性问题但也为队友们的工作造成了不少麻烦。好在队友都非常给力,至少在预赛阶段我们的AlphaFold和m5c赛题都有亮眼表现,以不错的成绩再次进入了决赛。

\n

\"ASC25来到美丽的青海大学\"

\n

来到青海大学,我们从一开始就各种问题不断,先是装完机发现IB起不来,排查了快一个小时发现是Clear Linux起不了opensm服务只能单开一个节点用Rocky Linux启动。而后又是HPL测试结果一直异常,我们只能用双机跑出正常结果超过双机就会性能暴跌。最后更严重的是功耗不知为何完全压不下来即便所有核心都调整频率至最低。结果就是决赛正式运行阶段的两天我们整个过程都非常崩溃,虽然有五个节点但是大部分赛题我们都只能用三节点来跑才能控制功耗不会超出4KW限制。赛后我感到非常自责,觉得自己在系统选择和功耗控制的失误给整个团队带来了很大的麻烦和压力。

\n

\"ASC25令我们绝望的现场\"

\n

这一年是我们表现最差的一年排名直接倒数,除了wlx负责的AlphaFold表现不错之外其他赛题得分都非常惨淡。好在队友们都非常理解和包容我,原本我打算这一年就从超算队退役,但是带着这份歉意我还是决定明年ASC26干回来争取用一个更好的结果来弥补。在AI工具方面,相比去年这一年的不同是我们在网上发现了一个叫Deepseek V2.5的国产大语言模型,其性能丝毫不逊于同期国外的大模型,我们第一次感受到了AI工具在报错分析和代码优化方面的巨大帮助。

\n

\"整理心情备战ASC26\"

\n

ASC26:Agent时代的机遇

终于来到了2025年下半年,我们在新学期的战队招新中又迎来了三位新队员分别是和我同级的dyz,计算机学院大一的qhy以及电子科学学院大二的chb。这一年是我们人力资源最为丰富的一年,lzd回归指导超算队,hxh和wlx也分别被清华大学和上海交大录取,他们作为外援继续参与到超算队的工作中,同时我们另一支参加ISC的超算队也提供了不少帮助。

\n

\"我们的ASC26全员合影\"

\n

这一次我的工作依旧包括系统运维和基准测试,同时还和chb一起负责了AMSS-NCKU数值相对论模拟程序的优化工作。在系统方面由于去年的经验教训我直接选择了Rocky Linux 10作为我们的操作系统,它在兼容性和稳定性方面都非常可靠没有问题。而在程序优化方面,Agent时代的到来也对我们的工作流带来了前所未有的改变,我们可以把精力更多的放在性能分析和优化思路的探索上,具体的实现细节则可以交给AI工具来完成,这大大提高了我们的效率和创新能力。在AMSS-NCKU的优化过程中,我们通过perf和vtune等工具分析了程序的性能瓶颈,发现问题主要集中在通信和访存上,而这些问题最终又主要由低效的插值函数引起,于是在预赛阶段我们针对MPI和polint相关函数做了充分的针对性优化,包括选用点对点通信和异步通信,以及插值方面调换遍历顺序和提供快路径等,最终在预赛阶段对于GW150914测例我们的每步耗时从基准14s降低到了3s,取得了非常显著的加速效果。同时在世界模型赛题上dyz和qhy也表现良好,我们得以第三次闯入决赛来到无锡学院与各路优秀的同仁汇合继续切磋交流。

\n

进入决赛阶段,我们在AMSS-NCKU的优化工作继续进行,但是当我们继续调整优化将步时压缩到了2.6s左右之后却陷入了一个瓶颈,即接下来我们无论再改动哪里耗时都不会降低了并且还会负优化,这对我和chb造成了巨大的困扰,接近两个礼拜我们都没有任何进展,并且由于没有明显进展汇报我们还不断被无理怀疑骄傲自满,躺平摆烂以及没有理解赛题,好在有学长的鼓励支持我还是坚持了下来尽量忽略掉不合理的外部干扰继续推进工作,最终我和chb决定推翻原本思路基于现在的CPU版本进行完整的GPU重构,虽然这个决定工作量巨大但是我们觉得这是唯一有可能突破瓶颈的办法。AMSS程序本身有着大量参数支持,为了充分准备决赛测例我们自费了大量token来利用Agent进行移植工作。移植初期由于效果不明显我们又被外部质疑找错了方向,然而我和chb都对我们的GPU方案很有信心知道还有大量优化空间,最终我们在前往无锡前取得了巨大突破,GPU版本的每步耗时从2.6s降低到了0.8s多同时还支持多种参数输入,满怀着信心和期待,我们踏上了前往无锡的旅程。

\n

来到无锡学院,相比去年一切都显得有条不紊尽在掌握,我利用clonezilla顺利完成了现场四节点装机并且功耗也完全可控不像去年难以控制,测试阶段每个赛题都进行了充分测试也不像去年磕磕绊绊。然而在正赛阶段却没有我们预期的那么容易。首先是硬件因素带来的劣势,导致我们在HPL/HPCG,UnifoLM-WMA和AMSS-NCKU上基本丧失了竞争力;同时由于大家都有了Agent加持,每个赛题都会出现超快版本导致很多团队只能拿到基础分数,这在过去是完全不可想象的,从前的ASC哪怕优化做得没那么好,但是只要能把所有测例运行完都能拿到不错的分数,而现在只要优化水平不在第一梯队就基本拿不到分数。最终我们大概除了AMSS以外其他赛题全都没能超过10分,也没能突破ASC24的成绩记录。

\n

\"难忘今宵,有缘再会\"

\n

ASC26的结果对我们而言并不完美,但是其经历依然非常宝贵。ASC的意义从来都不是最终排名,而是在优化过程中的技术积累,比赛过程中的团队协作,以及赛后交流中的友谊建立,这些都是无价的财富。通过这三年的ASC经历,我不仅提升了自己的技术能力,更重要的是结识了一群志同道合的朋友,收获了许多珍贵的回忆和经验。与此同时我们也充分认识到了自己和其他优秀团队之间的差距,这些差距既有技术层面的也有资源层面的,它们都为我们提供了未来努力的方向和动力。

\n

最后,我想对所有支持和帮助过我们的人表达最诚挚的感谢。首先是我的队友们,与你们共事让我感到无比幸运和自豪,我从未觉得上大学开心过,除了在超算队和你们一起工作的时候。其次是我在其他高校超算队尤其是齐鲁工业大学,福州大学,西北工业大学,山西大学认识的朋友们,与你们的交流和切磋让我受益匪浅,也让我感受到了ASC赛事的温暖和活力。最后是组委会的老师们,你们的辛勤付出和专业支持为大家提供了一个展示自我和提升自我的平台,非常感激你们的努力。闭幕式结束离开时碰到两位组委会老师还向我们热情告别希望明年再见,口头上我们答应了下来然而遗憾的是明年我们都要奔赴新的前程已经难以重回ASC27赛场了,但愿将来还能有机会以非参赛队员的身份继续参与到ASC的活动中来。

\n

\"我们的最后一张ASC合影\"

\n

虽然我们的ASC之旅已经结束了,但我相信在这个过程中建立的友谊和积累的经验将会伴随我们继续前行。在此衷心祝愿ASC赛事越办越好,培养出更多优秀的超算人才,也祝愿每一位为ASC挥洒汗水的同学都能在未来的道路上取得更大的成功!

\n
\n

彩蛋环节

感觉还是有很多比较有意义的照片,可惜受ASC投稿篇幅限制不好放在正文里,就统一放在这里留念吧,以后说不定还会更新,先放下面这些。

\n

\"ASC24初代最好的五个人\"

\n

\"正在进行ISC答辩的超人lzd\"

\n

\"ASC24超级团队赛合影\"

\n

\"ASC24晚宴合影\"

\n

\"ASC25秘密基地\"

\n

\"ASC25决赛机柜,恋恋可爱捏~\"

\n

\"ASC25高地队长黄主任我们的超人\"

\n

\"ASC25青海大学的飘絮\"

\n

\"ASC26决赛机柜,灵梦可爱捏~\"

\n

\"什么时候才能届到呢\"

\n"},{"title":"Archlinux的KDE Plasma优先启用Nvidia独立显卡和混合显卡配置指北","date":"2024-11-06T02:02:04.000Z","_content":"本文将介绍在 **X11** 和 **Wayland** 两种会话下,如何在 KDE Plasma 中优先启用 Nvidia 独立显卡,并提供 I+N 混合显卡的配置方案。**首先,请确保您已经正确安装了 Nvidia 驱动**(如果非 Mainline 内核,请使用 `nvidia-dkms` 或 `nvidia-open-dkms` 版本)。\n\n![Nvidia设置示意图](/images/5364bba6d035326e82c53504dd53e7c2454454985.png)\n\n### 适用系统\n\n对于 **CachyOS** 或 **EndeavourOS** 等 Arch Linux 衍生版,这些配置大多开箱即用,但对于刚刚入坑 Arch Linux 且使用 KDE 的新手可能会遇到这样的问题:\n\n- Nvidia 驱动已安装,`nvidia-smi` 输出正常\n- KDE 系统信息显示仍在使用核显,程序运行时也优先使用核显\n- 导致某些应用(如浏览器、Blender)可能出现卡顿或掉帧现象\n\n这是因为 Arch Linux 的高自定义性,许多功能需要用户手动配置。以下是详细的解决方案。\n\n---\n\n## 配置 X11 下的 Nvidia 显卡优先\n\n可以通过配置 `/etc/X11/xorg.conf` 实现 Nvidia 独显输出。幸运的是,Nvidia 提供了自动生成配置文件的工具,用户无需手动编写:\n\n```bash\nsudo nvidia-xconfig --prime\n```\n\n该命令会根据硬件情况自动生成配置文件。执行后 **重新登录会话** 即可生效(即使是 Wayland 用户也可以执行一次此命令)。\n\n![X11 Nvidia配置](/images/1613f5602b203b38230f19699deb0219454454985.png)\n\n---\n\n## 配置 Wayland 下的 Nvidia 显卡优先\n\n在 Wayland 下优先启用 Nvidia 显卡的步骤如下:\n\n1. 编辑 **GRUB 配置**文件:\n\n 打开 `/etc/default/grub` 文件,在 `GRUB_CMDLINE_LINUX_DEFAULT=\"\"` 中添加 `nvidia_drm.modeset=1`:\n\n ```bash\n GRUB_CMDLINE_LINUX_DEFAULT=\"nvidia_drm.modeset=1\"\n ```\n\n2. 重新生成 grub 配置:\n\n ```bash\n sudo grub-mkconfig -o /boot/grub/grub.cfg\n ```\n\n3. 配置 **Plasma 环境**文件:\n\n 在 `~/.config/plasma-workspace/env/nvidia.sh` 中写入以下内容:\n\n ```bash\n #!/bin/bash \n export __NV_PRIME_RENDER_OFFLOAD=1 \n export __GLX_VENDOR_LIBRARY_NAME=nvidia\n ```\n\n4. 保存并重启电脑,即可生效。\n\n![Wayland Nvidia配置](/images/b2054bbaf6197624d38cc2007d885fd1454454985.png)\n\n---\n\n## I+N 混合显卡方案\n\n如果不希望全局启用独显,可以选择让大部分程序默认使用核显,而少数高性能需求的程序使用独显。这种方法能有效节省功耗,同时将独显资源集中分配给需要的程序(如 Steam 游戏、Blender 等)。缺点是每个程序需要手动配置启动项。\n\n### 配置步骤\n\n1. 打开程序的 `.desktop` 启动文件:\n\n 位置可能在 `/usr/share/applications` 或 `~/.local/share/applications` 中。\n\n2. 在 `Exec=` 后添加 `prime-run` 参数。例如:\n\n ```text\n Exec=prime-run <程序启动命令>\n ```\n\n ![混合显卡配置示意图](/images/71a5357ef4bd808b10429bc2ea46cb6f454454985.png)\n\n### Vim 快捷配置\n\n如果使用 Vim,可以使用以下快捷键快速批量替换 `Exec=` 为 `Exec=prime-run`:\n\n```vim\nv -> G -> :s/Exec=/Exec=prime-run /g Enter -> :wq Enter\n```\n\n---\n\n## 让 Plasma 桌面也使用独显\n\n如果希望 Plasma 桌面也通过独显运行,可以修改 Wayland 配置文件并删除第二行:\n\n```bash\n#!/bin/bash \nexport __GLX_VENDOR_LIBRARY_NAME=nvidia\n```\n\n这样 Plasma 桌面会通过独显启动,其他程序则默认使用核显。\n\n---\n\n希望以上经验能为有此需求的用户提供参考帮助。\n","source":"_posts/arch-nvidia.md","raw":"---\ntitle: Archlinux的KDE Plasma优先启用Nvidia独立显卡和混合显卡配置指北\ndate: 2024-11-06 10:02:04\ntags: 技术\ncategories: [技术分享]\n---\n本文将介绍在 **X11** 和 **Wayland** 两种会话下,如何在 KDE Plasma 中优先启用 Nvidia 独立显卡,并提供 I+N 混合显卡的配置方案。**首先,请确保您已经正确安装了 Nvidia 驱动**(如果非 Mainline 内核,请使用 `nvidia-dkms` 或 `nvidia-open-dkms` 版本)。\n\n![Nvidia设置示意图](/images/5364bba6d035326e82c53504dd53e7c2454454985.png)\n\n### 适用系统\n\n对于 **CachyOS** 或 **EndeavourOS** 等 Arch Linux 衍生版,这些配置大多开箱即用,但对于刚刚入坑 Arch Linux 且使用 KDE 的新手可能会遇到这样的问题:\n\n- Nvidia 驱动已安装,`nvidia-smi` 输出正常\n- KDE 系统信息显示仍在使用核显,程序运行时也优先使用核显\n- 导致某些应用(如浏览器、Blender)可能出现卡顿或掉帧现象\n\n这是因为 Arch Linux 的高自定义性,许多功能需要用户手动配置。以下是详细的解决方案。\n\n---\n\n## 配置 X11 下的 Nvidia 显卡优先\n\n可以通过配置 `/etc/X11/xorg.conf` 实现 Nvidia 独显输出。幸运的是,Nvidia 提供了自动生成配置文件的工具,用户无需手动编写:\n\n```bash\nsudo nvidia-xconfig --prime\n```\n\n该命令会根据硬件情况自动生成配置文件。执行后 **重新登录会话** 即可生效(即使是 Wayland 用户也可以执行一次此命令)。\n\n![X11 Nvidia配置](/images/1613f5602b203b38230f19699deb0219454454985.png)\n\n---\n\n## 配置 Wayland 下的 Nvidia 显卡优先\n\n在 Wayland 下优先启用 Nvidia 显卡的步骤如下:\n\n1. 编辑 **GRUB 配置**文件:\n\n 打开 `/etc/default/grub` 文件,在 `GRUB_CMDLINE_LINUX_DEFAULT=\"\"` 中添加 `nvidia_drm.modeset=1`:\n\n ```bash\n GRUB_CMDLINE_LINUX_DEFAULT=\"nvidia_drm.modeset=1\"\n ```\n\n2. 重新生成 grub 配置:\n\n ```bash\n sudo grub-mkconfig -o /boot/grub/grub.cfg\n ```\n\n3. 配置 **Plasma 环境**文件:\n\n 在 `~/.config/plasma-workspace/env/nvidia.sh` 中写入以下内容:\n\n ```bash\n #!/bin/bash \n export __NV_PRIME_RENDER_OFFLOAD=1 \n export __GLX_VENDOR_LIBRARY_NAME=nvidia\n ```\n\n4. 保存并重启电脑,即可生效。\n\n![Wayland Nvidia配置](/images/b2054bbaf6197624d38cc2007d885fd1454454985.png)\n\n---\n\n## I+N 混合显卡方案\n\n如果不希望全局启用独显,可以选择让大部分程序默认使用核显,而少数高性能需求的程序使用独显。这种方法能有效节省功耗,同时将独显资源集中分配给需要的程序(如 Steam 游戏、Blender 等)。缺点是每个程序需要手动配置启动项。\n\n### 配置步骤\n\n1. 打开程序的 `.desktop` 启动文件:\n\n 位置可能在 `/usr/share/applications` 或 `~/.local/share/applications` 中。\n\n2. 在 `Exec=` 后添加 `prime-run` 参数。例如:\n\n ```text\n Exec=prime-run <程序启动命令>\n ```\n\n ![混合显卡配置示意图](/images/71a5357ef4bd808b10429bc2ea46cb6f454454985.png)\n\n### Vim 快捷配置\n\n如果使用 Vim,可以使用以下快捷键快速批量替换 `Exec=` 为 `Exec=prime-run`:\n\n```vim\nv -> G -> :s/Exec=/Exec=prime-run /g Enter -> :wq Enter\n```\n\n---\n\n## 让 Plasma 桌面也使用独显\n\n如果希望 Plasma 桌面也通过独显运行,可以修改 Wayland 配置文件并删除第二行:\n\n```bash\n#!/bin/bash \nexport __GLX_VENDOR_LIBRARY_NAME=nvidia\n```\n\n这样 Plasma 桌面会通过独显启动,其他程序则默认使用核显。\n\n---\n\n希望以上经验能为有此需求的用户提供参考帮助。\n","slug":"arch-nvidia","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblu000uidd09xaa6t9i","content":"

本文将介绍在 X11Wayland 两种会话下,如何在 KDE Plasma 中优先启用 Nvidia 独立显卡,并提供 I+N 混合显卡的配置方案。首先,请确保您已经正确安装了 Nvidia 驱动(如果非 Mainline 内核,请使用 nvidia-dkmsnvidia-open-dkms 版本)。

\n

\"Nvidia设置示意图\"

\n

适用系统

对于 CachyOSEndeavourOS 等 Arch Linux 衍生版,这些配置大多开箱即用,但对于刚刚入坑 Arch Linux 且使用 KDE 的新手可能会遇到这样的问题:

\n
    \n
  • Nvidia 驱动已安装,nvidia-smi 输出正常
  • \n
  • KDE 系统信息显示仍在使用核显,程序运行时也优先使用核显
  • \n
  • 导致某些应用(如浏览器、Blender)可能出现卡顿或掉帧现象
  • \n
\n

这是因为 Arch Linux 的高自定义性,许多功能需要用户手动配置。以下是详细的解决方案。

\n
\n

配置 X11 下的 Nvidia 显卡优先

可以通过配置 /etc/X11/xorg.conf 实现 Nvidia 独显输出。幸运的是,Nvidia 提供了自动生成配置文件的工具,用户无需手动编写:

\n
1
sudo nvidia-xconfig --prime
\n\n

该命令会根据硬件情况自动生成配置文件。执行后 重新登录会话 即可生效(即使是 Wayland 用户也可以执行一次此命令)。

\n

\"X11

\n
\n

配置 Wayland 下的 Nvidia 显卡优先

在 Wayland 下优先启用 Nvidia 显卡的步骤如下:

\n
    \n
  1. 编辑 GRUB 配置文件:

    \n

    打开 /etc/default/grub 文件,在 GRUB_CMDLINE_LINUX_DEFAULT="" 中添加 nvidia_drm.modeset=1

    \n
    1
    GRUB_CMDLINE_LINUX_DEFAULT="nvidia_drm.modeset=1"
    \n
  2. \n
  3. 重新生成 grub 配置:

    \n
    1
    sudo grub-mkconfig -o /boot/grub/grub.cfg
    \n
  4. \n
  5. 配置 Plasma 环境文件:

    \n

    ~/.config/plasma-workspace/env/nvidia.sh 中写入以下内容:

    \n
    1
    2
    3
    #!/bin/bash 
    export __NV_PRIME_RENDER_OFFLOAD=1
    export __GLX_VENDOR_LIBRARY_NAME=nvidia
    \n
  6. \n
  7. 保存并重启电脑,即可生效。

    \n
  8. \n
\n

\"Wayland

\n
\n

I+N 混合显卡方案

如果不希望全局启用独显,可以选择让大部分程序默认使用核显,而少数高性能需求的程序使用独显。这种方法能有效节省功耗,同时将独显资源集中分配给需要的程序(如 Steam 游戏、Blender 等)。缺点是每个程序需要手动配置启动项。

\n

配置步骤

    \n
  1. 打开程序的 .desktop 启动文件:

    \n

    位置可能在 /usr/share/applications~/.local/share/applications 中。

    \n
  2. \n
  3. Exec= 后添加 prime-run 参数。例如:

    \n
    1
    Exec=prime-run <程序启动命令>
    \n\n

    \"混合显卡配置示意图\"

    \n
  4. \n
\n

Vim 快捷配置

如果使用 Vim,可以使用以下快捷键快速批量替换 Exec=Exec=prime-run

\n
1
v -> G -> :s/Exec=/Exec=prime-run /g Enter -> :wq Enter
\n\n
\n

让 Plasma 桌面也使用独显

如果希望 Plasma 桌面也通过独显运行,可以修改 Wayland 配置文件并删除第二行:

\n
1
2
#!/bin/bash 
export __GLX_VENDOR_LIBRARY_NAME=nvidia
\n\n

这样 Plasma 桌面会通过独显启动,其他程序则默认使用核显。

\n
\n

希望以上经验能为有此需求的用户提供参考帮助。

\n","excerpt":"","more":"

本文将介绍在 X11Wayland 两种会话下,如何在 KDE Plasma 中优先启用 Nvidia 独立显卡,并提供 I+N 混合显卡的配置方案。首先,请确保您已经正确安装了 Nvidia 驱动(如果非 Mainline 内核,请使用 nvidia-dkmsnvidia-open-dkms 版本)。

\n

\"Nvidia设置示意图\"

\n

适用系统

对于 CachyOSEndeavourOS 等 Arch Linux 衍生版,这些配置大多开箱即用,但对于刚刚入坑 Arch Linux 且使用 KDE 的新手可能会遇到这样的问题:

\n
    \n
  • Nvidia 驱动已安装,nvidia-smi 输出正常
  • \n
  • KDE 系统信息显示仍在使用核显,程序运行时也优先使用核显
  • \n
  • 导致某些应用(如浏览器、Blender)可能出现卡顿或掉帧现象
  • \n
\n

这是因为 Arch Linux 的高自定义性,许多功能需要用户手动配置。以下是详细的解决方案。

\n
\n

配置 X11 下的 Nvidia 显卡优先

可以通过配置 /etc/X11/xorg.conf 实现 Nvidia 独显输出。幸运的是,Nvidia 提供了自动生成配置文件的工具,用户无需手动编写:

\n
1
sudo nvidia-xconfig --prime
\n\n

该命令会根据硬件情况自动生成配置文件。执行后 重新登录会话 即可生效(即使是 Wayland 用户也可以执行一次此命令)。

\n

\"X11

\n
\n

配置 Wayland 下的 Nvidia 显卡优先

在 Wayland 下优先启用 Nvidia 显卡的步骤如下:

\n
    \n
  1. 编辑 GRUB 配置文件:

    \n

    打开 /etc/default/grub 文件,在 GRUB_CMDLINE_LINUX_DEFAULT="" 中添加 nvidia_drm.modeset=1

    \n
    1
    GRUB_CMDLINE_LINUX_DEFAULT="nvidia_drm.modeset=1"
    \n
  2. \n
  3. 重新生成 grub 配置:

    \n
    1
    sudo grub-mkconfig -o /boot/grub/grub.cfg
    \n
  4. \n
  5. 配置 Plasma 环境文件:

    \n

    ~/.config/plasma-workspace/env/nvidia.sh 中写入以下内容:

    \n
    1
    2
    3
    #!/bin/bash 
    export __NV_PRIME_RENDER_OFFLOAD=1
    export __GLX_VENDOR_LIBRARY_NAME=nvidia
    \n
  6. \n
  7. 保存并重启电脑,即可生效。

    \n
  8. \n
\n

\"Wayland

\n
\n

I+N 混合显卡方案

如果不希望全局启用独显,可以选择让大部分程序默认使用核显,而少数高性能需求的程序使用独显。这种方法能有效节省功耗,同时将独显资源集中分配给需要的程序(如 Steam 游戏、Blender 等)。缺点是每个程序需要手动配置启动项。

\n

配置步骤

    \n
  1. 打开程序的 .desktop 启动文件:

    \n

    位置可能在 /usr/share/applications~/.local/share/applications 中。

    \n
  2. \n
  3. Exec= 后添加 prime-run 参数。例如:

    \n
    1
    Exec=prime-run <程序启动命令>
    \n\n

    \"混合显卡配置示意图\"

    \n
  4. \n
\n

Vim 快捷配置

如果使用 Vim,可以使用以下快捷键快速批量替换 Exec=Exec=prime-run

\n
1
v -> G -> :s/Exec=/Exec=prime-run /g Enter -> :wq Enter
\n\n
\n

让 Plasma 桌面也使用独显

如果希望 Plasma 桌面也通过独显运行,可以修改 Wayland 配置文件并删除第二行:

\n
1
2
#!/bin/bash 
export __GLX_VENDOR_LIBRARY_NAME=nvidia
\n\n

这样 Plasma 桌面会通过独显启动,其他程序则默认使用核显。

\n
\n

希望以上经验能为有此需求的用户提供参考帮助。

\n"},{"title":"Archlinux问题记录","date":"2025-03-10T14:47:08.000Z","_content":"\n前几周在使用 Arch Linux 时遇到了两个有趣的问题,顺手记录在此,或许能帮到有类似困扰的朋友~\n\n---\n\n### 1. 显卡功耗上限解锁\n**问题现象** \n使用 `nvidia-smi` 查看显卡功耗时,发现最大功耗被限制在 55W,性能无法完全释放:\n\n```bash\nnvidia-smi # 输出显示 Power Limit: 55.00 W\n```\n\n**解决方案** \n启用 NVIDIA 动态功耗管理服务即可:\n```bash\nsudo systemctl enable --now nvidia-powerd\n```\n\n---\n\n### 2. Steam Proton 输入失效\n**问题描述** \n使用 proton-cachyos 和 proton-ge-custom 版本时,Steam 游戏无法接收任何输入(键盘/手柄)。 \n⚠️ 经排查发现是 Wayland 协议兼容性问题导致。\n\n**解决方案** \n在 Steam 游戏启动选项中加入环境变量禁用 Wayland:\n```bash\nPROTON_ENABLE_WAYLAND=0 %command%\n```\n\n---\n\n","source":"_posts/archlinux-game-fix.md","raw":"---\ntitle: Archlinux问题记录\ndate: 2025-03-10 22:47:08\ntags: [生活, Archlinux]\n---\n\n前几周在使用 Arch Linux 时遇到了两个有趣的问题,顺手记录在此,或许能帮到有类似困扰的朋友~\n\n---\n\n### 1. 显卡功耗上限解锁\n**问题现象** \n使用 `nvidia-smi` 查看显卡功耗时,发现最大功耗被限制在 55W,性能无法完全释放:\n\n```bash\nnvidia-smi # 输出显示 Power Limit: 55.00 W\n```\n\n**解决方案** \n启用 NVIDIA 动态功耗管理服务即可:\n```bash\nsudo systemctl enable --now nvidia-powerd\n```\n\n---\n\n### 2. Steam Proton 输入失效\n**问题描述** \n使用 proton-cachyos 和 proton-ge-custom 版本时,Steam 游戏无法接收任何输入(键盘/手柄)。 \n⚠️ 经排查发现是 Wayland 协议兼容性问题导致。\n\n**解决方案** \n在 Steam 游戏启动选项中加入环境变量禁用 Wayland:\n```bash\nPROTON_ENABLE_WAYLAND=0 %command%\n```\n\n---\n\n","slug":"archlinux-game-fix","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblv000xidd01m864302","content":"

前几周在使用 Arch Linux 时遇到了两个有趣的问题,顺手记录在此,或许能帮到有类似困扰的朋友~

\n
\n

1. 显卡功耗上限解锁

问题现象
使用 nvidia-smi 查看显卡功耗时,发现最大功耗被限制在 55W,性能无法完全释放:

\n
1
nvidia-smi  # 输出显示 Power Limit: 55.00 W
\n\n

解决方案
启用 NVIDIA 动态功耗管理服务即可:

\n
1
sudo systemctl enable --now nvidia-powerd
\n\n
\n

2. Steam Proton 输入失效

问题描述
使用 proton-cachyos 和 proton-ge-custom 版本时,Steam 游戏无法接收任何输入(键盘/手柄)。
⚠️ 经排查发现是 Wayland 协议兼容性问题导致。

\n

解决方案
在 Steam 游戏启动选项中加入环境变量禁用 Wayland:

\n
1
PROTON_ENABLE_WAYLAND=0 %command%
\n\n
\n","excerpt":"","more":"

前几周在使用 Arch Linux 时遇到了两个有趣的问题,顺手记录在此,或许能帮到有类似困扰的朋友~

\n
\n

1. 显卡功耗上限解锁

问题现象
使用 nvidia-smi 查看显卡功耗时,发现最大功耗被限制在 55W,性能无法完全释放:

\n
1
nvidia-smi  # 输出显示 Power Limit: 55.00 W
\n\n

解决方案
启用 NVIDIA 动态功耗管理服务即可:

\n
1
sudo systemctl enable --now nvidia-powerd
\n\n
\n

2. Steam Proton 输入失效

问题描述
使用 proton-cachyos 和 proton-ge-custom 版本时,Steam 游戏无法接收任何输入(键盘/手柄)。
⚠️ 经排查发现是 Wayland 协议兼容性问题导致。

\n

解决方案
在 Steam 游戏启动选项中加入环境变量禁用 Wayland:

\n
1
PROTON_ENABLE_WAYLAND=0 %command%
\n\n
\n"},{"title":"Archlinux KDE体验优化总结","date":"2025-02-02T10:43:26.000Z","_content":"\n打算开一个坑记录这么久以来的Archlinux系统性能和操作体验优化经验\n\n本文章长期更新\n\n------\n\n## 更换CachyOS优化仓库\n\n![CachyOS Logo](https://wiki.cachyos.org/_astro/logo.DVTdAJi6.svg) \n通过 CachyOS 优化仓库获取 CPU 指令集级优化(x86-64-v3/v4/zen4)的软件包,提升 Arch Linux 系统性能。该仓库提供 PGO/LTO/BOLT 编译优化及持续维护的定制软件包。\n\n---\n\n### ▎前置准备\n**⚠️ 兼容性警告** \n1. 可以先通过命令`/usr/lib64/ld-linux-x86-64.so.2 --help | grep -i x86-64-`来查看你的处理器支持等级。\n2. 注意添加 `cachyos` 主仓库会替换官方 pacman 仓库(含 INSTALLED_FROM 等特性) \n ```bash\n cachyos-v3 # AVX2 优化\n cachyos-v4 # AVX512 优化\n cachyos-extra # 扩展软件包\n ```\n3. CachyOS官方在前段时间专门推出了针对 AMD 的Zen4和Zen5架构优化仓库,如有需要可以[点击这里](https://discuss.cachyos.org/t/zen-4-5-optimized-repository-testing/713/7)查看如何部署。\n\n---\n\n### ▎仓库配置流程\n#### ▶ 自动配置脚本\n```bash\n# 下载配置工具\ncurl -LO https://mirror.cachyos.org/cachyos-repo.tar.xz\ntar xvf cachyos-repo.tar.xz && cd cachyos-repo\n\n# 执行自动配置(自动检测 CPU 指令集)\nsudo ./cachyos-repo.sh\n```\n📌 脚本特性: \n- 自动备份 `/etc/pacman.conf` \n- 智能匹配最优指令集版本 (v3/v4) \n- 支持 x86_64 和 aarch64 架构 \n\n#### ▶ 手动配置方式\n1. 编辑 pacman.conf\n```ini\n# 在 /etc/pacman.conf 末尾添加(示例为 AVX2 优化)\n[cachyos-v3]\nSigLevel = Optional TrustAll\nInclude = /etc/pacman.d/cachyos-v3\n```\n\n2. ✅同步仓库数据库\n```bash\nsudo pacman -Syu\n```\n\n---\n\n### ▎仓库卸载方法\n#### ▶ 自动卸载\n```bash\ncd cachyos-repo\nsudo ./cachyos-repo.sh --remove\n```\n\n#### ▶ 手动卸载\n1. 删除 pacman.conf 中的 cachyos 仓库段\n2. 移除配置文件\n```bash\nsudo rm -rf /etc/pacman.d/cachyos*\n```\n---\n\n## 内核更换\n\n## KDE 配置\n\n\n\n","source":"_posts/archlinux-optimization.md","raw":"---\ntitle: Archlinux KDE体验优化总结\ndate: 2025-02-02 18:43:26\ntags: [Archlinux, 系统优化, 技术分享]\n---\n\n打算开一个坑记录这么久以来的Archlinux系统性能和操作体验优化经验\n\n本文章长期更新\n\n------\n\n## 更换CachyOS优化仓库\n\n![CachyOS Logo](https://wiki.cachyos.org/_astro/logo.DVTdAJi6.svg) \n通过 CachyOS 优化仓库获取 CPU 指令集级优化(x86-64-v3/v4/zen4)的软件包,提升 Arch Linux 系统性能。该仓库提供 PGO/LTO/BOLT 编译优化及持续维护的定制软件包。\n\n---\n\n### ▎前置准备\n**⚠️ 兼容性警告** \n1. 可以先通过命令`/usr/lib64/ld-linux-x86-64.so.2 --help | grep -i x86-64-`来查看你的处理器支持等级。\n2. 注意添加 `cachyos` 主仓库会替换官方 pacman 仓库(含 INSTALLED_FROM 等特性) \n ```bash\n cachyos-v3 # AVX2 优化\n cachyos-v4 # AVX512 优化\n cachyos-extra # 扩展软件包\n ```\n3. CachyOS官方在前段时间专门推出了针对 AMD 的Zen4和Zen5架构优化仓库,如有需要可以[点击这里](https://discuss.cachyos.org/t/zen-4-5-optimized-repository-testing/713/7)查看如何部署。\n\n---\n\n### ▎仓库配置流程\n#### ▶ 自动配置脚本\n```bash\n# 下载配置工具\ncurl -LO https://mirror.cachyos.org/cachyos-repo.tar.xz\ntar xvf cachyos-repo.tar.xz && cd cachyos-repo\n\n# 执行自动配置(自动检测 CPU 指令集)\nsudo ./cachyos-repo.sh\n```\n📌 脚本特性: \n- 自动备份 `/etc/pacman.conf` \n- 智能匹配最优指令集版本 (v3/v4) \n- 支持 x86_64 和 aarch64 架构 \n\n#### ▶ 手动配置方式\n1. 编辑 pacman.conf\n```ini\n# 在 /etc/pacman.conf 末尾添加(示例为 AVX2 优化)\n[cachyos-v3]\nSigLevel = Optional TrustAll\nInclude = /etc/pacman.d/cachyos-v3\n```\n\n2. ✅同步仓库数据库\n```bash\nsudo pacman -Syu\n```\n\n---\n\n### ▎仓库卸载方法\n#### ▶ 自动卸载\n```bash\ncd cachyos-repo\nsudo ./cachyos-repo.sh --remove\n```\n\n#### ▶ 手动卸载\n1. 删除 pacman.conf 中的 cachyos 仓库段\n2. 移除配置文件\n```bash\nsudo rm -rf /etc/pacman.d/cachyos*\n```\n---\n\n## 内核更换\n\n## KDE 配置\n\n\n\n","slug":"archlinux-optimization","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblv000zidd056tl5735","content":"

打算开一个坑记录这么久以来的Archlinux系统性能和操作体验优化经验

\n

本文章长期更新

\n
\n

更换CachyOS优化仓库

\"CachyOS
通过 CachyOS 优化仓库获取 CPU 指令集级优化(x86-64-v3/v4/zen4)的软件包,提升 Arch Linux 系统性能。该仓库提供 PGO/LTO/BOLT 编译优化及持续维护的定制软件包。

\n
\n

▎前置准备

⚠️ 兼容性警告

\n
    \n
  1. 可以先通过命令/usr/lib64/ld-linux-x86-64.so.2 --help | grep -i x86-64-来查看你的处理器支持等级。
  2. \n
  3. 注意添加 cachyos 主仓库会替换官方 pacman 仓库(含 INSTALLED_FROM 等特性)
    1
    2
    3
    cachyos-v3    # AVX2 优化
    cachyos-v4 # AVX512 优化
    cachyos-extra # 扩展软件包
  4. \n
  5. CachyOS官方在前段时间专门推出了针对 AMD 的Zen4和Zen5架构优化仓库,如有需要可以点击这里查看如何部署。
  6. \n
\n
\n

▎仓库配置流程

▶ 自动配置脚本

1
2
3
4
5
6
# 下载配置工具
curl -LO https://mirror.cachyos.org/cachyos-repo.tar.xz
tar xvf cachyos-repo.tar.xz && cd cachyos-repo

# 执行自动配置(自动检测 CPU 指令集)
sudo ./cachyos-repo.sh
\n

📌 脚本特性:

\n
    \n
  • 自动备份 /etc/pacman.conf
  • \n
  • 智能匹配最优指令集版本 (v3/v4)
  • \n
  • 支持 x86_64 和 aarch64 架构
  • \n
\n

▶ 手动配置方式

    \n
  1. 编辑 pacman.conf

    \n
    1
    2
    3
    4
    # 在 /etc/pacman.conf 末尾添加(示例为 AVX2 优化)
    [cachyos-v3]
    SigLevel = Optional TrustAll
    Include = /etc/pacman.d/cachyos-v3
    \n
  2. \n
  3. ✅同步仓库数据库

    \n
    1
    sudo pacman -Syu
  4. \n
\n
\n

▎仓库卸载方法

▶ 自动卸载

1
2
cd cachyos-repo
sudo ./cachyos-repo.sh --remove
\n\n

▶ 手动卸载

    \n
  1. 删除 pacman.conf 中的 cachyos 仓库段
  2. \n
  3. 移除配置文件
    1
    sudo rm -rf /etc/pacman.d/cachyos*
  4. \n
\n
\n

内核更换

KDE 配置

","excerpt":"","more":"

打算开一个坑记录这么久以来的Archlinux系统性能和操作体验优化经验

\n

本文章长期更新

\n
\n

更换CachyOS优化仓库

\"CachyOS
通过 CachyOS 优化仓库获取 CPU 指令集级优化(x86-64-v3/v4/zen4)的软件包,提升 Arch Linux 系统性能。该仓库提供 PGO/LTO/BOLT 编译优化及持续维护的定制软件包。

\n
\n

▎前置准备

⚠️ 兼容性警告

\n
    \n
  1. 可以先通过命令/usr/lib64/ld-linux-x86-64.so.2 --help | grep -i x86-64-来查看你的处理器支持等级。
  2. \n
  3. 注意添加 cachyos 主仓库会替换官方 pacman 仓库(含 INSTALLED_FROM 等特性)
    1
    2
    3
    cachyos-v3    # AVX2 优化
    cachyos-v4 # AVX512 优化
    cachyos-extra # 扩展软件包
  4. \n
  5. CachyOS官方在前段时间专门推出了针对 AMD 的Zen4和Zen5架构优化仓库,如有需要可以点击这里查看如何部署。
  6. \n
\n
\n

▎仓库配置流程

▶ 自动配置脚本

1
2
3
4
5
6
# 下载配置工具
curl -LO https://mirror.cachyos.org/cachyos-repo.tar.xz
tar xvf cachyos-repo.tar.xz && cd cachyos-repo

# 执行自动配置(自动检测 CPU 指令集)
sudo ./cachyos-repo.sh
\n

📌 脚本特性:

\n
    \n
  • 自动备份 /etc/pacman.conf
  • \n
  • 智能匹配最优指令集版本 (v3/v4)
  • \n
  • 支持 x86_64 和 aarch64 架构
  • \n
\n

▶ 手动配置方式

    \n
  1. 编辑 pacman.conf

    \n
    1
    2
    3
    4
    # 在 /etc/pacman.conf 末尾添加(示例为 AVX2 优化)
    [cachyos-v3]
    SigLevel = Optional TrustAll
    Include = /etc/pacman.d/cachyos-v3
    \n
  2. \n
  3. ✅同步仓库数据库

    \n
    1
    sudo pacman -Syu
  4. \n
\n
\n

▎仓库卸载方法

▶ 自动卸载

1
2
cd cachyos-repo
sudo ./cachyos-repo.sh --remove
\n\n

▶ 手动卸载

    \n
  1. 删除 pacman.conf 中的 cachyos 仓库段
  2. \n
  3. 移除配置文件
    1
    sudo rm -rf /etc/pacman.d/cachyos*
  4. \n
\n
\n

内核更换

KDE 配置

"},{"title":"使用Clonezilla备份和克隆系统","date":"2025-02-23T13:40:03.000Z","_content":"![Clonezilla官网](/images/clonezilla.png)\n[Clonezilla](https://clonezilla.org/)是一款非常好用的系统迁移工具,本文将介绍其基本用法(不包含网络迁移及Clonezilla服务器等进阶内容)以及在克隆Btrfs磁盘时遇到的问题解法。\n\n---\n### **零.事前准备**:\n - 下载Clonezilla Live镜像(ISO),制作启动U盘(有手就行)。\n - 准备目标存储设备(如U盘,需要迁移的新硬盘等),确保有足够空间(建议大于源硬盘已用空间的1.2倍)。\n\n\n### **一.备份镜像到硬盘(Device-to-Image)**\n这个模式可以将硬盘完整备份为一个镜像文件(可保存到本地硬盘、外置硬盘或网络存储)方便多机部署。注意如果只是将一个硬盘的系统完全克隆到新盘可以跳过此部分。\n\n#### **步骤说明**:\n2. **启动Clonezilla**:\n - 插入U盘,重启电脑并从U盘启动。\n - 选择默认选项(语言,键盘布局,Beginner模式)进入Clonezilla的TUI界面。\n\n3. **选择备份模式**:\n ```plaintext\n Choose mode: 选择 \"device-image\"(设备到镜像)\n Mount storage media: 选择 \"local_dev\"(本地存储设备)\n ```\n - 按提示挂载目标存储设备(注意这里选择的是你要存储镜像的设备且文件系统一般不限,如外置硬盘),确认路径(如 `/dev/sdb1`)。\n\n4. **配置备份参数**:\n - **源硬盘**:选择需要备份的硬盘(如 `/dev/sda`)。\n - **镜像存储路径**:指定目标位置(如外置硬盘的挂载目录)。\n - **镜像名称**:自定义名称(如 `2025-img-rockylinux-2-21`)。\n - **压缩选项**:默认即可,支持并行压缩加速。\n - **镜像分割**:若目标存储设备为FAT32格式(单文件最大4GB),选择自动分割。\n\n5. **确认操作**:\n - 检查提示信息,输入 `y` 开始备份。\n - 完成后关机或重启。\n\n6. **镜像还原**:和备份非常相似,只是选项换成restore to disk,顺着指引操作即可。\n---\n\n### **二、直接克隆硬盘(Device-to-Device)**\n将源硬盘完整克隆到目标硬盘(适合硬盘升级或快速迁移),比如笔者最近白嫖了一个三星的2T硬盘直接把原来512G硬盘里的CachyOS无损迁移了进去。\n\n#### **步骤说明**:\n1. **准备工作**:\n - 连接目标硬盘(需容量≥源硬盘已用空间,还原镜像操作也是如此,注意Clonezilla支持小分区到大分区迁移不支持大分区到小分区,后者出门右转Rsync)\n - **警告**:目标硬盘数据将被覆盖,操作前如有需要务必备份重要数据!\n\n2. **启动Clonezilla**:\n - 同上,从U盘启动进入Clonezilla界面。\n\n3. **选择克隆模式**:\n ```plaintext\n Choose mode: 选择 \"device-device\"(设备到设备)\n ```\n\n4. **选择硬盘**:\n - **母碟硬盘**:选择原始硬盘(如 `/dev/sda`)。\n - **目标硬盘**:选择新硬盘(如 `/dev/sdb`)。\n\n5. **克隆选项**:\n 需要进入专家模式才能看到,一般直接新手模式默认即可。\n\n6. **执行克隆**:\n - 确认提示信息后输入 `y`,等待完成。\n - 克隆结束后关机,移除旧硬盘并测试新硬盘(主要是查看能不能启动进入系统,若能进入一般不会有问题,而且一般都能进入因为Clonezilla是高精确的块对块克隆)。\n\n---\n### **两种模式对比**:\n| **模式** | 特点 | \n|----------------|---------------------------|\n| 备份镜像 | 方便多机部署也可以用于留档|\n| 直接克隆 | 换硬盘快速迁移无需恢复过程|\n---\n\n### **Btrfs务必注意**:\n对Btrfs直接进行Clonezilla克隆大概率会碰到一个边界错误,这是由于Btrfs本身使用一段时间后碎片化存储导致的,你需要执行`sudo btrfs balance start --full-balance /`来进行整理,但是这也算是一个风险操作要确保完整执行不能在执行时意外中断导致文件系统出错,并且在后续克隆时不能使用新手模式要进入专家模式勾选-p1支持所有文件系统但是效率降低的选项来确保顺利克隆(未勾选此选项可能导致文件系统无法识别的错误),其他步骤参照前文即可。\n\n","source":"_posts/clonezilla.md","raw":"---\ntitle: 使用Clonezilla备份和克隆系统\ndate: 2025-02-23 21:40:03\ntags: 技术分享\n---\n![Clonezilla官网](/images/clonezilla.png)\n[Clonezilla](https://clonezilla.org/)是一款非常好用的系统迁移工具,本文将介绍其基本用法(不包含网络迁移及Clonezilla服务器等进阶内容)以及在克隆Btrfs磁盘时遇到的问题解法。\n\n---\n### **零.事前准备**:\n - 下载Clonezilla Live镜像(ISO),制作启动U盘(有手就行)。\n - 准备目标存储设备(如U盘,需要迁移的新硬盘等),确保有足够空间(建议大于源硬盘已用空间的1.2倍)。\n\n\n### **一.备份镜像到硬盘(Device-to-Image)**\n这个模式可以将硬盘完整备份为一个镜像文件(可保存到本地硬盘、外置硬盘或网络存储)方便多机部署。注意如果只是将一个硬盘的系统完全克隆到新盘可以跳过此部分。\n\n#### **步骤说明**:\n2. **启动Clonezilla**:\n - 插入U盘,重启电脑并从U盘启动。\n - 选择默认选项(语言,键盘布局,Beginner模式)进入Clonezilla的TUI界面。\n\n3. **选择备份模式**:\n ```plaintext\n Choose mode: 选择 \"device-image\"(设备到镜像)\n Mount storage media: 选择 \"local_dev\"(本地存储设备)\n ```\n - 按提示挂载目标存储设备(注意这里选择的是你要存储镜像的设备且文件系统一般不限,如外置硬盘),确认路径(如 `/dev/sdb1`)。\n\n4. **配置备份参数**:\n - **源硬盘**:选择需要备份的硬盘(如 `/dev/sda`)。\n - **镜像存储路径**:指定目标位置(如外置硬盘的挂载目录)。\n - **镜像名称**:自定义名称(如 `2025-img-rockylinux-2-21`)。\n - **压缩选项**:默认即可,支持并行压缩加速。\n - **镜像分割**:若目标存储设备为FAT32格式(单文件最大4GB),选择自动分割。\n\n5. **确认操作**:\n - 检查提示信息,输入 `y` 开始备份。\n - 完成后关机或重启。\n\n6. **镜像还原**:和备份非常相似,只是选项换成restore to disk,顺着指引操作即可。\n---\n\n### **二、直接克隆硬盘(Device-to-Device)**\n将源硬盘完整克隆到目标硬盘(适合硬盘升级或快速迁移),比如笔者最近白嫖了一个三星的2T硬盘直接把原来512G硬盘里的CachyOS无损迁移了进去。\n\n#### **步骤说明**:\n1. **准备工作**:\n - 连接目标硬盘(需容量≥源硬盘已用空间,还原镜像操作也是如此,注意Clonezilla支持小分区到大分区迁移不支持大分区到小分区,后者出门右转Rsync)\n - **警告**:目标硬盘数据将被覆盖,操作前如有需要务必备份重要数据!\n\n2. **启动Clonezilla**:\n - 同上,从U盘启动进入Clonezilla界面。\n\n3. **选择克隆模式**:\n ```plaintext\n Choose mode: 选择 \"device-device\"(设备到设备)\n ```\n\n4. **选择硬盘**:\n - **母碟硬盘**:选择原始硬盘(如 `/dev/sda`)。\n - **目标硬盘**:选择新硬盘(如 `/dev/sdb`)。\n\n5. **克隆选项**:\n 需要进入专家模式才能看到,一般直接新手模式默认即可。\n\n6. **执行克隆**:\n - 确认提示信息后输入 `y`,等待完成。\n - 克隆结束后关机,移除旧硬盘并测试新硬盘(主要是查看能不能启动进入系统,若能进入一般不会有问题,而且一般都能进入因为Clonezilla是高精确的块对块克隆)。\n\n---\n### **两种模式对比**:\n| **模式** | 特点 | \n|----------------|---------------------------|\n| 备份镜像 | 方便多机部署也可以用于留档|\n| 直接克隆 | 换硬盘快速迁移无需恢复过程|\n---\n\n### **Btrfs务必注意**:\n对Btrfs直接进行Clonezilla克隆大概率会碰到一个边界错误,这是由于Btrfs本身使用一段时间后碎片化存储导致的,你需要执行`sudo btrfs balance start --full-balance /`来进行整理,但是这也算是一个风险操作要确保完整执行不能在执行时意外中断导致文件系统出错,并且在后续克隆时不能使用新手模式要进入专家模式勾选-p1支持所有文件系统但是效率降低的选项来确保顺利克隆(未勾选此选项可能导致文件系统无法识别的错误),其他步骤参照前文即可。\n\n","slug":"clonezilla","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblv0013idd0ag2t06tp","content":"

\"Clonezilla官网\"
Clonezilla是一款非常好用的系统迁移工具,本文将介绍其基本用法(不包含网络迁移及Clonezilla服务器等进阶内容)以及在克隆Btrfs磁盘时遇到的问题解法。

\n
\n

零.事前准备

    \n
  • 下载Clonezilla Live镜像(ISO),制作启动U盘(有手就行)。
  • \n
  • 准备目标存储设备(如U盘,需要迁移的新硬盘等),确保有足够空间(建议大于源硬盘已用空间的1.2倍)。
  • \n
\n

一.备份镜像到硬盘(Device-to-Image)

这个模式可以将硬盘完整备份为一个镜像文件(可保存到本地硬盘、外置硬盘或网络存储)方便多机部署。注意如果只是将一个硬盘的系统完全克隆到新盘可以跳过此部分。

\n

步骤说明

    \n
  1. 启动Clonezilla

    \n
      \n
    • 插入U盘,重启电脑并从U盘启动。
    • \n
    • 选择默认选项(语言,键盘布局,Beginner模式)进入Clonezilla的TUI界面。
    • \n
    \n
  2. \n
  3. 选择备份模式

    \n
    1
    2
    Choose mode:         选择 "device-image"(设备到镜像)
    Mount storage media: 选择 "local_dev"(本地存储设备)
    \n
      \n
    • 按提示挂载目标存储设备(注意这里选择的是你要存储镜像的设备且文件系统一般不限,如外置硬盘),确认路径(如 /dev/sdb1)。
    • \n
    \n
  4. \n
  5. 配置备份参数

    \n
      \n
    • 源硬盘:选择需要备份的硬盘(如 /dev/sda)。
    • \n
    • 镜像存储路径:指定目标位置(如外置硬盘的挂载目录)。
    • \n
    • 镜像名称:自定义名称(如 2025-img-rockylinux-2-21)。
    • \n
    • 压缩选项:默认即可,支持并行压缩加速。
    • \n
    • 镜像分割:若目标存储设备为FAT32格式(单文件最大4GB),选择自动分割。
    • \n
    \n
  6. \n
  7. 确认操作

    \n
      \n
    • 检查提示信息,输入 y 开始备份。
    • \n
    • 完成后关机或重启。
    • \n
    \n
  8. \n
  9. 镜像还原:和备份非常相似,只是选项换成restore to disk,顺着指引操作即可。

    \n
  10. \n
\n
\n

二、直接克隆硬盘(Device-to-Device)

将源硬盘完整克隆到目标硬盘(适合硬盘升级或快速迁移),比如笔者最近白嫖了一个三星的2T硬盘直接把原来512G硬盘里的CachyOS无损迁移了进去。

\n

步骤说明

    \n
  1. 准备工作

    \n
      \n
    • 连接目标硬盘(需容量≥源硬盘已用空间,还原镜像操作也是如此,注意Clonezilla支持小分区到大分区迁移不支持大分区到小分区,后者出门右转Rsync)
    • \n
    • 警告:目标硬盘数据将被覆盖,操作前如有需要务必备份重要数据!
    • \n
    \n
  2. \n
  3. 启动Clonezilla

    \n
      \n
    • 同上,从U盘启动进入Clonezilla界面。
    • \n
    \n
  4. \n
  5. 选择克隆模式

    \n
    1
    Choose mode:         选择 "device-device"(设备到设备)
    \n
  6. \n
  7. 选择硬盘

    \n
      \n
    • 母碟硬盘:选择原始硬盘(如 /dev/sda)。
    • \n
    • 目标硬盘:选择新硬盘(如 /dev/sdb)。
    • \n
    \n
  8. \n
  9. 克隆选项
    需要进入专家模式才能看到,一般直接新手模式默认即可。

    \n
  10. \n
  11. 执行克隆

    \n
      \n
    • 确认提示信息后输入 y,等待完成。
    • \n
    • 克隆结束后关机,移除旧硬盘并测试新硬盘(主要是查看能不能启动进入系统,若能进入一般不会有问题,而且一般都能进入因为Clonezilla是高精确的块对块克隆)。
    • \n
    \n
  12. \n
\n
\n

两种模式对比

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
模式特点
备份镜像方便多机部署也可以用于留档
直接克隆换硬盘快速迁移无需恢复过程
\n
\n

Btrfs务必注意

对Btrfs直接进行Clonezilla克隆大概率会碰到一个边界错误,这是由于Btrfs本身使用一段时间后碎片化存储导致的,你需要执行sudo btrfs balance start --full-balance /来进行整理,但是这也算是一个风险操作要确保完整执行不能在执行时意外中断导致文件系统出错,并且在后续克隆时不能使用新手模式要进入专家模式勾选-p1支持所有文件系统但是效率降低的选项来确保顺利克隆(未勾选此选项可能导致文件系统无法识别的错误),其他步骤参照前文即可。

\n","excerpt":"","more":"

\"Clonezilla官网\"
Clonezilla是一款非常好用的系统迁移工具,本文将介绍其基本用法(不包含网络迁移及Clonezilla服务器等进阶内容)以及在克隆Btrfs磁盘时遇到的问题解法。

\n
\n

零.事前准备

    \n
  • 下载Clonezilla Live镜像(ISO),制作启动U盘(有手就行)。
  • \n
  • 准备目标存储设备(如U盘,需要迁移的新硬盘等),确保有足够空间(建议大于源硬盘已用空间的1.2倍)。
  • \n
\n

一.备份镜像到硬盘(Device-to-Image)

这个模式可以将硬盘完整备份为一个镜像文件(可保存到本地硬盘、外置硬盘或网络存储)方便多机部署。注意如果只是将一个硬盘的系统完全克隆到新盘可以跳过此部分。

\n

步骤说明

    \n
  1. 启动Clonezilla

    \n
      \n
    • 插入U盘,重启电脑并从U盘启动。
    • \n
    • 选择默认选项(语言,键盘布局,Beginner模式)进入Clonezilla的TUI界面。
    • \n
    \n
  2. \n
  3. 选择备份模式

    \n
    1
    2
    Choose mode:         选择 "device-image"(设备到镜像)
    Mount storage media: 选择 "local_dev"(本地存储设备)
    \n
      \n
    • 按提示挂载目标存储设备(注意这里选择的是你要存储镜像的设备且文件系统一般不限,如外置硬盘),确认路径(如 /dev/sdb1)。
    • \n
    \n
  4. \n
  5. 配置备份参数

    \n
      \n
    • 源硬盘:选择需要备份的硬盘(如 /dev/sda)。
    • \n
    • 镜像存储路径:指定目标位置(如外置硬盘的挂载目录)。
    • \n
    • 镜像名称:自定义名称(如 2025-img-rockylinux-2-21)。
    • \n
    • 压缩选项:默认即可,支持并行压缩加速。
    • \n
    • 镜像分割:若目标存储设备为FAT32格式(单文件最大4GB),选择自动分割。
    • \n
    \n
  6. \n
  7. 确认操作

    \n
      \n
    • 检查提示信息,输入 y 开始备份。
    • \n
    • 完成后关机或重启。
    • \n
    \n
  8. \n
  9. 镜像还原:和备份非常相似,只是选项换成restore to disk,顺着指引操作即可。

    \n
  10. \n
\n
\n

二、直接克隆硬盘(Device-to-Device)

将源硬盘完整克隆到目标硬盘(适合硬盘升级或快速迁移),比如笔者最近白嫖了一个三星的2T硬盘直接把原来512G硬盘里的CachyOS无损迁移了进去。

\n

步骤说明

    \n
  1. 准备工作

    \n
      \n
    • 连接目标硬盘(需容量≥源硬盘已用空间,还原镜像操作也是如此,注意Clonezilla支持小分区到大分区迁移不支持大分区到小分区,后者出门右转Rsync)
    • \n
    • 警告:目标硬盘数据将被覆盖,操作前如有需要务必备份重要数据!
    • \n
    \n
  2. \n
  3. 启动Clonezilla

    \n
      \n
    • 同上,从U盘启动进入Clonezilla界面。
    • \n
    \n
  4. \n
  5. 选择克隆模式

    \n
    1
    Choose mode:         选择 "device-device"(设备到设备)
    \n
  6. \n
  7. 选择硬盘

    \n
      \n
    • 母碟硬盘:选择原始硬盘(如 /dev/sda)。
    • \n
    • 目标硬盘:选择新硬盘(如 /dev/sdb)。
    • \n
    \n
  8. \n
  9. 克隆选项
    需要进入专家模式才能看到,一般直接新手模式默认即可。

    \n
  10. \n
  11. 执行克隆

    \n
      \n
    • 确认提示信息后输入 y,等待完成。
    • \n
    • 克隆结束后关机,移除旧硬盘并测试新硬盘(主要是查看能不能启动进入系统,若能进入一般不会有问题,而且一般都能进入因为Clonezilla是高精确的块对块克隆)。
    • \n
    \n
  12. \n
\n
\n

两种模式对比

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
模式特点
备份镜像方便多机部署也可以用于留档
直接克隆换硬盘快速迁移无需恢复过程
\n
\n

Btrfs务必注意

对Btrfs直接进行Clonezilla克隆大概率会碰到一个边界错误,这是由于Btrfs本身使用一段时间后碎片化存储导致的,你需要执行sudo btrfs balance start --full-balance /来进行整理,但是这也算是一个风险操作要确保完整执行不能在执行时意外中断导致文件系统出错,并且在后续克隆时不能使用新手模式要进入专家模式勾选-p1支持所有文件系统但是效率降低的选项来确保顺利克隆(未勾选此选项可能导致文件系统无法识别的错误),其他步骤参照前文即可。

\n"},{"title":"为 Firefox 启用全局菜单集成到 KDE Plasma 任务栏","date":"2026-06-29T03:32:26.000Z","_content":"\n众所不周知, Chrome 菜单可以自动集成到 KDE Plasma 任务栏全局菜单组件,不管你喜不喜欢浏览器菜单至少看着很顺眼。然而,默认情况下,Firefox 并没有自动启用这个功能,需要进行一些配置。//本来我还以为是 Firefox 没有支持这个功能 TT\n\n本文将详细探讨 Linux 全局菜单的 D-Bus 工作原理,对比 Chrome 与 Firefox 的实现差异,并给出在 KDE Plasma 下完美启用 Firefox 全局菜单的配置方案(以下与 D-Bus 相关知识分享由 [Qwen 3.7](https://chat.qwen.ai)提供并由本人验证整理)。\n\n## 什么是全局菜单?其背后的 D-Bus 工作原理\n\nKDE Plasma 桌面的全局菜单(Global Menu)小组件可以把应用程序内部的菜单栏(如“文件”、“编辑”、“视图”等)提取出来,统一放置在桌面顶部的面板(Panel)中。这种机制实际上采用的是**“客户端-服务端”**的架构,其核心是基于 **D-Bus 消息总线系统** 与 **`dbusmenu` 协议**:\n\n1. **`dbusmenu` 协议**:最初由 Canonical 为 Ubuntu Unity 桌面环境设计,定义了一套标准的 D-Bus 接口,用来描述菜单的层次结构(菜单项、图标、快捷键、分隔符、启用状态等)以及用户交互事件。\n2. **应用端(D-Bus 导出者)**:支持全局菜单的应用启动时,会在 D-Bus 会话总线(Session Bus)上注册一个服务,把本应用的菜单树作为 D-Bus 对象暴露出来。此时,菜单的数据实质上依然保存在应用的内存中,只是通过 D-Bus 接口暴露给外部读取。\n3. **全局菜单小组件(D-Bus 消费者)**:\n * **焦点追踪**:小组件通过窗口管理器(KWin)实时监听当前获得焦点的活动窗口。\n * **数据拉取**:当用户切换窗口时,小组件定位到该窗口对应的 D-Bus 服务地址,通过 `dbusmenu` 协议拉取菜单树,并在顶栏上进行渲染。\n * **事件回传**:用户在顶栏点击菜单项时,小组件通过 D-Bus 向目标应用发送“激活”(Activate)信号,应用接收后执行具体的操作。\n\n---\n\n## 为什么不同应用的支持情况不同?\n\n在 Linux 桌面中,不同工具包(Toolkit)的底层架构差异,导致了全局菜单集成度的不同:\n\n* **Qt 程序**:由于 KDE 深度基于 Qt 构建,Qt 5/6 已经原生内置了对 `dbusmenu` 的支持(通过 `libdbusmenu-qt` 等)。Qt 应用的 `QMenuBar` 会自动被全局菜单组件无缝接管,体验最完美。\n* **Electron / Chromium 应用**:Chromium 内核在 Linux 下原生集成了对 `libdbusmenu` 库的调用,只要没有使用 HTML 自绘标题栏,就会自动在 D-Bus 上注册原生菜单。\n\n### Chrome 与 Firefox 的差异对比\n\n* **Chrome/Chromium**:作为“优等生”,它直接在底层自带了 `dbusmenu` 支持,能自动检测 KDE 环境并向 D-Bus 注册菜单。\n \n ![Chrome 全局菜单](/images/chrome-global-menu.png)\n\n* **Firefox**:\n 1. Firefox 在 Linux 下主要基于 GTK3 构建,并不像 Chrome 那样天然“自带干粮”支持 D-Bus 菜单,需要依赖系统的 `appmenu-gtk-module` 翻译器。\n 2. 默认情况下,Firefox 倾向于“自己画界面”(使用 XUL/HTML 自绘机制)。当我们按下 `Alt` 键调出菜单栏时,它弹出的其实是**自绘的虚拟菜单**,而非标准的原生 `GtkMenuBar`。这就导致 `appmenu-gtk-module` 无法拦截到任何菜单数据。\n 3. 此外,如果启用了“自定义标题栏”(CSD,客户端装饰),Firefox 会完全接管窗口装饰,导致系统模块彻底与菜单脱钩。\n \n---\n\n## 完美解决:Firefox 启用全局菜单的完整步骤\n\n要让 Firefox 的菜单能够正常进入全局菜单,我们需要:\n\n1. 让 Firefox 放弃自绘菜单,改用原生 GTK 菜单栏;\n2. 开启 Firefox 内部的强制 D-Bus 导出开关;\n3. 确保系统具备 GTK 菜单桥接模块。\n\n具体步骤如下:\n\n### 步骤 1:确保安装并加载了 `appmenu-gtk-module`\n\n确保系统里存在 GTK 桥接模块:\n\n1. 根据发行版安装对应的模块:\n * **Arch Linux**:`sudo pacman -S appmenu-gtk-module`\n * **Ubuntu / Debian / KDE Neon**:`sudo apt install appmenu-gtk3-module`\n * **Fedora**:`sudo dnf install gtk3-appmenu-gtk-module`\n2. 重启你的桌面会话(或注销重新登录),以保证 `GTK_MODULES=appmenu-gtk-module` 环境变量在全局生效。\n\n### 步骤 2:在 `about:config` 中强制开启全局菜单输出\n\n由于 Firefox 默认隐藏菜单需要按 alt 键显示,我们需要手动强制其导出:\n\n1. 在 Firefox 地址栏输入 `about:config` 并回车,接受风险提示。\n2. 搜索并修改/新建以下两个键值:\n * **`widget.gtk.global-menu.force`** 设置为 **`true`** (强制启用全局菜单)\n * **`widget.gtk.global-menu.force-wayland`** 设置为 **`true`** (如果在 Wayland 会话下,强制通过 D-Bus 导出菜单,非常关键)\n3. 重启 Firefox。\n\n![Firefox 全局菜单配置](/images/firefox-global-menu-solve.png)\n\n---\n\n## 最终效果\n\n经过以上配置,Firefox 的菜单成功脱离了浏览器窗口,并无缝集成到了 KDE Plasma 的全局菜单面板中,整体桌面体验更加干净统一:\n\n![Firefox 默认菜单显示在标题栏下方](/images/firefox-global-menu.png)\n\n如此一来,无论是在 X11 还是 Wayland 会话下,我们都能在 Firefox 中享受到类似 macOS 的全局菜单体验了。\n","source":"_posts/firefox-global-menu.md","raw":"---\ntitle: 为 Firefox 启用全局菜单集成到 KDE Plasma 任务栏\ndate: 2026-06-29 11:32:26\ntags: [Firefox, 技术分享]\n---\n\n众所不周知, Chrome 菜单可以自动集成到 KDE Plasma 任务栏全局菜单组件,不管你喜不喜欢浏览器菜单至少看着很顺眼。然而,默认情况下,Firefox 并没有自动启用这个功能,需要进行一些配置。//本来我还以为是 Firefox 没有支持这个功能 TT\n\n本文将详细探讨 Linux 全局菜单的 D-Bus 工作原理,对比 Chrome 与 Firefox 的实现差异,并给出在 KDE Plasma 下完美启用 Firefox 全局菜单的配置方案(以下与 D-Bus 相关知识分享由 [Qwen 3.7](https://chat.qwen.ai)提供并由本人验证整理)。\n\n## 什么是全局菜单?其背后的 D-Bus 工作原理\n\nKDE Plasma 桌面的全局菜单(Global Menu)小组件可以把应用程序内部的菜单栏(如“文件”、“编辑”、“视图”等)提取出来,统一放置在桌面顶部的面板(Panel)中。这种机制实际上采用的是**“客户端-服务端”**的架构,其核心是基于 **D-Bus 消息总线系统** 与 **`dbusmenu` 协议**:\n\n1. **`dbusmenu` 协议**:最初由 Canonical 为 Ubuntu Unity 桌面环境设计,定义了一套标准的 D-Bus 接口,用来描述菜单的层次结构(菜单项、图标、快捷键、分隔符、启用状态等)以及用户交互事件。\n2. **应用端(D-Bus 导出者)**:支持全局菜单的应用启动时,会在 D-Bus 会话总线(Session Bus)上注册一个服务,把本应用的菜单树作为 D-Bus 对象暴露出来。此时,菜单的数据实质上依然保存在应用的内存中,只是通过 D-Bus 接口暴露给外部读取。\n3. **全局菜单小组件(D-Bus 消费者)**:\n * **焦点追踪**:小组件通过窗口管理器(KWin)实时监听当前获得焦点的活动窗口。\n * **数据拉取**:当用户切换窗口时,小组件定位到该窗口对应的 D-Bus 服务地址,通过 `dbusmenu` 协议拉取菜单树,并在顶栏上进行渲染。\n * **事件回传**:用户在顶栏点击菜单项时,小组件通过 D-Bus 向目标应用发送“激活”(Activate)信号,应用接收后执行具体的操作。\n\n---\n\n## 为什么不同应用的支持情况不同?\n\n在 Linux 桌面中,不同工具包(Toolkit)的底层架构差异,导致了全局菜单集成度的不同:\n\n* **Qt 程序**:由于 KDE 深度基于 Qt 构建,Qt 5/6 已经原生内置了对 `dbusmenu` 的支持(通过 `libdbusmenu-qt` 等)。Qt 应用的 `QMenuBar` 会自动被全局菜单组件无缝接管,体验最完美。\n* **Electron / Chromium 应用**:Chromium 内核在 Linux 下原生集成了对 `libdbusmenu` 库的调用,只要没有使用 HTML 自绘标题栏,就会自动在 D-Bus 上注册原生菜单。\n\n### Chrome 与 Firefox 的差异对比\n\n* **Chrome/Chromium**:作为“优等生”,它直接在底层自带了 `dbusmenu` 支持,能自动检测 KDE 环境并向 D-Bus 注册菜单。\n \n ![Chrome 全局菜单](/images/chrome-global-menu.png)\n\n* **Firefox**:\n 1. Firefox 在 Linux 下主要基于 GTK3 构建,并不像 Chrome 那样天然“自带干粮”支持 D-Bus 菜单,需要依赖系统的 `appmenu-gtk-module` 翻译器。\n 2. 默认情况下,Firefox 倾向于“自己画界面”(使用 XUL/HTML 自绘机制)。当我们按下 `Alt` 键调出菜单栏时,它弹出的其实是**自绘的虚拟菜单**,而非标准的原生 `GtkMenuBar`。这就导致 `appmenu-gtk-module` 无法拦截到任何菜单数据。\n 3. 此外,如果启用了“自定义标题栏”(CSD,客户端装饰),Firefox 会完全接管窗口装饰,导致系统模块彻底与菜单脱钩。\n \n---\n\n## 完美解决:Firefox 启用全局菜单的完整步骤\n\n要让 Firefox 的菜单能够正常进入全局菜单,我们需要:\n\n1. 让 Firefox 放弃自绘菜单,改用原生 GTK 菜单栏;\n2. 开启 Firefox 内部的强制 D-Bus 导出开关;\n3. 确保系统具备 GTK 菜单桥接模块。\n\n具体步骤如下:\n\n### 步骤 1:确保安装并加载了 `appmenu-gtk-module`\n\n确保系统里存在 GTK 桥接模块:\n\n1. 根据发行版安装对应的模块:\n * **Arch Linux**:`sudo pacman -S appmenu-gtk-module`\n * **Ubuntu / Debian / KDE Neon**:`sudo apt install appmenu-gtk3-module`\n * **Fedora**:`sudo dnf install gtk3-appmenu-gtk-module`\n2. 重启你的桌面会话(或注销重新登录),以保证 `GTK_MODULES=appmenu-gtk-module` 环境变量在全局生效。\n\n### 步骤 2:在 `about:config` 中强制开启全局菜单输出\n\n由于 Firefox 默认隐藏菜单需要按 alt 键显示,我们需要手动强制其导出:\n\n1. 在 Firefox 地址栏输入 `about:config` 并回车,接受风险提示。\n2. 搜索并修改/新建以下两个键值:\n * **`widget.gtk.global-menu.force`** 设置为 **`true`** (强制启用全局菜单)\n * **`widget.gtk.global-menu.force-wayland`** 设置为 **`true`** (如果在 Wayland 会话下,强制通过 D-Bus 导出菜单,非常关键)\n3. 重启 Firefox。\n\n![Firefox 全局菜单配置](/images/firefox-global-menu-solve.png)\n\n---\n\n## 最终效果\n\n经过以上配置,Firefox 的菜单成功脱离了浏览器窗口,并无缝集成到了 KDE Plasma 的全局菜单面板中,整体桌面体验更加干净统一:\n\n![Firefox 默认菜单显示在标题栏下方](/images/firefox-global-menu.png)\n\n如此一来,无论是在 X11 还是 Wayland 会话下,我们都能在 Firefox 中享受到类似 macOS 的全局菜单体验了。\n","slug":"firefox-global-menu","published":1,"updated":"2026-06-29T06:22:27.518Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblw0015idd028lkhco5","content":"

众所不周知, Chrome 菜单可以自动集成到 KDE Plasma 任务栏全局菜单组件,不管你喜不喜欢浏览器菜单至少看着很顺眼。然而,默认情况下,Firefox 并没有自动启用这个功能,需要进行一些配置。//本来我还以为是 Firefox 没有支持这个功能 TT

\n

本文将详细探讨 Linux 全局菜单的 D-Bus 工作原理,对比 Chrome 与 Firefox 的实现差异,并给出在 KDE Plasma 下完美启用 Firefox 全局菜单的配置方案(以下与 D-Bus 相关知识分享由 Qwen 3.7提供并由本人验证整理)。

\n

什么是全局菜单?其背后的 D-Bus 工作原理

KDE Plasma 桌面的全局菜单(Global Menu)小组件可以把应用程序内部的菜单栏(如“文件”、“编辑”、“视图”等)提取出来,统一放置在桌面顶部的面板(Panel)中。这种机制实际上采用的是“客户端-服务端”的架构,其核心是基于 D-Bus 消息总线系统dbusmenu 协议

\n
    \n
  1. dbusmenu 协议:最初由 Canonical 为 Ubuntu Unity 桌面环境设计,定义了一套标准的 D-Bus 接口,用来描述菜单的层次结构(菜单项、图标、快捷键、分隔符、启用状态等)以及用户交互事件。
  2. \n
  3. 应用端(D-Bus 导出者):支持全局菜单的应用启动时,会在 D-Bus 会话总线(Session Bus)上注册一个服务,把本应用的菜单树作为 D-Bus 对象暴露出来。此时,菜单的数据实质上依然保存在应用的内存中,只是通过 D-Bus 接口暴露给外部读取。
  4. \n
  5. 全局菜单小组件(D-Bus 消费者)
      \n
    • 焦点追踪:小组件通过窗口管理器(KWin)实时监听当前获得焦点的活动窗口。
    • \n
    • 数据拉取:当用户切换窗口时,小组件定位到该窗口对应的 D-Bus 服务地址,通过 dbusmenu 协议拉取菜单树,并在顶栏上进行渲染。
    • \n
    • 事件回传:用户在顶栏点击菜单项时,小组件通过 D-Bus 向目标应用发送“激活”(Activate)信号,应用接收后执行具体的操作。
    • \n
    \n
  6. \n
\n
\n

为什么不同应用的支持情况不同?

在 Linux 桌面中,不同工具包(Toolkit)的底层架构差异,导致了全局菜单集成度的不同:

\n
    \n
  • Qt 程序:由于 KDE 深度基于 Qt 构建,Qt 5/6 已经原生内置了对 dbusmenu 的支持(通过 libdbusmenu-qt 等)。Qt 应用的 QMenuBar 会自动被全局菜单组件无缝接管,体验最完美。
  • \n
  • Electron / Chromium 应用:Chromium 内核在 Linux 下原生集成了对 libdbusmenu 库的调用,只要没有使用 HTML 自绘标题栏,就会自动在 D-Bus 上注册原生菜单。
  • \n
\n

Chrome 与 Firefox 的差异对比

    \n
  • Chrome/Chromium:作为“优等生”,它直接在底层自带了 dbusmenu 支持,能自动检测 KDE 环境并向 D-Bus 注册菜单。

    \n

    \"Chrome

    \n
  • \n
  • Firefox

    \n
      \n
    1. Firefox 在 Linux 下主要基于 GTK3 构建,并不像 Chrome 那样天然“自带干粮”支持 D-Bus 菜单,需要依赖系统的 appmenu-gtk-module 翻译器。
    2. \n
    3. 默认情况下,Firefox 倾向于“自己画界面”(使用 XUL/HTML 自绘机制)。当我们按下 Alt 键调出菜单栏时,它弹出的其实是自绘的虚拟菜单,而非标准的原生 GtkMenuBar。这就导致 appmenu-gtk-module 无法拦截到任何菜单数据。
    4. \n
    5. 此外,如果启用了“自定义标题栏”(CSD,客户端装饰),Firefox 会完全接管窗口装饰,导致系统模块彻底与菜单脱钩。
    6. \n
    \n
  • \n
\n
\n

完美解决:Firefox 启用全局菜单的完整步骤

要让 Firefox 的菜单能够正常进入全局菜单,我们需要:

\n
    \n
  1. 让 Firefox 放弃自绘菜单,改用原生 GTK 菜单栏;
  2. \n
  3. 开启 Firefox 内部的强制 D-Bus 导出开关;
  4. \n
  5. 确保系统具备 GTK 菜单桥接模块。
  6. \n
\n

具体步骤如下:

\n

步骤 1:确保安装并加载了 appmenu-gtk-module

确保系统里存在 GTK 桥接模块:

\n
    \n
  1. 根据发行版安装对应的模块:
      \n
    • Arch Linuxsudo pacman -S appmenu-gtk-module
    • \n
    • Ubuntu / Debian / KDE Neonsudo apt install appmenu-gtk3-module
    • \n
    • Fedorasudo dnf install gtk3-appmenu-gtk-module
    • \n
    \n
  2. \n
  3. 重启你的桌面会话(或注销重新登录),以保证 GTK_MODULES=appmenu-gtk-module 环境变量在全局生效。
  4. \n
\n

步骤 2:在 about:config 中强制开启全局菜单输出

由于 Firefox 默认隐藏菜单需要按 alt 键显示,我们需要手动强制其导出:

\n
    \n
  1. 在 Firefox 地址栏输入 about:config 并回车,接受风险提示。
  2. \n
  3. 搜索并修改/新建以下两个键值:
      \n
    • widget.gtk.global-menu.force 设置为 true (强制启用全局菜单)
    • \n
    • widget.gtk.global-menu.force-wayland 设置为 true (如果在 Wayland 会话下,强制通过 D-Bus 导出菜单,非常关键)
    • \n
    \n
  4. \n
  5. 重启 Firefox。
  6. \n
\n

\"Firefox

\n
\n

最终效果

经过以上配置,Firefox 的菜单成功脱离了浏览器窗口,并无缝集成到了 KDE Plasma 的全局菜单面板中,整体桌面体验更加干净统一:

\n

\"Firefox

\n

如此一来,无论是在 X11 还是 Wayland 会话下,我们都能在 Firefox 中享受到类似 macOS 的全局菜单体验了。

\n","excerpt":"","more":"

众所不周知, Chrome 菜单可以自动集成到 KDE Plasma 任务栏全局菜单组件,不管你喜不喜欢浏览器菜单至少看着很顺眼。然而,默认情况下,Firefox 并没有自动启用这个功能,需要进行一些配置。//本来我还以为是 Firefox 没有支持这个功能 TT

\n

本文将详细探讨 Linux 全局菜单的 D-Bus 工作原理,对比 Chrome 与 Firefox 的实现差异,并给出在 KDE Plasma 下完美启用 Firefox 全局菜单的配置方案(以下与 D-Bus 相关知识分享由 Qwen 3.7提供并由本人验证整理)。

\n

什么是全局菜单?其背后的 D-Bus 工作原理

KDE Plasma 桌面的全局菜单(Global Menu)小组件可以把应用程序内部的菜单栏(如“文件”、“编辑”、“视图”等)提取出来,统一放置在桌面顶部的面板(Panel)中。这种机制实际上采用的是“客户端-服务端”的架构,其核心是基于 D-Bus 消息总线系统dbusmenu 协议

\n
    \n
  1. dbusmenu 协议:最初由 Canonical 为 Ubuntu Unity 桌面环境设计,定义了一套标准的 D-Bus 接口,用来描述菜单的层次结构(菜单项、图标、快捷键、分隔符、启用状态等)以及用户交互事件。
  2. \n
  3. 应用端(D-Bus 导出者):支持全局菜单的应用启动时,会在 D-Bus 会话总线(Session Bus)上注册一个服务,把本应用的菜单树作为 D-Bus 对象暴露出来。此时,菜单的数据实质上依然保存在应用的内存中,只是通过 D-Bus 接口暴露给外部读取。
  4. \n
  5. 全局菜单小组件(D-Bus 消费者)
      \n
    • 焦点追踪:小组件通过窗口管理器(KWin)实时监听当前获得焦点的活动窗口。
    • \n
    • 数据拉取:当用户切换窗口时,小组件定位到该窗口对应的 D-Bus 服务地址,通过 dbusmenu 协议拉取菜单树,并在顶栏上进行渲染。
    • \n
    • 事件回传:用户在顶栏点击菜单项时,小组件通过 D-Bus 向目标应用发送“激活”(Activate)信号,应用接收后执行具体的操作。
    • \n
    \n
  6. \n
\n
\n

为什么不同应用的支持情况不同?

在 Linux 桌面中,不同工具包(Toolkit)的底层架构差异,导致了全局菜单集成度的不同:

\n
    \n
  • Qt 程序:由于 KDE 深度基于 Qt 构建,Qt 5/6 已经原生内置了对 dbusmenu 的支持(通过 libdbusmenu-qt 等)。Qt 应用的 QMenuBar 会自动被全局菜单组件无缝接管,体验最完美。
  • \n
  • Electron / Chromium 应用:Chromium 内核在 Linux 下原生集成了对 libdbusmenu 库的调用,只要没有使用 HTML 自绘标题栏,就会自动在 D-Bus 上注册原生菜单。
  • \n
\n

Chrome 与 Firefox 的差异对比

    \n
  • Chrome/Chromium:作为“优等生”,它直接在底层自带了 dbusmenu 支持,能自动检测 KDE 环境并向 D-Bus 注册菜单。

    \n

    \"Chrome

    \n
  • \n
  • Firefox

    \n
      \n
    1. Firefox 在 Linux 下主要基于 GTK3 构建,并不像 Chrome 那样天然“自带干粮”支持 D-Bus 菜单,需要依赖系统的 appmenu-gtk-module 翻译器。
    2. \n
    3. 默认情况下,Firefox 倾向于“自己画界面”(使用 XUL/HTML 自绘机制)。当我们按下 Alt 键调出菜单栏时,它弹出的其实是自绘的虚拟菜单,而非标准的原生 GtkMenuBar。这就导致 appmenu-gtk-module 无法拦截到任何菜单数据。
    4. \n
    5. 此外,如果启用了“自定义标题栏”(CSD,客户端装饰),Firefox 会完全接管窗口装饰,导致系统模块彻底与菜单脱钩。
    6. \n
    \n
  • \n
\n
\n

完美解决:Firefox 启用全局菜单的完整步骤

要让 Firefox 的菜单能够正常进入全局菜单,我们需要:

\n
    \n
  1. 让 Firefox 放弃自绘菜单,改用原生 GTK 菜单栏;
  2. \n
  3. 开启 Firefox 内部的强制 D-Bus 导出开关;
  4. \n
  5. 确保系统具备 GTK 菜单桥接模块。
  6. \n
\n

具体步骤如下:

\n

步骤 1:确保安装并加载了 appmenu-gtk-module

确保系统里存在 GTK 桥接模块:

\n
    \n
  1. 根据发行版安装对应的模块:
      \n
    • Arch Linuxsudo pacman -S appmenu-gtk-module
    • \n
    • Ubuntu / Debian / KDE Neonsudo apt install appmenu-gtk3-module
    • \n
    • Fedorasudo dnf install gtk3-appmenu-gtk-module
    • \n
    \n
  2. \n
  3. 重启你的桌面会话(或注销重新登录),以保证 GTK_MODULES=appmenu-gtk-module 环境变量在全局生效。
  4. \n
\n

步骤 2:在 about:config 中强制开启全局菜单输出

由于 Firefox 默认隐藏菜单需要按 alt 键显示,我们需要手动强制其导出:

\n
    \n
  1. 在 Firefox 地址栏输入 about:config 并回车,接受风险提示。
  2. \n
  3. 搜索并修改/新建以下两个键值:
      \n
    • widget.gtk.global-menu.force 设置为 true (强制启用全局菜单)
    • \n
    • widget.gtk.global-menu.force-wayland 设置为 true (如果在 Wayland 会话下,强制通过 D-Bus 导出菜单,非常关键)
    • \n
    \n
  4. \n
  5. 重启 Firefox。
  6. \n
\n

\"Firefox

\n
\n

最终效果

经过以上配置,Firefox 的菜单成功脱离了浏览器窗口,并无缝集成到了 KDE Plasma 的全局菜单面板中,整体桌面体验更加干净统一:

\n

\"Firefox

\n

如此一来,无论是在 X11 还是 Wayland 会话下,我们都能在 Firefox 中享受到类似 macOS 的全局菜单体验了。

\n"},{"title":"Random Fortune","date":"2025-05-22T07:29:07.000Z","_content":"\nKeep peace of mind with fortune...\n\n❯ fortune\n\nThe nice thing about standards is that there are so many of them to choose\nfrom.\n -- Andrew S. Tanenbaum\n\n❯ fortune\n\n看蓬门秋草,年年破巷,疏窗细雨,夜夜孤灯。\n -- 郑板桥《沁园春·恨》\n\n❯ fortune\n\nAn alcoholic is someone you don't like who drinks as much as you do.\n -- Dylan Thomas\n\n❯ fortune\n\nIf you put tomfoolery into a computer, nothing comes out but tomfoolery.\nBut this tomfoolery, having passed through a very expensive machine,\nis somehow ennobled and no-one dare criticise it.\n -- Pierre Gallois\n\n❯ fortune\n\n秋色渐将晚,霜信报黄花。\n -- 叶梦得《水调歌头·秋色渐将晚》\n\n❯ fortune\n\n水精帘里颇黎枕,暖香惹梦鸳鸯锦。江上柳如烟,雁飞残月天。\n藕丝秋色浅,人胜参差剪。双鬓隔香红,玉钗头上风。\n -- 温庭筠《菩萨蛮》\n\n❯ fortune\n\n A disciple of another sect once came to Drescher as he was eating\nhis morning meal. \"I would like to give you this personality test\", said\nthe outsider, \"because I want you to be happy.\"\n Drescher took the paper that was offered him and put it into the\ntoaster -- \"I wish the toaster to be happy too\".\n\n❯ fortune\n\nExample is not the main thing in influencing others. It is the only thing.\n -- Albert Schweitzer\n\n❯ fortune\n\nWe have art that we do not die of the truth.\n -- Nietzsche\n\n❯ fortune\n\n \"Java for the COBOL Programmer\"\n who writes these things?\n people on crack\n and cobol programmers\n :)\n that's redundant.\n\n❯ fortune\n\nI was in this prematurely air conditioned supermarket and there were all\nthese aisles and there were these bathing caps you could buy that had these\nkind of Fourth of July plumes on them that were red and yellow and blue and\nI wasn't tempted to buy one but I was reminded of the fact that I had been\navoiding the beach.\n -- Lucinda Childs \"Einstein On The Beach\"\n\n❯ fortune\n\nIt doesn't matter whether you win or lose -- until you lose.\n\n❯ fortune\n\nTrue leadership is the art of changing a group from what it is to what\nit ought to be.\n -- Virginia Allan\n\n❯ fortune\n\nI keep hearing and reading this nice proverb *if it ain't broke, don't fix\nit*. The latest appearance was in response to [Shlomi\nFish](http://community.livejournal.com/shlomif_tech/37969.html) suggesting\nthat some Ancient Perl code should be replaced by Modern Perl code.\n\nI am not saying that every piece of code should be rewritten every 6 months,\nbut in my understanding that sentence actually translates to *let's wait till\nit breaks and then panic*.\n\nI think people who say that sentence are afraid that the new version will\nbreak something. Sure, there is always a chance that a change introduces an\nerror, but, if we are afraid to touch the code, what will happen when later on\nwe encounter a case where it does not work? For example, if we need to use it\nin a new environment. Will we have the courage to change the code then? How\nmuch will it cost in money, time, and lost sleep?\n\nI think we have been trying to teach ourselves that we should have really good\ntest coverage of our code and then we can easily refactor it and get rid of\ntechnical debt. So why do we keep hearing that sentence?\n\n -- Gabor Szabo\n -- What does \"If it ain't broke, don't fix it.\" really mean? ( )\n\n❯ fortune\n\nPsychoanalysis is that mental illness for which it regards itself a therapy.\n -- Karl Kraus\n\n❯ fortune\n\nWith a rubber duck, one's never alone.\n -- \"The Hitchhiker's Guide to the Galaxy\"\n\n❯ fortune\n\n(Presuming for the sake of argument that it's even *possible* to design\nbetter code in Perl than in C. :-)\n -- Larry Wall on core code vs. module code design\n\n❯ fortune\n\nBut, officer, he's not drunk, I just saw his fingers twitch!\n\n❯ fortune\n\nImbalance of power corrupts and monopoly of power corrupts absolutely.\n -- Genji\n\n❯ fortune\n\nI want a VEGETARIAN BURRITO to go ... with EXTRA MSG!!\n\n❯ fortune\n\n如果你不知道如何表现地高人一等,找个Unix用户,让他做给你看。\n -- Dilbert newsletter 3.0, 1994\n\n❯ fortune\n\n\"The picture's pretty bleak, gentlemen... The world's climates are changing,\nthe mammals are taking over, and we all have a brain about the size of a\nwalnut.\"\n -- some dinosaurs from The Far Side, by Gary Larson\n\n❯ fortune\n\nMost of us feel that marketing types are like a dangerous weapon - keep\n'em unloaded and locked up in a cupboard, and only bring them out when\nyou need them to do a job.\n -- Craig Sanders\n\n❯ fortune\n\n不寐倦长更,披衣出户行。月寒秋竹冷,风切夜窗声。\n -- 李煜《三台令》\n\n❯ fortune\n\n子曰:“由,诲女知之乎!知之为知之,不知为不知,是知也。”\n-- 论语,为政篇\n\n❯ fortune\n\n \"The jig's up, Elman.\"\n \"Which jig?\"\n -- Jeff Elman\n\n❯ fortune\n\n\"First things first -- but not necessarily in that order\"\n -- The Doctor, \"Doctor Who\"\n\n❯ fortune\n\n波影摇涟,趁熏风,一舸来时,翠阴清昼。去郭轩楹才数里,藓磴松关云岫。\n快屐齿,筇枝先后。空半危亭堪聚远,看洞庭,缥缈争奇秀。人自老,景如旧。\n来帆去棹还知否,问古今,几度斜阳,几番回首?晓色一川谁管领,都付雨荷烟柳,\n知我者,燕朋鸥友。笑拍阑干呼范蠡,甚平吴,却倩垂纶手?吁万古,付卮酒。\n -- 周密《长亭怨慢》\n\n❯ fortune\n\nI'm telling you that the kernel is stable not because it's a kernel,\nbut because I refuse to listen to arguments like this.\n -- Linus Torvalds\n\n❯ fortune\n\nA girl's conscience doesn't really keep her from doing anything wrong--\nit merely keeps her from enjoying it.\n\n❯ fortune\n\nThere are few people more often in the wrong than those who cannot endure\nto be thought so.\n\n❯ fortune\n\nOne does not thank logic.\n -- Sarek, \"Journey to Babel\", stardate 3842.4\n\n❯ fortune\n\nFree yourself from negative influence. Negative thoughts are the old\nhabits that gnaw at the roots of the soul.\nMoses Shongo, (Seneca)\n\n❯ fortune\n\n日月照之何不及此?惟有北风号怒天上来。\n -- 李白《北风行》\n\n❯ fortune\n\nAnyone who is capable of getting themselves made President should on no\naccount be allowed to do the job.\n -- Douglas Adams, \"The Hitchhiker's Guide to the Galaxy\"\n\nAnd here are my mods:-)\n\n❯ pacman -Qs fortune\nlocal/cowfortune 0.1.2-8\n Configurable fortune cookie proclaiming cow (and a few other creatures)\nlocal/fortune-mod 3.22.0-1.1\n The Fortune Cookie Program from BSD games\nlocal/fortune-mod-zh 2.98-1 (fortune-mods)\n Chinese poems for fortune-mod\n","source":"_posts/fortune.md","raw":"---\ntitle: Random fortune \ndate: 2025-05-22 15:29:07\ntags: 日志\n---\n\nKeep peace of mind with fortune...\n\n❯ fortune\n\nThe nice thing about standards is that there are so many of them to choose\nfrom.\n -- Andrew S. Tanenbaum\n\n❯ fortune\n\n看蓬门秋草,年年破巷,疏窗细雨,夜夜孤灯。\n -- 郑板桥《沁园春·恨》\n\n❯ fortune\n\nAn alcoholic is someone you don't like who drinks as much as you do.\n -- Dylan Thomas\n\n❯ fortune\n\nIf you put tomfoolery into a computer, nothing comes out but tomfoolery.\nBut this tomfoolery, having passed through a very expensive machine,\nis somehow ennobled and no-one dare criticise it.\n -- Pierre Gallois\n\n❯ fortune\n\n秋色渐将晚,霜信报黄花。\n -- 叶梦得《水调歌头·秋色渐将晚》\n\n❯ fortune\n\n水精帘里颇黎枕,暖香惹梦鸳鸯锦。江上柳如烟,雁飞残月天。\n藕丝秋色浅,人胜参差剪。双鬓隔香红,玉钗头上风。\n -- 温庭筠《菩萨蛮》\n\n❯ fortune\n\n A disciple of another sect once came to Drescher as he was eating\nhis morning meal. \"I would like to give you this personality test\", said\nthe outsider, \"because I want you to be happy.\"\n Drescher took the paper that was offered him and put it into the\ntoaster -- \"I wish the toaster to be happy too\".\n\n❯ fortune\n\nExample is not the main thing in influencing others. It is the only thing.\n -- Albert Schweitzer\n\n❯ fortune\n\nWe have art that we do not die of the truth.\n -- Nietzsche\n\n❯ fortune\n\n \"Java for the COBOL Programmer\"\n who writes these things?\n people on crack\n and cobol programmers\n :)\n that's redundant.\n\n❯ fortune\n\nI was in this prematurely air conditioned supermarket and there were all\nthese aisles and there were these bathing caps you could buy that had these\nkind of Fourth of July plumes on them that were red and yellow and blue and\nI wasn't tempted to buy one but I was reminded of the fact that I had been\navoiding the beach.\n -- Lucinda Childs \"Einstein On The Beach\"\n\n❯ fortune\n\nIt doesn't matter whether you win or lose -- until you lose.\n\n❯ fortune\n\nTrue leadership is the art of changing a group from what it is to what\nit ought to be.\n -- Virginia Allan\n\n❯ fortune\n\nI keep hearing and reading this nice proverb *if it ain't broke, don't fix\nit*. The latest appearance was in response to [Shlomi\nFish](http://community.livejournal.com/shlomif_tech/37969.html) suggesting\nthat some Ancient Perl code should be replaced by Modern Perl code.\n\nI am not saying that every piece of code should be rewritten every 6 months,\nbut in my understanding that sentence actually translates to *let's wait till\nit breaks and then panic*.\n\nI think people who say that sentence are afraid that the new version will\nbreak something. Sure, there is always a chance that a change introduces an\nerror, but, if we are afraid to touch the code, what will happen when later on\nwe encounter a case where it does not work? For example, if we need to use it\nin a new environment. Will we have the courage to change the code then? How\nmuch will it cost in money, time, and lost sleep?\n\nI think we have been trying to teach ourselves that we should have really good\ntest coverage of our code and then we can easily refactor it and get rid of\ntechnical debt. So why do we keep hearing that sentence?\n\n -- Gabor Szabo\n -- What does \"If it ain't broke, don't fix it.\" really mean? ( )\n\n❯ fortune\n\nPsychoanalysis is that mental illness for which it regards itself a therapy.\n -- Karl Kraus\n\n❯ fortune\n\nWith a rubber duck, one's never alone.\n -- \"The Hitchhiker's Guide to the Galaxy\"\n\n❯ fortune\n\n(Presuming for the sake of argument that it's even *possible* to design\nbetter code in Perl than in C. :-)\n -- Larry Wall on core code vs. module code design\n\n❯ fortune\n\nBut, officer, he's not drunk, I just saw his fingers twitch!\n\n❯ fortune\n\nImbalance of power corrupts and monopoly of power corrupts absolutely.\n -- Genji\n\n❯ fortune\n\nI want a VEGETARIAN BURRITO to go ... with EXTRA MSG!!\n\n❯ fortune\n\n如果你不知道如何表现地高人一等,找个Unix用户,让他做给你看。\n -- Dilbert newsletter 3.0, 1994\n\n❯ fortune\n\n\"The picture's pretty bleak, gentlemen... The world's climates are changing,\nthe mammals are taking over, and we all have a brain about the size of a\nwalnut.\"\n -- some dinosaurs from The Far Side, by Gary Larson\n\n❯ fortune\n\nMost of us feel that marketing types are like a dangerous weapon - keep\n'em unloaded and locked up in a cupboard, and only bring them out when\nyou need them to do a job.\n -- Craig Sanders\n\n❯ fortune\n\n不寐倦长更,披衣出户行。月寒秋竹冷,风切夜窗声。\n -- 李煜《三台令》\n\n❯ fortune\n\n子曰:“由,诲女知之乎!知之为知之,不知为不知,是知也。”\n-- 论语,为政篇\n\n❯ fortune\n\n \"The jig's up, Elman.\"\n \"Which jig?\"\n -- Jeff Elman\n\n❯ fortune\n\n\"First things first -- but not necessarily in that order\"\n -- The Doctor, \"Doctor Who\"\n\n❯ fortune\n\n波影摇涟,趁熏风,一舸来时,翠阴清昼。去郭轩楹才数里,藓磴松关云岫。\n快屐齿,筇枝先后。空半危亭堪聚远,看洞庭,缥缈争奇秀。人自老,景如旧。\n来帆去棹还知否,问古今,几度斜阳,几番回首?晓色一川谁管领,都付雨荷烟柳,\n知我者,燕朋鸥友。笑拍阑干呼范蠡,甚平吴,却倩垂纶手?吁万古,付卮酒。\n -- 周密《长亭怨慢》\n\n❯ fortune\n\nI'm telling you that the kernel is stable not because it's a kernel,\nbut because I refuse to listen to arguments like this.\n -- Linus Torvalds\n\n❯ fortune\n\nA girl's conscience doesn't really keep her from doing anything wrong--\nit merely keeps her from enjoying it.\n\n❯ fortune\n\nThere are few people more often in the wrong than those who cannot endure\nto be thought so.\n\n❯ fortune\n\nOne does not thank logic.\n -- Sarek, \"Journey to Babel\", stardate 3842.4\n\n❯ fortune\n\nFree yourself from negative influence. Negative thoughts are the old\nhabits that gnaw at the roots of the soul.\nMoses Shongo, (Seneca)\n\n❯ fortune\n\n日月照之何不及此?惟有北风号怒天上来。\n -- 李白《北风行》\n\n❯ fortune\n\nAnyone who is capable of getting themselves made President should on no\naccount be allowed to do the job.\n -- Douglas Adams, \"The Hitchhiker's Guide to the Galaxy\"\n\nAnd here are my mods:-)\n\n❯ pacman -Qs fortune\nlocal/cowfortune 0.1.2-8\n Configurable fortune cookie proclaiming cow (and a few other creatures)\nlocal/fortune-mod 3.22.0-1.1\n The Fortune Cookie Program from BSD games\nlocal/fortune-mod-zh 2.98-1 (fortune-mods)\n Chinese poems for fortune-mod\n","slug":"fortune","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblw0017idd0dgm4cijt","content":"

Keep peace of mind with fortune…

\n

❯ fortune

\n

The nice thing about standards is that there are so many of them to choose
from.
– Andrew S. Tanenbaum

\n

❯ fortune

\n

看蓬门秋草,年年破巷,疏窗细雨,夜夜孤灯。
– 郑板桥《沁园春·恨》

\n

❯ fortune

\n

An alcoholic is someone you don’t like who drinks as much as you do.
– Dylan Thomas

\n

❯ fortune

\n

If you put tomfoolery into a computer, nothing comes out but tomfoolery.
But this tomfoolery, having passed through a very expensive machine,
is somehow ennobled and no-one dare criticise it.
– Pierre Gallois

\n

❯ fortune

\n

秋色渐将晚,霜信报黄花。
– 叶梦得《水调歌头·秋色渐将晚》

\n

❯ fortune

\n

水精帘里颇黎枕,暖香惹梦鸳鸯锦。江上柳如烟,雁飞残月天。
藕丝秋色浅,人胜参差剪。双鬓隔香红,玉钗头上风。
– 温庭筠《菩萨蛮》

\n

❯ fortune

\n
    A disciple of another sect once came to Drescher as he was eating\n
\n

his morning meal. “I would like to give you this personality test”, said
the outsider, “because I want you to be happy.”
Drescher took the paper that was offered him and put it into the
toaster – “I wish the toaster to be happy too”.

\n

❯ fortune

\n

Example is not the main thing in influencing others. It is the only thing.
– Albert Schweitzer

\n

❯ fortune

\n

We have art that we do not die of the truth.
– Nietzsche

\n

❯ fortune

\n

“Java for the COBOL Programmer”
who writes these things?
people on crack
and cobol programmers
:)
that’s redundant.

\n

❯ fortune

\n

I was in this prematurely air conditioned supermarket and there were all
these aisles and there were these bathing caps you could buy that had these
kind of Fourth of July plumes on them that were red and yellow and blue and
I wasn’t tempted to buy one but I was reminded of the fact that I had been
avoiding the beach.
– Lucinda Childs “Einstein On The Beach”

\n

❯ fortune

\n

It doesn’t matter whether you win or lose – until you lose.

\n

❯ fortune

\n

True leadership is the art of changing a group from what it is to what
it ought to be.
– Virginia Allan

\n

❯ fortune

\n

I keep hearing and reading this nice proverb if it ain’t broke, don’t fix
it
. The latest appearance was in response to Shlomi
Fish
suggesting
that some Ancient Perl code should be replaced by Modern Perl code.

\n

I am not saying that every piece of code should be rewritten every 6 months,
but in my understanding that sentence actually translates to let’s wait till
it breaks and then panic
.

\n

I think people who say that sentence are afraid that the new version will
break something. Sure, there is always a chance that a change introduces an
error, but, if we are afraid to touch the code, what will happen when later on
we encounter a case where it does not work? For example, if we need to use it
in a new environment. Will we have the courage to change the code then? How
much will it cost in money, time, and lost sleep?

\n

I think we have been trying to teach ourselves that we should have really good
test coverage of our code and then we can easily refactor it and get rid of
technical debt. So why do we keep hearing that sentence?

\n
-- Gabor Szabo\n-- What does "If it ain't broke, don't fix it." really mean? (  )\n
\n

❯ fortune

\n

Psychoanalysis is that mental illness for which it regards itself a therapy.
– Karl Kraus

\n

❯ fortune

\n

With a rubber duck, one’s never alone.
– “The Hitchhiker’s Guide to the Galaxy”

\n

❯ fortune

\n

(Presuming for the sake of argument that it’s even possible to design
better code in Perl than in C. :-)
– Larry Wall on core code vs. module code design

\n

❯ fortune

\n

But, officer, he’s not drunk, I just saw his fingers twitch!

\n

❯ fortune

\n

Imbalance of power corrupts and monopoly of power corrupts absolutely.
– Genji

\n

❯ fortune

\n

I want a VEGETARIAN BURRITO to go … with EXTRA MSG!!

\n

❯ fortune

\n

如果你不知道如何表现地高人一等,找个Unix用户,让他做给你看。
– Dilbert newsletter 3.0, 1994

\n

❯ fortune

\n

“The picture’s pretty bleak, gentlemen… The world’s climates are changing,
the mammals are taking over, and we all have a brain about the size of a
walnut.”
– some dinosaurs from The Far Side, by Gary Larson

\n

❯ fortune

\n

Most of us feel that marketing types are like a dangerous weapon - keep
‘em unloaded and locked up in a cupboard, and only bring them out when
you need them to do a job.
– Craig Sanders

\n

❯ fortune

\n

不寐倦长更,披衣出户行。月寒秋竹冷,风切夜窗声。
– 李煜《三台令》

\n

❯ fortune

\n

子曰:“由,诲女知之乎!知之为知之,不知为不知,是知也。”
– 论语,为政篇

\n

❯ fortune

\n
    "The jig's up, Elman."\n    "Which jig?"\n            -- Jeff Elman\n
\n

❯ fortune

\n

“First things first – but not necessarily in that order”
– The Doctor, “Doctor Who”

\n

❯ fortune

\n

波影摇涟,趁熏风,一舸来时,翠阴清昼。去郭轩楹才数里,藓磴松关云岫。
快屐齿,筇枝先后。空半危亭堪聚远,看洞庭,缥缈争奇秀。人自老,景如旧。
来帆去棹还知否,问古今,几度斜阳,几番回首?晓色一川谁管领,都付雨荷烟柳,
知我者,燕朋鸥友。笑拍阑干呼范蠡,甚平吴,却倩垂纶手?吁万古,付卮酒。
– 周密《长亭怨慢》

\n

❯ fortune

\n

I’m telling you that the kernel is stable not because it’s a kernel,
but because I refuse to listen to arguments like this.
– Linus Torvalds

\n

❯ fortune

\n

A girl’s conscience doesn’t really keep her from doing anything wrong–
it merely keeps her from enjoying it.

\n

❯ fortune

\n

There are few people more often in the wrong than those who cannot endure
to be thought so.

\n

❯ fortune

\n

One does not thank logic.
– Sarek, “Journey to Babel”, stardate 3842.4

\n

❯ fortune

\n

Free yourself from negative influence. Negative thoughts are the old
habits that gnaw at the roots of the soul.
Moses Shongo, (Seneca)

\n

❯ fortune

\n

日月照之何不及此?惟有北风号怒天上来。
– 李白《北风行》

\n

❯ fortune

\n

Anyone who is capable of getting themselves made President should on no
account be allowed to do the job.
– Douglas Adams, “The Hitchhiker’s Guide to the Galaxy”

\n

And here are my mods:-)

\n

❯ pacman -Qs fortune
local/cowfortune 0.1.2-8
Configurable fortune cookie proclaiming cow (and a few other creatures)
local/fortune-mod 3.22.0-1.1
The Fortune Cookie Program from BSD games
local/fortune-mod-zh 2.98-1 (fortune-mods)
Chinese poems for fortune-mod

\n","excerpt":"","more":"

Keep peace of mind with fortune…

\n

❯ fortune

\n

The nice thing about standards is that there are so many of them to choose
from.
– Andrew S. Tanenbaum

\n

❯ fortune

\n

看蓬门秋草,年年破巷,疏窗细雨,夜夜孤灯。
– 郑板桥《沁园春·恨》

\n

❯ fortune

\n

An alcoholic is someone you don’t like who drinks as much as you do.
– Dylan Thomas

\n

❯ fortune

\n

If you put tomfoolery into a computer, nothing comes out but tomfoolery.
But this tomfoolery, having passed through a very expensive machine,
is somehow ennobled and no-one dare criticise it.
– Pierre Gallois

\n

❯ fortune

\n

秋色渐将晚,霜信报黄花。
– 叶梦得《水调歌头·秋色渐将晚》

\n

❯ fortune

\n

水精帘里颇黎枕,暖香惹梦鸳鸯锦。江上柳如烟,雁飞残月天。
藕丝秋色浅,人胜参差剪。双鬓隔香红,玉钗头上风。
– 温庭筠《菩萨蛮》

\n

❯ fortune

\n
    A disciple of another sect once came to Drescher as he was eating\n
\n

his morning meal. “I would like to give you this personality test”, said
the outsider, “because I want you to be happy.”
Drescher took the paper that was offered him and put it into the
toaster – “I wish the toaster to be happy too”.

\n

❯ fortune

\n

Example is not the main thing in influencing others. It is the only thing.
– Albert Schweitzer

\n

❯ fortune

\n

We have art that we do not die of the truth.
– Nietzsche

\n

❯ fortune

\n

“Java for the COBOL Programmer”
who writes these things?
people on crack
and cobol programmers
:)
that’s redundant.

\n

❯ fortune

\n

I was in this prematurely air conditioned supermarket and there were all
these aisles and there were these bathing caps you could buy that had these
kind of Fourth of July plumes on them that were red and yellow and blue and
I wasn’t tempted to buy one but I was reminded of the fact that I had been
avoiding the beach.
– Lucinda Childs “Einstein On The Beach”

\n

❯ fortune

\n

It doesn’t matter whether you win or lose – until you lose.

\n

❯ fortune

\n

True leadership is the art of changing a group from what it is to what
it ought to be.
– Virginia Allan

\n

❯ fortune

\n

I keep hearing and reading this nice proverb if it ain’t broke, don’t fix
it
. The latest appearance was in response to Shlomi
Fish
suggesting
that some Ancient Perl code should be replaced by Modern Perl code.

\n

I am not saying that every piece of code should be rewritten every 6 months,
but in my understanding that sentence actually translates to let’s wait till
it breaks and then panic
.

\n

I think people who say that sentence are afraid that the new version will
break something. Sure, there is always a chance that a change introduces an
error, but, if we are afraid to touch the code, what will happen when later on
we encounter a case where it does not work? For example, if we need to use it
in a new environment. Will we have the courage to change the code then? How
much will it cost in money, time, and lost sleep?

\n

I think we have been trying to teach ourselves that we should have really good
test coverage of our code and then we can easily refactor it and get rid of
technical debt. So why do we keep hearing that sentence?

\n
-- Gabor Szabo\n-- What does "If it ain't broke, don't fix it." really mean? (  )\n
\n

❯ fortune

\n

Psychoanalysis is that mental illness for which it regards itself a therapy.
– Karl Kraus

\n

❯ fortune

\n

With a rubber duck, one’s never alone.
– “The Hitchhiker’s Guide to the Galaxy”

\n

❯ fortune

\n

(Presuming for the sake of argument that it’s even possible to design
better code in Perl than in C. :-)
– Larry Wall on core code vs. module code design

\n

❯ fortune

\n

But, officer, he’s not drunk, I just saw his fingers twitch!

\n

❯ fortune

\n

Imbalance of power corrupts and monopoly of power corrupts absolutely.
– Genji

\n

❯ fortune

\n

I want a VEGETARIAN BURRITO to go … with EXTRA MSG!!

\n

❯ fortune

\n

如果你不知道如何表现地高人一等,找个Unix用户,让他做给你看。
– Dilbert newsletter 3.0, 1994

\n

❯ fortune

\n

“The picture’s pretty bleak, gentlemen… The world’s climates are changing,
the mammals are taking over, and we all have a brain about the size of a
walnut.”
– some dinosaurs from The Far Side, by Gary Larson

\n

❯ fortune

\n

Most of us feel that marketing types are like a dangerous weapon - keep
‘em unloaded and locked up in a cupboard, and only bring them out when
you need them to do a job.
– Craig Sanders

\n

❯ fortune

\n

不寐倦长更,披衣出户行。月寒秋竹冷,风切夜窗声。
– 李煜《三台令》

\n

❯ fortune

\n

子曰:“由,诲女知之乎!知之为知之,不知为不知,是知也。”
– 论语,为政篇

\n

❯ fortune

\n
    "The jig's up, Elman."\n    "Which jig?"\n            -- Jeff Elman\n
\n

❯ fortune

\n

“First things first – but not necessarily in that order”
– The Doctor, “Doctor Who”

\n

❯ fortune

\n

波影摇涟,趁熏风,一舸来时,翠阴清昼。去郭轩楹才数里,藓磴松关云岫。
快屐齿,筇枝先后。空半危亭堪聚远,看洞庭,缥缈争奇秀。人自老,景如旧。
来帆去棹还知否,问古今,几度斜阳,几番回首?晓色一川谁管领,都付雨荷烟柳,
知我者,燕朋鸥友。笑拍阑干呼范蠡,甚平吴,却倩垂纶手?吁万古,付卮酒。
– 周密《长亭怨慢》

\n

❯ fortune

\n

I’m telling you that the kernel is stable not because it’s a kernel,
but because I refuse to listen to arguments like this.
– Linus Torvalds

\n

❯ fortune

\n

A girl’s conscience doesn’t really keep her from doing anything wrong–
it merely keeps her from enjoying it.

\n

❯ fortune

\n

There are few people more often in the wrong than those who cannot endure
to be thought so.

\n

❯ fortune

\n

One does not thank logic.
– Sarek, “Journey to Babel”, stardate 3842.4

\n

❯ fortune

\n

Free yourself from negative influence. Negative thoughts are the old
habits that gnaw at the roots of the soul.
Moses Shongo, (Seneca)

\n

❯ fortune

\n

日月照之何不及此?惟有北风号怒天上来。
– 李白《北风行》

\n

❯ fortune

\n

Anyone who is capable of getting themselves made President should on no
account be allowed to do the job.
– Douglas Adams, “The Hitchhiker’s Guide to the Galaxy”

\n

And here are my mods:-)

\n

❯ pacman -Qs fortune
local/cowfortune 0.1.2-8
Configurable fortune cookie proclaiming cow (and a few other creatures)
local/fortune-mod 3.22.0-1.1
The Fortune Cookie Program from BSD games
local/fortune-mod-zh 2.98-1 (fortune-mods)
Chinese poems for fortune-mod

\n"},{"title":"GCC15 函数指针类型检查可能导致编译错误","date":"2025-05-23T13:49:02.000Z","_content":"\n今天准备继续做一下OS课的实验,实验代码当然是基于著名的xv6项目,意外地发现原本明明正常运行的系统再次构建时却失败了,具体报错如下:\n\n```GCC\nuser/usertests.c:2598:4: error: initialization of 'void (*)(char *)' from incompatible pointer type 'void (*)(void)' [-Wincompatible-pointer-types]\n 2598 | {rwsbrk, \"rwsbrk\" },\n | ^~~~~~\nuser/usertests.c:2598:4: note: (near initialization for 'quicktests[5].f')\nuser/usertests.c:247:1: note: 'rwsbrk' declared here\n 247 | rwsbrk()\n | ^~~~~~\nmake: *** [<内置>:user/usertests.o] 错误 1\n\n```\n而具体定位到的代码则是:\n```C\nstruct test {\n void (*f)(char *);\n char *s;\n} quicktests[] = {\n {copyin, \"copyin\"},\n {copyout, \"copyout\"},\n {copyinstr1, \"copyinstr1\"},\n {copyinstr2, \"copyinstr2\"},\n {copyinstr3, \"copyinstr3\"},\n {rwsbrk, \"rwsbrk\" },\n ...\n { 0, 0},\n};\n// See if the kernel refuses to read/write user memory that the\n// application doesn't have anymore, because it returned it.\nvoid\nrwsbrk()\n{\n int fd, n;\n \n uint64 a = (uint64) sbrk(8192);\n\n if(a == 0xffffffffffffffffLL) {\n printf(\"sbrk(rwsbrk) failed\\n\");\n exit(1);\n }\n ...\n exit(0);\n}\n```\n\n这段代码定义了一个函数指针数组,其中每个元素都指向一个函数和它的名称。结构体期望的函数签名是 `void(*)(char*)`,但传入的是一个没有参数的函数 `void(*)(void)`,这会导致函数指针类型不匹配。\n\n但是之前明明是没有问题的怎么现在突然出错呢?仔细一想立刻就怀疑是之前arch更新的时候把gcc升上去导致的,结果查了下大概确实是这样。\n\n- 在 **GCC 14 及之前版本**,这类赋值虽然不符合 C 标准,但 GCC 作为扩展允许这种行为。\n- 从 **GCC 15 开始**,这类隐式转换默认触发编译错误或警告(取决于编译器配置),因为 GCC 变得更加严格地遵循标准 C 的函数指针类型规则。\n\n这并不是 xv6 本身的 bug,而是 GCC 编译器对语言规范支持的更新带来的兼容性问题。解决方法倒是简单,直接给`rwsbrk`函数添加参数改为`void rwsbrk(char* arg)`即可解决,强制类型转换也是一种办法。这次事件再次告诉我们什么都升到最新并不是什么好事情,arch用户总是要花时间处理一些莫名其妙的兼容性问题:-)\n\n\n","source":"_posts/gcc15restrict.md","raw":"---\ntitle: GCC15 函数指针类型检查可能导致编译错误\ndate: 2025-05-23 21:49:02\ntags: 技术\n---\n\n今天准备继续做一下OS课的实验,实验代码当然是基于著名的xv6项目,意外地发现原本明明正常运行的系统再次构建时却失败了,具体报错如下:\n\n```GCC\nuser/usertests.c:2598:4: error: initialization of 'void (*)(char *)' from incompatible pointer type 'void (*)(void)' [-Wincompatible-pointer-types]\n 2598 | {rwsbrk, \"rwsbrk\" },\n | ^~~~~~\nuser/usertests.c:2598:4: note: (near initialization for 'quicktests[5].f')\nuser/usertests.c:247:1: note: 'rwsbrk' declared here\n 247 | rwsbrk()\n | ^~~~~~\nmake: *** [<内置>:user/usertests.o] 错误 1\n\n```\n而具体定位到的代码则是:\n```C\nstruct test {\n void (*f)(char *);\n char *s;\n} quicktests[] = {\n {copyin, \"copyin\"},\n {copyout, \"copyout\"},\n {copyinstr1, \"copyinstr1\"},\n {copyinstr2, \"copyinstr2\"},\n {copyinstr3, \"copyinstr3\"},\n {rwsbrk, \"rwsbrk\" },\n ...\n { 0, 0},\n};\n// See if the kernel refuses to read/write user memory that the\n// application doesn't have anymore, because it returned it.\nvoid\nrwsbrk()\n{\n int fd, n;\n \n uint64 a = (uint64) sbrk(8192);\n\n if(a == 0xffffffffffffffffLL) {\n printf(\"sbrk(rwsbrk) failed\\n\");\n exit(1);\n }\n ...\n exit(0);\n}\n```\n\n这段代码定义了一个函数指针数组,其中每个元素都指向一个函数和它的名称。结构体期望的函数签名是 `void(*)(char*)`,但传入的是一个没有参数的函数 `void(*)(void)`,这会导致函数指针类型不匹配。\n\n但是之前明明是没有问题的怎么现在突然出错呢?仔细一想立刻就怀疑是之前arch更新的时候把gcc升上去导致的,结果查了下大概确实是这样。\n\n- 在 **GCC 14 及之前版本**,这类赋值虽然不符合 C 标准,但 GCC 作为扩展允许这种行为。\n- 从 **GCC 15 开始**,这类隐式转换默认触发编译错误或警告(取决于编译器配置),因为 GCC 变得更加严格地遵循标准 C 的函数指针类型规则。\n\n这并不是 xv6 本身的 bug,而是 GCC 编译器对语言规范支持的更新带来的兼容性问题。解决方法倒是简单,直接给`rwsbrk`函数添加参数改为`void rwsbrk(char* arg)`即可解决,强制类型转换也是一种办法。这次事件再次告诉我们什么都升到最新并不是什么好事情,arch用户总是要花时间处理一些莫名其妙的兼容性问题:-)\n\n\n","slug":"gcc15restrict","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblw0019idd05a1ph9vo","content":"

今天准备继续做一下OS课的实验,实验代码当然是基于著名的xv6项目,意外地发现原本明明正常运行的系统再次构建时却失败了,具体报错如下:

\n
1
2
3
4
5
6
7
8
9
user/usertests.c:2598:4: error: initialization of 'void (*)(char *)' from incompatible pointer type 'void (*)(void)' [-Wincompatible-pointer-types]
2598 | {rwsbrk, "rwsbrk" },
| ^~~~~~
user/usertests.c:2598:4: note: (near initialization for 'quicktests[5].f')
user/usertests.c:247:1: note: 'rwsbrk' declared here
247 | rwsbrk()
| ^~~~~~
make: *** [<内置>:user/usertests.o] 错误 1

\n

而具体定位到的代码则是:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct test {
void (*f)(char *);
char *s;
} quicktests[] = {
{copyin, "copyin"},
{copyout, "copyout"},
{copyinstr1, "copyinstr1"},
{copyinstr2, "copyinstr2"},
{copyinstr3, "copyinstr3"},
{rwsbrk, "rwsbrk" },
...
{ 0, 0},
};
// See if the kernel refuses to read/write user memory that the
// application doesn't have anymore, because it returned it.
void
rwsbrk()
{
int fd, n;

uint64 a = (uint64) sbrk(8192);

if(a == 0xffffffffffffffffLL) {
printf("sbrk(rwsbrk) failed\\n");
exit(1);
}
...
exit(0);
}
\n\n

这段代码定义了一个函数指针数组,其中每个元素都指向一个函数和它的名称。结构体期望的函数签名是 void(*)(char*),但传入的是一个没有参数的函数 void(*)(void),这会导致函数指针类型不匹配。

\n

但是之前明明是没有问题的怎么现在突然出错呢?仔细一想立刻就怀疑是之前arch更新的时候把gcc升上去导致的,结果查了下大概确实是这样。

\n
    \n
  • GCC 14 及之前版本,这类赋值虽然不符合 C 标准,但 GCC 作为扩展允许这种行为。
  • \n
  • GCC 15 开始,这类隐式转换默认触发编译错误或警告(取决于编译器配置),因为 GCC 变得更加严格地遵循标准 C 的函数指针类型规则。
  • \n
\n

这并不是 xv6 本身的 bug,而是 GCC 编译器对语言规范支持的更新带来的兼容性问题。解决方法倒是简单,直接给rwsbrk函数添加参数改为void rwsbrk(char* arg)即可解决,强制类型转换也是一种办法。这次事件再次告诉我们什么都升到最新并不是什么好事情,arch用户总是要花时间处理一些莫名其妙的兼容性问题:-)

\n","excerpt":"","more":"

今天准备继续做一下OS课的实验,实验代码当然是基于著名的xv6项目,意外地发现原本明明正常运行的系统再次构建时却失败了,具体报错如下:

\n
1
2
3
4
5
6
7
8
9
user/usertests.c:2598:4: error: initialization of 'void (*)(char *)' from incompatible pointer type 'void (*)(void)' [-Wincompatible-pointer-types]
2598 | {rwsbrk, "rwsbrk" },
| ^~~~~~
user/usertests.c:2598:4: note: (near initialization for 'quicktests[5].f')
user/usertests.c:247:1: note: 'rwsbrk' declared here
247 | rwsbrk()
| ^~~~~~
make: *** [<内置>:user/usertests.o] 错误 1

\n

而具体定位到的代码则是:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct test {
void (*f)(char *);
char *s;
} quicktests[] = {
{copyin, "copyin"},
{copyout, "copyout"},
{copyinstr1, "copyinstr1"},
{copyinstr2, "copyinstr2"},
{copyinstr3, "copyinstr3"},
{rwsbrk, "rwsbrk" },
...
{ 0, 0},
};
// See if the kernel refuses to read/write user memory that the
// application doesn't have anymore, because it returned it.
void
rwsbrk()
{
int fd, n;

uint64 a = (uint64) sbrk(8192);

if(a == 0xffffffffffffffffLL) {
printf("sbrk(rwsbrk) failed\\n");
exit(1);
}
...
exit(0);
}
\n\n

这段代码定义了一个函数指针数组,其中每个元素都指向一个函数和它的名称。结构体期望的函数签名是 void(*)(char*),但传入的是一个没有参数的函数 void(*)(void),这会导致函数指针类型不匹配。

\n

但是之前明明是没有问题的怎么现在突然出错呢?仔细一想立刻就怀疑是之前arch更新的时候把gcc升上去导致的,结果查了下大概确实是这样。

\n
    \n
  • GCC 14 及之前版本,这类赋值虽然不符合 C 标准,但 GCC 作为扩展允许这种行为。
  • \n
  • GCC 15 开始,这类隐式转换默认触发编译错误或警告(取决于编译器配置),因为 GCC 变得更加严格地遵循标准 C 的函数指针类型规则。
  • \n
\n

这并不是 xv6 本身的 bug,而是 GCC 编译器对语言规范支持的更新带来的兼容性问题。解决方法倒是简单,直接给rwsbrk函数添加参数改为void rwsbrk(char* arg)即可解决,强制类型转换也是一种办法。这次事件再次告诉我们什么都升到最新并不是什么好事情,arch用户总是要花时间处理一些莫名其妙的兼容性问题:-)

\n"},{"title":"时隔一年再次拿起数位板能画出什么东西","date":"2024-11-04T15:47:54.000Z","_content":"\n如题,前段时间推完LOOPERS的时候有感而发对着画了张海报\n\n时隔一年,终于又拿起了数位板\n\n![LOOPERS](/images/20241027_222225.png \"LOOPERS\")\n","source":"_posts/loopers.md","raw":"---\ntitle: 时隔一年再次拿起数位板能画出什么东西\ndate: 2024-11-04 23:47:54\ntags: [板绘, 生活]\n---\n\n如题,前段时间推完LOOPERS的时候有感而发对着画了张海报\n\n时隔一年,终于又拿起了数位板\n\n![LOOPERS](/images/20241027_222225.png \"LOOPERS\")\n","slug":"loopers","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblw001cidd0aajv1hpn","content":"

如题,前段时间推完LOOPERS的时候有感而发对着画了张海报

\n

时隔一年,终于又拿起了数位板

\n

\"LOOPERS\"

\n","excerpt":"","more":"

如题,前段时间推完LOOPERS的时候有感而发对着画了张海报

\n

时隔一年,终于又拿起了数位板

\n

\"LOOPERS\"

\n"},{"title":"愿指引明路的苍蓝星永远为你闪耀","date":"2024-11-11T13:46:49.000Z","_content":"使用东方Project软音源THFont简单重置的MHWI主题曲\n\n雄关漫道真如铁,而今迈步从头越\n\n{% raw %}\n\n{% endraw %}\n\n","source":"_posts/mhwi.md","raw":"---\ntitle: 愿指引明路的苍蓝星永远为你闪耀\ndate: 2024-11-11 21:46:49\ntags: 音乐\ncategories: Rearrangement\n---\n使用东方Project软音源THFont简单重置的MHWI主题曲\n\n雄关漫道真如铁,而今迈步从头越\n\n{% raw %}\n\n{% endraw %}\n\n","slug":"mhwi","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblx001fidd07h7dbl8c","content":"

使用东方Project软音源THFont简单重置的MHWI主题曲

\n

雄关漫道真如铁,而今迈步从头越

\n\n\n\n\n","excerpt":"","more":"

使用东方Project软音源THFont简单重置的MHWI主题曲

\n

雄关漫道真如铁,而今迈步从头越

\n\n\n\n\n"},{"title":"Krita 为什么是神?","date":"2025-07-13T11:46:39.000Z","_content":"\n![神的官网](/images/krita-official.png)\n\n在谈论这个问题之前,我想先说说其他软件相较于 [Krita](https://krita.org/en/) 究竟差在了哪里。\n\n---\n\n### 首先是犯下傲慢之罪的 Adobe Photoshop\n\n依靠其在商业设计领域的绝对霸主地位赢来的市场,却不愿真正听取数字绘画创作者的声音!这种傲慢的软件注定无法触及绘画的灵魂。事实也是如此,因为高昂的订阅费用和臃肿的体量,以及对触摸屏设备支持的迟滞,Photoshop 在许多数字画师心中的地位一直在走下坡路,最终沦为商业生产的工具而非艺术创作的乐园。与之相比,**神** 就很谦卑,它免费开源,轻巧灵活,全平台支持,即便在某些方面为了兼容性和稳定性而做出妥协,那也是 **神** 留给凡人的慈悲。**神** 不是不能做到像 PS 那样无所不能,只是不想通过太完美的表现让“友商”绝望所以故意不用。可笑有些人不理解 **神** 的良苦用心,竟然还用其某些非核心功能上的不足来讥讽 **神**,我劝你们好自为之。\n\n---\n\n### 然后是犯下愤怒之罪的 GIMP\n\n因为 **神** 在数字绘画领域的崛起,GIMP 就一直对神记恨在心,甚至固守其“万能”的图像处理定位,对绘画功能上的迭代迟迟不肯投入资源。这无疑违背了 **神** 在《新约》中的一句话:“**Krita** 只为创作者而生!”**神** 当然也不能容忍其他开源软件对绘画创作的敷衍,于是 **神** 出动了它的“代码大军”降下了神罚,GIMP 在绘画圈的风评一蹶不振,即便有诸多插件以及强势归来的3.0大版本更新也无法挽回其作为“绘画软件”使用时的颓势。\n\n---\n\n### 接着是犯下懒惰之罪的 Procreate\n\n自以为有 iPad 的独占优势和流畅的笔触就了不起了,对图层管理、多文档支持以及跨平台协作就一直摆烂。另外大家有所不知,其实在 Procreate 首次发布前夕,其开发者闭上眼睛做冥想的时候,其脑海中看见的画面,正是站在光芒之中的 **神**。那时神告诉他:“你只可到这里,不可越过。”然而,**神** 的劝说不但没有让 Procreate 迷途知返,竟然还敢在移动设备上挑战神的威严,妄图通过封闭的生态垄断市场。于是 **神** 降下了他的惩罚,Procreate 在专业性上身败名裂,其以流畅为名的笔刷也被 **神** 在笔刷引擎上轻松超越,iPad Pro 独占的优势也没能阻止 **神** 在更广阔的平台生根发芽,粉丝也成为了众人口中的“果曲是这样的”。\n\n---\n\n### 再然后是犯下了嫉妒之罪的 SAI\n\n屡次被 **神** 击败,但口服心不服的 SAI,暗中嫉妒着 **神** ,甚至胆敢当众对 **神** 说出“你才是挑战者”这种话。于是,在含金量最高的活动上,即专业画师的日常工作中,SAI 被 **神** 正面击溃。它那单一的笔刷类型和有限的功能,在 **神** 面前显得如此苍白无力。不过,念在 SAI 最终皈依了 **神** 的教,并且以其轻量和独特的抖动修正功能,在某些特定绘画风格上仍有信徒,**神** 应许了它在特定小众市场的一切。\n\n---\n\n### 最后犯下贪婪之罪的 Clip Studio Paint\n\n只是初出茅庐,Clip Studio Paint 就夺得了漫画创作软件的市场份额,而这,自然是因为 **神** 的应允。**神** 本来钦定它为自己在漫画领域的代言人,在自己更专注于绘画自由之后替自己继续讨伐那些缺乏专业性的绘画工具,为此甚至不惜亲自为 Clip Studio Paint 铺路,主动减少自己在漫画素材库上的投入,将一切荣耀都归给他。但最后 Clip Studio Paint 却被利益蒙蔽了双眼,不再以践行 **神** 的理想为自己的理想,也不再聆听 **神** 的教诲,甚至自以为荣光已经超过了 **神**。于是 **神** 降下了他的惩罚,Clip Studio Paint 从此订阅制饱受诟病,其3D模型功能再也不能在线上教学中占据优势,其引以为傲的漫画分镜功能再也不能强势压制对手,最终市场被 **神** 在免费开源和强大功能上的结合所夺舍。\n\n// 都很好用口牙,本文仅供娱乐\n\n![老八可爱捏](/images/yana.jpg)\n","source":"_posts/kritabest.md","raw":"---\ntitle: Krita 为什么是神?\ndate: 2025-07-13 19:46:39\ntags: [生活]\n---\n\n![神的官网](/images/krita-official.png)\n\n在谈论这个问题之前,我想先说说其他软件相较于 [Krita](https://krita.org/en/) 究竟差在了哪里。\n\n---\n\n### 首先是犯下傲慢之罪的 Adobe Photoshop\n\n依靠其在商业设计领域的绝对霸主地位赢来的市场,却不愿真正听取数字绘画创作者的声音!这种傲慢的软件注定无法触及绘画的灵魂。事实也是如此,因为高昂的订阅费用和臃肿的体量,以及对触摸屏设备支持的迟滞,Photoshop 在许多数字画师心中的地位一直在走下坡路,最终沦为商业生产的工具而非艺术创作的乐园。与之相比,**神** 就很谦卑,它免费开源,轻巧灵活,全平台支持,即便在某些方面为了兼容性和稳定性而做出妥协,那也是 **神** 留给凡人的慈悲。**神** 不是不能做到像 PS 那样无所不能,只是不想通过太完美的表现让“友商”绝望所以故意不用。可笑有些人不理解 **神** 的良苦用心,竟然还用其某些非核心功能上的不足来讥讽 **神**,我劝你们好自为之。\n\n---\n\n### 然后是犯下愤怒之罪的 GIMP\n\n因为 **神** 在数字绘画领域的崛起,GIMP 就一直对神记恨在心,甚至固守其“万能”的图像处理定位,对绘画功能上的迭代迟迟不肯投入资源。这无疑违背了 **神** 在《新约》中的一句话:“**Krita** 只为创作者而生!”**神** 当然也不能容忍其他开源软件对绘画创作的敷衍,于是 **神** 出动了它的“代码大军”降下了神罚,GIMP 在绘画圈的风评一蹶不振,即便有诸多插件以及强势归来的3.0大版本更新也无法挽回其作为“绘画软件”使用时的颓势。\n\n---\n\n### 接着是犯下懒惰之罪的 Procreate\n\n自以为有 iPad 的独占优势和流畅的笔触就了不起了,对图层管理、多文档支持以及跨平台协作就一直摆烂。另外大家有所不知,其实在 Procreate 首次发布前夕,其开发者闭上眼睛做冥想的时候,其脑海中看见的画面,正是站在光芒之中的 **神**。那时神告诉他:“你只可到这里,不可越过。”然而,**神** 的劝说不但没有让 Procreate 迷途知返,竟然还敢在移动设备上挑战神的威严,妄图通过封闭的生态垄断市场。于是 **神** 降下了他的惩罚,Procreate 在专业性上身败名裂,其以流畅为名的笔刷也被 **神** 在笔刷引擎上轻松超越,iPad Pro 独占的优势也没能阻止 **神** 在更广阔的平台生根发芽,粉丝也成为了众人口中的“果曲是这样的”。\n\n---\n\n### 再然后是犯下了嫉妒之罪的 SAI\n\n屡次被 **神** 击败,但口服心不服的 SAI,暗中嫉妒着 **神** ,甚至胆敢当众对 **神** 说出“你才是挑战者”这种话。于是,在含金量最高的活动上,即专业画师的日常工作中,SAI 被 **神** 正面击溃。它那单一的笔刷类型和有限的功能,在 **神** 面前显得如此苍白无力。不过,念在 SAI 最终皈依了 **神** 的教,并且以其轻量和独特的抖动修正功能,在某些特定绘画风格上仍有信徒,**神** 应许了它在特定小众市场的一切。\n\n---\n\n### 最后犯下贪婪之罪的 Clip Studio Paint\n\n只是初出茅庐,Clip Studio Paint 就夺得了漫画创作软件的市场份额,而这,自然是因为 **神** 的应允。**神** 本来钦定它为自己在漫画领域的代言人,在自己更专注于绘画自由之后替自己继续讨伐那些缺乏专业性的绘画工具,为此甚至不惜亲自为 Clip Studio Paint 铺路,主动减少自己在漫画素材库上的投入,将一切荣耀都归给他。但最后 Clip Studio Paint 却被利益蒙蔽了双眼,不再以践行 **神** 的理想为自己的理想,也不再聆听 **神** 的教诲,甚至自以为荣光已经超过了 **神**。于是 **神** 降下了他的惩罚,Clip Studio Paint 从此订阅制饱受诟病,其3D模型功能再也不能在线上教学中占据优势,其引以为傲的漫画分镜功能再也不能强势压制对手,最终市场被 **神** 在免费开源和强大功能上的结合所夺舍。\n\n// 都很好用口牙,本文仅供娱乐\n\n![老八可爱捏](/images/yana.jpg)\n","slug":"kritabest","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblx001hidd06c6f23wg","content":"

\"神的官网\"

\n

在谈论这个问题之前,我想先说说其他软件相较于 Krita 究竟差在了哪里。

\n
\n

首先是犯下傲慢之罪的 Adobe Photoshop

依靠其在商业设计领域的绝对霸主地位赢来的市场,却不愿真正听取数字绘画创作者的声音!这种傲慢的软件注定无法触及绘画的灵魂。事实也是如此,因为高昂的订阅费用和臃肿的体量,以及对触摸屏设备支持的迟滞,Photoshop 在许多数字画师心中的地位一直在走下坡路,最终沦为商业生产的工具而非艺术创作的乐园。与之相比, 就很谦卑,它免费开源,轻巧灵活,全平台支持,即便在某些方面为了兼容性和稳定性而做出妥协,那也是 留给凡人的慈悲。 不是不能做到像 PS 那样无所不能,只是不想通过太完美的表现让“友商”绝望所以故意不用。可笑有些人不理解 的良苦用心,竟然还用其某些非核心功能上的不足来讥讽 ,我劝你们好自为之。

\n
\n

然后是犯下愤怒之罪的 GIMP

因为 在数字绘画领域的崛起,GIMP 就一直对神记恨在心,甚至固守其“万能”的图像处理定位,对绘画功能上的迭代迟迟不肯投入资源。这无疑违背了 在《新约》中的一句话:“Krita 只为创作者而生!” 当然也不能容忍其他开源软件对绘画创作的敷衍,于是 出动了它的“代码大军”降下了神罚,GIMP 在绘画圈的风评一蹶不振,即便有诸多插件以及强势归来的3.0大版本更新也无法挽回其作为“绘画软件”使用时的颓势。

\n
\n

接着是犯下懒惰之罪的 Procreate

自以为有 iPad 的独占优势和流畅的笔触就了不起了,对图层管理、多文档支持以及跨平台协作就一直摆烂。另外大家有所不知,其实在 Procreate 首次发布前夕,其开发者闭上眼睛做冥想的时候,其脑海中看见的画面,正是站在光芒之中的 。那时神告诉他:“你只可到这里,不可越过。”然而, 的劝说不但没有让 Procreate 迷途知返,竟然还敢在移动设备上挑战神的威严,妄图通过封闭的生态垄断市场。于是 降下了他的惩罚,Procreate 在专业性上身败名裂,其以流畅为名的笔刷也被 在笔刷引擎上轻松超越,iPad Pro 独占的优势也没能阻止 在更广阔的平台生根发芽,粉丝也成为了众人口中的“果曲是这样的”。

\n
\n

再然后是犯下了嫉妒之罪的 SAI

屡次被 击败,但口服心不服的 SAI,暗中嫉妒着 ,甚至胆敢当众对 说出“你才是挑战者”这种话。于是,在含金量最高的活动上,即专业画师的日常工作中,SAI 被 正面击溃。它那单一的笔刷类型和有限的功能,在 面前显得如此苍白无力。不过,念在 SAI 最终皈依了 的教,并且以其轻量和独特的抖动修正功能,在某些特定绘画风格上仍有信徒, 应许了它在特定小众市场的一切。

\n
\n

最后犯下贪婪之罪的 Clip Studio Paint

只是初出茅庐,Clip Studio Paint 就夺得了漫画创作软件的市场份额,而这,自然是因为 的应允。 本来钦定它为自己在漫画领域的代言人,在自己更专注于绘画自由之后替自己继续讨伐那些缺乏专业性的绘画工具,为此甚至不惜亲自为 Clip Studio Paint 铺路,主动减少自己在漫画素材库上的投入,将一切荣耀都归给他。但最后 Clip Studio Paint 却被利益蒙蔽了双眼,不再以践行 的理想为自己的理想,也不再聆听 的教诲,甚至自以为荣光已经超过了 。于是 降下了他的惩罚,Clip Studio Paint 从此订阅制饱受诟病,其3D模型功能再也不能在线上教学中占据优势,其引以为傲的漫画分镜功能再也不能强势压制对手,最终市场被 在免费开源和强大功能上的结合所夺舍。

\n

// 都很好用口牙,本文仅供娱乐

\n

\"老八可爱捏\"

\n","excerpt":"","more":"

\"神的官网\"

\n

在谈论这个问题之前,我想先说说其他软件相较于 Krita 究竟差在了哪里。

\n
\n

首先是犯下傲慢之罪的 Adobe Photoshop

依靠其在商业设计领域的绝对霸主地位赢来的市场,却不愿真正听取数字绘画创作者的声音!这种傲慢的软件注定无法触及绘画的灵魂。事实也是如此,因为高昂的订阅费用和臃肿的体量,以及对触摸屏设备支持的迟滞,Photoshop 在许多数字画师心中的地位一直在走下坡路,最终沦为商业生产的工具而非艺术创作的乐园。与之相比, 就很谦卑,它免费开源,轻巧灵活,全平台支持,即便在某些方面为了兼容性和稳定性而做出妥协,那也是 留给凡人的慈悲。 不是不能做到像 PS 那样无所不能,只是不想通过太完美的表现让“友商”绝望所以故意不用。可笑有些人不理解 的良苦用心,竟然还用其某些非核心功能上的不足来讥讽 ,我劝你们好自为之。

\n
\n

然后是犯下愤怒之罪的 GIMP

因为 在数字绘画领域的崛起,GIMP 就一直对神记恨在心,甚至固守其“万能”的图像处理定位,对绘画功能上的迭代迟迟不肯投入资源。这无疑违背了 在《新约》中的一句话:“Krita 只为创作者而生!” 当然也不能容忍其他开源软件对绘画创作的敷衍,于是 出动了它的“代码大军”降下了神罚,GIMP 在绘画圈的风评一蹶不振,即便有诸多插件以及强势归来的3.0大版本更新也无法挽回其作为“绘画软件”使用时的颓势。

\n
\n

接着是犯下懒惰之罪的 Procreate

自以为有 iPad 的独占优势和流畅的笔触就了不起了,对图层管理、多文档支持以及跨平台协作就一直摆烂。另外大家有所不知,其实在 Procreate 首次发布前夕,其开发者闭上眼睛做冥想的时候,其脑海中看见的画面,正是站在光芒之中的 。那时神告诉他:“你只可到这里,不可越过。”然而, 的劝说不但没有让 Procreate 迷途知返,竟然还敢在移动设备上挑战神的威严,妄图通过封闭的生态垄断市场。于是 降下了他的惩罚,Procreate 在专业性上身败名裂,其以流畅为名的笔刷也被 在笔刷引擎上轻松超越,iPad Pro 独占的优势也没能阻止 在更广阔的平台生根发芽,粉丝也成为了众人口中的“果曲是这样的”。

\n
\n

再然后是犯下了嫉妒之罪的 SAI

屡次被 击败,但口服心不服的 SAI,暗中嫉妒着 ,甚至胆敢当众对 说出“你才是挑战者”这种话。于是,在含金量最高的活动上,即专业画师的日常工作中,SAI 被 正面击溃。它那单一的笔刷类型和有限的功能,在 面前显得如此苍白无力。不过,念在 SAI 最终皈依了 的教,并且以其轻量和独特的抖动修正功能,在某些特定绘画风格上仍有信徒, 应许了它在特定小众市场的一切。

\n
\n

最后犯下贪婪之罪的 Clip Studio Paint

只是初出茅庐,Clip Studio Paint 就夺得了漫画创作软件的市场份额,而这,自然是因为 的应允。 本来钦定它为自己在漫画领域的代言人,在自己更专注于绘画自由之后替自己继续讨伐那些缺乏专业性的绘画工具,为此甚至不惜亲自为 Clip Studio Paint 铺路,主动减少自己在漫画素材库上的投入,将一切荣耀都归给他。但最后 Clip Studio Paint 却被利益蒙蔽了双眼,不再以践行 的理想为自己的理想,也不再聆听 的教诲,甚至自以为荣光已经超过了 。于是 降下了他的惩罚,Clip Studio Paint 从此订阅制饱受诟病,其3D模型功能再也不能在线上教学中占据优势,其引以为傲的漫画分镜功能再也不能强势压制对手,最终市场被 在免费开源和强大功能上的结合所夺舍。

\n

// 都很好用口牙,本文仅供娱乐

\n

\"老八可爱捏\"

\n"},{"title":"高地特供版CSAPP Bomb Lab全流程攻略","date":"2025-02-24T07:09:11.000Z","_content":"\n这篇文章记录高地CSAPP课程Bomblab实验操作流程,仅供参考交流(答案是随机生成的和学号相关)。\n\n笔者实验环境为Archlinux/CachyOS,使用lldb作为调试器(和gdb操作差不多),其余用到的工具主要为objdump,strings,neovim/helix和zellij,全程开源环境不使用IDA。\n\n## **Phase_1**\n\n### **静态分析**\n\n#### **`strings`扫描**\n```bash\nstrings bomb_linux\n```\n先用strings寻找可能与`phase_1`相关的字符串或函数名,运气好说不定能直接找到密码毕竟是第一题。\n![strings](/images/phase1_strings.png)\n - 结果没有明文密码无法直接秒掉第一问,可惜。\n - 但是找到`GenerateRandomString`函数可能与密码生成相关。\n\n#### **用`objdump`反汇编**\n```bash\nobjdump -d bomb_linux > bomb.asm\n```\n搜索`GenerateRandomString`和`phase_1`函数的汇编代码。\n ```assembly\n 401b53 :\n 401b53: endbr64\n 401b57: push %rbp\n 401b58: mov %rsp,%rbp\n 401b5b: sub $0x20,%rsp\n 401b5f: mov %rdi,-0x18(%rbp)\n 401b63: lea -0xb(%rbp),%rax\n 401b67: mov %rax,%rdi\n 401b6a: callq 401ac1 # 调用密码生成函数\n 401b6f: lea -0xb(%rbp),%rdx # 生成的字符串地址%rbp-0xb存入%rdx,即密码存储位置\n 401b73: mov -0x18(%rbp),%rax\n 401b77: mov %rdx,%rsi\n 401b7a: mov %rax,%rdi\n 401b7d: callq 401c0c # 调用字符串比较函数\n 401b82: test %eax,%eax\n 401b84: je 401b8d \n 401b86: callq 401d67 # 比较失败则引爆炸弹\n ```\n - `phase_1`调用`GenerateRandomString`生成一个字符串。\n - 用户输入的字符串需要与此生成的字符串完全匹配。\n\n---\n\n### **动态调试**\n![phase_1](/images/phase1.png)\n下面是phase_1求解的完整流程:\n```lldb\nlldb bomb_linux <你的学号后六位>\n(lldb) b phase_1 # 在phase_1入口断点\n(lldb) run # 从入口开始执行\n请输入第1级的密码:114514 # 随便输入触发断点\n(lldb) b 0x401b6f # 在GenerateRandomString返回后断点\n(lldb) continue # 继续执行\n(lldb) x/s $rbp - 0xb # 计算字符串地址(-0xb偏移量)\n0x7fffffffdaf5: \"mJHurpQZtY\" # 轻松拿下,这里是根据学号伪随机生成的哦\n```\n将得到的密码保存入bomb_<学号后六位>.txt即可,避免后续重复输入。\n\n---\n\n## **Phase_2**\n\n### **静态分析**\n\n这道题目还是比较一目了然的,观察`phase_2`代码不难发现其实构建了一张跳转表:\n```assembly\n0000000000401b8e :\n 401b8e:\tf3 0f 1e fa \tendbr64\n 401b92:\t55 \tpush %rbp\n 401b93:\t48 89 e5 \tmov %rsp,%rbp\n 401b96:\t48 83 ec 10 \tsub $0x10,%rsp\n 401b9a:\t48 89 7d f8 \tmov %rdi,-0x8(%rbp)\n 401b9e:\tbf 10 00 00 00 \tmov $0x10,%edi\n 401ba3:\te8 05 fb ff ff \tcall 4016ad \n 401ba8:\t48 8b 05 71 6c 00 00 \tmov 0x6c71(%rip),%rax # 408820 \n 401baf:\t48 83 f8 0f \tcmp $0xf,%rax\n 401bb3:\t0f 87 16 01 00 00 \tja 401ccf \n 401bb9:\t48 8d 14 85 00 00 00 \tlea 0x0(,%rax,4),%rdx\n 401bc0:\t00 \n 401bc1:\t48 8d 05 4c 4a 00 00 \tlea 0x4a4c(%rip),%rax # 406614 <_IO_stdin_used+0x614>\n 401bc8:\t8b 04 02 \tmov (%rdx,%rax,1),%eax\n 401bcb:\t48 98 \tcltq\n 401bcd:\t48 8d 15 40 4a 00 00 \tlea 0x4a40(%rip),%rdx # 406614 <_IO_stdin_used+0x614>\n 401bd4:\t48 01 d0 \tadd %rdx,%rax\n 401bd7:\t3e ff e0 \tnotrack jmp *%rax\n 401bda:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax\n 401bde:\t48 89 c7 \tmov %rax,%rdi\n 401be1:\te8 f2 00 00 00 \tcall 401cd8 \n 401be6:\te9 ea 00 00 00 \tjmp 401cd5 \n 401beb:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax\n 401bef:\t48 89 c7 \tmov %rax,%rdi\n 401bf2:\te8 8b 01 00 00 \tcall 401d82 \n 401bf7:\te9 d9 00 00 00 \tjmp 401cd5 \n 401bfc:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax\n 401c00:\t48 89 c7 \tmov %rax,%rdi\n ...\n```\n这里面需要注意的关键点是rand_div,它会决定你的跳转方向,而你的学号又决定了它的取值。然后是`GenerateRandomNumber`这个函数的原理需要了解一下,而这个函数将在跳转前后分别调用一次,第一次决定你的跳转方向,第二次则决定了你的密码线索。\n\n---\n\n### **动态调试**\n理解原理就没什么难度了,自己找几个断点打好然后关注一下`rand_div`的值就好,观察自己的学号向哪个函数跳转并理解相应函数计算即可,比如我这里向`phase_2_14`跳转:\n![phase_2_14](/images/phase_2_14.png)\n\n而除了`phase_2_14`还有其他函数也是非常好理解的,第二题依旧可以轻松拿下。\n\n---\n## **Phase_3**\n\n### **静态分析**\n\n和Phase_2一样开局先跳转尽可能防止同学们答案雷同互相帮助(bushi\n\n本体其实没有什么好说的,这里我跳转的方向是`Phase_3_5`简要解释一下可供参考:\n\n```assembly\n0000000000403001 :\n 403001:\tf3 0f 1e fa \tendbr64\n 403005:\t55 \tpush %rbp\n 403006:\t48 89 e5 \tmov %rsp,%rbp\n 403009:\t48 83 ec 20 \tsub $0x20,%rsp\n 40300d:\t48 89 7d e8 \tmov %rdi,-0x18(%rbp)\n 403011:\tc7 45 fc 00 00 00 00 \tmovl $0x0,-0x4(%rbp)\n 403018:\tc7 45 f8 00 00 00 00 \tmovl $0x0,-0x8(%rbp)\n 40301f:\t48 8d 4d f0 \tlea -0x10(%rbp),%rcx\n 403023:\t48 8d 55 f4 \tlea -0xc(%rbp),%rdx\n 403027:\t48 8b 45 e8 \tmov -0x18(%rbp),%rax\n 40302b:\t48 8d 35 5a 36 00 00 \tlea 0x365a(%rip),%rsi # 40668c <_IO_stdin_used+0x68c>\n 403032:\t48 89 c7 \tmov %rax,%rdi\n 403035:\tb8 00 00 00 00 \tmov $0x0,%eax\n 40303a:\te8 51 e1 ff ff \tcall 401190 <__isoc99_sscanf@plt>\n 40303f:\t89 45 f8 \tmov %eax,-0x8(%rbp)\n 403042:\t83 7d f8 01 \tcmpl $0x1,-0x8(%rbp)\n 403046:\t7f 05 \tjg 40304d \n 403048:\te8 a9 2b 00 00 \tcall 405bf6 \n 40304d:\tbf 08 00 00 00 \tmov $0x8,%edi\n 403052:\te8 56 e6 ff ff \tcall 4016ad \n 403057:\t8b 45 f4 \tmov -0xc(%rbp),%eax\n 40305a:\t48 63 d0 \tmovslq %eax,%rdx\n 40305d:\t48 8b 05 bc 57 00 00 \tmov 0x57bc(%rip),%rax # 408820 \n 403064:\t48 39 c2 \tcmp %rax,%rdx\n 403067:\t74 05 \tje 40306e \n 403069:\te8 88 2b 00 00 \tcall 405bf6 \n 40306e:\tbf c8 00 00 00 \tmov $0xc8,%edi\n 403073:\te8 35 e6 ff ff \tcall 4016ad \n 403078:\t8b 45 f4 \tmov -0xc(%rbp),%eax\n 40307b:\t83 f8 07 \tcmp $0x7,%eax\n 40307e:\t0f 87 eb 00 00 00 \tja 40316f \n 403084:\t89 c0 \tmov %eax,%eax\n 403086:\t48 8d 14 85 00 00 00 \tlea 0x0(,%rax,4),%rdx\n 40308d:\t00 \n 40308e:\t48 8d 05 9f 36 00 00 \tlea 0x369f(%rip),%rax # 406734 <_IO_stdin_used+0x734>\n 403095:\t8b 04 02 \tmov (%rdx,%rax,1),%eax\n 403098:\t48 98 \tcltq\n 40309a:\t48 8d 15 93 36 00 00 \tlea 0x3693(%rip),%rdx # 406734 <_IO_stdin_used+0x734>\n 4030a1:\t48 01 d0 \tadd %rdx,%rax\n 4030a4:\t3e ff e0 \tnotrack jmp *%rax\n 4030a7:\t48 8b 05 72 57 00 00 \tmov 0x5772(%rip),%rax # 408820 \n 4030ae:\t89 c2 \tmov %eax,%edx\n 4030b0:\t8b 45 fc \tmov -0x4(%rbp),%eax\n 4030b3:\t01 d0 \tadd %edx,%eax\n 4030b5:\t89 45 fc \tmov %eax,-0x4(%rbp)\n 4030b8:\tbf c8 00 00 00 \tmov $0xc8,%edi\n 4030bd:\te8 eb e5 ff ff \tcall 4016ad \n ...\n 403174:\t8b 45 f0 \tmov -0x10(%rbp),%eax\n 403177:\t39 45 fc \tcmp %eax,-0x4(%rbp) # 注意这里\n 40317a:\t74 05 \tje 403181 \n 40317c:\te8 75 2a 00 00 \tcall 405bf6 \n 403181:\t90 \tnop\n 403182:\tc9 \tleave\n 403183:\tc3 \tret\n\n```\n看起来一大堆很吓人对不对?实际上确实很吓人。\n\n但是发现其中玄机后其实简单的没边,最终答案就藏在`0x403177`里面,前提是确保这一步前炸弹不爆炸(意识到要爆炸了直接`run`一下重开qwq)。\n\n---\n\n### **动态调试**\n\n阅读`Phase_3_5`发现这一关其实需要两个输入,并且第一个输入必须是`rand_div`,这里建议通过`si`单步执行监控好`rand_div`值变化,确定正确结果后使用`run`重开正确输入第一个密码后才能进行下一步求解:\n```lldb\n(lldb) si\nProcess 13376 stopped\n* thread #1, name = 'bomb_linux', stop reason = instruction step into\n frame #0: 0x000000000040317a bomb_linux`phase_3_5 + 377\nbomb_linux`phase_3_5:\n-> 0x40317a <+377>: je 0x403181 ; <+384>\n 0x40317c <+379>: callq 0x405bf6 ; explode_bomb\n 0x403181 <+384>: nop\n 0x403182 <+385>: leave\n(lldb) x/wx $rbp-0x4\n0x7fffffffdb0c: 0xffffffd7\n```\n例如这里我可以打印出第二个值结合第一个值得到第三关正确结果。\n\n---\n\n## **Phase_4**\n\n### **静态分析**\n\n本题依旧开局跳转,笔者的跳转方向是`phase_4_01`,如何跳转不再强调关注`rand_div`的值即可,下面请D指导解读一下`phase_4_01`的内容:\n```assembly\n0000000000404895 :\n ; 函数入口,初始化栈帧\n 404895:\tf3 0f 1e fa \tendbr64 \n 404899:\t55 \tpush %rbp\n 40489a:\t48 89 e5 \tmov %rsp,%rbp\n 40489d:\t48 83 ec 70 \tsub $0x70,%rsp ; 分配栈空间\n\n ; 初始化斐波那契数组(F(10)~F(24)的十六进制值)\n 4048a1:\t48 89 7d 98 \tmov %rdi,-0x68(%rbp) ; 保存输入字符串指针\n 4048a5:\tc7 45 b0 37 00 00 00 \tmovl $0x37,-0x50(%rbp) ; F(10)=55\n 4048ac:\tc7 45 b4 59 00 00 00 \tmovl $0x59,-0x4c(%rbp) ; F(11)=89\n 4048b3:\tc7 45 b8 90 00 00 00 \tmovl $0x90,-0x48(%rbp) ; F(12)=144\n 4048ba:\tc7 45 bc e9 00 00 00 \tmovl $0xe9,-0x44(%rbp) ; F(13)=233\n 4048c1:\tc7 45 c0 79 01 00 00 \tmovl $0x179,-0x40(%rbp) ; F(14)=377\n 4048c8:\tc7 45 c4 62 02 00 00 \tmovl $0x262,-0x3c(%rbp) ; F(15)=610\n 4048cf:\tc7 45 c8 db 03 00 00 \tmovl $0x3db,-0x38(%rbp) ; F(16)=987\n 4048d6:\tc7 45 cc 3d 06 00 00 \tmovl $0x63d,-0x34(%rbp) ; F(17)=1597\n 4048dd:\tc7 45 d0 18 0a 00 00 \tmovl $0xa18,-0x30(%rbp) ; F(18)=2584\n 4048e4:\tc7 45 d4 55 10 00 00 \tmovl $0x1055,-0x2c(%rbp) ; F(19)=4181\n 4048eb:\tc7 45 d8 6d 1a 00 00 \tmovl $0x1a6d,-0x28(%rbp) ; F(20)=6765\n 4048f2:\tc7 45 dc c2 2a 00 00 \tmovl $0x2ac2,-0x24(%rbp) ; F(21)=10946\n 4048f9:\tc7 45 e0 2f 45 00 00 \tmovl $0x452f,-0x20(%rbp) ; F(22)=17711\n 404900:\tc7 45 e4 f1 6f 00 00 \tmovl $0x6ff1,-0x1c(%rbp) ; F(23)=28657\n 404907:\tc7 45 e8 20 b5 00 00 \tmovl $0xb520,-0x18(%rbp) ; F(24)=46368\n\n ; 读取输入到局部变量(格式为\"%d\")\n 40490e:\t48 8d 55 ac \tlea -0x54(%rbp),%rdx ; 输入存储地址\n 404912:\t48 8b 45 98 \tmov -0x68(%rbp),%rax ; 输入字符串\n 404916:\t48 8d 0d 93 1f 00 00 \tlea 0x1f93(%rip),%rcx ; 格式字符串\"%d\"\n 40491d:\t48 89 ce \tmov %rcx,%rsi\n 404920:\t48 89 c7 \tmov %rax,%rdi\n 404923:\tb8 00 00 00 00 \tmov $0x0,%eax\n 404928:\te8 63 c8 ff ff \tcall 401190 <__isoc99_sscanf@plt>\n\n ; 验证输入有效性(必须为1个正数)\n 40492d:\t89 45 fc \tmov %eax,-0x4(%rbp) ; sscanf返回值\n 404930:\t83 7d fc 01 \tcmpl $0x1,-0x4(%rbp) ; 检查是否读取1个参数\n 404934:\t75 07 \tjne 40493d ; 失败则爆炸\n 404936:\t8b 45 ac \tmov -0x54(%rbp),%eax ; 获取输入值N\n 404939:\t85 c0 \ttest %eax,%eax ; 检查N > 0\n 40493b:\t7f 05 \tjg 404942 \n 40493d:\te8 b4 12 00 00 \tcall 405bf6 \n\n ; 检查输入值上限(必须 > 1999)\n 404942:\t8b 45 ac \tmov -0x54(%rbp),%eax \n 404945:\t3d cf 07 00 00 \tcmp $0x7cf,%eax ; 1999的十六进制\n 40494a:\t7f 05 \tjg 404951 ; N > 1999?\n 40494c:\te8 a5 12 00 00 \tcall 405bf6 \n\n ; 计算 N/2000(通过定点数乘法优化)\n 404951:\t8b 45 ac \tmov -0x54(%rbp),%eax ; 输入值N\n 404954:\t48 63 d0 \tmovslq %eax,%rdx ; 符号扩展\n 404957:\t48 69 d2 d3 4d 62 10 \timul $0x10624dd3,%rdx,%rdx ; 乘以274877907(≈2^32/2000)\n 40495e:\t48 c1 ea 20 \tshr $0x20,%rdx ; 取高32位\n 404962:\tc1 fa 07 \tsar $0x7,%edx ; 算术右移7位 → N/2000\n 404965:\tc1 f8 1f \tsar $0x1f,%eax ; 符号位扩展\n 404968:\t89 c1 \tmov %eax,%ecx \n 40496a:\t89 d0 \tmov %edx,%eax \n 40496c:\t29 c8 \tsub %ecx,%eax ; 处理负数情况\n 40496e:\t89 45 ac \tmov %eax,-0x54(%rbp) ; 保存k = N/2000\n\n ; 调用递归函数func4_0(k), 这个函数用于计算斐波那契数列\n 404971:\t8b 45 ac \tmov -0x54(%rbp),%eax \n 404974:\t89 c7 \tmov %eax,%edi ; 参数k\n 404976:\te8 ce fd ff ff \tcall 404749 ; 返回值eax=F(k+1)\n 40497b:\t89 45 f8 \tmov %eax,-0x8(%rbp) ; 保存结果\n\n ; 生成随机索引并验证结果\n 40497e:\tbf 0f 00 00 00 \tmov $0xf,%edi ; 参数15\n 404983:\te8 25 cd ff ff \tcall 4016ad ; 生成0~14随机数\n 404988:\t48 8b 05 91 3e 00 00 \tmov 0x3e91(%rip),%rax # 408820 ; 获取随机索引\n 40498f:\t8b 44 85 b0 \tmov -0x50(%rbp,%rax,4),%eax ; 取数组[rand_div]的值\n 404993:\t39 45 f8 \tcmp %eax,-0x8(%rbp) ; 比较func4_0(k) == 数组值?\n 404996:\t74 05 \tje 40499d \n 404998:\te8 59 12 00 00 \tcall 405bf6 \n```\n所以相对还是很明了的,依旧是关注`rand_div`。\n\n### **动态调试**\n先找出`rand_div`在最后判断前的取值,比如我下面的0xa:\n\n```lldb\n(lldb) si\nProcess 27027 stopped\n* thread #1, name = 'bomb_linux', stop reason = instruction step into\n frame #0: 0x0000000000401719 bomb_linux`GenerateRandomNumber + 108\nbomb_linux`GenerateRandomNumber:\n-> 0x401719 <+108>: movq %rax, 0x7100(%rip) ; rand_div\n 0x401720 <+115>: jmp 0x401723 ; <+118>\n 0x401722 <+117>: nop\n 0x401723 <+118>: popq %rbp\n(lldb) si\nProcess 27027 stopped\n* thread #1, name = 'bomb_linux', stop reason = instruction step into\n frame #0: 0x0000000000401720 bomb_linux`GenerateRandomNumber + 115\nbomb_linux`GenerateRandomNumber:\n-> 0x401720 <+115>: jmp 0x401723 ; <+118>\n 0x401722 <+117>: nop\n 0x401723 <+118>: popq %rbp\n 0x401724 <+119>: retq\n(lldb) x/gx &rand_div\n0x00408820: 0x000000000000000a\n```\n\n而当 `rand_div = 0xa`(即十进制 **10**)时,输入值 `N` 的计算步骤如下:\n\n- 数组索引 **10** 的值是 **斐波那契数列第 20 项**(`F(20) = 6765`)。\n\n- `func4_0(k)` 实际计算的是 **标准斐波那契数列的第 `k+1` 项**(例如,`func4_0(0) = 1 = F(2)`) 需要满足:\n ```c\n func4_0(k) = F(k+1) = F(20)\n ```\n 解得:\n k + 1 = 20 → k = 19\n- `k = N / 2000` → `N = 2000 * k = 2000 * 19 = 38000`.\n从而得解。\n![phase_4](/images/phase_4.png)\n\n---\n\n## **Phase_Impossible**\n\nImpossible?\n\n从这道题开始偷懒了,掏出ghidra直接看c代码了解一下大概流程再去objdump看汇编:\n```c\nvoid phase_impossible(char *param_1)\n\n{\n int iVar1;\n size_t sVar2;\n undefined local_118 [256];\n long local_18;\n long local_10;\n \n local_10 = GetTickCount();\n sVar2 = strlen(param_1);\n if ((sVar2 < 10) || (sVar2 = strlen(param_1), 0x300 < sVar2)) {\n explode_bomb();\n }\n memset(local_118,0,0x100);\n tohex(local_118,param_1);\n GenerateRandomNumber(0x400);\n iVar1 = check_buf_valid(local_118,rand_div & 0xffffffff);\n if (iVar1 == 0) {\n puts(&DAT_00406518);\n explode_bomb();\n }\n GenerateRandomNumber(3);\n if (rand_div != 2) {\n if (2 < rand_div) goto LAB_00401891;\n if (rand_div == 0) {\n goto_buf_0(local_118);\n }\n else if (rand_div != 1) goto LAB_00401891;\n goto_buf_1(local_118);\n }\n goto_buf_2(local_118);\nLAB_00401891:\n explode_bomb();\n GenerateRandomNumber(0x400);\n if ((long)(int)result != rand_div) {\n printf(&DAT_00406560,rand_div,(ulong)result);\n explode_bomb();\n }\n local_18 = GetTickCount();\n if (1000 < (ulong)(local_18 - local_10)) {\n puts(&DAT_004065a8);\n explode_bomb();\n }\n return;\n}\n```\n最终任务还是很明确的,需要写一段机器码修改`result`的数值,但是注意要能通过`check_buf_valid`检测,并且最后指令必须是跳转到`0x401896`不然就会触发`phase_impossible`中`0x401891`处的`explode_bomb`函数,唯一的难点是跟踪`rand_div`的数值变化,建议使用`register write`来修改`check_buf_valid`的返回值使其强制通过然后监控`rand_div`每一次的数值变化(`x/gx &rand_div`),记录好`rand_div`的结果后开始指令设计,需要满足:\n\n - 指令的异或和为`rand_div`第一次的数值末尾八位以通过检查;\n - 修改`result`使其数值等于`rand_div`第三次数值;\n - 跳转到`0x401896`避免炸弹;\n\n 如果前几问都完成了到这里应该是没有问题的。\n\n---\n\n## **Phase_Secret**\n\n隐藏彩蛋,并非隐藏。汇编里写的非常清楚:\n```assembly\n0000000000401a8b :\n 401a8b:\tf3 0f 1e fa \tendbr64\n 401a8f:\t55 \tpush %rbp\n 401a90:\t48 89 e5 \tmov %rsp,%rbp\n 401a93:\t48 83 ec 10 \tsub $0x10,%rsp\n 401a97:\t48 89 7d f8 \tmov %rdi,-0x8(%rbp)\n 401a9b:\t48 8d 05 26 4b 00 00 \tlea 0x4b26(%rip),%rax # 4065c8 <_IO_stdin_used+0x5c8>\n 401aa2:\t48 89 c7 \tmov %rax,%rdi\n 401aa5:\te8 76 f6 ff ff \tcall 401120 \n 401aaa:\t90 \tnop\n 401aab:\tc9 \tleave\n 401aac:\tc3 \tret\n```\n注意到这段指令在原程序中完全没有执行说明是需要用户自己跳转的,也非常简单只需要在`phase_5`中设计指令时加一个要求跳转到`0x401a8b`即可。\n\n完结\n![Case Closed](/images/caseclosed.png)\n","source":"_posts/nudtbomblab.md","raw":"---\ntitle: 高地特供版CSAPP Bomb Lab全流程攻略\ndate: 2025-02-24 15:09:11\ntags: [技术, 学习, 生活]\n---\n\n这篇文章记录高地CSAPP课程Bomblab实验操作流程,仅供参考交流(答案是随机生成的和学号相关)。\n\n笔者实验环境为Archlinux/CachyOS,使用lldb作为调试器(和gdb操作差不多),其余用到的工具主要为objdump,strings,neovim/helix和zellij,全程开源环境不使用IDA。\n\n## **Phase_1**\n\n### **静态分析**\n\n#### **`strings`扫描**\n```bash\nstrings bomb_linux\n```\n先用strings寻找可能与`phase_1`相关的字符串或函数名,运气好说不定能直接找到密码毕竟是第一题。\n![strings](/images/phase1_strings.png)\n - 结果没有明文密码无法直接秒掉第一问,可惜。\n - 但是找到`GenerateRandomString`函数可能与密码生成相关。\n\n#### **用`objdump`反汇编**\n```bash\nobjdump -d bomb_linux > bomb.asm\n```\n搜索`GenerateRandomString`和`phase_1`函数的汇编代码。\n ```assembly\n 401b53 :\n 401b53: endbr64\n 401b57: push %rbp\n 401b58: mov %rsp,%rbp\n 401b5b: sub $0x20,%rsp\n 401b5f: mov %rdi,-0x18(%rbp)\n 401b63: lea -0xb(%rbp),%rax\n 401b67: mov %rax,%rdi\n 401b6a: callq 401ac1 # 调用密码生成函数\n 401b6f: lea -0xb(%rbp),%rdx # 生成的字符串地址%rbp-0xb存入%rdx,即密码存储位置\n 401b73: mov -0x18(%rbp),%rax\n 401b77: mov %rdx,%rsi\n 401b7a: mov %rax,%rdi\n 401b7d: callq 401c0c # 调用字符串比较函数\n 401b82: test %eax,%eax\n 401b84: je 401b8d \n 401b86: callq 401d67 # 比较失败则引爆炸弹\n ```\n - `phase_1`调用`GenerateRandomString`生成一个字符串。\n - 用户输入的字符串需要与此生成的字符串完全匹配。\n\n---\n\n### **动态调试**\n![phase_1](/images/phase1.png)\n下面是phase_1求解的完整流程:\n```lldb\nlldb bomb_linux <你的学号后六位>\n(lldb) b phase_1 # 在phase_1入口断点\n(lldb) run # 从入口开始执行\n请输入第1级的密码:114514 # 随便输入触发断点\n(lldb) b 0x401b6f # 在GenerateRandomString返回后断点\n(lldb) continue # 继续执行\n(lldb) x/s $rbp - 0xb # 计算字符串地址(-0xb偏移量)\n0x7fffffffdaf5: \"mJHurpQZtY\" # 轻松拿下,这里是根据学号伪随机生成的哦\n```\n将得到的密码保存入bomb_<学号后六位>.txt即可,避免后续重复输入。\n\n---\n\n## **Phase_2**\n\n### **静态分析**\n\n这道题目还是比较一目了然的,观察`phase_2`代码不难发现其实构建了一张跳转表:\n```assembly\n0000000000401b8e :\n 401b8e:\tf3 0f 1e fa \tendbr64\n 401b92:\t55 \tpush %rbp\n 401b93:\t48 89 e5 \tmov %rsp,%rbp\n 401b96:\t48 83 ec 10 \tsub $0x10,%rsp\n 401b9a:\t48 89 7d f8 \tmov %rdi,-0x8(%rbp)\n 401b9e:\tbf 10 00 00 00 \tmov $0x10,%edi\n 401ba3:\te8 05 fb ff ff \tcall 4016ad \n 401ba8:\t48 8b 05 71 6c 00 00 \tmov 0x6c71(%rip),%rax # 408820 \n 401baf:\t48 83 f8 0f \tcmp $0xf,%rax\n 401bb3:\t0f 87 16 01 00 00 \tja 401ccf \n 401bb9:\t48 8d 14 85 00 00 00 \tlea 0x0(,%rax,4),%rdx\n 401bc0:\t00 \n 401bc1:\t48 8d 05 4c 4a 00 00 \tlea 0x4a4c(%rip),%rax # 406614 <_IO_stdin_used+0x614>\n 401bc8:\t8b 04 02 \tmov (%rdx,%rax,1),%eax\n 401bcb:\t48 98 \tcltq\n 401bcd:\t48 8d 15 40 4a 00 00 \tlea 0x4a40(%rip),%rdx # 406614 <_IO_stdin_used+0x614>\n 401bd4:\t48 01 d0 \tadd %rdx,%rax\n 401bd7:\t3e ff e0 \tnotrack jmp *%rax\n 401bda:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax\n 401bde:\t48 89 c7 \tmov %rax,%rdi\n 401be1:\te8 f2 00 00 00 \tcall 401cd8 \n 401be6:\te9 ea 00 00 00 \tjmp 401cd5 \n 401beb:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax\n 401bef:\t48 89 c7 \tmov %rax,%rdi\n 401bf2:\te8 8b 01 00 00 \tcall 401d82 \n 401bf7:\te9 d9 00 00 00 \tjmp 401cd5 \n 401bfc:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax\n 401c00:\t48 89 c7 \tmov %rax,%rdi\n ...\n```\n这里面需要注意的关键点是rand_div,它会决定你的跳转方向,而你的学号又决定了它的取值。然后是`GenerateRandomNumber`这个函数的原理需要了解一下,而这个函数将在跳转前后分别调用一次,第一次决定你的跳转方向,第二次则决定了你的密码线索。\n\n---\n\n### **动态调试**\n理解原理就没什么难度了,自己找几个断点打好然后关注一下`rand_div`的值就好,观察自己的学号向哪个函数跳转并理解相应函数计算即可,比如我这里向`phase_2_14`跳转:\n![phase_2_14](/images/phase_2_14.png)\n\n而除了`phase_2_14`还有其他函数也是非常好理解的,第二题依旧可以轻松拿下。\n\n---\n## **Phase_3**\n\n### **静态分析**\n\n和Phase_2一样开局先跳转尽可能防止同学们答案雷同互相帮助(bushi\n\n本体其实没有什么好说的,这里我跳转的方向是`Phase_3_5`简要解释一下可供参考:\n\n```assembly\n0000000000403001 :\n 403001:\tf3 0f 1e fa \tendbr64\n 403005:\t55 \tpush %rbp\n 403006:\t48 89 e5 \tmov %rsp,%rbp\n 403009:\t48 83 ec 20 \tsub $0x20,%rsp\n 40300d:\t48 89 7d e8 \tmov %rdi,-0x18(%rbp)\n 403011:\tc7 45 fc 00 00 00 00 \tmovl $0x0,-0x4(%rbp)\n 403018:\tc7 45 f8 00 00 00 00 \tmovl $0x0,-0x8(%rbp)\n 40301f:\t48 8d 4d f0 \tlea -0x10(%rbp),%rcx\n 403023:\t48 8d 55 f4 \tlea -0xc(%rbp),%rdx\n 403027:\t48 8b 45 e8 \tmov -0x18(%rbp),%rax\n 40302b:\t48 8d 35 5a 36 00 00 \tlea 0x365a(%rip),%rsi # 40668c <_IO_stdin_used+0x68c>\n 403032:\t48 89 c7 \tmov %rax,%rdi\n 403035:\tb8 00 00 00 00 \tmov $0x0,%eax\n 40303a:\te8 51 e1 ff ff \tcall 401190 <__isoc99_sscanf@plt>\n 40303f:\t89 45 f8 \tmov %eax,-0x8(%rbp)\n 403042:\t83 7d f8 01 \tcmpl $0x1,-0x8(%rbp)\n 403046:\t7f 05 \tjg 40304d \n 403048:\te8 a9 2b 00 00 \tcall 405bf6 \n 40304d:\tbf 08 00 00 00 \tmov $0x8,%edi\n 403052:\te8 56 e6 ff ff \tcall 4016ad \n 403057:\t8b 45 f4 \tmov -0xc(%rbp),%eax\n 40305a:\t48 63 d0 \tmovslq %eax,%rdx\n 40305d:\t48 8b 05 bc 57 00 00 \tmov 0x57bc(%rip),%rax # 408820 \n 403064:\t48 39 c2 \tcmp %rax,%rdx\n 403067:\t74 05 \tje 40306e \n 403069:\te8 88 2b 00 00 \tcall 405bf6 \n 40306e:\tbf c8 00 00 00 \tmov $0xc8,%edi\n 403073:\te8 35 e6 ff ff \tcall 4016ad \n 403078:\t8b 45 f4 \tmov -0xc(%rbp),%eax\n 40307b:\t83 f8 07 \tcmp $0x7,%eax\n 40307e:\t0f 87 eb 00 00 00 \tja 40316f \n 403084:\t89 c0 \tmov %eax,%eax\n 403086:\t48 8d 14 85 00 00 00 \tlea 0x0(,%rax,4),%rdx\n 40308d:\t00 \n 40308e:\t48 8d 05 9f 36 00 00 \tlea 0x369f(%rip),%rax # 406734 <_IO_stdin_used+0x734>\n 403095:\t8b 04 02 \tmov (%rdx,%rax,1),%eax\n 403098:\t48 98 \tcltq\n 40309a:\t48 8d 15 93 36 00 00 \tlea 0x3693(%rip),%rdx # 406734 <_IO_stdin_used+0x734>\n 4030a1:\t48 01 d0 \tadd %rdx,%rax\n 4030a4:\t3e ff e0 \tnotrack jmp *%rax\n 4030a7:\t48 8b 05 72 57 00 00 \tmov 0x5772(%rip),%rax # 408820 \n 4030ae:\t89 c2 \tmov %eax,%edx\n 4030b0:\t8b 45 fc \tmov -0x4(%rbp),%eax\n 4030b3:\t01 d0 \tadd %edx,%eax\n 4030b5:\t89 45 fc \tmov %eax,-0x4(%rbp)\n 4030b8:\tbf c8 00 00 00 \tmov $0xc8,%edi\n 4030bd:\te8 eb e5 ff ff \tcall 4016ad \n ...\n 403174:\t8b 45 f0 \tmov -0x10(%rbp),%eax\n 403177:\t39 45 fc \tcmp %eax,-0x4(%rbp) # 注意这里\n 40317a:\t74 05 \tje 403181 \n 40317c:\te8 75 2a 00 00 \tcall 405bf6 \n 403181:\t90 \tnop\n 403182:\tc9 \tleave\n 403183:\tc3 \tret\n\n```\n看起来一大堆很吓人对不对?实际上确实很吓人。\n\n但是发现其中玄机后其实简单的没边,最终答案就藏在`0x403177`里面,前提是确保这一步前炸弹不爆炸(意识到要爆炸了直接`run`一下重开qwq)。\n\n---\n\n### **动态调试**\n\n阅读`Phase_3_5`发现这一关其实需要两个输入,并且第一个输入必须是`rand_div`,这里建议通过`si`单步执行监控好`rand_div`值变化,确定正确结果后使用`run`重开正确输入第一个密码后才能进行下一步求解:\n```lldb\n(lldb) si\nProcess 13376 stopped\n* thread #1, name = 'bomb_linux', stop reason = instruction step into\n frame #0: 0x000000000040317a bomb_linux`phase_3_5 + 377\nbomb_linux`phase_3_5:\n-> 0x40317a <+377>: je 0x403181 ; <+384>\n 0x40317c <+379>: callq 0x405bf6 ; explode_bomb\n 0x403181 <+384>: nop\n 0x403182 <+385>: leave\n(lldb) x/wx $rbp-0x4\n0x7fffffffdb0c: 0xffffffd7\n```\n例如这里我可以打印出第二个值结合第一个值得到第三关正确结果。\n\n---\n\n## **Phase_4**\n\n### **静态分析**\n\n本题依旧开局跳转,笔者的跳转方向是`phase_4_01`,如何跳转不再强调关注`rand_div`的值即可,下面请D指导解读一下`phase_4_01`的内容:\n```assembly\n0000000000404895 :\n ; 函数入口,初始化栈帧\n 404895:\tf3 0f 1e fa \tendbr64 \n 404899:\t55 \tpush %rbp\n 40489a:\t48 89 e5 \tmov %rsp,%rbp\n 40489d:\t48 83 ec 70 \tsub $0x70,%rsp ; 分配栈空间\n\n ; 初始化斐波那契数组(F(10)~F(24)的十六进制值)\n 4048a1:\t48 89 7d 98 \tmov %rdi,-0x68(%rbp) ; 保存输入字符串指针\n 4048a5:\tc7 45 b0 37 00 00 00 \tmovl $0x37,-0x50(%rbp) ; F(10)=55\n 4048ac:\tc7 45 b4 59 00 00 00 \tmovl $0x59,-0x4c(%rbp) ; F(11)=89\n 4048b3:\tc7 45 b8 90 00 00 00 \tmovl $0x90,-0x48(%rbp) ; F(12)=144\n 4048ba:\tc7 45 bc e9 00 00 00 \tmovl $0xe9,-0x44(%rbp) ; F(13)=233\n 4048c1:\tc7 45 c0 79 01 00 00 \tmovl $0x179,-0x40(%rbp) ; F(14)=377\n 4048c8:\tc7 45 c4 62 02 00 00 \tmovl $0x262,-0x3c(%rbp) ; F(15)=610\n 4048cf:\tc7 45 c8 db 03 00 00 \tmovl $0x3db,-0x38(%rbp) ; F(16)=987\n 4048d6:\tc7 45 cc 3d 06 00 00 \tmovl $0x63d,-0x34(%rbp) ; F(17)=1597\n 4048dd:\tc7 45 d0 18 0a 00 00 \tmovl $0xa18,-0x30(%rbp) ; F(18)=2584\n 4048e4:\tc7 45 d4 55 10 00 00 \tmovl $0x1055,-0x2c(%rbp) ; F(19)=4181\n 4048eb:\tc7 45 d8 6d 1a 00 00 \tmovl $0x1a6d,-0x28(%rbp) ; F(20)=6765\n 4048f2:\tc7 45 dc c2 2a 00 00 \tmovl $0x2ac2,-0x24(%rbp) ; F(21)=10946\n 4048f9:\tc7 45 e0 2f 45 00 00 \tmovl $0x452f,-0x20(%rbp) ; F(22)=17711\n 404900:\tc7 45 e4 f1 6f 00 00 \tmovl $0x6ff1,-0x1c(%rbp) ; F(23)=28657\n 404907:\tc7 45 e8 20 b5 00 00 \tmovl $0xb520,-0x18(%rbp) ; F(24)=46368\n\n ; 读取输入到局部变量(格式为\"%d\")\n 40490e:\t48 8d 55 ac \tlea -0x54(%rbp),%rdx ; 输入存储地址\n 404912:\t48 8b 45 98 \tmov -0x68(%rbp),%rax ; 输入字符串\n 404916:\t48 8d 0d 93 1f 00 00 \tlea 0x1f93(%rip),%rcx ; 格式字符串\"%d\"\n 40491d:\t48 89 ce \tmov %rcx,%rsi\n 404920:\t48 89 c7 \tmov %rax,%rdi\n 404923:\tb8 00 00 00 00 \tmov $0x0,%eax\n 404928:\te8 63 c8 ff ff \tcall 401190 <__isoc99_sscanf@plt>\n\n ; 验证输入有效性(必须为1个正数)\n 40492d:\t89 45 fc \tmov %eax,-0x4(%rbp) ; sscanf返回值\n 404930:\t83 7d fc 01 \tcmpl $0x1,-0x4(%rbp) ; 检查是否读取1个参数\n 404934:\t75 07 \tjne 40493d ; 失败则爆炸\n 404936:\t8b 45 ac \tmov -0x54(%rbp),%eax ; 获取输入值N\n 404939:\t85 c0 \ttest %eax,%eax ; 检查N > 0\n 40493b:\t7f 05 \tjg 404942 \n 40493d:\te8 b4 12 00 00 \tcall 405bf6 \n\n ; 检查输入值上限(必须 > 1999)\n 404942:\t8b 45 ac \tmov -0x54(%rbp),%eax \n 404945:\t3d cf 07 00 00 \tcmp $0x7cf,%eax ; 1999的十六进制\n 40494a:\t7f 05 \tjg 404951 ; N > 1999?\n 40494c:\te8 a5 12 00 00 \tcall 405bf6 \n\n ; 计算 N/2000(通过定点数乘法优化)\n 404951:\t8b 45 ac \tmov -0x54(%rbp),%eax ; 输入值N\n 404954:\t48 63 d0 \tmovslq %eax,%rdx ; 符号扩展\n 404957:\t48 69 d2 d3 4d 62 10 \timul $0x10624dd3,%rdx,%rdx ; 乘以274877907(≈2^32/2000)\n 40495e:\t48 c1 ea 20 \tshr $0x20,%rdx ; 取高32位\n 404962:\tc1 fa 07 \tsar $0x7,%edx ; 算术右移7位 → N/2000\n 404965:\tc1 f8 1f \tsar $0x1f,%eax ; 符号位扩展\n 404968:\t89 c1 \tmov %eax,%ecx \n 40496a:\t89 d0 \tmov %edx,%eax \n 40496c:\t29 c8 \tsub %ecx,%eax ; 处理负数情况\n 40496e:\t89 45 ac \tmov %eax,-0x54(%rbp) ; 保存k = N/2000\n\n ; 调用递归函数func4_0(k), 这个函数用于计算斐波那契数列\n 404971:\t8b 45 ac \tmov -0x54(%rbp),%eax \n 404974:\t89 c7 \tmov %eax,%edi ; 参数k\n 404976:\te8 ce fd ff ff \tcall 404749 ; 返回值eax=F(k+1)\n 40497b:\t89 45 f8 \tmov %eax,-0x8(%rbp) ; 保存结果\n\n ; 生成随机索引并验证结果\n 40497e:\tbf 0f 00 00 00 \tmov $0xf,%edi ; 参数15\n 404983:\te8 25 cd ff ff \tcall 4016ad ; 生成0~14随机数\n 404988:\t48 8b 05 91 3e 00 00 \tmov 0x3e91(%rip),%rax # 408820 ; 获取随机索引\n 40498f:\t8b 44 85 b0 \tmov -0x50(%rbp,%rax,4),%eax ; 取数组[rand_div]的值\n 404993:\t39 45 f8 \tcmp %eax,-0x8(%rbp) ; 比较func4_0(k) == 数组值?\n 404996:\t74 05 \tje 40499d \n 404998:\te8 59 12 00 00 \tcall 405bf6 \n```\n所以相对还是很明了的,依旧是关注`rand_div`。\n\n### **动态调试**\n先找出`rand_div`在最后判断前的取值,比如我下面的0xa:\n\n```lldb\n(lldb) si\nProcess 27027 stopped\n* thread #1, name = 'bomb_linux', stop reason = instruction step into\n frame #0: 0x0000000000401719 bomb_linux`GenerateRandomNumber + 108\nbomb_linux`GenerateRandomNumber:\n-> 0x401719 <+108>: movq %rax, 0x7100(%rip) ; rand_div\n 0x401720 <+115>: jmp 0x401723 ; <+118>\n 0x401722 <+117>: nop\n 0x401723 <+118>: popq %rbp\n(lldb) si\nProcess 27027 stopped\n* thread #1, name = 'bomb_linux', stop reason = instruction step into\n frame #0: 0x0000000000401720 bomb_linux`GenerateRandomNumber + 115\nbomb_linux`GenerateRandomNumber:\n-> 0x401720 <+115>: jmp 0x401723 ; <+118>\n 0x401722 <+117>: nop\n 0x401723 <+118>: popq %rbp\n 0x401724 <+119>: retq\n(lldb) x/gx &rand_div\n0x00408820: 0x000000000000000a\n```\n\n而当 `rand_div = 0xa`(即十进制 **10**)时,输入值 `N` 的计算步骤如下:\n\n- 数组索引 **10** 的值是 **斐波那契数列第 20 项**(`F(20) = 6765`)。\n\n- `func4_0(k)` 实际计算的是 **标准斐波那契数列的第 `k+1` 项**(例如,`func4_0(0) = 1 = F(2)`) 需要满足:\n ```c\n func4_0(k) = F(k+1) = F(20)\n ```\n 解得:\n k + 1 = 20 → k = 19\n- `k = N / 2000` → `N = 2000 * k = 2000 * 19 = 38000`.\n从而得解。\n![phase_4](/images/phase_4.png)\n\n---\n\n## **Phase_Impossible**\n\nImpossible?\n\n从这道题开始偷懒了,掏出ghidra直接看c代码了解一下大概流程再去objdump看汇编:\n```c\nvoid phase_impossible(char *param_1)\n\n{\n int iVar1;\n size_t sVar2;\n undefined local_118 [256];\n long local_18;\n long local_10;\n \n local_10 = GetTickCount();\n sVar2 = strlen(param_1);\n if ((sVar2 < 10) || (sVar2 = strlen(param_1), 0x300 < sVar2)) {\n explode_bomb();\n }\n memset(local_118,0,0x100);\n tohex(local_118,param_1);\n GenerateRandomNumber(0x400);\n iVar1 = check_buf_valid(local_118,rand_div & 0xffffffff);\n if (iVar1 == 0) {\n puts(&DAT_00406518);\n explode_bomb();\n }\n GenerateRandomNumber(3);\n if (rand_div != 2) {\n if (2 < rand_div) goto LAB_00401891;\n if (rand_div == 0) {\n goto_buf_0(local_118);\n }\n else if (rand_div != 1) goto LAB_00401891;\n goto_buf_1(local_118);\n }\n goto_buf_2(local_118);\nLAB_00401891:\n explode_bomb();\n GenerateRandomNumber(0x400);\n if ((long)(int)result != rand_div) {\n printf(&DAT_00406560,rand_div,(ulong)result);\n explode_bomb();\n }\n local_18 = GetTickCount();\n if (1000 < (ulong)(local_18 - local_10)) {\n puts(&DAT_004065a8);\n explode_bomb();\n }\n return;\n}\n```\n最终任务还是很明确的,需要写一段机器码修改`result`的数值,但是注意要能通过`check_buf_valid`检测,并且最后指令必须是跳转到`0x401896`不然就会触发`phase_impossible`中`0x401891`处的`explode_bomb`函数,唯一的难点是跟踪`rand_div`的数值变化,建议使用`register write`来修改`check_buf_valid`的返回值使其强制通过然后监控`rand_div`每一次的数值变化(`x/gx &rand_div`),记录好`rand_div`的结果后开始指令设计,需要满足:\n\n - 指令的异或和为`rand_div`第一次的数值末尾八位以通过检查;\n - 修改`result`使其数值等于`rand_div`第三次数值;\n - 跳转到`0x401896`避免炸弹;\n\n 如果前几问都完成了到这里应该是没有问题的。\n\n---\n\n## **Phase_Secret**\n\n隐藏彩蛋,并非隐藏。汇编里写的非常清楚:\n```assembly\n0000000000401a8b :\n 401a8b:\tf3 0f 1e fa \tendbr64\n 401a8f:\t55 \tpush %rbp\n 401a90:\t48 89 e5 \tmov %rsp,%rbp\n 401a93:\t48 83 ec 10 \tsub $0x10,%rsp\n 401a97:\t48 89 7d f8 \tmov %rdi,-0x8(%rbp)\n 401a9b:\t48 8d 05 26 4b 00 00 \tlea 0x4b26(%rip),%rax # 4065c8 <_IO_stdin_used+0x5c8>\n 401aa2:\t48 89 c7 \tmov %rax,%rdi\n 401aa5:\te8 76 f6 ff ff \tcall 401120 \n 401aaa:\t90 \tnop\n 401aab:\tc9 \tleave\n 401aac:\tc3 \tret\n```\n注意到这段指令在原程序中完全没有执行说明是需要用户自己跳转的,也非常简单只需要在`phase_5`中设计指令时加一个要求跳转到`0x401a8b`即可。\n\n完结\n![Case Closed](/images/caseclosed.png)\n","slug":"nudtbomblab","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblx001jidd008c7c46c","content":"

这篇文章记录高地CSAPP课程Bomblab实验操作流程,仅供参考交流(答案是随机生成的和学号相关)。

\n

笔者实验环境为Archlinux/CachyOS,使用lldb作为调试器(和gdb操作差不多),其余用到的工具主要为objdump,strings,neovim/helix和zellij,全程开源环境不使用IDA。

\n

Phase_1

静态分析

strings扫描

1
strings bomb_linux
\n

先用strings寻找可能与phase_1相关的字符串或函数名,运气好说不定能直接找到密码毕竟是第一题。
\"strings\"

\n
    \n
  • 结果没有明文密码无法直接秒掉第一问,可惜。
  • \n
  • 但是找到GenerateRandomString函数可能与密码生成相关。
  • \n
\n

objdump反汇编

1
objdump -d bomb_linux > bomb.asm
\n

搜索GenerateRandomStringphase_1函数的汇编代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
401b53 <phase_1>:
401b53: endbr64
401b57: push %rbp
401b58: mov %rsp,%rbp
401b5b: sub $0x20,%rsp
401b5f: mov %rdi,-0x18(%rbp)
401b63: lea -0xb(%rbp),%rax
401b67: mov %rax,%rdi
401b6a: callq 401ac1 <GenerateRandomString> # 调用密码生成函数
401b6f: lea -0xb(%rbp),%rdx # 生成的字符串地址%rbp-0xb存入%rdx,即密码存储位置
401b73: mov -0x18(%rbp),%rax
401b77: mov %rdx,%rsi
401b7a: mov %rax,%rdi
401b7d: callq 401c0c <string_compare> # 调用字符串比较函数
401b82: test %eax,%eax
401b84: je 401b8d <phase_1+0x3a>
401b86: callq 401d67 <explode_bomb> # 比较失败则引爆炸弹

\n
    \n
  • phase_1调用GenerateRandomString生成一个字符串。
  • \n
  • 用户输入的字符串需要与此生成的字符串完全匹配。
  • \n
\n
\n

动态调试

\"phase_1\"
下面是phase_1求解的完整流程:

\n
1
2
3
4
5
6
7
8
lldb bomb_linux <你的学号后六位>
(lldb) b phase_1 # 在phase_1入口断点
(lldb) run # 从入口开始执行
请输入第1级的密码:114514 # 随便输入触发断点
(lldb) b 0x401b6f # 在GenerateRandomString返回后断点
(lldb) continue # 继续执行
(lldb) x/s $rbp - 0xb # 计算字符串地址(-0xb偏移量)
0x7fffffffdaf5: "mJHurpQZtY" # 轻松拿下,这里是根据学号伪随机生成的哦
\n

将得到的密码保存入bomb_<学号后六位>.txt即可,避免后续重复输入。

\n
\n

Phase_2

静态分析

这道题目还是比较一目了然的,观察phase_2代码不难发现其实构建了一张跳转表:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
0000000000401b8e <phase_2>:
401b8e:\tf3 0f 1e fa \tendbr64
401b92:\t55 \tpush %rbp
401b93:\t48 89 e5 \tmov %rsp,%rbp
401b96:\t48 83 ec 10 \tsub $0x10,%rsp
401b9a:\t48 89 7d f8 \tmov %rdi,-0x8(%rbp)
401b9e:\tbf 10 00 00 00 \tmov $0x10,%edi
401ba3:\te8 05 fb ff ff \tcall 4016ad <GenerateRandomNumber>
401ba8:\t48 8b 05 71 6c 00 00 \tmov 0x6c71(%rip),%rax # 408820 <rand_div>
401baf:\t48 83 f8 0f \tcmp $0xf,%rax
401bb3:\t0f 87 16 01 00 00 \tja 401ccf <phase_2+0x141>
401bb9:\t48 8d 14 85 00 00 00 \tlea 0x0(,%rax,4),%rdx
401bc0:\t00
401bc1:\t48 8d 05 4c 4a 00 00 \tlea 0x4a4c(%rip),%rax # 406614 <_IO_stdin_used+0x614>
401bc8:\t8b 04 02 \tmov (%rdx,%rax,1),%eax
401bcb:\t48 98 \tcltq
401bcd:\t48 8d 15 40 4a 00 00 \tlea 0x4a40(%rip),%rdx # 406614 <_IO_stdin_used+0x614>
401bd4:\t48 01 d0 \tadd %rdx,%rax
401bd7:\t3e ff e0 \tnotrack jmp *%rax
401bda:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax
401bde:\t48 89 c7 \tmov %rax,%rdi
401be1:\te8 f2 00 00 00 \tcall 401cd8 <phase_2_0>
401be6:\te9 ea 00 00 00 \tjmp 401cd5 <phase_2+0x147>
401beb:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax
401bef:\t48 89 c7 \tmov %rax,%rdi
401bf2:\te8 8b 01 00 00 \tcall 401d82 <phase_2_1>
401bf7:\te9 d9 00 00 00 \tjmp 401cd5 <phase_2+0x147>
401bfc:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax
401c00:\t48 89 c7 \tmov %rax,%rdi
...
\n

这里面需要注意的关键点是rand_div,它会决定你的跳转方向,而你的学号又决定了它的取值。然后是GenerateRandomNumber这个函数的原理需要了解一下,而这个函数将在跳转前后分别调用一次,第一次决定你的跳转方向,第二次则决定了你的密码线索。

\n
\n

动态调试

理解原理就没什么难度了,自己找几个断点打好然后关注一下rand_div的值就好,观察自己的学号向哪个函数跳转并理解相应函数计算即可,比如我这里向phase_2_14跳转:
\"phase_2_14\"

\n

而除了phase_2_14还有其他函数也是非常好理解的,第二题依旧可以轻松拿下。

\n
\n

Phase_3

静态分析

和Phase_2一样开局先跳转尽可能防止同学们答案雷同互相帮助(bushi

\n

本体其实没有什么好说的,这里我跳转的方向是Phase_3_5简要解释一下可供参考:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
0000000000403001 <phase_3_5>:
403001:\tf3 0f 1e fa \tendbr64
403005:\t55 \tpush %rbp
403006:\t48 89 e5 \tmov %rsp,%rbp
403009:\t48 83 ec 20 \tsub $0x20,%rsp
40300d:\t48 89 7d e8 \tmov %rdi,-0x18(%rbp)
403011:\tc7 45 fc 00 00 00 00 \tmovl $0x0,-0x4(%rbp)
403018:\tc7 45 f8 00 00 00 00 \tmovl $0x0,-0x8(%rbp)
40301f:\t48 8d 4d f0 \tlea -0x10(%rbp),%rcx
403023:\t48 8d 55 f4 \tlea -0xc(%rbp),%rdx
403027:\t48 8b 45 e8 \tmov -0x18(%rbp),%rax
40302b:\t48 8d 35 5a 36 00 00 \tlea 0x365a(%rip),%rsi # 40668c <_IO_stdin_used+0x68c>
403032:\t48 89 c7 \tmov %rax,%rdi
403035:\tb8 00 00 00 00 \tmov $0x0,%eax
40303a:\te8 51 e1 ff ff \tcall 401190 <__isoc99_sscanf@plt>
40303f:\t89 45 f8 \tmov %eax,-0x8(%rbp)
403042:\t83 7d f8 01 \tcmpl $0x1,-0x8(%rbp)
403046:\t7f 05 \tjg 40304d <phase_3_5+0x4c>
403048:\te8 a9 2b 00 00 \tcall 405bf6 <explode_bomb>
40304d:\tbf 08 00 00 00 \tmov $0x8,%edi
403052:\te8 56 e6 ff ff \tcall 4016ad <GenerateRandomNumber>
403057:\t8b 45 f4 \tmov -0xc(%rbp),%eax
40305a:\t48 63 d0 \tmovslq %eax,%rdx
40305d:\t48 8b 05 bc 57 00 00 \tmov 0x57bc(%rip),%rax # 408820 <rand_div>
403064:\t48 39 c2 \tcmp %rax,%rdx
403067:\t74 05 \tje 40306e <phase_3_5+0x6d>
403069:\te8 88 2b 00 00 \tcall 405bf6 <explode_bomb>
40306e:\tbf c8 00 00 00 \tmov $0xc8,%edi
403073:\te8 35 e6 ff ff \tcall 4016ad <GenerateRandomNumber>
403078:\t8b 45 f4 \tmov -0xc(%rbp),%eax
40307b:\t83 f8 07 \tcmp $0x7,%eax
40307e:\t0f 87 eb 00 00 00 \tja 40316f <phase_3_5+0x16e>
403084:\t89 c0 \tmov %eax,%eax
403086:\t48 8d 14 85 00 00 00 \tlea 0x0(,%rax,4),%rdx
40308d:\t00
40308e:\t48 8d 05 9f 36 00 00 \tlea 0x369f(%rip),%rax # 406734 <_IO_stdin_used+0x734>
403095:\t8b 04 02 \tmov (%rdx,%rax,1),%eax
403098:\t48 98 \tcltq
40309a:\t48 8d 15 93 36 00 00 \tlea 0x3693(%rip),%rdx # 406734 <_IO_stdin_used+0x734>
4030a1:\t48 01 d0 \tadd %rdx,%rax
4030a4:\t3e ff e0 \tnotrack jmp *%rax
4030a7:\t48 8b 05 72 57 00 00 \tmov 0x5772(%rip),%rax # 408820 <rand_div>
4030ae:\t89 c2 \tmov %eax,%edx
4030b0:\t8b 45 fc \tmov -0x4(%rbp),%eax
4030b3:\t01 d0 \tadd %edx,%eax
4030b5:\t89 45 fc \tmov %eax,-0x4(%rbp)
4030b8:\tbf c8 00 00 00 \tmov $0xc8,%edi
4030bd:\te8 eb e5 ff ff \tcall 4016ad <GenerateRandomNumber>
...
403174:\t8b 45 f0 \tmov -0x10(%rbp),%eax
403177:\t39 45 fc \tcmp %eax,-0x4(%rbp) # 注意这里
40317a:\t74 05 \tje 403181 <phase_3_5+0x180>
40317c:\te8 75 2a 00 00 \tcall 405bf6 <explode_bomb>
403181:\t90 \tnop
403182:\tc9 \tleave
403183:\tc3 \tret

\n

看起来一大堆很吓人对不对?实际上确实很吓人。

\n

但是发现其中玄机后其实简单的没边,最终答案就藏在0x403177里面,前提是确保这一步前炸弹不爆炸(意识到要爆炸了直接run一下重开qwq)。

\n
\n

动态调试

阅读Phase_3_5发现这一关其实需要两个输入,并且第一个输入必须是rand_div,这里建议通过si单步执行监控好rand_div值变化,确定正确结果后使用run重开正确输入第一个密码后才能进行下一步求解:

\n
1
2
3
4
5
6
7
8
9
10
11
(lldb) si
Process 13376 stopped
* thread #1, name = 'bomb_linux', stop reason = instruction step into
frame #0: 0x000000000040317a bomb_linux`phase_3_5 + 377
bomb_linux`phase_3_5:
-> 0x40317a <+377>: je 0x403181 ; <+384>
0x40317c <+379>: callq 0x405bf6 ; explode_bomb
0x403181 <+384>: nop
0x403182 <+385>: leave
(lldb) x/wx $rbp-0x4
0x7fffffffdb0c: 0xffffffd7
\n

例如这里我可以打印出第二个值结合第一个值得到第三关正确结果。

\n
\n

Phase_4

静态分析

本题依旧开局跳转,笔者的跳转方向是phase_4_01,如何跳转不再强调关注rand_div的值即可,下面请D指导解读一下phase_4_01的内容:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
0000000000404895 <phase_4_01>:
; 函数入口,初始化栈帧
404895:\tf3 0f 1e fa \tendbr64
404899:\t55 \tpush %rbp
40489a:\t48 89 e5 \tmov %rsp,%rbp
40489d:\t48 83 ec 70 \tsub $0x70,%rsp ; 分配栈空间

; 初始化斐波那契数组(F(10)~F(24)的十六进制值)
4048a1:\t48 89 7d 98 \tmov %rdi,-0x68(%rbp) ; 保存输入字符串指针
4048a5:\tc7 45 b0 37 00 00 00 \tmovl $0x37,-0x50(%rbp) ; F(10)=55
4048ac:\tc7 45 b4 59 00 00 00 \tmovl $0x59,-0x4c(%rbp) ; F(11)=89
4048b3:\tc7 45 b8 90 00 00 00 \tmovl $0x90,-0x48(%rbp) ; F(12)=144
4048ba:\tc7 45 bc e9 00 00 00 \tmovl $0xe9,-0x44(%rbp) ; F(13)=233
4048c1:\tc7 45 c0 79 01 00 00 \tmovl $0x179,-0x40(%rbp) ; F(14)=377
4048c8:\tc7 45 c4 62 02 00 00 \tmovl $0x262,-0x3c(%rbp) ; F(15)=610
4048cf:\tc7 45 c8 db 03 00 00 \tmovl $0x3db,-0x38(%rbp) ; F(16)=987
4048d6:\tc7 45 cc 3d 06 00 00 \tmovl $0x63d,-0x34(%rbp) ; F(17)=1597
4048dd:\tc7 45 d0 18 0a 00 00 \tmovl $0xa18,-0x30(%rbp) ; F(18)=2584
4048e4:\tc7 45 d4 55 10 00 00 \tmovl $0x1055,-0x2c(%rbp) ; F(19)=4181
4048eb:\tc7 45 d8 6d 1a 00 00 \tmovl $0x1a6d,-0x28(%rbp) ; F(20)=6765
4048f2:\tc7 45 dc c2 2a 00 00 \tmovl $0x2ac2,-0x24(%rbp) ; F(21)=10946
4048f9:\tc7 45 e0 2f 45 00 00 \tmovl $0x452f,-0x20(%rbp) ; F(22)=17711
404900:\tc7 45 e4 f1 6f 00 00 \tmovl $0x6ff1,-0x1c(%rbp) ; F(23)=28657
404907:\tc7 45 e8 20 b5 00 00 \tmovl $0xb520,-0x18(%rbp) ; F(24)=46368

; 读取输入到局部变量(格式为"%d")
40490e:\t48 8d 55 ac \tlea -0x54(%rbp),%rdx ; 输入存储地址
404912:\t48 8b 45 98 \tmov -0x68(%rbp),%rax ; 输入字符串
404916:\t48 8d 0d 93 1f 00 00 \tlea 0x1f93(%rip),%rcx ; 格式字符串"%d"
40491d:\t48 89 ce \tmov %rcx,%rsi
404920:\t48 89 c7 \tmov %rax,%rdi
404923:\tb8 00 00 00 00 \tmov $0x0,%eax
404928:\te8 63 c8 ff ff \tcall 401190 <__isoc99_sscanf@plt>

; 验证输入有效性(必须为1个正数)
40492d:\t89 45 fc \tmov %eax,-0x4(%rbp) ; sscanf返回值
404930:\t83 7d fc 01 \tcmpl $0x1,-0x4(%rbp) ; 检查是否读取1个参数
404934:\t75 07 \tjne 40493d <phase_4_01+0xa8> ; 失败则爆炸
404936:\t8b 45 ac \tmov -0x54(%rbp),%eax ; 获取输入值N
404939:\t85 c0 \ttest %eax,%eax ; 检查N > 0
40493b:\t7f 05 \tjg 404942 <phase_4_01+0xad>
40493d:\te8 b4 12 00 00 \tcall 405bf6 <explode_bomb>

; 检查输入值上限(必须 > 1999)
404942:\t8b 45 ac \tmov -0x54(%rbp),%eax
404945:\t3d cf 07 00 00 \tcmp $0x7cf,%eax ; 1999的十六进制
40494a:\t7f 05 \tjg 404951 <phase_4_01+0xbc> ; N > 1999?
40494c:\te8 a5 12 00 00 \tcall 405bf6 <explode_bomb>

; 计算 N/2000(通过定点数乘法优化)
404951:\t8b 45 ac \tmov -0x54(%rbp),%eax ; 输入值N
404954:\t48 63 d0 \tmovslq %eax,%rdx ; 符号扩展
404957:\t48 69 d2 d3 4d 62 10 \timul $0x10624dd3,%rdx,%rdx ; 乘以274877907(≈2^32/2000)
40495e:\t48 c1 ea 20 \tshr $0x20,%rdx ; 取高32位
404962:\tc1 fa 07 \tsar $0x7,%edx ; 算术右移7位 → N/2000
404965:\tc1 f8 1f \tsar $0x1f,%eax ; 符号位扩展
404968:\t89 c1 \tmov %eax,%ecx
40496a:\t89 d0 \tmov %edx,%eax
40496c:\t29 c8 \tsub %ecx,%eax ; 处理负数情况
40496e:\t89 45 ac \tmov %eax,-0x54(%rbp) ; 保存k = N/2000

; 调用递归函数func4_0(k), 这个函数用于计算斐波那契数列
404971:\t8b 45 ac \tmov -0x54(%rbp),%eax
404974:\t89 c7 \tmov %eax,%edi ; 参数k
404976:\te8 ce fd ff ff \tcall 404749 <func4_0> ; 返回值eax=F(k+1)
40497b:\t89 45 f8 \tmov %eax,-0x8(%rbp) ; 保存结果

; 生成随机索引并验证结果
40497e:\tbf 0f 00 00 00 \tmov $0xf,%edi ; 参数15
404983:\te8 25 cd ff ff \tcall 4016ad <GenerateRandomNumber> ; 生成0~14随机数
404988:\t48 8b 05 91 3e 00 00 \tmov 0x3e91(%rip),%rax # 408820 <rand_div> ; 获取随机索引
40498f:\t8b 44 85 b0 \tmov -0x50(%rbp,%rax,4),%eax ; 取数组[rand_div]的值
404993:\t39 45 f8 \tcmp %eax,-0x8(%rbp) ; 比较func4_0(k) == 数组值?
404996:\t74 05 \tje 40499d <phase_4_01+0x108>
404998:\te8 59 12 00 00 \tcall 405bf6 <explode_bomb>
\n

所以相对还是很明了的,依旧是关注rand_div

\n

动态调试

先找出rand_div在最后判断前的取值,比如我下面的0xa:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(lldb) si
Process 27027 stopped
* thread #1, name = 'bomb_linux', stop reason = instruction step into
frame #0: 0x0000000000401719 bomb_linux`GenerateRandomNumber + 108
bomb_linux`GenerateRandomNumber:
-> 0x401719 <+108>: movq %rax, 0x7100(%rip) ; rand_div
0x401720 <+115>: jmp 0x401723 ; <+118>
0x401722 <+117>: nop
0x401723 <+118>: popq %rbp
(lldb) si
Process 27027 stopped
* thread #1, name = 'bomb_linux', stop reason = instruction step into
frame #0: 0x0000000000401720 bomb_linux`GenerateRandomNumber + 115
bomb_linux`GenerateRandomNumber:
-> 0x401720 <+115>: jmp 0x401723 ; <+118>
0x401722 <+117>: nop
0x401723 <+118>: popq %rbp
0x401724 <+119>: retq
(lldb) x/gx &rand_div
0x00408820: 0x000000000000000a
\n\n

而当 rand_div = 0xa(即十进制 10)时,输入值 N 的计算步骤如下:

\n
    \n
  • 数组索引 10 的值是 斐波那契数列第 20 项F(20) = 6765)。

    \n
  • \n
  • func4_0(k) 实际计算的是 标准斐波那契数列的第 k+1(例如,func4_0(0) = 1 = F(2)) 需要满足:

    \n
    1
    func4_0(k) = F(k+1) = F(20)
    \n

    解得:
    k + 1 = 20 → k = 19

    \n
  • \n
  • k = N / 2000N = 2000 * k = 2000 * 19 = 38000.
    从而得解。
    \"phase_4\"

    \n
  • \n
\n
\n

Phase_Impossible

Impossible?

\n

从这道题开始偷懒了,掏出ghidra直接看c代码了解一下大概流程再去objdump看汇编:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
void phase_impossible(char *param_1)

{
int iVar1;
size_t sVar2;
undefined local_118 [256];
long local_18;
long local_10;

local_10 = GetTickCount();
sVar2 = strlen(param_1);
if ((sVar2 < 10) || (sVar2 = strlen(param_1), 0x300 < sVar2)) {
explode_bomb();
}
memset(local_118,0,0x100);
tohex(local_118,param_1);
GenerateRandomNumber(0x400);
iVar1 = check_buf_valid(local_118,rand_div & 0xffffffff);
if (iVar1 == 0) {
puts(&DAT_00406518);
explode_bomb();
}
GenerateRandomNumber(3);
if (rand_div != 2) {
if (2 < rand_div) goto LAB_00401891;
if (rand_div == 0) {
goto_buf_0(local_118);
}
else if (rand_div != 1) goto LAB_00401891;
goto_buf_1(local_118);
}
goto_buf_2(local_118);
LAB_00401891:
explode_bomb();
GenerateRandomNumber(0x400);
if ((long)(int)result != rand_div) {
printf(&DAT_00406560,rand_div,(ulong)result);
explode_bomb();
}
local_18 = GetTickCount();
if (1000 < (ulong)(local_18 - local_10)) {
puts(&DAT_004065a8);
explode_bomb();
}
return;
}
\n

最终任务还是很明确的,需要写一段机器码修改result的数值,但是注意要能通过check_buf_valid检测,并且最后指令必须是跳转到0x401896不然就会触发phase_impossible0x401891处的explode_bomb函数,唯一的难点是跟踪rand_div的数值变化,建议使用register write来修改check_buf_valid的返回值使其强制通过然后监控rand_div每一次的数值变化(x/gx &rand_div),记录好rand_div的结果后开始指令设计,需要满足:

\n
    \n
  • 指令的异或和为rand_div第一次的数值末尾八位以通过检查;
  • \n
  • 修改result使其数值等于rand_div第三次数值;
  • \n
  • 跳转到0x401896避免炸弹;
  • \n
\n

如果前几问都完成了到这里应该是没有问题的。

\n
\n

Phase_Secret

隐藏彩蛋,并非隐藏。汇编里写的非常清楚:

\n
1
2
3
4
5
6
7
8
9
10
11
12
0000000000401a8b <phase_secret>:
401a8b:\tf3 0f 1e fa \tendbr64
401a8f:\t55 \tpush %rbp
401a90:\t48 89 e5 \tmov %rsp,%rbp
401a93:\t48 83 ec 10 \tsub $0x10,%rsp
401a97:\t48 89 7d f8 \tmov %rdi,-0x8(%rbp)
401a9b:\t48 8d 05 26 4b 00 00 \tlea 0x4b26(%rip),%rax # 4065c8 <_IO_stdin_used+0x5c8>
401aa2:\t48 89 c7 \tmov %rax,%rdi
401aa5:\te8 76 f6 ff ff \tcall 401120 <puts@plt>
401aaa:\t90 \tnop
401aab:\tc9 \tleave
401aac:\tc3 \tret
\n

注意到这段指令在原程序中完全没有执行说明是需要用户自己跳转的,也非常简单只需要在phase_5中设计指令时加一个要求跳转到0x401a8b即可。

\n

完结
\"Case

\n","excerpt":"","more":"

这篇文章记录高地CSAPP课程Bomblab实验操作流程,仅供参考交流(答案是随机生成的和学号相关)。

\n

笔者实验环境为Archlinux/CachyOS,使用lldb作为调试器(和gdb操作差不多),其余用到的工具主要为objdump,strings,neovim/helix和zellij,全程开源环境不使用IDA。

\n

Phase_1

静态分析

strings扫描

1
strings bomb_linux
\n

先用strings寻找可能与phase_1相关的字符串或函数名,运气好说不定能直接找到密码毕竟是第一题。
\"strings\"

\n
    \n
  • 结果没有明文密码无法直接秒掉第一问,可惜。
  • \n
  • 但是找到GenerateRandomString函数可能与密码生成相关。
  • \n
\n

objdump反汇编

1
objdump -d bomb_linux > bomb.asm
\n

搜索GenerateRandomStringphase_1函数的汇编代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
401b53 <phase_1>:
401b53: endbr64
401b57: push %rbp
401b58: mov %rsp,%rbp
401b5b: sub $0x20,%rsp
401b5f: mov %rdi,-0x18(%rbp)
401b63: lea -0xb(%rbp),%rax
401b67: mov %rax,%rdi
401b6a: callq 401ac1 <GenerateRandomString> # 调用密码生成函数
401b6f: lea -0xb(%rbp),%rdx # 生成的字符串地址%rbp-0xb存入%rdx,即密码存储位置
401b73: mov -0x18(%rbp),%rax
401b77: mov %rdx,%rsi
401b7a: mov %rax,%rdi
401b7d: callq 401c0c <string_compare> # 调用字符串比较函数
401b82: test %eax,%eax
401b84: je 401b8d <phase_1+0x3a>
401b86: callq 401d67 <explode_bomb> # 比较失败则引爆炸弹

\n
    \n
  • phase_1调用GenerateRandomString生成一个字符串。
  • \n
  • 用户输入的字符串需要与此生成的字符串完全匹配。
  • \n
\n
\n

动态调试

\"phase_1\"
下面是phase_1求解的完整流程:

\n
1
2
3
4
5
6
7
8
lldb bomb_linux <你的学号后六位>
(lldb) b phase_1 # 在phase_1入口断点
(lldb) run # 从入口开始执行
请输入第1级的密码:114514 # 随便输入触发断点
(lldb) b 0x401b6f # 在GenerateRandomString返回后断点
(lldb) continue # 继续执行
(lldb) x/s $rbp - 0xb # 计算字符串地址(-0xb偏移量)
0x7fffffffdaf5: "mJHurpQZtY" # 轻松拿下,这里是根据学号伪随机生成的哦
\n

将得到的密码保存入bomb_<学号后六位>.txt即可,避免后续重复输入。

\n
\n

Phase_2

静态分析

这道题目还是比较一目了然的,观察phase_2代码不难发现其实构建了一张跳转表:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
0000000000401b8e <phase_2>:
401b8e:\tf3 0f 1e fa \tendbr64
401b92:\t55 \tpush %rbp
401b93:\t48 89 e5 \tmov %rsp,%rbp
401b96:\t48 83 ec 10 \tsub $0x10,%rsp
401b9a:\t48 89 7d f8 \tmov %rdi,-0x8(%rbp)
401b9e:\tbf 10 00 00 00 \tmov $0x10,%edi
401ba3:\te8 05 fb ff ff \tcall 4016ad <GenerateRandomNumber>
401ba8:\t48 8b 05 71 6c 00 00 \tmov 0x6c71(%rip),%rax # 408820 <rand_div>
401baf:\t48 83 f8 0f \tcmp $0xf,%rax
401bb3:\t0f 87 16 01 00 00 \tja 401ccf <phase_2+0x141>
401bb9:\t48 8d 14 85 00 00 00 \tlea 0x0(,%rax,4),%rdx
401bc0:\t00
401bc1:\t48 8d 05 4c 4a 00 00 \tlea 0x4a4c(%rip),%rax # 406614 <_IO_stdin_used+0x614>
401bc8:\t8b 04 02 \tmov (%rdx,%rax,1),%eax
401bcb:\t48 98 \tcltq
401bcd:\t48 8d 15 40 4a 00 00 \tlea 0x4a40(%rip),%rdx # 406614 <_IO_stdin_used+0x614>
401bd4:\t48 01 d0 \tadd %rdx,%rax
401bd7:\t3e ff e0 \tnotrack jmp *%rax
401bda:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax
401bde:\t48 89 c7 \tmov %rax,%rdi
401be1:\te8 f2 00 00 00 \tcall 401cd8 <phase_2_0>
401be6:\te9 ea 00 00 00 \tjmp 401cd5 <phase_2+0x147>
401beb:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax
401bef:\t48 89 c7 \tmov %rax,%rdi
401bf2:\te8 8b 01 00 00 \tcall 401d82 <phase_2_1>
401bf7:\te9 d9 00 00 00 \tjmp 401cd5 <phase_2+0x147>
401bfc:\t48 8b 45 f8 \tmov -0x8(%rbp),%rax
401c00:\t48 89 c7 \tmov %rax,%rdi
...
\n

这里面需要注意的关键点是rand_div,它会决定你的跳转方向,而你的学号又决定了它的取值。然后是GenerateRandomNumber这个函数的原理需要了解一下,而这个函数将在跳转前后分别调用一次,第一次决定你的跳转方向,第二次则决定了你的密码线索。

\n
\n

动态调试

理解原理就没什么难度了,自己找几个断点打好然后关注一下rand_div的值就好,观察自己的学号向哪个函数跳转并理解相应函数计算即可,比如我这里向phase_2_14跳转:
\"phase_2_14\"

\n

而除了phase_2_14还有其他函数也是非常好理解的,第二题依旧可以轻松拿下。

\n
\n

Phase_3

静态分析

和Phase_2一样开局先跳转尽可能防止同学们答案雷同互相帮助(bushi

\n

本体其实没有什么好说的,这里我跳转的方向是Phase_3_5简要解释一下可供参考:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
0000000000403001 <phase_3_5>:
403001:\tf3 0f 1e fa \tendbr64
403005:\t55 \tpush %rbp
403006:\t48 89 e5 \tmov %rsp,%rbp
403009:\t48 83 ec 20 \tsub $0x20,%rsp
40300d:\t48 89 7d e8 \tmov %rdi,-0x18(%rbp)
403011:\tc7 45 fc 00 00 00 00 \tmovl $0x0,-0x4(%rbp)
403018:\tc7 45 f8 00 00 00 00 \tmovl $0x0,-0x8(%rbp)
40301f:\t48 8d 4d f0 \tlea -0x10(%rbp),%rcx
403023:\t48 8d 55 f4 \tlea -0xc(%rbp),%rdx
403027:\t48 8b 45 e8 \tmov -0x18(%rbp),%rax
40302b:\t48 8d 35 5a 36 00 00 \tlea 0x365a(%rip),%rsi # 40668c <_IO_stdin_used+0x68c>
403032:\t48 89 c7 \tmov %rax,%rdi
403035:\tb8 00 00 00 00 \tmov $0x0,%eax
40303a:\te8 51 e1 ff ff \tcall 401190 <__isoc99_sscanf@plt>
40303f:\t89 45 f8 \tmov %eax,-0x8(%rbp)
403042:\t83 7d f8 01 \tcmpl $0x1,-0x8(%rbp)
403046:\t7f 05 \tjg 40304d <phase_3_5+0x4c>
403048:\te8 a9 2b 00 00 \tcall 405bf6 <explode_bomb>
40304d:\tbf 08 00 00 00 \tmov $0x8,%edi
403052:\te8 56 e6 ff ff \tcall 4016ad <GenerateRandomNumber>
403057:\t8b 45 f4 \tmov -0xc(%rbp),%eax
40305a:\t48 63 d0 \tmovslq %eax,%rdx
40305d:\t48 8b 05 bc 57 00 00 \tmov 0x57bc(%rip),%rax # 408820 <rand_div>
403064:\t48 39 c2 \tcmp %rax,%rdx
403067:\t74 05 \tje 40306e <phase_3_5+0x6d>
403069:\te8 88 2b 00 00 \tcall 405bf6 <explode_bomb>
40306e:\tbf c8 00 00 00 \tmov $0xc8,%edi
403073:\te8 35 e6 ff ff \tcall 4016ad <GenerateRandomNumber>
403078:\t8b 45 f4 \tmov -0xc(%rbp),%eax
40307b:\t83 f8 07 \tcmp $0x7,%eax
40307e:\t0f 87 eb 00 00 00 \tja 40316f <phase_3_5+0x16e>
403084:\t89 c0 \tmov %eax,%eax
403086:\t48 8d 14 85 00 00 00 \tlea 0x0(,%rax,4),%rdx
40308d:\t00
40308e:\t48 8d 05 9f 36 00 00 \tlea 0x369f(%rip),%rax # 406734 <_IO_stdin_used+0x734>
403095:\t8b 04 02 \tmov (%rdx,%rax,1),%eax
403098:\t48 98 \tcltq
40309a:\t48 8d 15 93 36 00 00 \tlea 0x3693(%rip),%rdx # 406734 <_IO_stdin_used+0x734>
4030a1:\t48 01 d0 \tadd %rdx,%rax
4030a4:\t3e ff e0 \tnotrack jmp *%rax
4030a7:\t48 8b 05 72 57 00 00 \tmov 0x5772(%rip),%rax # 408820 <rand_div>
4030ae:\t89 c2 \tmov %eax,%edx
4030b0:\t8b 45 fc \tmov -0x4(%rbp),%eax
4030b3:\t01 d0 \tadd %edx,%eax
4030b5:\t89 45 fc \tmov %eax,-0x4(%rbp)
4030b8:\tbf c8 00 00 00 \tmov $0xc8,%edi
4030bd:\te8 eb e5 ff ff \tcall 4016ad <GenerateRandomNumber>
...
403174:\t8b 45 f0 \tmov -0x10(%rbp),%eax
403177:\t39 45 fc \tcmp %eax,-0x4(%rbp) # 注意这里
40317a:\t74 05 \tje 403181 <phase_3_5+0x180>
40317c:\te8 75 2a 00 00 \tcall 405bf6 <explode_bomb>
403181:\t90 \tnop
403182:\tc9 \tleave
403183:\tc3 \tret

\n

看起来一大堆很吓人对不对?实际上确实很吓人。

\n

但是发现其中玄机后其实简单的没边,最终答案就藏在0x403177里面,前提是确保这一步前炸弹不爆炸(意识到要爆炸了直接run一下重开qwq)。

\n
\n

动态调试

阅读Phase_3_5发现这一关其实需要两个输入,并且第一个输入必须是rand_div,这里建议通过si单步执行监控好rand_div值变化,确定正确结果后使用run重开正确输入第一个密码后才能进行下一步求解:

\n
1
2
3
4
5
6
7
8
9
10
11
(lldb) si
Process 13376 stopped
* thread #1, name = 'bomb_linux', stop reason = instruction step into
frame #0: 0x000000000040317a bomb_linux`phase_3_5 + 377
bomb_linux`phase_3_5:
-> 0x40317a <+377>: je 0x403181 ; <+384>
0x40317c <+379>: callq 0x405bf6 ; explode_bomb
0x403181 <+384>: nop
0x403182 <+385>: leave
(lldb) x/wx $rbp-0x4
0x7fffffffdb0c: 0xffffffd7
\n

例如这里我可以打印出第二个值结合第一个值得到第三关正确结果。

\n
\n

Phase_4

静态分析

本题依旧开局跳转,笔者的跳转方向是phase_4_01,如何跳转不再强调关注rand_div的值即可,下面请D指导解读一下phase_4_01的内容:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
0000000000404895 <phase_4_01>:
; 函数入口,初始化栈帧
404895:\tf3 0f 1e fa \tendbr64
404899:\t55 \tpush %rbp
40489a:\t48 89 e5 \tmov %rsp,%rbp
40489d:\t48 83 ec 70 \tsub $0x70,%rsp ; 分配栈空间

; 初始化斐波那契数组(F(10)~F(24)的十六进制值)
4048a1:\t48 89 7d 98 \tmov %rdi,-0x68(%rbp) ; 保存输入字符串指针
4048a5:\tc7 45 b0 37 00 00 00 \tmovl $0x37,-0x50(%rbp) ; F(10)=55
4048ac:\tc7 45 b4 59 00 00 00 \tmovl $0x59,-0x4c(%rbp) ; F(11)=89
4048b3:\tc7 45 b8 90 00 00 00 \tmovl $0x90,-0x48(%rbp) ; F(12)=144
4048ba:\tc7 45 bc e9 00 00 00 \tmovl $0xe9,-0x44(%rbp) ; F(13)=233
4048c1:\tc7 45 c0 79 01 00 00 \tmovl $0x179,-0x40(%rbp) ; F(14)=377
4048c8:\tc7 45 c4 62 02 00 00 \tmovl $0x262,-0x3c(%rbp) ; F(15)=610
4048cf:\tc7 45 c8 db 03 00 00 \tmovl $0x3db,-0x38(%rbp) ; F(16)=987
4048d6:\tc7 45 cc 3d 06 00 00 \tmovl $0x63d,-0x34(%rbp) ; F(17)=1597
4048dd:\tc7 45 d0 18 0a 00 00 \tmovl $0xa18,-0x30(%rbp) ; F(18)=2584
4048e4:\tc7 45 d4 55 10 00 00 \tmovl $0x1055,-0x2c(%rbp) ; F(19)=4181
4048eb:\tc7 45 d8 6d 1a 00 00 \tmovl $0x1a6d,-0x28(%rbp) ; F(20)=6765
4048f2:\tc7 45 dc c2 2a 00 00 \tmovl $0x2ac2,-0x24(%rbp) ; F(21)=10946
4048f9:\tc7 45 e0 2f 45 00 00 \tmovl $0x452f,-0x20(%rbp) ; F(22)=17711
404900:\tc7 45 e4 f1 6f 00 00 \tmovl $0x6ff1,-0x1c(%rbp) ; F(23)=28657
404907:\tc7 45 e8 20 b5 00 00 \tmovl $0xb520,-0x18(%rbp) ; F(24)=46368

; 读取输入到局部变量(格式为"%d")
40490e:\t48 8d 55 ac \tlea -0x54(%rbp),%rdx ; 输入存储地址
404912:\t48 8b 45 98 \tmov -0x68(%rbp),%rax ; 输入字符串
404916:\t48 8d 0d 93 1f 00 00 \tlea 0x1f93(%rip),%rcx ; 格式字符串"%d"
40491d:\t48 89 ce \tmov %rcx,%rsi
404920:\t48 89 c7 \tmov %rax,%rdi
404923:\tb8 00 00 00 00 \tmov $0x0,%eax
404928:\te8 63 c8 ff ff \tcall 401190 <__isoc99_sscanf@plt>

; 验证输入有效性(必须为1个正数)
40492d:\t89 45 fc \tmov %eax,-0x4(%rbp) ; sscanf返回值
404930:\t83 7d fc 01 \tcmpl $0x1,-0x4(%rbp) ; 检查是否读取1个参数
404934:\t75 07 \tjne 40493d <phase_4_01+0xa8> ; 失败则爆炸
404936:\t8b 45 ac \tmov -0x54(%rbp),%eax ; 获取输入值N
404939:\t85 c0 \ttest %eax,%eax ; 检查N > 0
40493b:\t7f 05 \tjg 404942 <phase_4_01+0xad>
40493d:\te8 b4 12 00 00 \tcall 405bf6 <explode_bomb>

; 检查输入值上限(必须 > 1999)
404942:\t8b 45 ac \tmov -0x54(%rbp),%eax
404945:\t3d cf 07 00 00 \tcmp $0x7cf,%eax ; 1999的十六进制
40494a:\t7f 05 \tjg 404951 <phase_4_01+0xbc> ; N > 1999?
40494c:\te8 a5 12 00 00 \tcall 405bf6 <explode_bomb>

; 计算 N/2000(通过定点数乘法优化)
404951:\t8b 45 ac \tmov -0x54(%rbp),%eax ; 输入值N
404954:\t48 63 d0 \tmovslq %eax,%rdx ; 符号扩展
404957:\t48 69 d2 d3 4d 62 10 \timul $0x10624dd3,%rdx,%rdx ; 乘以274877907(≈2^32/2000)
40495e:\t48 c1 ea 20 \tshr $0x20,%rdx ; 取高32位
404962:\tc1 fa 07 \tsar $0x7,%edx ; 算术右移7位 → N/2000
404965:\tc1 f8 1f \tsar $0x1f,%eax ; 符号位扩展
404968:\t89 c1 \tmov %eax,%ecx
40496a:\t89 d0 \tmov %edx,%eax
40496c:\t29 c8 \tsub %ecx,%eax ; 处理负数情况
40496e:\t89 45 ac \tmov %eax,-0x54(%rbp) ; 保存k = N/2000

; 调用递归函数func4_0(k), 这个函数用于计算斐波那契数列
404971:\t8b 45 ac \tmov -0x54(%rbp),%eax
404974:\t89 c7 \tmov %eax,%edi ; 参数k
404976:\te8 ce fd ff ff \tcall 404749 <func4_0> ; 返回值eax=F(k+1)
40497b:\t89 45 f8 \tmov %eax,-0x8(%rbp) ; 保存结果

; 生成随机索引并验证结果
40497e:\tbf 0f 00 00 00 \tmov $0xf,%edi ; 参数15
404983:\te8 25 cd ff ff \tcall 4016ad <GenerateRandomNumber> ; 生成0~14随机数
404988:\t48 8b 05 91 3e 00 00 \tmov 0x3e91(%rip),%rax # 408820 <rand_div> ; 获取随机索引
40498f:\t8b 44 85 b0 \tmov -0x50(%rbp,%rax,4),%eax ; 取数组[rand_div]的值
404993:\t39 45 f8 \tcmp %eax,-0x8(%rbp) ; 比较func4_0(k) == 数组值?
404996:\t74 05 \tje 40499d <phase_4_01+0x108>
404998:\te8 59 12 00 00 \tcall 405bf6 <explode_bomb>
\n

所以相对还是很明了的,依旧是关注rand_div

\n

动态调试

先找出rand_div在最后判断前的取值,比如我下面的0xa:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(lldb) si
Process 27027 stopped
* thread #1, name = 'bomb_linux', stop reason = instruction step into
frame #0: 0x0000000000401719 bomb_linux`GenerateRandomNumber + 108
bomb_linux`GenerateRandomNumber:
-> 0x401719 <+108>: movq %rax, 0x7100(%rip) ; rand_div
0x401720 <+115>: jmp 0x401723 ; <+118>
0x401722 <+117>: nop
0x401723 <+118>: popq %rbp
(lldb) si
Process 27027 stopped
* thread #1, name = 'bomb_linux', stop reason = instruction step into
frame #0: 0x0000000000401720 bomb_linux`GenerateRandomNumber + 115
bomb_linux`GenerateRandomNumber:
-> 0x401720 <+115>: jmp 0x401723 ; <+118>
0x401722 <+117>: nop
0x401723 <+118>: popq %rbp
0x401724 <+119>: retq
(lldb) x/gx &rand_div
0x00408820: 0x000000000000000a
\n\n

而当 rand_div = 0xa(即十进制 10)时,输入值 N 的计算步骤如下:

\n
    \n
  • 数组索引 10 的值是 斐波那契数列第 20 项F(20) = 6765)。

    \n
  • \n
  • func4_0(k) 实际计算的是 标准斐波那契数列的第 k+1(例如,func4_0(0) = 1 = F(2)) 需要满足:

    \n
    1
    func4_0(k) = F(k+1) = F(20)
    \n

    解得:
    k + 1 = 20 → k = 19

    \n
  • \n
  • k = N / 2000N = 2000 * k = 2000 * 19 = 38000.
    从而得解。
    \"phase_4\"

    \n
  • \n
\n
\n

Phase_Impossible

Impossible?

\n

从这道题开始偷懒了,掏出ghidra直接看c代码了解一下大概流程再去objdump看汇编:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
void phase_impossible(char *param_1)

{
int iVar1;
size_t sVar2;
undefined local_118 [256];
long local_18;
long local_10;

local_10 = GetTickCount();
sVar2 = strlen(param_1);
if ((sVar2 < 10) || (sVar2 = strlen(param_1), 0x300 < sVar2)) {
explode_bomb();
}
memset(local_118,0,0x100);
tohex(local_118,param_1);
GenerateRandomNumber(0x400);
iVar1 = check_buf_valid(local_118,rand_div & 0xffffffff);
if (iVar1 == 0) {
puts(&DAT_00406518);
explode_bomb();
}
GenerateRandomNumber(3);
if (rand_div != 2) {
if (2 < rand_div) goto LAB_00401891;
if (rand_div == 0) {
goto_buf_0(local_118);
}
else if (rand_div != 1) goto LAB_00401891;
goto_buf_1(local_118);
}
goto_buf_2(local_118);
LAB_00401891:
explode_bomb();
GenerateRandomNumber(0x400);
if ((long)(int)result != rand_div) {
printf(&DAT_00406560,rand_div,(ulong)result);
explode_bomb();
}
local_18 = GetTickCount();
if (1000 < (ulong)(local_18 - local_10)) {
puts(&DAT_004065a8);
explode_bomb();
}
return;
}
\n

最终任务还是很明确的,需要写一段机器码修改result的数值,但是注意要能通过check_buf_valid检测,并且最后指令必须是跳转到0x401896不然就会触发phase_impossible0x401891处的explode_bomb函数,唯一的难点是跟踪rand_div的数值变化,建议使用register write来修改check_buf_valid的返回值使其强制通过然后监控rand_div每一次的数值变化(x/gx &rand_div),记录好rand_div的结果后开始指令设计,需要满足:

\n
    \n
  • 指令的异或和为rand_div第一次的数值末尾八位以通过检查;
  • \n
  • 修改result使其数值等于rand_div第三次数值;
  • \n
  • 跳转到0x401896避免炸弹;
  • \n
\n

如果前几问都完成了到这里应该是没有问题的。

\n
\n

Phase_Secret

隐藏彩蛋,并非隐藏。汇编里写的非常清楚:

\n
1
2
3
4
5
6
7
8
9
10
11
12
0000000000401a8b <phase_secret>:
401a8b:\tf3 0f 1e fa \tendbr64
401a8f:\t55 \tpush %rbp
401a90:\t48 89 e5 \tmov %rsp,%rbp
401a93:\t48 83 ec 10 \tsub $0x10,%rsp
401a97:\t48 89 7d f8 \tmov %rdi,-0x8(%rbp)
401a9b:\t48 8d 05 26 4b 00 00 \tlea 0x4b26(%rip),%rax # 4065c8 <_IO_stdin_used+0x5c8>
401aa2:\t48 89 c7 \tmov %rax,%rdi
401aa5:\te8 76 f6 ff ff \tcall 401120 <puts@plt>
401aaa:\t90 \tnop
401aab:\tc9 \tleave
401aac:\tc3 \tret
\n

注意到这段指令在原程序中完全没有执行说明是需要用户自己跳转的,也非常简单只需要在phase_5中设计指令时加一个要求跳转到0x401a8b即可。

\n

完结
\"Case

\n"},{"title":"面向竞赛环境的Linux系统性能调优实践","date":"2026-01-18T04:14:04.000Z","_content":"\n最初,我的想法是现在大伙都有AI工具了码力差距将不再像过去那么明显,这种情况下系统调优一定是有实战价值的。尤其是在面对极端计算任务时,操作系统内核的调度策略和中断处理方式对性能必然有着直接且显著的影响。为了在计算密集型任务(如本次天体物理模拟)中获得极致的性能与确定性,我作为运维必须尝试对Linux内核进行深度定制与隔离。\n\n本文将详细记录我在Intel Sapphire Rapids平台(Dual Xeon 8480P+)上进行的系统调优探索。通过内核参数隔离、用户态调度器(sched_ext)以及IRQ亲和性管理,我成功在AMSS-NCKU双黑洞模拟算例中取得了肉眼可见的性能收益。\n\n## 1. 测试环境与硬件拓扑\n\n本次调优的硬件平台具备极高的并行计算能力,但NUMA架构带来的延迟敏感性也对系统配置提出了挑战:\n\n* **CPU**: 2x Intel Xeon Platinum 8480+ (Sapphire Rapids)\n* **核心配置**: 单路56核心,双路共112物理核心。\n* **超线程 (Hyper-Threading)**: **Disabled**(为了避免逻辑核争抢浮点单元及缓存资源,确保计算流水线的独占性)。\n* **GPU**: 无(纯CPU计算任务)。\n\n针对该拓扑,我制定了**“核心隔离(Core Isolation)”**策略:将系统中断、管理进程及内核Housekeeping任务通过`cpuset`和内核参数限制在特定的少数核心上(Housekeeping Cores),将绝大多数核心完全腾空,仅运行计算任务(Isolation Cores)。\n\n* **Housekeeping Cores**: 0-3 (Socket 0), 56-59 (Socket 1) — 共8核。\n* **Isolation Cores**: 4-55 (Socket 0), 60-111 (Socket 1) — 共104核,用于纯计算。\n\n## 2. 内核启动参数(Kernel Command Line)深度解析\n\n首先需要确定的是这次我将选择Rockylinux 10作为操作系统基础,内核版本为6.12是首个支持sched_ext调度器的稳定版本,并且Rockylinux 10首次完全基于x86-64-v3构建,相比其他发行版有着必然的性能优势,而少数比它性能更好的发行版(CachyOS和已经逝世的Clearlinux)又没它稳定还不受nvidia ofed驱动支持,考虑到这些因素我不会多看其他任何发行版一秒。(现在都是2026年了坚决抵制CentOS 7/8或Rockylinux 8信徒遗老,这么老的内核挂个exFAT硬盘传东西都费劲)。\n\n为了实现我预期的隔离策略,我使用grubby在GRUB配置中启用了以下内核参数。这些参数共同构成了一个低延迟、低抖动的运行环境。\n\n```bash\ntransparent_hugepage=never iommu=pt mitigations=off \\\nnohz_full=4-55,60-111 rcu_nocbs=4-55,60-111 \\\nisolcpus=managed_irq,4-55,60-111\n```\n\n### 2.1 内存与IO子系统优化\n\n* **`transparent_hugepage=never`**: 禁用透明大页(THP)。虽然THP能减少TLB Miss,但在高强度的随机内存访问或频繁申请释放场景下,THP的后台合并线程(khugepaged)会引入不可预测的延迟抖动(Jitter)。\n* **`iommu=pt`**: 将IOMMU设置为Pass-Through模式。这允许设备在DMA传输时直接使用物理地址,绕过IOMMU的地址转换层,显著降低了IO密集型操作的CPU开销,同时保留了IOMMU的基本功能。\n\n### 2.2 安全缓解措施禁用\n\n* **`mitigations=off`**(让CPU回归其原本的样子): 该参数关闭了针对Spectre、Meltdown等侧信道攻击的所有CPU漏洞缓解措施。这些缓解措施通常涉及频繁的页表隔离(PTI)和间接分支预测限制,在旧时代这会带来非常明显的性能损耗(通常在15%-25%之间),但是在今天能够提供的性能提升根据之前不知道在哪里看到的Intel测评已经不到5%了。在追求极致速度的物理隔离环境中,我还是要物尽其用,认为关闭它们是提升IPC(Instructions Per Cycle)的必要手段。\n\n### 2.3 核心隔离与时钟中断消除 (核心措施)\n\n* **`nohz_full=4-55,60-111`**: 启用Full Dynticks模式。在指定的核心上,如果运行队列中只有一个任务(即我们的计算进程),内核将停止发送调度时钟中断(Tick)。这消除了周期性的上下文切换干扰,实现了“无干扰(Tickless)”执行。\n* **`rcu_nocbs=4-55,60-111`**: 将指定核心上的RCU(Read-Copy-Update)回调函数卸载到非隔离核心(Housekeeping cores)的线程中执行。这防止了RCU软中断打断关键计算路径。\n* **`isolcpus=managed_irq,4-55,60-111`**: 将指定核心从内核的通用SMP均衡调度器中移除,确保普通进程不会被随机调度到这些核心上。\n* **`managed_irq`** 标志进一步指示内核尽量避免将受管中断(Managed Interrupts)分配给这些核心,为后续的用户态中断控制打下基础。\n\n## 3. 用户态环境与调度优化\n\n除了内核层面的静态配置,运行时的动态调优同样关键。\n\n### 3.1 基础系统调优 (`tuned`)\n\n```bash\ntuned-adm profile hpc-compute\n```\n\n启用RedHat系的`hpc-compute`配置文件。该Profile会自动调整虚拟内存参数(如`vm.dirty_ratio`)、网络栈缓冲区大小以及CPU的电源管理策略(强制最大C-State为C1或C0),确保CPU始终处于高性能状态,避免频率升降带来的延迟。\n\n### 3.2 scx调度器:sched_ext (`scx_tickless`)\n\n```bash\n/usr/local/bin/scx_tickless -m 0x0F0000000000000F\n```\n\n利用较新的Linux内核特性(BPF-based extensible scheduler class),我引入了`scx_tickless`调度器。\n\n* 该调度器由英伟达工程师设计,通过eBPF程序接管调度决策,相比完全公平调度器(CFS),它能更激进地维持计算核心的Tickless状态。\n* 参数 `-m 0x0F0000000000000F` 是一个掩码配置,精确指定了Housekeeping核心的拓扑位置,确保调度器仅在管理核心上处理必要的调度逻辑,从而保护计算核心的“静默”状态。\n\n### 3.3 中断亲和性管理 (`irqbalance`)\n\n为了防止网卡、磁盘控制器的硬中断打断计算流水线,必须严格限制中断响应的核心范围。\n\n```bash\n# 配置文件/etc/sysconfig/irqbalance中设置\nIRQBALANCE_BANNED_CPUS=\"FFFFFFFFFFFFF0FFFFFFFFFFFFF0\"\n```\n\n这是一个64位/128位的十六进制掩码。该配置显式**禁止**了所有隔离核心(即计算核心)参与中断负载均衡。结果是所有硬件中断都被强行“钉”在了Housekeeping Cores(Socket 0的0-3和Socket 1的56-59)上,保证了计算核心100%的CPU时间片用于业务逻辑。\n\n### 3.4 进程绑核 (`taskset`)\n\n最后,在启动计算任务时,最好显式指定亲和性不能光依赖scx_tickless调度,防止跨NUMA节点的内存访问惩罚。\n\n```makefile_and_run.py\n# 添加一个启动脚本变量\nNUMACTL_CPU_BIND = \"taskset -c 4-55,60-111\"\n```\n\n结合`isolcpus`,这确保了计算线程被牢牢锁定在无干扰的核心上,且每个线程独占物理核L1/L2缓存。\n\n## 4. 性能验证与结论\n\n**测试用例**: AMSS-NCKU双黑洞模拟(基于广义相对论数值解法)。\n**评估指标**: 单个时间步(Time Step)的计算耗时。\n\n在应用上述全套优化措施后,系统表现出极高的确定性。对比默认配置(Default CFS + On-Demand Power + Default IRQ Balance):\n\n1. **稳定性提升**: 计算过程中的抖动(Jitter)几乎消失,btop监控可以看到各核心负载曲线呈现完美的平直线。\n2. **性能收益**:\n\n* 在计算前期,单步时间步稳定降低 **0.1 - 0.2秒**。\n* 随着模拟进入后期(网格加密或相互作用增强),计算密度增加,该优化带来的累计收益更为显著。\n* 48核心运行总时间预计缩短约 **800秒**。\n\n### 总结\n\n过去参加比赛就没有一次是能够在限定功耗下启用所有核心不降频全力跑程序的,我不禁思考既然用不完何不将单核计算性能调优做到极致,牺牲一些核心处理杂活不参与计算,剩余的核心全力以赴跑程序不要受干扰最好也不要降频,这就是我的大致思路。\n\n通过`nohz_full`与`isolcpus`实现核心隔离,配合`mitigations=off`释放硬件潜能,并利用`scx_tickless`进行用户态调度增强,我按照设想将这台Xeon 8480+服务器改造成了一个OS Noise几乎消除的专用计算平台。后续需要关注的问题是迁移服务器后如何快速重现这些调优配置,或者干脆设计动态调控脚本来根据硬件信息调整内核参数。另外还有多机运行也没有进行测试,未来会关注在MPI环境下验证这些调优措施的适用性。\n\n---\n\n### 赛后更新\n\n散了吧孩子们,H100/H200已经杀死比赛了:(\n","source":"_posts/os-optimization.md","raw":"---\ntitle: 面向竞赛环境的Linux系统性能调优实践\ndate: 2026-01-18 12:14:04\ntags: [技术, 系统优化, HPC, Linux内核]\ncategories: [技术分享,HPC]\n---\n\n最初,我的想法是现在大伙都有AI工具了码力差距将不再像过去那么明显,这种情况下系统调优一定是有实战价值的。尤其是在面对极端计算任务时,操作系统内核的调度策略和中断处理方式对性能必然有着直接且显著的影响。为了在计算密集型任务(如本次天体物理模拟)中获得极致的性能与确定性,我作为运维必须尝试对Linux内核进行深度定制与隔离。\n\n本文将详细记录我在Intel Sapphire Rapids平台(Dual Xeon 8480P+)上进行的系统调优探索。通过内核参数隔离、用户态调度器(sched_ext)以及IRQ亲和性管理,我成功在AMSS-NCKU双黑洞模拟算例中取得了肉眼可见的性能收益。\n\n## 1. 测试环境与硬件拓扑\n\n本次调优的硬件平台具备极高的并行计算能力,但NUMA架构带来的延迟敏感性也对系统配置提出了挑战:\n\n* **CPU**: 2x Intel Xeon Platinum 8480+ (Sapphire Rapids)\n* **核心配置**: 单路56核心,双路共112物理核心。\n* **超线程 (Hyper-Threading)**: **Disabled**(为了避免逻辑核争抢浮点单元及缓存资源,确保计算流水线的独占性)。\n* **GPU**: 无(纯CPU计算任务)。\n\n针对该拓扑,我制定了**“核心隔离(Core Isolation)”**策略:将系统中断、管理进程及内核Housekeeping任务通过`cpuset`和内核参数限制在特定的少数核心上(Housekeeping Cores),将绝大多数核心完全腾空,仅运行计算任务(Isolation Cores)。\n\n* **Housekeeping Cores**: 0-3 (Socket 0), 56-59 (Socket 1) — 共8核。\n* **Isolation Cores**: 4-55 (Socket 0), 60-111 (Socket 1) — 共104核,用于纯计算。\n\n## 2. 内核启动参数(Kernel Command Line)深度解析\n\n首先需要确定的是这次我将选择Rockylinux 10作为操作系统基础,内核版本为6.12是首个支持sched_ext调度器的稳定版本,并且Rockylinux 10首次完全基于x86-64-v3构建,相比其他发行版有着必然的性能优势,而少数比它性能更好的发行版(CachyOS和已经逝世的Clearlinux)又没它稳定还不受nvidia ofed驱动支持,考虑到这些因素我不会多看其他任何发行版一秒。(现在都是2026年了坚决抵制CentOS 7/8或Rockylinux 8信徒遗老,这么老的内核挂个exFAT硬盘传东西都费劲)。\n\n为了实现我预期的隔离策略,我使用grubby在GRUB配置中启用了以下内核参数。这些参数共同构成了一个低延迟、低抖动的运行环境。\n\n```bash\ntransparent_hugepage=never iommu=pt mitigations=off \\\nnohz_full=4-55,60-111 rcu_nocbs=4-55,60-111 \\\nisolcpus=managed_irq,4-55,60-111\n```\n\n### 2.1 内存与IO子系统优化\n\n* **`transparent_hugepage=never`**: 禁用透明大页(THP)。虽然THP能减少TLB Miss,但在高强度的随机内存访问或频繁申请释放场景下,THP的后台合并线程(khugepaged)会引入不可预测的延迟抖动(Jitter)。\n* **`iommu=pt`**: 将IOMMU设置为Pass-Through模式。这允许设备在DMA传输时直接使用物理地址,绕过IOMMU的地址转换层,显著降低了IO密集型操作的CPU开销,同时保留了IOMMU的基本功能。\n\n### 2.2 安全缓解措施禁用\n\n* **`mitigations=off`**(让CPU回归其原本的样子): 该参数关闭了针对Spectre、Meltdown等侧信道攻击的所有CPU漏洞缓解措施。这些缓解措施通常涉及频繁的页表隔离(PTI)和间接分支预测限制,在旧时代这会带来非常明显的性能损耗(通常在15%-25%之间),但是在今天能够提供的性能提升根据之前不知道在哪里看到的Intel测评已经不到5%了。在追求极致速度的物理隔离环境中,我还是要物尽其用,认为关闭它们是提升IPC(Instructions Per Cycle)的必要手段。\n\n### 2.3 核心隔离与时钟中断消除 (核心措施)\n\n* **`nohz_full=4-55,60-111`**: 启用Full Dynticks模式。在指定的核心上,如果运行队列中只有一个任务(即我们的计算进程),内核将停止发送调度时钟中断(Tick)。这消除了周期性的上下文切换干扰,实现了“无干扰(Tickless)”执行。\n* **`rcu_nocbs=4-55,60-111`**: 将指定核心上的RCU(Read-Copy-Update)回调函数卸载到非隔离核心(Housekeeping cores)的线程中执行。这防止了RCU软中断打断关键计算路径。\n* **`isolcpus=managed_irq,4-55,60-111`**: 将指定核心从内核的通用SMP均衡调度器中移除,确保普通进程不会被随机调度到这些核心上。\n* **`managed_irq`** 标志进一步指示内核尽量避免将受管中断(Managed Interrupts)分配给这些核心,为后续的用户态中断控制打下基础。\n\n## 3. 用户态环境与调度优化\n\n除了内核层面的静态配置,运行时的动态调优同样关键。\n\n### 3.1 基础系统调优 (`tuned`)\n\n```bash\ntuned-adm profile hpc-compute\n```\n\n启用RedHat系的`hpc-compute`配置文件。该Profile会自动调整虚拟内存参数(如`vm.dirty_ratio`)、网络栈缓冲区大小以及CPU的电源管理策略(强制最大C-State为C1或C0),确保CPU始终处于高性能状态,避免频率升降带来的延迟。\n\n### 3.2 scx调度器:sched_ext (`scx_tickless`)\n\n```bash\n/usr/local/bin/scx_tickless -m 0x0F0000000000000F\n```\n\n利用较新的Linux内核特性(BPF-based extensible scheduler class),我引入了`scx_tickless`调度器。\n\n* 该调度器由英伟达工程师设计,通过eBPF程序接管调度决策,相比完全公平调度器(CFS),它能更激进地维持计算核心的Tickless状态。\n* 参数 `-m 0x0F0000000000000F` 是一个掩码配置,精确指定了Housekeeping核心的拓扑位置,确保调度器仅在管理核心上处理必要的调度逻辑,从而保护计算核心的“静默”状态。\n\n### 3.3 中断亲和性管理 (`irqbalance`)\n\n为了防止网卡、磁盘控制器的硬中断打断计算流水线,必须严格限制中断响应的核心范围。\n\n```bash\n# 配置文件/etc/sysconfig/irqbalance中设置\nIRQBALANCE_BANNED_CPUS=\"FFFFFFFFFFFFF0FFFFFFFFFFFFF0\"\n```\n\n这是一个64位/128位的十六进制掩码。该配置显式**禁止**了所有隔离核心(即计算核心)参与中断负载均衡。结果是所有硬件中断都被强行“钉”在了Housekeeping Cores(Socket 0的0-3和Socket 1的56-59)上,保证了计算核心100%的CPU时间片用于业务逻辑。\n\n### 3.4 进程绑核 (`taskset`)\n\n最后,在启动计算任务时,最好显式指定亲和性不能光依赖scx_tickless调度,防止跨NUMA节点的内存访问惩罚。\n\n```makefile_and_run.py\n# 添加一个启动脚本变量\nNUMACTL_CPU_BIND = \"taskset -c 4-55,60-111\"\n```\n\n结合`isolcpus`,这确保了计算线程被牢牢锁定在无干扰的核心上,且每个线程独占物理核L1/L2缓存。\n\n## 4. 性能验证与结论\n\n**测试用例**: AMSS-NCKU双黑洞模拟(基于广义相对论数值解法)。\n**评估指标**: 单个时间步(Time Step)的计算耗时。\n\n在应用上述全套优化措施后,系统表现出极高的确定性。对比默认配置(Default CFS + On-Demand Power + Default IRQ Balance):\n\n1. **稳定性提升**: 计算过程中的抖动(Jitter)几乎消失,btop监控可以看到各核心负载曲线呈现完美的平直线。\n2. **性能收益**:\n\n* 在计算前期,单步时间步稳定降低 **0.1 - 0.2秒**。\n* 随着模拟进入后期(网格加密或相互作用增强),计算密度增加,该优化带来的累计收益更为显著。\n* 48核心运行总时间预计缩短约 **800秒**。\n\n### 总结\n\n过去参加比赛就没有一次是能够在限定功耗下启用所有核心不降频全力跑程序的,我不禁思考既然用不完何不将单核计算性能调优做到极致,牺牲一些核心处理杂活不参与计算,剩余的核心全力以赴跑程序不要受干扰最好也不要降频,这就是我的大致思路。\n\n通过`nohz_full`与`isolcpus`实现核心隔离,配合`mitigations=off`释放硬件潜能,并利用`scx_tickless`进行用户态调度增强,我按照设想将这台Xeon 8480+服务器改造成了一个OS Noise几乎消除的专用计算平台。后续需要关注的问题是迁移服务器后如何快速重现这些调优配置,或者干脆设计动态调控脚本来根据硬件信息调整内核参数。另外还有多机运行也没有进行测试,未来会关注在MPI环境下验证这些调优措施的适用性。\n\n---\n\n### 赛后更新\n\n散了吧孩子们,H100/H200已经杀死比赛了:(\n","slug":"os-optimization","published":1,"updated":"2026-05-30T12:54:00.163Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudblx001midd0c8u7g9hr","content":"

最初,我的想法是现在大伙都有AI工具了码力差距将不再像过去那么明显,这种情况下系统调优一定是有实战价值的。尤其是在面对极端计算任务时,操作系统内核的调度策略和中断处理方式对性能必然有着直接且显著的影响。为了在计算密集型任务(如本次天体物理模拟)中获得极致的性能与确定性,我作为运维必须尝试对Linux内核进行深度定制与隔离。

\n

本文将详细记录我在Intel Sapphire Rapids平台(Dual Xeon 8480P+)上进行的系统调优探索。通过内核参数隔离、用户态调度器(sched_ext)以及IRQ亲和性管理,我成功在AMSS-NCKU双黑洞模拟算例中取得了肉眼可见的性能收益。

\n

1. 测试环境与硬件拓扑

本次调优的硬件平台具备极高的并行计算能力,但NUMA架构带来的延迟敏感性也对系统配置提出了挑战:

\n
    \n
  • CPU: 2x Intel Xeon Platinum 8480+ (Sapphire Rapids)
  • \n
  • 核心配置: 单路56核心,双路共112物理核心。
  • \n
  • 超线程 (Hyper-Threading): Disabled(为了避免逻辑核争抢浮点单元及缓存资源,确保计算流水线的独占性)。
  • \n
  • GPU: 无(纯CPU计算任务)。
  • \n
\n

针对该拓扑,我制定了“核心隔离(Core Isolation)”策略:将系统中断、管理进程及内核Housekeeping任务通过cpuset和内核参数限制在特定的少数核心上(Housekeeping Cores),将绝大多数核心完全腾空,仅运行计算任务(Isolation Cores)。

\n
    \n
  • Housekeeping Cores: 0-3 (Socket 0), 56-59 (Socket 1) — 共8核。
  • \n
  • Isolation Cores: 4-55 (Socket 0), 60-111 (Socket 1) — 共104核,用于纯计算。
  • \n
\n

2. 内核启动参数(Kernel Command Line)深度解析

首先需要确定的是这次我将选择Rockylinux 10作为操作系统基础,内核版本为6.12是首个支持sched_ext调度器的稳定版本,并且Rockylinux 10首次完全基于x86-64-v3构建,相比其他发行版有着必然的性能优势,而少数比它性能更好的发行版(CachyOS和已经逝世的Clearlinux)又没它稳定还不受nvidia ofed驱动支持,考虑到这些因素我不会多看其他任何发行版一秒。(现在都是2026年了坚决抵制CentOS 7/8或Rockylinux 8信徒遗老,这么老的内核挂个exFAT硬盘传东西都费劲)。

\n

为了实现我预期的隔离策略,我使用grubby在GRUB配置中启用了以下内核参数。这些参数共同构成了一个低延迟、低抖动的运行环境。

\n
1
2
3
transparent_hugepage=never iommu=pt mitigations=off \\
nohz_full=4-55,60-111 rcu_nocbs=4-55,60-111 \\
isolcpus=managed_irq,4-55,60-111
\n\n

2.1 内存与IO子系统优化

    \n
  • transparent_hugepage=never: 禁用透明大页(THP)。虽然THP能减少TLB Miss,但在高强度的随机内存访问或频繁申请释放场景下,THP的后台合并线程(khugepaged)会引入不可预测的延迟抖动(Jitter)。
  • \n
  • iommu=pt: 将IOMMU设置为Pass-Through模式。这允许设备在DMA传输时直接使用物理地址,绕过IOMMU的地址转换层,显著降低了IO密集型操作的CPU开销,同时保留了IOMMU的基本功能。
  • \n
\n

2.2 安全缓解措施禁用

    \n
  • mitigations=off(让CPU回归其原本的样子): 该参数关闭了针对Spectre、Meltdown等侧信道攻击的所有CPU漏洞缓解措施。这些缓解措施通常涉及频繁的页表隔离(PTI)和间接分支预测限制,在旧时代这会带来非常明显的性能损耗(通常在15%-25%之间),但是在今天能够提供的性能提升根据之前不知道在哪里看到的Intel测评已经不到5%了。在追求极致速度的物理隔离环境中,我还是要物尽其用,认为关闭它们是提升IPC(Instructions Per Cycle)的必要手段。
  • \n
\n

2.3 核心隔离与时钟中断消除 (核心措施)

    \n
  • nohz_full=4-55,60-111: 启用Full Dynticks模式。在指定的核心上,如果运行队列中只有一个任务(即我们的计算进程),内核将停止发送调度时钟中断(Tick)。这消除了周期性的上下文切换干扰,实现了“无干扰(Tickless)”执行。
  • \n
  • rcu_nocbs=4-55,60-111: 将指定核心上的RCU(Read-Copy-Update)回调函数卸载到非隔离核心(Housekeeping cores)的线程中执行。这防止了RCU软中断打断关键计算路径。
  • \n
  • isolcpus=managed_irq,4-55,60-111: 将指定核心从内核的通用SMP均衡调度器中移除,确保普通进程不会被随机调度到这些核心上。
  • \n
  • managed_irq 标志进一步指示内核尽量避免将受管中断(Managed Interrupts)分配给这些核心,为后续的用户态中断控制打下基础。
  • \n
\n

3. 用户态环境与调度优化

除了内核层面的静态配置,运行时的动态调优同样关键。

\n

3.1 基础系统调优 (tuned)

1
tuned-adm profile hpc-compute
\n\n

启用RedHat系的hpc-compute配置文件。该Profile会自动调整虚拟内存参数(如vm.dirty_ratio)、网络栈缓冲区大小以及CPU的电源管理策略(强制最大C-State为C1或C0),确保CPU始终处于高性能状态,避免频率升降带来的延迟。

\n

3.2 scx调度器:sched_ext (scx_tickless)

1
/usr/local/bin/scx_tickless -m 0x0F0000000000000F
\n\n

利用较新的Linux内核特性(BPF-based extensible scheduler class),我引入了scx_tickless调度器。

\n
    \n
  • 该调度器由英伟达工程师设计,通过eBPF程序接管调度决策,相比完全公平调度器(CFS),它能更激进地维持计算核心的Tickless状态。
  • \n
  • 参数 -m 0x0F0000000000000F 是一个掩码配置,精确指定了Housekeeping核心的拓扑位置,确保调度器仅在管理核心上处理必要的调度逻辑,从而保护计算核心的“静默”状态。
  • \n
\n

3.3 中断亲和性管理 (irqbalance)

为了防止网卡、磁盘控制器的硬中断打断计算流水线,必须严格限制中断响应的核心范围。

\n
1
2
# 配置文件/etc/sysconfig/irqbalance中设置
IRQBALANCE_BANNED_CPUS="FFFFFFFFFFFFF0FFFFFFFFFFFFF0"
\n\n

这是一个64位/128位的十六进制掩码。该配置显式禁止了所有隔离核心(即计算核心)参与中断负载均衡。结果是所有硬件中断都被强行“钉”在了Housekeeping Cores(Socket 0的0-3和Socket 1的56-59)上,保证了计算核心100%的CPU时间片用于业务逻辑。

\n

3.4 进程绑核 (taskset)

最后,在启动计算任务时,最好显式指定亲和性不能光依赖scx_tickless调度,防止跨NUMA节点的内存访问惩罚。

\n
1
2
# 添加一个启动脚本变量
NUMACTL_CPU_BIND = "taskset -c 4-55,60-111"
\n\n

结合isolcpus,这确保了计算线程被牢牢锁定在无干扰的核心上,且每个线程独占物理核L1/L2缓存。

\n

4. 性能验证与结论

测试用例: AMSS-NCKU双黑洞模拟(基于广义相对论数值解法)。
评估指标: 单个时间步(Time Step)的计算耗时。

\n

在应用上述全套优化措施后,系统表现出极高的确定性。对比默认配置(Default CFS + On-Demand Power + Default IRQ Balance):

\n
    \n
  1. 稳定性提升: 计算过程中的抖动(Jitter)几乎消失,btop监控可以看到各核心负载曲线呈现完美的平直线。
  2. \n
  3. 性能收益:
  4. \n
\n
    \n
  • 在计算前期,单步时间步稳定降低 0.1 - 0.2秒
  • \n
  • 随着模拟进入后期(网格加密或相互作用增强),计算密度增加,该优化带来的累计收益更为显著。
  • \n
  • 48核心运行总时间预计缩短约 800秒
  • \n
\n

总结

过去参加比赛就没有一次是能够在限定功耗下启用所有核心不降频全力跑程序的,我不禁思考既然用不完何不将单核计算性能调优做到极致,牺牲一些核心处理杂活不参与计算,剩余的核心全力以赴跑程序不要受干扰最好也不要降频,这就是我的大致思路。

\n

通过nohz_fullisolcpus实现核心隔离,配合mitigations=off释放硬件潜能,并利用scx_tickless进行用户态调度增强,我按照设想将这台Xeon 8480+服务器改造成了一个OS Noise几乎消除的专用计算平台。后续需要关注的问题是迁移服务器后如何快速重现这些调优配置,或者干脆设计动态调控脚本来根据硬件信息调整内核参数。另外还有多机运行也没有进行测试,未来会关注在MPI环境下验证这些调优措施的适用性。

\n
\n

赛后更新

散了吧孩子们,H100/H200已经杀死比赛了:(

\n","excerpt":"","more":"

最初,我的想法是现在大伙都有AI工具了码力差距将不再像过去那么明显,这种情况下系统调优一定是有实战价值的。尤其是在面对极端计算任务时,操作系统内核的调度策略和中断处理方式对性能必然有着直接且显著的影响。为了在计算密集型任务(如本次天体物理模拟)中获得极致的性能与确定性,我作为运维必须尝试对Linux内核进行深度定制与隔离。

\n

本文将详细记录我在Intel Sapphire Rapids平台(Dual Xeon 8480P+)上进行的系统调优探索。通过内核参数隔离、用户态调度器(sched_ext)以及IRQ亲和性管理,我成功在AMSS-NCKU双黑洞模拟算例中取得了肉眼可见的性能收益。

\n

1. 测试环境与硬件拓扑

本次调优的硬件平台具备极高的并行计算能力,但NUMA架构带来的延迟敏感性也对系统配置提出了挑战:

\n
    \n
  • CPU: 2x Intel Xeon Platinum 8480+ (Sapphire Rapids)
  • \n
  • 核心配置: 单路56核心,双路共112物理核心。
  • \n
  • 超线程 (Hyper-Threading): Disabled(为了避免逻辑核争抢浮点单元及缓存资源,确保计算流水线的独占性)。
  • \n
  • GPU: 无(纯CPU计算任务)。
  • \n
\n

针对该拓扑,我制定了“核心隔离(Core Isolation)”策略:将系统中断、管理进程及内核Housekeeping任务通过cpuset和内核参数限制在特定的少数核心上(Housekeeping Cores),将绝大多数核心完全腾空,仅运行计算任务(Isolation Cores)。

\n
    \n
  • Housekeeping Cores: 0-3 (Socket 0), 56-59 (Socket 1) — 共8核。
  • \n
  • Isolation Cores: 4-55 (Socket 0), 60-111 (Socket 1) — 共104核,用于纯计算。
  • \n
\n

2. 内核启动参数(Kernel Command Line)深度解析

首先需要确定的是这次我将选择Rockylinux 10作为操作系统基础,内核版本为6.12是首个支持sched_ext调度器的稳定版本,并且Rockylinux 10首次完全基于x86-64-v3构建,相比其他发行版有着必然的性能优势,而少数比它性能更好的发行版(CachyOS和已经逝世的Clearlinux)又没它稳定还不受nvidia ofed驱动支持,考虑到这些因素我不会多看其他任何发行版一秒。(现在都是2026年了坚决抵制CentOS 7/8或Rockylinux 8信徒遗老,这么老的内核挂个exFAT硬盘传东西都费劲)。

\n

为了实现我预期的隔离策略,我使用grubby在GRUB配置中启用了以下内核参数。这些参数共同构成了一个低延迟、低抖动的运行环境。

\n
1
2
3
transparent_hugepage=never iommu=pt mitigations=off \\
nohz_full=4-55,60-111 rcu_nocbs=4-55,60-111 \\
isolcpus=managed_irq,4-55,60-111
\n\n

2.1 内存与IO子系统优化

    \n
  • transparent_hugepage=never: 禁用透明大页(THP)。虽然THP能减少TLB Miss,但在高强度的随机内存访问或频繁申请释放场景下,THP的后台合并线程(khugepaged)会引入不可预测的延迟抖动(Jitter)。
  • \n
  • iommu=pt: 将IOMMU设置为Pass-Through模式。这允许设备在DMA传输时直接使用物理地址,绕过IOMMU的地址转换层,显著降低了IO密集型操作的CPU开销,同时保留了IOMMU的基本功能。
  • \n
\n

2.2 安全缓解措施禁用

    \n
  • mitigations=off(让CPU回归其原本的样子): 该参数关闭了针对Spectre、Meltdown等侧信道攻击的所有CPU漏洞缓解措施。这些缓解措施通常涉及频繁的页表隔离(PTI)和间接分支预测限制,在旧时代这会带来非常明显的性能损耗(通常在15%-25%之间),但是在今天能够提供的性能提升根据之前不知道在哪里看到的Intel测评已经不到5%了。在追求极致速度的物理隔离环境中,我还是要物尽其用,认为关闭它们是提升IPC(Instructions Per Cycle)的必要手段。
  • \n
\n

2.3 核心隔离与时钟中断消除 (核心措施)

    \n
  • nohz_full=4-55,60-111: 启用Full Dynticks模式。在指定的核心上,如果运行队列中只有一个任务(即我们的计算进程),内核将停止发送调度时钟中断(Tick)。这消除了周期性的上下文切换干扰,实现了“无干扰(Tickless)”执行。
  • \n
  • rcu_nocbs=4-55,60-111: 将指定核心上的RCU(Read-Copy-Update)回调函数卸载到非隔离核心(Housekeeping cores)的线程中执行。这防止了RCU软中断打断关键计算路径。
  • \n
  • isolcpus=managed_irq,4-55,60-111: 将指定核心从内核的通用SMP均衡调度器中移除,确保普通进程不会被随机调度到这些核心上。
  • \n
  • managed_irq 标志进一步指示内核尽量避免将受管中断(Managed Interrupts)分配给这些核心,为后续的用户态中断控制打下基础。
  • \n
\n

3. 用户态环境与调度优化

除了内核层面的静态配置,运行时的动态调优同样关键。

\n

3.1 基础系统调优 (tuned)

1
tuned-adm profile hpc-compute
\n\n

启用RedHat系的hpc-compute配置文件。该Profile会自动调整虚拟内存参数(如vm.dirty_ratio)、网络栈缓冲区大小以及CPU的电源管理策略(强制最大C-State为C1或C0),确保CPU始终处于高性能状态,避免频率升降带来的延迟。

\n

3.2 scx调度器:sched_ext (scx_tickless)

1
/usr/local/bin/scx_tickless -m 0x0F0000000000000F
\n\n

利用较新的Linux内核特性(BPF-based extensible scheduler class),我引入了scx_tickless调度器。

\n
    \n
  • 该调度器由英伟达工程师设计,通过eBPF程序接管调度决策,相比完全公平调度器(CFS),它能更激进地维持计算核心的Tickless状态。
  • \n
  • 参数 -m 0x0F0000000000000F 是一个掩码配置,精确指定了Housekeeping核心的拓扑位置,确保调度器仅在管理核心上处理必要的调度逻辑,从而保护计算核心的“静默”状态。
  • \n
\n

3.3 中断亲和性管理 (irqbalance)

为了防止网卡、磁盘控制器的硬中断打断计算流水线,必须严格限制中断响应的核心范围。

\n
1
2
# 配置文件/etc/sysconfig/irqbalance中设置
IRQBALANCE_BANNED_CPUS="FFFFFFFFFFFFF0FFFFFFFFFFFFF0"
\n\n

这是一个64位/128位的十六进制掩码。该配置显式禁止了所有隔离核心(即计算核心)参与中断负载均衡。结果是所有硬件中断都被强行“钉”在了Housekeeping Cores(Socket 0的0-3和Socket 1的56-59)上,保证了计算核心100%的CPU时间片用于业务逻辑。

\n

3.4 进程绑核 (taskset)

最后,在启动计算任务时,最好显式指定亲和性不能光依赖scx_tickless调度,防止跨NUMA节点的内存访问惩罚。

\n
1
2
# 添加一个启动脚本变量
NUMACTL_CPU_BIND = "taskset -c 4-55,60-111"
\n\n

结合isolcpus,这确保了计算线程被牢牢锁定在无干扰的核心上,且每个线程独占物理核L1/L2缓存。

\n

4. 性能验证与结论

测试用例: AMSS-NCKU双黑洞模拟(基于广义相对论数值解法)。
评估指标: 单个时间步(Time Step)的计算耗时。

\n

在应用上述全套优化措施后,系统表现出极高的确定性。对比默认配置(Default CFS + On-Demand Power + Default IRQ Balance):

\n
    \n
  1. 稳定性提升: 计算过程中的抖动(Jitter)几乎消失,btop监控可以看到各核心负载曲线呈现完美的平直线。
  2. \n
  3. 性能收益:
  4. \n
\n
    \n
  • 在计算前期,单步时间步稳定降低 0.1 - 0.2秒
  • \n
  • 随着模拟进入后期(网格加密或相互作用增强),计算密度增加,该优化带来的累计收益更为显著。
  • \n
  • 48核心运行总时间预计缩短约 800秒
  • \n
\n

总结

过去参加比赛就没有一次是能够在限定功耗下启用所有核心不降频全力跑程序的,我不禁思考既然用不完何不将单核计算性能调优做到极致,牺牲一些核心处理杂活不参与计算,剩余的核心全力以赴跑程序不要受干扰最好也不要降频,这就是我的大致思路。

\n

通过nohz_fullisolcpus实现核心隔离,配合mitigations=off释放硬件潜能,并利用scx_tickless进行用户态调度增强,我按照设想将这台Xeon 8480+服务器改造成了一个OS Noise几乎消除的专用计算平台。后续需要关注的问题是迁移服务器后如何快速重现这些调优配置,或者干脆设计动态调控脚本来根据硬件信息调整内核参数。另外还有多机运行也没有进行测试,未来会关注在MPI环境下验证这些调优措施的适用性。

\n
\n

赛后更新

散了吧孩子们,H100/H200已经杀死比赛了:(

\n"},{"title":"Overleaf Toolkit踩坑记录","date":"2024-11-06T01:56:18.000Z","_content":"\n在安装 Overleaf Toolkit 时,表面上看起来很简单只要执行一些脚本就行,但是在某地区网络环境下还是遇到了一些问题和困难,这里记录安装过程中的问题和解决方案,方便以后参考。\n\n具体流程就不赘述了,先将访问[Overleaf Toolkit官方仓库](https://github.com/overleaf/toolkit)将代码clone下来并按照手册执行即可。\n\n## 问题一:无法 Pull Mongo、Redis 和 Sharelatex 镜像\n\n在执行 `bin/up` 脚本启动服务时,发现 Mongo、Redis 和 Sharelatex 镜像无法拉取。主要原因是 Docker 在国内网络环境中,直接访问 Docker Hub 可能会被限制,导致拉取镜像失败。\n\n截至这篇博客编写时网上提供的镜像均无法解决只能使用代理。\n\n### 解决方案:为 Docker 设置代理\n\n通过配置 Docker 的代理,可以解决拉取镜像受限的问题。步骤如下:\n\n1. 创建一个 `systemd` 服务文件,为 Docker 设置代理。\n2. 编辑 `/etc/systemd/system/docker.service.d/proxy.conf` 文件,添加以下内容(确保已经设置了代理服务器):\n\n ```ini\n [Service]\n Environment=\"HTTP_PROXY=http://:\"\n Environment=\"HTTPS_PROXY=http://:\"\n Environment=\"NO_PROXY=localhost,127.0.0.1\"\n ```\n\n3. 重新加载 `systemd` 配置并重启 Docker:\n\n ```bash\n sudo systemctl daemon-reload\n sudo systemctl restart docker\n ```\n\n4. 重启 Docker 后再次执行 `sudo ./up`,此时应该可以正常拉取 Mongo、Redis 和 Sharelatex 的镜像。等待执行完成即可。\n\n\n---\n\n## 问题二:外部机器无法访问 Overleaf Web 服务\n\n先确认ipv4和ipv6转发功能没有问题,但是依旧出现只能本机访问127.0.0.1,其他方式均无法访问,甚至nmap扫描端口也发现并未开放sharelatex端口,可以通过修改docker-compose配置文件解决。\n\n注意到在默认的 `lib/docker-compose.base.yml` 配置中,Overleaf Web 服务的端口映射方式为 `\"${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80\"`。\n\n### 解决方案:修改端口映射\n\n在 `docker-compose.base.yml` 文件中,删除 `${OVERLEAF_LISTEN_IP:-127.0.0.1}` 前缀,将 `\"${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80\"` 修改为 `\"${OVERLEAF_PORT:-80}:80\"`。这样可以使 Docker 将 Overleaf 的 Web 服务端口暴露给所有网络接口,从而允许外部机器访问。\n\n修改后的 `docker-compose.base.yml` 端口映射配置如下:\n\n```yaml\n---\nservices:\n\n sharelatex:\n restart: always\n image: \"${IMAGE}\"\n container_name: sharelatex\n volumes:\n - \"${OVERLEAF_DATA_PATH}:${OVERLEAF_IN_CONTAINER_DATA_PATH}\"\n ports:\n #- \"${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80\"\n - \"${OVERLEAF_PORT:-80}:80\"\n environment:\n GIT_BRIDGE_ENABLED: \"${GIT_BRIDGE_ENABLED}\"\n GIT_BRIDGE_HOST: \"git-bridge\"\n GIT_BRIDGE_PORT: \"8000\"\n REDIS_HOST: \"${REDIS_HOST}\"\n REDIS_PORT: \"${REDIS_PORT}\"\n V1_HISTORY_URL: \"http://sharelatex:3100/api\"\n env_file:\n - ../config/variables.env\n stop_grace_period: 60s\n```\n\n完成修改后,重新启动 Docker 服务:\n\n```bash\nsudo ./start\n```\n\n现在,外部机器可以通过服务器的 IP 地址加端口 `{Overleaf_Port}` 访问 Overleaf Web 服务。\n\n---\n\n## 总结\n\n这次安装 Overleaf Toolkit 时,主要遇到的两个问题分别是镜像拉取失败和端口映射受限。通过为 Docker 设置代理解决了拉取镜像的问题,而通过修改 `docker-compose.base.yml` 中的端口映射使外部设备可以访问 Overleaf 服务。\n\n可以在这里参考我的本地[Overleaf](https://overleaf.hifuu.ink)效果,注意安装完后还需要配置latex包和中文字体。\n\n这篇记录希望能为遇到类似问题的朋友提供帮助。\n","source":"_posts/overleaf.md","raw":"---\ntitle: Overleaf Toolkit踩坑记录\ndate: 2024-11-06 09:56:18\ntags: 技术\ncategories: [技术分享]\n---\n\n在安装 Overleaf Toolkit 时,表面上看起来很简单只要执行一些脚本就行,但是在某地区网络环境下还是遇到了一些问题和困难,这里记录安装过程中的问题和解决方案,方便以后参考。\n\n具体流程就不赘述了,先将访问[Overleaf Toolkit官方仓库](https://github.com/overleaf/toolkit)将代码clone下来并按照手册执行即可。\n\n## 问题一:无法 Pull Mongo、Redis 和 Sharelatex 镜像\n\n在执行 `bin/up` 脚本启动服务时,发现 Mongo、Redis 和 Sharelatex 镜像无法拉取。主要原因是 Docker 在国内网络环境中,直接访问 Docker Hub 可能会被限制,导致拉取镜像失败。\n\n截至这篇博客编写时网上提供的镜像均无法解决只能使用代理。\n\n### 解决方案:为 Docker 设置代理\n\n通过配置 Docker 的代理,可以解决拉取镜像受限的问题。步骤如下:\n\n1. 创建一个 `systemd` 服务文件,为 Docker 设置代理。\n2. 编辑 `/etc/systemd/system/docker.service.d/proxy.conf` 文件,添加以下内容(确保已经设置了代理服务器):\n\n ```ini\n [Service]\n Environment=\"HTTP_PROXY=http://:\"\n Environment=\"HTTPS_PROXY=http://:\"\n Environment=\"NO_PROXY=localhost,127.0.0.1\"\n ```\n\n3. 重新加载 `systemd` 配置并重启 Docker:\n\n ```bash\n sudo systemctl daemon-reload\n sudo systemctl restart docker\n ```\n\n4. 重启 Docker 后再次执行 `sudo ./up`,此时应该可以正常拉取 Mongo、Redis 和 Sharelatex 的镜像。等待执行完成即可。\n\n\n---\n\n## 问题二:外部机器无法访问 Overleaf Web 服务\n\n先确认ipv4和ipv6转发功能没有问题,但是依旧出现只能本机访问127.0.0.1,其他方式均无法访问,甚至nmap扫描端口也发现并未开放sharelatex端口,可以通过修改docker-compose配置文件解决。\n\n注意到在默认的 `lib/docker-compose.base.yml` 配置中,Overleaf Web 服务的端口映射方式为 `\"${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80\"`。\n\n### 解决方案:修改端口映射\n\n在 `docker-compose.base.yml` 文件中,删除 `${OVERLEAF_LISTEN_IP:-127.0.0.1}` 前缀,将 `\"${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80\"` 修改为 `\"${OVERLEAF_PORT:-80}:80\"`。这样可以使 Docker 将 Overleaf 的 Web 服务端口暴露给所有网络接口,从而允许外部机器访问。\n\n修改后的 `docker-compose.base.yml` 端口映射配置如下:\n\n```yaml\n---\nservices:\n\n sharelatex:\n restart: always\n image: \"${IMAGE}\"\n container_name: sharelatex\n volumes:\n - \"${OVERLEAF_DATA_PATH}:${OVERLEAF_IN_CONTAINER_DATA_PATH}\"\n ports:\n #- \"${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80\"\n - \"${OVERLEAF_PORT:-80}:80\"\n environment:\n GIT_BRIDGE_ENABLED: \"${GIT_BRIDGE_ENABLED}\"\n GIT_BRIDGE_HOST: \"git-bridge\"\n GIT_BRIDGE_PORT: \"8000\"\n REDIS_HOST: \"${REDIS_HOST}\"\n REDIS_PORT: \"${REDIS_PORT}\"\n V1_HISTORY_URL: \"http://sharelatex:3100/api\"\n env_file:\n - ../config/variables.env\n stop_grace_period: 60s\n```\n\n完成修改后,重新启动 Docker 服务:\n\n```bash\nsudo ./start\n```\n\n现在,外部机器可以通过服务器的 IP 地址加端口 `{Overleaf_Port}` 访问 Overleaf Web 服务。\n\n---\n\n## 总结\n\n这次安装 Overleaf Toolkit 时,主要遇到的两个问题分别是镜像拉取失败和端口映射受限。通过为 Docker 设置代理解决了拉取镜像的问题,而通过修改 `docker-compose.base.yml` 中的端口映射使外部设备可以访问 Overleaf 服务。\n\n可以在这里参考我的本地[Overleaf](https://overleaf.hifuu.ink)效果,注意安装完后还需要配置latex包和中文字体。\n\n这篇记录希望能为遇到类似问题的朋友提供帮助。\n","slug":"overleaf","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudbly001oidd04rcohmn6","content":"

在安装 Overleaf Toolkit 时,表面上看起来很简单只要执行一些脚本就行,但是在某地区网络环境下还是遇到了一些问题和困难,这里记录安装过程中的问题和解决方案,方便以后参考。

\n

具体流程就不赘述了,先将访问Overleaf Toolkit官方仓库将代码clone下来并按照手册执行即可。

\n

问题一:无法 Pull Mongo、Redis 和 Sharelatex 镜像

在执行 bin/up 脚本启动服务时,发现 Mongo、Redis 和 Sharelatex 镜像无法拉取。主要原因是 Docker 在国内网络环境中,直接访问 Docker Hub 可能会被限制,导致拉取镜像失败。

\n

截至这篇博客编写时网上提供的镜像均无法解决只能使用代理。

\n

解决方案:为 Docker 设置代理

通过配置 Docker 的代理,可以解决拉取镜像受限的问题。步骤如下:

\n
    \n
  1. 创建一个 systemd 服务文件,为 Docker 设置代理。

    \n
  2. \n
  3. 编辑 /etc/systemd/system/docker.service.d/proxy.conf 文件,添加以下内容(确保已经设置了代理服务器):

    \n
    1
    2
    3
    4
    [Service]
    Environment="HTTP_PROXY=http://<your-proxy-server>:<port>"
    Environment="HTTPS_PROXY=http://<your-proxy-server>:<port>"
    Environment="NO_PROXY=localhost,127.0.0.1"
    \n
  4. \n
  5. 重新加载 systemd 配置并重启 Docker:

    \n
    1
    2
    sudo systemctl daemon-reload
    sudo systemctl restart docker
    \n
  6. \n
  7. 重启 Docker 后再次执行 sudo ./up,此时应该可以正常拉取 Mongo、Redis 和 Sharelatex 的镜像。等待执行完成即可。

    \n
  8. \n
\n
\n

问题二:外部机器无法访问 Overleaf Web 服务

先确认ipv4和ipv6转发功能没有问题,但是依旧出现只能本机访问127.0.0.1,其他方式均无法访问,甚至nmap扫描端口也发现并未开放sharelatex端口,可以通过修改docker-compose配置文件解决。

\n

注意到在默认的 lib/docker-compose.base.yml 配置中,Overleaf Web 服务的端口映射方式为 "${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80"

\n

解决方案:修改端口映射

docker-compose.base.yml 文件中,删除 ${OVERLEAF_LISTEN_IP:-127.0.0.1} 前缀,将 "${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80" 修改为 "${OVERLEAF_PORT:-80}:80"。这样可以使 Docker 将 Overleaf 的 Web 服务端口暴露给所有网络接口,从而允许外部机器访问。

\n

修改后的 docker-compose.base.yml 端口映射配置如下:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
---
services:

sharelatex:
restart: always
image: "${IMAGE}"
container_name: sharelatex
volumes:
- "${OVERLEAF_DATA_PATH}:${OVERLEAF_IN_CONTAINER_DATA_PATH}"
ports:
#- "${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80"
- "${OVERLEAF_PORT:-80}:80"
environment:
GIT_BRIDGE_ENABLED: "${GIT_BRIDGE_ENABLED}"
GIT_BRIDGE_HOST: "git-bridge"
GIT_BRIDGE_PORT: "8000"
REDIS_HOST: "${REDIS_HOST}"
REDIS_PORT: "${REDIS_PORT}"
V1_HISTORY_URL: "http://sharelatex:3100/api"
env_file:
- ../config/variables.env
stop_grace_period: 60s
\n\n

完成修改后,重新启动 Docker 服务:

\n
1
sudo ./start
\n\n

现在,外部机器可以通过服务器的 IP 地址加端口 {Overleaf_Port} 访问 Overleaf Web 服务。

\n
\n

总结

这次安装 Overleaf Toolkit 时,主要遇到的两个问题分别是镜像拉取失败和端口映射受限。通过为 Docker 设置代理解决了拉取镜像的问题,而通过修改 docker-compose.base.yml 中的端口映射使外部设备可以访问 Overleaf 服务。

\n

可以在这里参考我的本地Overleaf效果,注意安装完后还需要配置latex包和中文字体。

\n

这篇记录希望能为遇到类似问题的朋友提供帮助。

\n","excerpt":"","more":"

在安装 Overleaf Toolkit 时,表面上看起来很简单只要执行一些脚本就行,但是在某地区网络环境下还是遇到了一些问题和困难,这里记录安装过程中的问题和解决方案,方便以后参考。

\n

具体流程就不赘述了,先将访问Overleaf Toolkit官方仓库将代码clone下来并按照手册执行即可。

\n

问题一:无法 Pull Mongo、Redis 和 Sharelatex 镜像

在执行 bin/up 脚本启动服务时,发现 Mongo、Redis 和 Sharelatex 镜像无法拉取。主要原因是 Docker 在国内网络环境中,直接访问 Docker Hub 可能会被限制,导致拉取镜像失败。

\n

截至这篇博客编写时网上提供的镜像均无法解决只能使用代理。

\n

解决方案:为 Docker 设置代理

通过配置 Docker 的代理,可以解决拉取镜像受限的问题。步骤如下:

\n
    \n
  1. 创建一个 systemd 服务文件,为 Docker 设置代理。

    \n
  2. \n
  3. 编辑 /etc/systemd/system/docker.service.d/proxy.conf 文件,添加以下内容(确保已经设置了代理服务器):

    \n
    1
    2
    3
    4
    [Service]
    Environment="HTTP_PROXY=http://<your-proxy-server>:<port>"
    Environment="HTTPS_PROXY=http://<your-proxy-server>:<port>"
    Environment="NO_PROXY=localhost,127.0.0.1"
    \n
  4. \n
  5. 重新加载 systemd 配置并重启 Docker:

    \n
    1
    2
    sudo systemctl daemon-reload
    sudo systemctl restart docker
    \n
  6. \n
  7. 重启 Docker 后再次执行 sudo ./up,此时应该可以正常拉取 Mongo、Redis 和 Sharelatex 的镜像。等待执行完成即可。

    \n
  8. \n
\n
\n

问题二:外部机器无法访问 Overleaf Web 服务

先确认ipv4和ipv6转发功能没有问题,但是依旧出现只能本机访问127.0.0.1,其他方式均无法访问,甚至nmap扫描端口也发现并未开放sharelatex端口,可以通过修改docker-compose配置文件解决。

\n

注意到在默认的 lib/docker-compose.base.yml 配置中,Overleaf Web 服务的端口映射方式为 "${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80"

\n

解决方案:修改端口映射

docker-compose.base.yml 文件中,删除 ${OVERLEAF_LISTEN_IP:-127.0.0.1} 前缀,将 "${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80" 修改为 "${OVERLEAF_PORT:-80}:80"。这样可以使 Docker 将 Overleaf 的 Web 服务端口暴露给所有网络接口,从而允许外部机器访问。

\n

修改后的 docker-compose.base.yml 端口映射配置如下:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
---
services:

sharelatex:
restart: always
image: "${IMAGE}"
container_name: sharelatex
volumes:
- "${OVERLEAF_DATA_PATH}:${OVERLEAF_IN_CONTAINER_DATA_PATH}"
ports:
#- "${OVERLEAF_LISTEN_IP:-127.0.0.1}:${OVERLEAF_PORT:-80}:80"
- "${OVERLEAF_PORT:-80}:80"
environment:
GIT_BRIDGE_ENABLED: "${GIT_BRIDGE_ENABLED}"
GIT_BRIDGE_HOST: "git-bridge"
GIT_BRIDGE_PORT: "8000"
REDIS_HOST: "${REDIS_HOST}"
REDIS_PORT: "${REDIS_PORT}"
V1_HISTORY_URL: "http://sharelatex:3100/api"
env_file:
- ../config/variables.env
stop_grace_period: 60s
\n\n

完成修改后,重新启动 Docker 服务:

\n
1
sudo ./start
\n\n

现在,外部机器可以通过服务器的 IP 地址加端口 {Overleaf_Port} 访问 Overleaf Web 服务。

\n
\n

总结

这次安装 Overleaf Toolkit 时,主要遇到的两个问题分别是镜像拉取失败和端口映射受限。通过为 Docker 设置代理解决了拉取镜像的问题,而通过修改 docker-compose.base.yml 中的端口映射使外部设备可以访问 Overleaf 服务。

\n

可以在这里参考我的本地Overleaf效果,注意安装完后还需要配置latex包和中文字体。

\n

这篇记录希望能为遇到类似问题的朋友提供帮助。

\n"},{"title":"如何在笔记本电脑插拔电源时自动切换 SCX 调度模式","date":"2026-06-14T11:24:35.000Z","_content":"\n一直觉得scx_lavd和scx_flow支持自动判断负载来选择合适的调度模式很不错,但是我还是更信赖Nvidia工程师出品的scx_bpfland,尤其是CachyOS定义的scx_bpfland低延迟模式和省电模式在我笔记本上的表现都非常好。但有一个痛点——笔记本插电和用电池的场景对调度器的需求完全不同:\n\n- **插电时**:希望低延迟、高性能,用得爽,程序编译快游戏帧数高\n- **电池时**:希望尽可能省电,续航优先尽量用久一点,不在乎性能牺牲\n\n每次手动敲命令切换实在太蠢了使用CachyOS的scx-manager也不太方便。研究了一下后发现可以借助 Linux 的 `udev` 规则来监听电源事件,实现全自动切换。记录一下折腾过程。\n\n---\n\n## 现状:scx_loader 配置文件\n\n这是我的 `/etc/scx_loader.toml`(CachyOS已提前为各个调度器配好了 mode):\n\n```toml\ndefault_sched = \"scx_bpfland\"\ndefault_mode = \"LowLatency\"\n\n[scheds.scx_rusty]\nauto_mode = []\ngaming_mode = []\nlowlatency_mode = []\npowersave_mode = []\nserver_mode = []\n\n[scheds.scx_p2dq]\nauto_mode = []\ngaming_mode = []\nlowlatency_mode = [\"-y\"]\npowersave_mode = []\nserver_mode = [\"--keep-running\"]\n\n[scheds.scx_bpfland]\nauto_mode = []\ngaming_mode = [\"-m\", \"performance\"]\nlowlatency_mode = [\"-s\", \"5000\", \"-S\", \"500\", \"-l\", \"5000\", \"-m\", \"performance\"]\npowersave_mode = [\"-m\", \"powersave\"]\nserver_mode = [\"-p\"]\n\n[scheds.scx_lavd]\nauto_mode = [\"--autopower\"]\ngaming_mode = [\"--performance\"]\nlowlatency_mode = [\"--performance\"]\npowersave_mode = [\"--powersave\"]\nserver_mode = []\n\n[scheds.scx_tickless]\nauto_mode = []\ngaming_mode = [\"-f\", \"5000\", \"-s\", \"5000\"]\nlowlatency_mode = [\"-f\", \"5000\", \"-s\", \"1000\"]\npowersave_mode = [\"-f\", \"50\", \"-p\"]\nserver_mode = [\"-f\", \"100\"]\n\n[scheds.scx_flash]\nauto_mode = []\ngaming_mode = []\nlowlatency_mode = []\npowersave_mode = []\nserver_mode = []\n```\n\n仔细观察 `[scheds.scx_bpfland]` 这一块:\n\n- `lowlatency_mode` 正好对应插电状态想要的参数:`-s 5000 -S 500 -l 5000 -m performance`\n- `powersave_mode` 正好对应电池状态想要的参数:`-m powersave`\n\n不用自己写额外参数配置,直接复用已有的 mode 定义就好。\n\n---\n\n## 思路\n\n`scx_loader` 提供了配套的命令行工具 `scxctl`,可以用来切换调度器和模式。剩下的就是找一个能在电源事件发生时自动执行命令的机制——Linux 内核的 `udev` 子系统天生就是干这个的。\n\n整体流程很简单:**插拔电源 → 内核发出 uevent → udev 匹配规则 → systemd-run 异步执行 → scxctl switch 切换模式**。\n\n---\n\n## 步骤一:测试手动切换命令\n\n先确认 `scxctl` 能正常工作(需要 root 权限):\n\n```bash\n# 模拟插电 → 低延迟/高性能模式\nsudo scxctl switch -s bpfland -m lowlatency\n\n# 模拟拔电 → 省电模式\nsudo scxctl switch -s bpfland -m powersave\n```\n\n如果这两条命令都能成功切换,就可以继续下一步。\n\n---\n\n## 步骤二:编写 udev 规则\n\n创建规则文件:\n\n```bash\nsudo nano /etc/udev/rules.d/99-scx-power.rules\n```\n\n写入以下内容:\n\n```udev\n# 插入电源 (AC) → 切换为 bpfland 的 lowlatency 模式\nSUBSYSTEM==\"power_supply\", ATTR{type}==\"Mains\", ATTR{online}==\"1\", RUN+=\"/usr/bin/systemd-run --no-block /usr/bin/scxctl switch -s bpfland -m lowlatency\"\n\n# 拔出电源 (Battery) → 切换为 bpfland 的 powersave 模式\nSUBSYSTEM==\"power_supply\", ATTR{type}==\"Mains\", ATTR{online}==\"0\", RUN+=\"/usr/bin/systemd-run --no-block /usr/bin/scxctl switch -s bpfland -m powersave\"\n```\n\n> **为什么用 `systemd-run --no-block` 包裹?**\n>\n> `scxctl` 底层通过 D-Bus 与 `scx_loader` 守护进程通信。如果让 `udev` 直接执行,可能会因为 udev 的受限执行环境(缺少 D-Bus session、环境变量缺失)或者命令执行时间过长阻塞 udev 进程导致失败。交给 systemd 后台异步执行是最稳妥的做法。\n\n---\n\n## 步骤三:重载规则使其生效\n\n```bash\nsudo udevadm control --reload-rules\nsudo udevadm trigger\n```\n\n`udevadm trigger` 会立即重放一次当前设备状态,所以如果此时已经插着电源,规则会立刻触发一次,可以顺便验证是否生效。\n\n---\n\n## 步骤四(可选):验证电源类型\n\n绝大多数笔记本的外部电源在内核中的设备类型都是 `Mains`,上面规则直接就能用。如果插拔电源发现没反应,检查一下你的电源设备类型:\n\n```bash\ncat /sys/class/power_supply/*/type\n```\n\n如果输出的不是 `Mains`(比如是 `AC` 或其他名字),把 udev 规则里的 `ATTR{type}==\"Mains\"` 换成实际类型即可。\n\n---\n\n## 验证效果\n\n插拔电源后,可以用以下命令确认模式是否已经切换:\n\n```bash\nscxctl get\n```\n\n---\n\n## 总结\n\n整个过程其实非常简单——核心思路就是「udev 监听电源事件 → systemd-run 异步调用 scxctl 切换 mode」。全程没有写一个新参数,完全复用 `scx_loader.toml` 里已有的 mode 定义。\n\n这个方案同样适用于其他调度器:只要在 `scx_loader.toml` 里给对应调度器配好 `lowlatency_mode` 和 `powersave_mode`,然后把 udev 规则里的 `-s bpfland` 换成其他调度器名字就行。\n\n这下笔记本插拔电源再也不用惦记着手动切调度器了:D\n","source":"_posts/scx-auto.md","raw":"---\ntitle: 如何在笔记本电脑插拔电源时自动切换 SCX 调度模式\ndate: 2026-06-14 19:24:35\ntags: [Archlinux, 系统优化, 技术分享, SCX调度器]\n---\n\n一直觉得scx_lavd和scx_flow支持自动判断负载来选择合适的调度模式很不错,但是我还是更信赖Nvidia工程师出品的scx_bpfland,尤其是CachyOS定义的scx_bpfland低延迟模式和省电模式在我笔记本上的表现都非常好。但有一个痛点——笔记本插电和用电池的场景对调度器的需求完全不同:\n\n- **插电时**:希望低延迟、高性能,用得爽,程序编译快游戏帧数高\n- **电池时**:希望尽可能省电,续航优先尽量用久一点,不在乎性能牺牲\n\n每次手动敲命令切换实在太蠢了使用CachyOS的scx-manager也不太方便。研究了一下后发现可以借助 Linux 的 `udev` 规则来监听电源事件,实现全自动切换。记录一下折腾过程。\n\n---\n\n## 现状:scx_loader 配置文件\n\n这是我的 `/etc/scx_loader.toml`(CachyOS已提前为各个调度器配好了 mode):\n\n```toml\ndefault_sched = \"scx_bpfland\"\ndefault_mode = \"LowLatency\"\n\n[scheds.scx_rusty]\nauto_mode = []\ngaming_mode = []\nlowlatency_mode = []\npowersave_mode = []\nserver_mode = []\n\n[scheds.scx_p2dq]\nauto_mode = []\ngaming_mode = []\nlowlatency_mode = [\"-y\"]\npowersave_mode = []\nserver_mode = [\"--keep-running\"]\n\n[scheds.scx_bpfland]\nauto_mode = []\ngaming_mode = [\"-m\", \"performance\"]\nlowlatency_mode = [\"-s\", \"5000\", \"-S\", \"500\", \"-l\", \"5000\", \"-m\", \"performance\"]\npowersave_mode = [\"-m\", \"powersave\"]\nserver_mode = [\"-p\"]\n\n[scheds.scx_lavd]\nauto_mode = [\"--autopower\"]\ngaming_mode = [\"--performance\"]\nlowlatency_mode = [\"--performance\"]\npowersave_mode = [\"--powersave\"]\nserver_mode = []\n\n[scheds.scx_tickless]\nauto_mode = []\ngaming_mode = [\"-f\", \"5000\", \"-s\", \"5000\"]\nlowlatency_mode = [\"-f\", \"5000\", \"-s\", \"1000\"]\npowersave_mode = [\"-f\", \"50\", \"-p\"]\nserver_mode = [\"-f\", \"100\"]\n\n[scheds.scx_flash]\nauto_mode = []\ngaming_mode = []\nlowlatency_mode = []\npowersave_mode = []\nserver_mode = []\n```\n\n仔细观察 `[scheds.scx_bpfland]` 这一块:\n\n- `lowlatency_mode` 正好对应插电状态想要的参数:`-s 5000 -S 500 -l 5000 -m performance`\n- `powersave_mode` 正好对应电池状态想要的参数:`-m powersave`\n\n不用自己写额外参数配置,直接复用已有的 mode 定义就好。\n\n---\n\n## 思路\n\n`scx_loader` 提供了配套的命令行工具 `scxctl`,可以用来切换调度器和模式。剩下的就是找一个能在电源事件发生时自动执行命令的机制——Linux 内核的 `udev` 子系统天生就是干这个的。\n\n整体流程很简单:**插拔电源 → 内核发出 uevent → udev 匹配规则 → systemd-run 异步执行 → scxctl switch 切换模式**。\n\n---\n\n## 步骤一:测试手动切换命令\n\n先确认 `scxctl` 能正常工作(需要 root 权限):\n\n```bash\n# 模拟插电 → 低延迟/高性能模式\nsudo scxctl switch -s bpfland -m lowlatency\n\n# 模拟拔电 → 省电模式\nsudo scxctl switch -s bpfland -m powersave\n```\n\n如果这两条命令都能成功切换,就可以继续下一步。\n\n---\n\n## 步骤二:编写 udev 规则\n\n创建规则文件:\n\n```bash\nsudo nano /etc/udev/rules.d/99-scx-power.rules\n```\n\n写入以下内容:\n\n```udev\n# 插入电源 (AC) → 切换为 bpfland 的 lowlatency 模式\nSUBSYSTEM==\"power_supply\", ATTR{type}==\"Mains\", ATTR{online}==\"1\", RUN+=\"/usr/bin/systemd-run --no-block /usr/bin/scxctl switch -s bpfland -m lowlatency\"\n\n# 拔出电源 (Battery) → 切换为 bpfland 的 powersave 模式\nSUBSYSTEM==\"power_supply\", ATTR{type}==\"Mains\", ATTR{online}==\"0\", RUN+=\"/usr/bin/systemd-run --no-block /usr/bin/scxctl switch -s bpfland -m powersave\"\n```\n\n> **为什么用 `systemd-run --no-block` 包裹?**\n>\n> `scxctl` 底层通过 D-Bus 与 `scx_loader` 守护进程通信。如果让 `udev` 直接执行,可能会因为 udev 的受限执行环境(缺少 D-Bus session、环境变量缺失)或者命令执行时间过长阻塞 udev 进程导致失败。交给 systemd 后台异步执行是最稳妥的做法。\n\n---\n\n## 步骤三:重载规则使其生效\n\n```bash\nsudo udevadm control --reload-rules\nsudo udevadm trigger\n```\n\n`udevadm trigger` 会立即重放一次当前设备状态,所以如果此时已经插着电源,规则会立刻触发一次,可以顺便验证是否生效。\n\n---\n\n## 步骤四(可选):验证电源类型\n\n绝大多数笔记本的外部电源在内核中的设备类型都是 `Mains`,上面规则直接就能用。如果插拔电源发现没反应,检查一下你的电源设备类型:\n\n```bash\ncat /sys/class/power_supply/*/type\n```\n\n如果输出的不是 `Mains`(比如是 `AC` 或其他名字),把 udev 规则里的 `ATTR{type}==\"Mains\"` 换成实际类型即可。\n\n---\n\n## 验证效果\n\n插拔电源后,可以用以下命令确认模式是否已经切换:\n\n```bash\nscxctl get\n```\n\n---\n\n## 总结\n\n整个过程其实非常简单——核心思路就是「udev 监听电源事件 → systemd-run 异步调用 scxctl 切换 mode」。全程没有写一个新参数,完全复用 `scx_loader.toml` 里已有的 mode 定义。\n\n这个方案同样适用于其他调度器:只要在 `scx_loader.toml` 里给对应调度器配好 `lowlatency_mode` 和 `powersave_mode`,然后把 udev 规则里的 `-s bpfland` 换成其他调度器名字就行。\n\n这下笔记本插拔电源再也不用惦记着手动切调度器了:D\n","slug":"scx-auto","published":1,"updated":"2026-06-14T11:43:21.327Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudbly001ridd08t5dekob","content":"

一直觉得scx_lavd和scx_flow支持自动判断负载来选择合适的调度模式很不错,但是我还是更信赖Nvidia工程师出品的scx_bpfland,尤其是CachyOS定义的scx_bpfland低延迟模式和省电模式在我笔记本上的表现都非常好。但有一个痛点——笔记本插电和用电池的场景对调度器的需求完全不同:

\n
    \n
  • 插电时:希望低延迟、高性能,用得爽,程序编译快游戏帧数高
  • \n
  • 电池时:希望尽可能省电,续航优先尽量用久一点,不在乎性能牺牲
  • \n
\n

每次手动敲命令切换实在太蠢了使用CachyOS的scx-manager也不太方便。研究了一下后发现可以借助 Linux 的 udev 规则来监听电源事件,实现全自动切换。记录一下折腾过程。

\n
\n

现状:scx_loader 配置文件

这是我的 /etc/scx_loader.toml(CachyOS已提前为各个调度器配好了 mode):

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
default_sched = "scx_bpfland"
default_mode = "LowLatency"

[scheds.scx_rusty]
auto_mode = []
gaming_mode = []
lowlatency_mode = []
powersave_mode = []
server_mode = []

[scheds.scx_p2dq]
auto_mode = []
gaming_mode = []
lowlatency_mode = ["-y"]
powersave_mode = []
server_mode = ["--keep-running"]

[scheds.scx_bpfland]
auto_mode = []
gaming_mode = ["-m", "performance"]
lowlatency_mode = ["-s", "5000", "-S", "500", "-l", "5000", "-m", "performance"]
powersave_mode = ["-m", "powersave"]
server_mode = ["-p"]

[scheds.scx_lavd]
auto_mode = ["--autopower"]
gaming_mode = ["--performance"]
lowlatency_mode = ["--performance"]
powersave_mode = ["--powersave"]
server_mode = []

[scheds.scx_tickless]
auto_mode = []
gaming_mode = ["-f", "5000", "-s", "5000"]
lowlatency_mode = ["-f", "5000", "-s", "1000"]
powersave_mode = ["-f", "50", "-p"]
server_mode = ["-f", "100"]

[scheds.scx_flash]
auto_mode = []
gaming_mode = []
lowlatency_mode = []
powersave_mode = []
server_mode = []
\n\n

仔细观察 [scheds.scx_bpfland] 这一块:

\n
    \n
  • lowlatency_mode 正好对应插电状态想要的参数:-s 5000 -S 500 -l 5000 -m performance
  • \n
  • powersave_mode 正好对应电池状态想要的参数:-m powersave
  • \n
\n

不用自己写额外参数配置,直接复用已有的 mode 定义就好。

\n
\n

思路

scx_loader 提供了配套的命令行工具 scxctl,可以用来切换调度器和模式。剩下的就是找一个能在电源事件发生时自动执行命令的机制——Linux 内核的 udev 子系统天生就是干这个的。

\n

整体流程很简单:插拔电源 → 内核发出 uevent → udev 匹配规则 → systemd-run 异步执行 → scxctl switch 切换模式

\n
\n

步骤一:测试手动切换命令

先确认 scxctl 能正常工作(需要 root 权限):

\n
1
2
3
4
5
# 模拟插电 → 低延迟/高性能模式
sudo scxctl switch -s bpfland -m lowlatency

# 模拟拔电 → 省电模式
sudo scxctl switch -s bpfland -m powersave
\n\n

如果这两条命令都能成功切换,就可以继续下一步。

\n
\n

步骤二:编写 udev 规则

创建规则文件:

\n
1
sudo nano /etc/udev/rules.d/99-scx-power.rules
\n\n

写入以下内容:

\n
1
2
3
4
5
# 插入电源 (AC) → 切换为 bpfland 的 lowlatency 模式
SUBSYSTEM=="power_supply", ATTR{type}=="Mains", ATTR{online}=="1", RUN+="/usr/bin/systemd-run --no-block /usr/bin/scxctl switch -s bpfland -m lowlatency"

# 拔出电源 (Battery) → 切换为 bpfland 的 powersave 模式
SUBSYSTEM=="power_supply", ATTR{type}=="Mains", ATTR{online}=="0", RUN+="/usr/bin/systemd-run --no-block /usr/bin/scxctl switch -s bpfland -m powersave"
\n\n
\n

为什么用 systemd-run --no-block 包裹?

\n

scxctl 底层通过 D-Bus 与 scx_loader 守护进程通信。如果让 udev 直接执行,可能会因为 udev 的受限执行环境(缺少 D-Bus session、环境变量缺失)或者命令执行时间过长阻塞 udev 进程导致失败。交给 systemd 后台异步执行是最稳妥的做法。

\n
\n
\n

步骤三:重载规则使其生效

1
2
sudo udevadm control --reload-rules
sudo udevadm trigger
\n\n

udevadm trigger 会立即重放一次当前设备状态,所以如果此时已经插着电源,规则会立刻触发一次,可以顺便验证是否生效。

\n
\n

步骤四(可选):验证电源类型

绝大多数笔记本的外部电源在内核中的设备类型都是 Mains,上面规则直接就能用。如果插拔电源发现没反应,检查一下你的电源设备类型:

\n
1
cat /sys/class/power_supply/*/type
\n\n

如果输出的不是 Mains(比如是 AC 或其他名字),把 udev 规则里的 ATTR{type}=="Mains" 换成实际类型即可。

\n
\n

验证效果

插拔电源后,可以用以下命令确认模式是否已经切换:

\n
1
scxctl get
\n\n
\n

总结

整个过程其实非常简单——核心思路就是「udev 监听电源事件 → systemd-run 异步调用 scxctl 切换 mode」。全程没有写一个新参数,完全复用 scx_loader.toml 里已有的 mode 定义。

\n

这个方案同样适用于其他调度器:只要在 scx_loader.toml 里给对应调度器配好 lowlatency_modepowersave_mode,然后把 udev 规则里的 -s bpfland 换成其他调度器名字就行。

\n

这下笔记本插拔电源再也不用惦记着手动切调度器了:D

\n","excerpt":"","more":"

一直觉得scx_lavd和scx_flow支持自动判断负载来选择合适的调度模式很不错,但是我还是更信赖Nvidia工程师出品的scx_bpfland,尤其是CachyOS定义的scx_bpfland低延迟模式和省电模式在我笔记本上的表现都非常好。但有一个痛点——笔记本插电和用电池的场景对调度器的需求完全不同:

\n
    \n
  • 插电时:希望低延迟、高性能,用得爽,程序编译快游戏帧数高
  • \n
  • 电池时:希望尽可能省电,续航优先尽量用久一点,不在乎性能牺牲
  • \n
\n

每次手动敲命令切换实在太蠢了使用CachyOS的scx-manager也不太方便。研究了一下后发现可以借助 Linux 的 udev 规则来监听电源事件,实现全自动切换。记录一下折腾过程。

\n
\n

现状:scx_loader 配置文件

这是我的 /etc/scx_loader.toml(CachyOS已提前为各个调度器配好了 mode):

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
default_sched = "scx_bpfland"
default_mode = "LowLatency"

[scheds.scx_rusty]
auto_mode = []
gaming_mode = []
lowlatency_mode = []
powersave_mode = []
server_mode = []

[scheds.scx_p2dq]
auto_mode = []
gaming_mode = []
lowlatency_mode = ["-y"]
powersave_mode = []
server_mode = ["--keep-running"]

[scheds.scx_bpfland]
auto_mode = []
gaming_mode = ["-m", "performance"]
lowlatency_mode = ["-s", "5000", "-S", "500", "-l", "5000", "-m", "performance"]
powersave_mode = ["-m", "powersave"]
server_mode = ["-p"]

[scheds.scx_lavd]
auto_mode = ["--autopower"]
gaming_mode = ["--performance"]
lowlatency_mode = ["--performance"]
powersave_mode = ["--powersave"]
server_mode = []

[scheds.scx_tickless]
auto_mode = []
gaming_mode = ["-f", "5000", "-s", "5000"]
lowlatency_mode = ["-f", "5000", "-s", "1000"]
powersave_mode = ["-f", "50", "-p"]
server_mode = ["-f", "100"]

[scheds.scx_flash]
auto_mode = []
gaming_mode = []
lowlatency_mode = []
powersave_mode = []
server_mode = []
\n\n

仔细观察 [scheds.scx_bpfland] 这一块:

\n
    \n
  • lowlatency_mode 正好对应插电状态想要的参数:-s 5000 -S 500 -l 5000 -m performance
  • \n
  • powersave_mode 正好对应电池状态想要的参数:-m powersave
  • \n
\n

不用自己写额外参数配置,直接复用已有的 mode 定义就好。

\n
\n

思路

scx_loader 提供了配套的命令行工具 scxctl,可以用来切换调度器和模式。剩下的就是找一个能在电源事件发生时自动执行命令的机制——Linux 内核的 udev 子系统天生就是干这个的。

\n

整体流程很简单:插拔电源 → 内核发出 uevent → udev 匹配规则 → systemd-run 异步执行 → scxctl switch 切换模式

\n
\n

步骤一:测试手动切换命令

先确认 scxctl 能正常工作(需要 root 权限):

\n
1
2
3
4
5
# 模拟插电 → 低延迟/高性能模式
sudo scxctl switch -s bpfland -m lowlatency

# 模拟拔电 → 省电模式
sudo scxctl switch -s bpfland -m powersave
\n\n

如果这两条命令都能成功切换,就可以继续下一步。

\n
\n

步骤二:编写 udev 规则

创建规则文件:

\n
1
sudo nano /etc/udev/rules.d/99-scx-power.rules
\n\n

写入以下内容:

\n
1
2
3
4
5
# 插入电源 (AC) → 切换为 bpfland 的 lowlatency 模式
SUBSYSTEM=="power_supply", ATTR{type}=="Mains", ATTR{online}=="1", RUN+="/usr/bin/systemd-run --no-block /usr/bin/scxctl switch -s bpfland -m lowlatency"

# 拔出电源 (Battery) → 切换为 bpfland 的 powersave 模式
SUBSYSTEM=="power_supply", ATTR{type}=="Mains", ATTR{online}=="0", RUN+="/usr/bin/systemd-run --no-block /usr/bin/scxctl switch -s bpfland -m powersave"
\n\n
\n

为什么用 systemd-run --no-block 包裹?

\n

scxctl 底层通过 D-Bus 与 scx_loader 守护进程通信。如果让 udev 直接执行,可能会因为 udev 的受限执行环境(缺少 D-Bus session、环境变量缺失)或者命令执行时间过长阻塞 udev 进程导致失败。交给 systemd 后台异步执行是最稳妥的做法。

\n
\n
\n

步骤三:重载规则使其生效

1
2
sudo udevadm control --reload-rules
sudo udevadm trigger
\n\n

udevadm trigger 会立即重放一次当前设备状态,所以如果此时已经插着电源,规则会立刻触发一次,可以顺便验证是否生效。

\n
\n

步骤四(可选):验证电源类型

绝大多数笔记本的外部电源在内核中的设备类型都是 Mains,上面规则直接就能用。如果插拔电源发现没反应,检查一下你的电源设备类型:

\n
1
cat /sys/class/power_supply/*/type
\n\n

如果输出的不是 Mains(比如是 AC 或其他名字),把 udev 规则里的 ATTR{type}=="Mains" 换成实际类型即可。

\n
\n

验证效果

插拔电源后,可以用以下命令确认模式是否已经切换:

\n
1
scxctl get
\n\n
\n

总结

整个过程其实非常简单——核心思路就是「udev 监听电源事件 → systemd-run 异步调用 scxctl 切换 mode」。全程没有写一个新参数,完全复用 scx_loader.toml 里已有的 mode 定义。

\n

这个方案同样适用于其他调度器:只要在 scx_loader.toml 里给对应调度器配好 lowlatency_modepowersave_mode,然后把 udev 规则里的 -s bpfland 换成其他调度器名字就行。

\n

这下笔记本插拔电源再也不用惦记着手动切调度器了:D

\n"},{"title":"相见恨晚的 SearXNG:打造私人搜索引擎全指南","date":"2025-03-10T14:55:04.000Z","_content":"\n![SearXNG 界面预览](/images/searxng.png) \n**厌倦了商业搜索引擎的广告追踪?** 受够技术社区被 SEO 污染的水文?希望搜索结果里尽量不要呈现C*DN等平台的低质内容?这款开源元搜索引擎 [SearXNG](https://github.com/searxng/searxng) 现在让我直呼真香爱不释手,使用频率远超 Nextcloud 等自建服务。\n\n---\n\n### ▍ 为什么选择 SearXNG?\n✅ **核心优势矩阵** \n| 特性 | 实现方式 | 用户收益 |\n|--------------------|----------------------------|------------------------|\n| 隐私保护 | 匿名聚合第三方结果 | 无搜索历史追踪 |\n| 结果去商业化 | 过滤 SEO 优化内容 | 提升技术资料检索效率 |\n| 多引擎支持 | 集成 Google/Bing/Brave 等 70+ 源 | 规避单一引擎局限 |\n| 界面定制 | 主题引擎分离设计 | 打造个性化搜索门户 |\n\n⚠️ 同类工具对比: \n此前使用的 [luxirty-search](https://github.com/KoriIku/luxirty-search) 也很不错但是依赖 Google CSE,对于内网环境日常使用多少有点不友好。\n\n之前看了下网上普遍推荐docker方案不过部署流程与官方文档有不少出入而且在我本地没法正常运行,这里我们直接按照官方文档来,事不宜迟直接开始部署。\n\n---\n\n### ▍ 极简部署方案\n#### ▶ 环境准备\n```bash\nmkdir -p ~/services/searxng && cd $_ # 随意找个地方创建专用目录\nexport SEARXNG_PORT=8080 # 设置服务端口\n```\n\n#### ▶ 容器化部署\n```bash\n# 拉取官方镜像\ndocker pull searxng/searxng\n\n# 启动容器(推荐绑定持久化配置)\ndocker run -d --restart=unless-stopped \\\n -p ${SEARXNG_PORT}:8080 \\\n -v \"${PWD}/config:/etc/searxng\" \\\n -e \"BASE_URL=http://your-domain.com\" \\\n -e \"INSTANCE_NAME=PrivateSearch\" \\\n searxng/searxng\n```\n📌 参数说明: \n- `-v` 挂载配置文件实现持久化 \n- `--restart` 确保服务异常退出后自动重启 \n- `BASE_URL` 需替换为实际访问域名,不过也可以后续再配置\n\n---\n\n### ▍ 高频问题排雷\n#### 🔴 镜像拉取失败\n**现象**:`Error response from daemon: pull access denied` \n✅ 解决方案: \n参考先前的 [Overleaf Toolkit 踩坑记录](https://blog.hifuu.ink/2024/11/06/overleaf/) 配置镜像加速源\n\n#### 🔴 Google 频繁拦截\n**现象**:`Too Many Requests` 错误频发 \n\n⚠️ 这算是一个玄学问题除了更换代理我还没有发现更好的解决方案\n\n✅ 实战验证方案: \n更换你的代理服务器比如笔者目前在🇺🇸🇭🇰🇯🇵均有购买VPS服务器可作为代理,经尝试总会有可以使用的节点。\n\n📌 在配置文件设置代理: \n```yaml\noutgoing:\n proxies:\n all://:\n - http://
:\n```\n\n---\n\n### ▍ 效果展示与体验\n访问我的生产环境实例 [search.hifuu.ink](https://search.hifuu.ink) 可体验以下功能: \n- 🔍 多引擎结果聚合对比 \n- 🌍 支持 30+ 语言实时翻译 \n- 🛡️ 零 Cookie 跟踪的隐私模式 \n- 🎨 主题切换 \n\n---\n### ▍ 进阶优化建议\n1. **反向代理配置** \n\n网上相关教程应该很多这里不再重复,我的SearXNG服务器通过香港VPS反代到公网使用。\n\n2. **定期维护命令** \n```bash\n# 更新搜索引擎引擎列表\ndocker exec -it searxng python -m searx.engines --update\n```\n\n3. **监控指标集成** \n通过 Prometheus 导出搜索统计:\n```bash\ndocker run -d --network=searxng-net \\\n -v \"${PWD}/metrics:/metrics\" \\\n prom/prometheus --config.file=/metrics/searxng.yml\n``` \n\n---\n\n**🚀 立即行动** \n\n快速拥有对抗 SEO 污染的利器,快来打造你的私人搜索门户吧!部署过程遇到问题欢迎联系笔者交流。\n","source":"_posts/searxng.md","raw":"---\ntitle: 相见恨晚的 SearXNG:打造私人搜索引擎全指南\ndate: 2025-03-10 22:55:04\ntags: [开源工具, 技术分享]\n---\n\n![SearXNG 界面预览](/images/searxng.png) \n**厌倦了商业搜索引擎的广告追踪?** 受够技术社区被 SEO 污染的水文?希望搜索结果里尽量不要呈现C*DN等平台的低质内容?这款开源元搜索引擎 [SearXNG](https://github.com/searxng/searxng) 现在让我直呼真香爱不释手,使用频率远超 Nextcloud 等自建服务。\n\n---\n\n### ▍ 为什么选择 SearXNG?\n✅ **核心优势矩阵** \n| 特性 | 实现方式 | 用户收益 |\n|--------------------|----------------------------|------------------------|\n| 隐私保护 | 匿名聚合第三方结果 | 无搜索历史追踪 |\n| 结果去商业化 | 过滤 SEO 优化内容 | 提升技术资料检索效率 |\n| 多引擎支持 | 集成 Google/Bing/Brave 等 70+ 源 | 规避单一引擎局限 |\n| 界面定制 | 主题引擎分离设计 | 打造个性化搜索门户 |\n\n⚠️ 同类工具对比: \n此前使用的 [luxirty-search](https://github.com/KoriIku/luxirty-search) 也很不错但是依赖 Google CSE,对于内网环境日常使用多少有点不友好。\n\n之前看了下网上普遍推荐docker方案不过部署流程与官方文档有不少出入而且在我本地没法正常运行,这里我们直接按照官方文档来,事不宜迟直接开始部署。\n\n---\n\n### ▍ 极简部署方案\n#### ▶ 环境准备\n```bash\nmkdir -p ~/services/searxng && cd $_ # 随意找个地方创建专用目录\nexport SEARXNG_PORT=8080 # 设置服务端口\n```\n\n#### ▶ 容器化部署\n```bash\n# 拉取官方镜像\ndocker pull searxng/searxng\n\n# 启动容器(推荐绑定持久化配置)\ndocker run -d --restart=unless-stopped \\\n -p ${SEARXNG_PORT}:8080 \\\n -v \"${PWD}/config:/etc/searxng\" \\\n -e \"BASE_URL=http://your-domain.com\" \\\n -e \"INSTANCE_NAME=PrivateSearch\" \\\n searxng/searxng\n```\n📌 参数说明: \n- `-v` 挂载配置文件实现持久化 \n- `--restart` 确保服务异常退出后自动重启 \n- `BASE_URL` 需替换为实际访问域名,不过也可以后续再配置\n\n---\n\n### ▍ 高频问题排雷\n#### 🔴 镜像拉取失败\n**现象**:`Error response from daemon: pull access denied` \n✅ 解决方案: \n参考先前的 [Overleaf Toolkit 踩坑记录](https://blog.hifuu.ink/2024/11/06/overleaf/) 配置镜像加速源\n\n#### 🔴 Google 频繁拦截\n**现象**:`Too Many Requests` 错误频发 \n\n⚠️ 这算是一个玄学问题除了更换代理我还没有发现更好的解决方案\n\n✅ 实战验证方案: \n更换你的代理服务器比如笔者目前在🇺🇸🇭🇰🇯🇵均有购买VPS服务器可作为代理,经尝试总会有可以使用的节点。\n\n📌 在配置文件设置代理: \n```yaml\noutgoing:\n proxies:\n all://:\n - http://
:\n```\n\n---\n\n### ▍ 效果展示与体验\n访问我的生产环境实例 [search.hifuu.ink](https://search.hifuu.ink) 可体验以下功能: \n- 🔍 多引擎结果聚合对比 \n- 🌍 支持 30+ 语言实时翻译 \n- 🛡️ 零 Cookie 跟踪的隐私模式 \n- 🎨 主题切换 \n\n---\n### ▍ 进阶优化建议\n1. **反向代理配置** \n\n网上相关教程应该很多这里不再重复,我的SearXNG服务器通过香港VPS反代到公网使用。\n\n2. **定期维护命令** \n```bash\n# 更新搜索引擎引擎列表\ndocker exec -it searxng python -m searx.engines --update\n```\n\n3. **监控指标集成** \n通过 Prometheus 导出搜索统计:\n```bash\ndocker run -d --network=searxng-net \\\n -v \"${PWD}/metrics:/metrics\" \\\n prom/prometheus --config.file=/metrics/searxng.yml\n``` \n\n---\n\n**🚀 立即行动** \n\n快速拥有对抗 SEO 污染的利器,快来打造你的私人搜索门户吧!部署过程遇到问题欢迎联系笔者交流。\n","slug":"searxng","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudbly001sidd0c1p4fvk6","content":"

\"SearXNG
厌倦了商业搜索引擎的广告追踪? 受够技术社区被 SEO 污染的水文?希望搜索结果里尽量不要呈现C*DN等平台的低质内容?这款开源元搜索引擎 SearXNG 现在让我直呼真香爱不释手,使用频率远超 Nextcloud 等自建服务。

\n
\n

▍ 为什么选择 SearXNG?

核心优势矩阵

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
特性实现方式用户收益
隐私保护匿名聚合第三方结果无搜索历史追踪
结果去商业化过滤 SEO 优化内容提升技术资料检索效率
多引擎支持集成 Google/Bing/Brave 等 70+ 源规避单一引擎局限
界面定制主题引擎分离设计打造个性化搜索门户
\n

⚠️ 同类工具对比:
此前使用的 luxirty-search 也很不错但是依赖 Google CSE,对于内网环境日常使用多少有点不友好。

\n

之前看了下网上普遍推荐docker方案不过部署流程与官方文档有不少出入而且在我本地没法正常运行,这里我们直接按照官方文档来,事不宜迟直接开始部署。

\n
\n

▍ 极简部署方案

▶ 环境准备

1
2
mkdir -p ~/services/searxng && cd $_  # 随意找个地方创建专用目录
export SEARXNG_PORT=8080 # 设置服务端口
\n\n

▶ 容器化部署

1
2
3
4
5
6
7
8
9
10
# 拉取官方镜像
docker pull searxng/searxng

# 启动容器(推荐绑定持久化配置)
docker run -d --restart=unless-stopped \\
-p ${SEARXNG_PORT}:8080 \\
-v "${PWD}/config:/etc/searxng" \\
-e "BASE_URL=http://your-domain.com" \\
-e "INSTANCE_NAME=PrivateSearch" \\
searxng/searxng
\n

📌 参数说明:

\n
    \n
  • -v 挂载配置文件实现持久化
  • \n
  • --restart 确保服务异常退出后自动重启
  • \n
  • BASE_URL 需替换为实际访问域名,不过也可以后续再配置
  • \n
\n
\n

▍ 高频问题排雷

🔴 镜像拉取失败

现象Error response from daemon: pull access denied
✅ 解决方案:
参考先前的 Overleaf Toolkit 踩坑记录 配置镜像加速源

\n

🔴 Google 频繁拦截

现象Too Many Requests 错误频发

\n

⚠️ 这算是一个玄学问题除了更换代理我还没有发现更好的解决方案

\n

✅ 实战验证方案:
更换你的代理服务器比如笔者目前在🇺🇸🇭🇰🇯🇵均有购买VPS服务器可作为代理,经尝试总会有可以使用的节点。

\n

📌 在配置文件设置代理:

\n
1
2
3
4
outgoing:
proxies:
all://:
- http://<address>:<port>
\n\n
\n

▍ 效果展示与体验

访问我的生产环境实例 search.hifuu.ink 可体验以下功能:

\n
    \n
  • 🔍 多引擎结果聚合对比
  • \n
  • 🌍 支持 30+ 语言实时翻译
  • \n
  • 🛡️ 零 Cookie 跟踪的隐私模式
  • \n
  • 🎨 主题切换
  • \n
\n
\n

▍ 进阶优化建议

    \n
  1. 反向代理配置
  2. \n
\n

网上相关教程应该很多这里不再重复,我的SearXNG服务器通过香港VPS反代到公网使用。

\n
    \n
  1. 定期维护命令

    \n
    1
    2
    # 更新搜索引擎引擎列表
    docker exec -it searxng python -m searx.engines --update
    \n
  2. \n
  3. 监控指标集成
    通过 Prometheus 导出搜索统计:

    \n
    1
    2
    3
    docker run -d --network=searxng-net \\
    -v "${PWD}/metrics:/metrics" \\
    prom/prometheus --config.file=/metrics/searxng.yml
  4. \n
\n
\n

🚀 立即行动

\n

快速拥有对抗 SEO 污染的利器,快来打造你的私人搜索门户吧!部署过程遇到问题欢迎联系笔者交流。

\n","excerpt":"","more":"

\"SearXNG
厌倦了商业搜索引擎的广告追踪? 受够技术社区被 SEO 污染的水文?希望搜索结果里尽量不要呈现C*DN等平台的低质内容?这款开源元搜索引擎 SearXNG 现在让我直呼真香爱不释手,使用频率远超 Nextcloud 等自建服务。

\n
\n

▍ 为什么选择 SearXNG?

核心优势矩阵

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
特性实现方式用户收益
隐私保护匿名聚合第三方结果无搜索历史追踪
结果去商业化过滤 SEO 优化内容提升技术资料检索效率
多引擎支持集成 Google/Bing/Brave 等 70+ 源规避单一引擎局限
界面定制主题引擎分离设计打造个性化搜索门户
\n

⚠️ 同类工具对比:
此前使用的 luxirty-search 也很不错但是依赖 Google CSE,对于内网环境日常使用多少有点不友好。

\n

之前看了下网上普遍推荐docker方案不过部署流程与官方文档有不少出入而且在我本地没法正常运行,这里我们直接按照官方文档来,事不宜迟直接开始部署。

\n
\n

▍ 极简部署方案

▶ 环境准备

1
2
mkdir -p ~/services/searxng && cd $_  # 随意找个地方创建专用目录
export SEARXNG_PORT=8080 # 设置服务端口
\n\n

▶ 容器化部署

1
2
3
4
5
6
7
8
9
10
# 拉取官方镜像
docker pull searxng/searxng

# 启动容器(推荐绑定持久化配置)
docker run -d --restart=unless-stopped \\
-p ${SEARXNG_PORT}:8080 \\
-v "${PWD}/config:/etc/searxng" \\
-e "BASE_URL=http://your-domain.com" \\
-e "INSTANCE_NAME=PrivateSearch" \\
searxng/searxng
\n

📌 参数说明:

\n
    \n
  • -v 挂载配置文件实现持久化
  • \n
  • --restart 确保服务异常退出后自动重启
  • \n
  • BASE_URL 需替换为实际访问域名,不过也可以后续再配置
  • \n
\n
\n

▍ 高频问题排雷

🔴 镜像拉取失败

现象Error response from daemon: pull access denied
✅ 解决方案:
参考先前的 Overleaf Toolkit 踩坑记录 配置镜像加速源

\n

🔴 Google 频繁拦截

现象Too Many Requests 错误频发

\n

⚠️ 这算是一个玄学问题除了更换代理我还没有发现更好的解决方案

\n

✅ 实战验证方案:
更换你的代理服务器比如笔者目前在🇺🇸🇭🇰🇯🇵均有购买VPS服务器可作为代理,经尝试总会有可以使用的节点。

\n

📌 在配置文件设置代理:

\n
1
2
3
4
outgoing:
proxies:
all://:
- http://<address>:<port>
\n\n
\n

▍ 效果展示与体验

访问我的生产环境实例 search.hifuu.ink 可体验以下功能:

\n
    \n
  • 🔍 多引擎结果聚合对比
  • \n
  • 🌍 支持 30+ 语言实时翻译
  • \n
  • 🛡️ 零 Cookie 跟踪的隐私模式
  • \n
  • 🎨 主题切换
  • \n
\n
\n

▍ 进阶优化建议

    \n
  1. 反向代理配置
  2. \n
\n

网上相关教程应该很多这里不再重复,我的SearXNG服务器通过香港VPS反代到公网使用。

\n
    \n
  1. 定期维护命令

    \n
    1
    2
    # 更新搜索引擎引擎列表
    docker exec -it searxng python -m searx.engines --update
    \n
  2. \n
  3. 监控指标集成
    通过 Prometheus 导出搜索统计:

    \n
    1
    2
    3
    docker run -d --network=searxng-net \\
    -v "${PWD}/metrics:/metrics" \\
    prom/prometheus --config.file=/metrics/searxng.yml
  4. \n
\n
\n

🚀 立即行动

\n

快速拥有对抗 SEO 污染的利器,快来打造你的私人搜索门户吧!部署过程遇到问题欢迎联系笔者交流。

\n"},{"title":"命令行编辑器的优雅新选择","date":"2024-12-12T17:40:27.000Z","_content":"\n意外发现Zellij+Helix还挺好用的\n\n避免了vim/neovim的配置流程直接就能上手的轻量命令行开发环境\n\n稍微了解一下二者的快捷键就能舒适码字了\n\n甚至还都是rust出品\n\n即刻尝试一下[Zellij](https://zellij.dev/)和[Helix](https://helix-editor.com/)吧!\n\n// 至于Helix没有文件树显示的方案,反正Helix选择文件挺方便的要文件树无非是希望编辑窗口居于窗口中央,倒是可以用watch和tree命令来代替还能手动设置哪些文件不用显示hhh\n\n![实际效果](/images/zellij-helix.jpg)\n","source":"_posts/zellij-helix.md","raw":"---\ntitle: 命令行编辑器的优雅新选择\ndate: 2024-12-13 01:40:27\ntags: [技术分享]\n---\n\n意外发现Zellij+Helix还挺好用的\n\n避免了vim/neovim的配置流程直接就能上手的轻量命令行开发环境\n\n稍微了解一下二者的快捷键就能舒适码字了\n\n甚至还都是rust出品\n\n即刻尝试一下[Zellij](https://zellij.dev/)和[Helix](https://helix-editor.com/)吧!\n\n// 至于Helix没有文件树显示的方案,反正Helix选择文件挺方便的要文件树无非是希望编辑窗口居于窗口中央,倒是可以用watch和tree命令来代替还能手动设置哪些文件不用显示hhh\n\n![实际效果](/images/zellij-helix.jpg)\n","slug":"zellij-helix","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudbm1003gidd076wd2bca","content":"

意外发现Zellij+Helix还挺好用的

\n

避免了vim/neovim的配置流程直接就能上手的轻量命令行开发环境

\n

稍微了解一下二者的快捷键就能舒适码字了

\n

甚至还都是rust出品

\n

即刻尝试一下ZellijHelix吧!

\n

// 至于Helix没有文件树显示的方案,反正Helix选择文件挺方便的要文件树无非是希望编辑窗口居于窗口中央,倒是可以用watch和tree命令来代替还能手动设置哪些文件不用显示hhh

\n

\"实际效果\"

\n","excerpt":"","more":"

意外发现Zellij+Helix还挺好用的

\n

避免了vim/neovim的配置流程直接就能上手的轻量命令行开发环境

\n

稍微了解一下二者的快捷键就能舒适码字了

\n

甚至还都是rust出品

\n

即刻尝试一下ZellijHelix吧!

\n

// 至于Helix没有文件树显示的方案,反正Helix选择文件挺方便的要文件树无非是希望编辑窗口居于窗口中央,倒是可以用watch和tree命令来代替还能手动设置哪些文件不用显示hhh

\n

\"实际效果\"

\n"},{"title":"原来我还有个博客","date":"2024-06-02T16:19:55.000Z","_content":"\n2025-02-25\n\n最近整理了一下缓存部署起来方便多了,有空就写点东西记录一下吧~\n","source":"_posts/原来我还有个博客.md","raw":"---\ntitle: 原来我还有个博客\ndate: 2024-06-03 00:19:55\ntags: 日志\n---\n\n2025-02-25\n\n最近整理了一下缓存部署起来方便多了,有空就写点东西记录一下吧~\n","slug":"原来我还有个博客","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudbm2003hidd077r6be6a","content":"

2025-02-25

\n

最近整理了一下缓存部署起来方便多了,有空就写点东西记录一下吧~

\n","excerpt":"","more":"

2025-02-25

\n

最近整理了一下缓存部署起来方便多了,有空就写点东西记录一下吧~

\n"},{"title":"梦开始的地方","date":"2023-12-06T14:53:35.000Z","_content":"\n不知不觉已经高中毕业快半年了,现在在NUDT的生活还算适应吧,交到了一些很有趣的朋友并且在技术方面取得了一定突破(很期待明年的asc2024呢),今天看到华科一位学长的博客突然想起我还没好好搭建过自己的Blog,于是心血来潮搞了下我的Github Pages。\n\n今后会在这里更新记录自己的生活,学习,工作,以及一些想法,希望能坚持下来吧。\n\n> P.S.最近我都经历了些什么:\n>\n> 1. 时长21天军训,认识了一群很可爱的班长(尤其是负责我们5班6班的英子),初步和队里的同学了解;\n> 2. 学习了一些C++基础,在洛谷上刷了不少算法题;\n> 3. ACM招新赛被薄纱,差一题进入校队;\n> 4. 对算法竞赛感到疑惑,尝试学习Flutter和操作系统开阔视野;\n> 5. 入坑战地,爽爽爽;\n> 6. 被一位巨强的学长发掘,加入NUDT超算队;\n> 7. 面临三个考试周,熬过去就是胜利;\n\n虽然天天早八满课很不爽,但是平时还是可以学习自己想学的技术周末也可以打游戏感觉还行吧,我还是相信NUDT,既来之则安之。\n\n这里,毕竟是我梦开始的地方。\n\n> 胸怀祖国,团结协作,志在高峰,奋勇拼搏!\n\n今天是2023年12月6日,加油!\n\n------------------------\n\n修改一下,有的话还是不适合明说哦\n\n5YK75a2p5a2Q5Lus77yM5b+r6YCD5ZWK77yB77yB77yBCg==\n\n","source":"_posts/梦开始的地方.md","raw":"---\ntitle: 梦开始的地方\ndate: 2023-12-06 22:53:35\ntags: 日志\n---\n\n不知不觉已经高中毕业快半年了,现在在NUDT的生活还算适应吧,交到了一些很有趣的朋友并且在技术方面取得了一定突破(很期待明年的asc2024呢),今天看到华科一位学长的博客突然想起我还没好好搭建过自己的Blog,于是心血来潮搞了下我的Github Pages。\n\n今后会在这里更新记录自己的生活,学习,工作,以及一些想法,希望能坚持下来吧。\n\n> P.S.最近我都经历了些什么:\n>\n> 1. 时长21天军训,认识了一群很可爱的班长(尤其是负责我们5班6班的英子),初步和队里的同学了解;\n> 2. 学习了一些C++基础,在洛谷上刷了不少算法题;\n> 3. ACM招新赛被薄纱,差一题进入校队;\n> 4. 对算法竞赛感到疑惑,尝试学习Flutter和操作系统开阔视野;\n> 5. 入坑战地,爽爽爽;\n> 6. 被一位巨强的学长发掘,加入NUDT超算队;\n> 7. 面临三个考试周,熬过去就是胜利;\n\n虽然天天早八满课很不爽,但是平时还是可以学习自己想学的技术周末也可以打游戏感觉还行吧,我还是相信NUDT,既来之则安之。\n\n这里,毕竟是我梦开始的地方。\n\n> 胸怀祖国,团结协作,志在高峰,奋勇拼搏!\n\n今天是2023年12月6日,加油!\n\n------------------------\n\n修改一下,有的话还是不适合明说哦\n\n5YK75a2p5a2Q5Lus77yM5b+r6YCD5ZWK77yB77yB77yBCg==\n\n","slug":"梦开始的地方","published":1,"updated":"2026-05-29T16:03:35.032Z","comments":1,"layout":"post","photos":[],"_id":"cmqyudbm2003jidd022qvdidp","content":"

不知不觉已经高中毕业快半年了,现在在NUDT的生活还算适应吧,交到了一些很有趣的朋友并且在技术方面取得了一定突破(很期待明年的asc2024呢),今天看到华科一位学长的博客突然想起我还没好好搭建过自己的Blog,于是心血来潮搞了下我的Github Pages。

\n

今后会在这里更新记录自己的生活,学习,工作,以及一些想法,希望能坚持下来吧。

\n
\n

P.S.最近我都经历了些什么:

\n
    \n
  1. 时长21天军训,认识了一群很可爱的班长(尤其是负责我们5班6班的英子),初步和队里的同学了解;
  2. \n
  3. 学习了一些C++基础,在洛谷上刷了不少算法题;
  4. \n
  5. ACM招新赛被薄纱,差一题进入校队;
  6. \n
  7. 对算法竞赛感到疑惑,尝试学习Flutter和操作系统开阔视野;
  8. \n
  9. 入坑战地,爽爽爽;
  10. \n
  11. 被一位巨强的学长发掘,加入NUDT超算队;
  12. \n
  13. 面临三个考试周,熬过去就是胜利;
  14. \n
\n
\n

虽然天天早八满课很不爽,但是平时还是可以学习自己想学的技术周末也可以打游戏感觉还行吧,我还是相信NUDT,既来之则安之。

\n

这里,毕竟是我梦开始的地方。

\n
\n

胸怀祖国,团结协作,志在高峰,奋勇拼搏!

\n
\n

今天是2023年12月6日,加油!

\n
\n

修改一下,有的话还是不适合明说哦

\n

5YK75a2p5a2Q5Lus77yM5b+r6YCD5ZWK77yB77yB77yBCg==

\n","excerpt":"","more":"

不知不觉已经高中毕业快半年了,现在在NUDT的生活还算适应吧,交到了一些很有趣的朋友并且在技术方面取得了一定突破(很期待明年的asc2024呢),今天看到华科一位学长的博客突然想起我还没好好搭建过自己的Blog,于是心血来潮搞了下我的Github Pages。

\n

今后会在这里更新记录自己的生活,学习,工作,以及一些想法,希望能坚持下来吧。

\n
\n

P.S.最近我都经历了些什么:

\n
    \n
  1. 时长21天军训,认识了一群很可爱的班长(尤其是负责我们5班6班的英子),初步和队里的同学了解;
  2. \n
  3. 学习了一些C++基础,在洛谷上刷了不少算法题;
  4. \n
  5. ACM招新赛被薄纱,差一题进入校队;
  6. \n
  7. 对算法竞赛感到疑惑,尝试学习Flutter和操作系统开阔视野;
  8. \n
  9. 入坑战地,爽爽爽;
  10. \n
  11. 被一位巨强的学长发掘,加入NUDT超算队;
  12. \n
  13. 面临三个考试周,熬过去就是胜利;
  14. \n
\n
\n

虽然天天早八满课很不爽,但是平时还是可以学习自己想学的技术周末也可以打游戏感觉还行吧,我还是相信NUDT,既来之则安之。

\n

这里,毕竟是我梦开始的地方。

\n
\n

胸怀祖国,团结协作,志在高峰,奋勇拼搏!

\n
\n

今天是2023年12月6日,加油!

\n
\n

修改一下,有的话还是不适合明说哦

\n

5YK75a2p5a2Q5Lus77yM5b+r6YCD5ZWK77yB77yB77yBCg==

\n"}],"PostAsset":[],"PostCategory":[{"post_id":"cmqyudbls000eidd032cxh5sc","category_id":"cmqyudblt000hidd063smcf8e","_id":"cmqyudblv000vidd0e53lerro"},{"post_id":"cmqyudblt000jidd0cw3ca8si","category_id":"cmqyudblu000pidd0gsplawn2","_id":"cmqyudblv0011idd0awm1akmt"},{"post_id":"cmqyudblt000midd00rrqai7r","category_id":"cmqyudblv000widd05uyn9gyz","_id":"cmqyudblw0016idd09rzucxyr"},{"post_id":"cmqyudblu000uidd09xaa6t9i","category_id":"cmqyudblv0012idd04skr2tck","_id":"cmqyudblw001bidd09s3x6za2"},{"post_id":"cmqyudblx001fidd07h7dbl8c","category_id":"cmqyudblu000pidd0gsplawn2","_id":"cmqyudbly001nidd02n13hax1"},{"post_id":"cmqyudbly001oidd04rcohmn6","category_id":"cmqyudblv0012idd04skr2tck","_id":"cmqyudblz001uidd0goof2lft"},{"post_id":"cmqyudblx001midd0c8u7g9hr","category_id":"cmqyudbly001qidd04zjib1zi","_id":"cmqyudblz001widd02vpi9ef6"}],"PostTag":[{"post_id":"cmqyudblq0005idd01wqnhilv","tag_id":"cmqyudblq0003idd031a3f6vs","_id":"cmqyudblr0008idd04ek301fo"},{"post_id":"cmqyudblo0001idd0h14q071s","tag_id":"cmqyudblq0003idd031a3f6vs","_id":"cmqyudbls000aidd0g0iff21b"},{"post_id":"cmqyudblr0006idd01uzzddnf","tag_id":"cmqyudblq0003idd031a3f6vs","_id":"cmqyudbls000didd09iq52yf6"},{"post_id":"cmqyudblr0009idd00sob470f","tag_id":"cmqyudblq0003idd031a3f6vs","_id":"cmqyudbls000fidd0ckd43yh0"},{"post_id":"cmqyudblp0002idd05h21800i","tag_id":"cmqyudblq0003idd031a3f6vs","_id":"cmqyudblt000iidd05zfr0fwj"},{"post_id":"cmqyudbls000bidd023vmejmk","tag_id":"cmqyudblq0003idd031a3f6vs","_id":"cmqyudblt000lidd02283g2xd"},{"post_id":"cmqyudbls000eidd032cxh5sc","tag_id":"cmqyudblq0003idd031a3f6vs","_id":"cmqyudblu000nidd083u8gaw9"},{"post_id":"cmqyudblq0004idd014047n93","tag_id":"cmqyudblq0003idd031a3f6vs","_id":"cmqyudblu000qidd09ijc63p0"},{"post_id":"cmqyudbls000gidd0bei0509p","tag_id":"cmqyudblt000kidd0e0fu2e4j","_id":"cmqyudblu000tidd08wpy5evn"},{"post_id":"cmqyudblt000jidd0cw3ca8si","tag_id":"cmqyudblu000sidd099rf4xuc","_id":"cmqyudblv0010idd0fxyybk6f"},{"post_id":"cmqyudblt000midd00rrqai7r","tag_id":"cmqyudblv000yidd0d8oi9n0s","_id":"cmqyudblw001aidd02mkf2rnc"},{"post_id":"cmqyudblt000midd00rrqai7r","tag_id":"cmqyudblu000sidd099rf4xuc","_id":"cmqyudblx001didd0249a8jn9"},{"post_id":"cmqyudblw0017idd0dgm4cijt","tag_id":"cmqyudblq0003idd031a3f6vs","_id":"cmqyudblx001gidd07n3xfpva"},{"post_id":"cmqyudblu000oidd01yznbp5t","tag_id":"cmqyudblw0018idd0c291fgky","_id":"cmqyudblx001iidd0athy1wpx"},{"post_id":"cmqyudblx001fidd07h7dbl8c","tag_id":"cmqyudblu000sidd099rf4xuc","_id":"cmqyudblx001lidd09mzy3tqr"},{"post_id":"cmqyudblu000ridd04wc3d8td","tag_id":"cmqyudblx001eidd0414tauy6","_id":"cmqyudblz001xidd0d2b29n91"},{"post_id":"cmqyudblu000ridd04wc3d8td","tag_id":"cmqyudblx001kidd07cyg1ga8","_id":"cmqyudblz001yidd0hds516bw"},{"post_id":"cmqyudblu000ridd04wc3d8td","tag_id":"cmqyudbly001pidd04s69e3nx","_id":"cmqyudblz0020idd00fuw21v7"},{"post_id":"cmqyudblu000ridd04wc3d8td","tag_id":"cmqyudbly001tidd09ynhgci6","_id":"cmqyudblz0021idd07y67be4m"},{"post_id":"cmqyudblu000uidd09xaa6t9i","tag_id":"cmqyudblz001vidd0ccy5ancj","_id":"cmqyudblz0023idd0a4s140c1"},{"post_id":"cmqyudblv000xidd01m864302","tag_id":"cmqyudblx001kidd07cyg1ga8","_id":"cmqyudblz0025idd0ac7g4ihw"},{"post_id":"cmqyudblv000xidd01m864302","tag_id":"cmqyudblz0022idd09l5u269h","_id":"cmqyudblz0026idd0gj714ecy"},{"post_id":"cmqyudblv000zidd056tl5735","tag_id":"cmqyudblz0022idd09l5u269h","_id":"cmqyudblz002aidd05bty7okm"},{"post_id":"cmqyudblv000zidd056tl5735","tag_id":"cmqyudblz0027idd074d177v1","_id":"cmqyudblz002bidd02cb38jr2"},{"post_id":"cmqyudblv000zidd056tl5735","tag_id":"cmqyudblw0018idd0c291fgky","_id":"cmqyudblz002didd0hfoa3bp4"},{"post_id":"cmqyudblv0013idd0ag2t06tp","tag_id":"cmqyudblw0018idd0c291fgky","_id":"cmqyudblz002eidd0hq5cc3n8"},{"post_id":"cmqyudblw0015idd028lkhco5","tag_id":"cmqyudblz002cidd0cfoo5ah4","_id":"cmqyudbm0002hidd026cg98dt"},{"post_id":"cmqyudblw0015idd028lkhco5","tag_id":"cmqyudblw0018idd0c291fgky","_id":"cmqyudbm0002iidd05jnnf16u"},{"post_id":"cmqyudblw0019idd05a1ph9vo","tag_id":"cmqyudblz001vidd0ccy5ancj","_id":"cmqyudbm0002kidd0228oh4yf"},{"post_id":"cmqyudblw001cidd0aajv1hpn","tag_id":"cmqyudbm0002jidd0crga3gtk","_id":"cmqyudbm0002nidd07ylg19oy"},{"post_id":"cmqyudblw001cidd0aajv1hpn","tag_id":"cmqyudblx001kidd07cyg1ga8","_id":"cmqyudbm0002oidd0b2zgfk2u"},{"post_id":"cmqyudblx001hidd06c6f23wg","tag_id":"cmqyudblx001kidd07cyg1ga8","_id":"cmqyudbm0002qidd0avwz2gyo"},{"post_id":"cmqyudblx001jidd008c7c46c","tag_id":"cmqyudblz001vidd0ccy5ancj","_id":"cmqyudbm0002uidd005eb1tkp"},{"post_id":"cmqyudblx001jidd008c7c46c","tag_id":"cmqyudbly001pidd04s69e3nx","_id":"cmqyudbm0002vidd00od8axh4"},{"post_id":"cmqyudblx001jidd008c7c46c","tag_id":"cmqyudblx001kidd07cyg1ga8","_id":"cmqyudbm0002xidd0cqsdgnft"},{"post_id":"cmqyudblx001midd0c8u7g9hr","tag_id":"cmqyudblz001vidd0ccy5ancj","_id":"cmqyudbm00031idd0ffplhw3d"},{"post_id":"cmqyudblx001midd0c8u7g9hr","tag_id":"cmqyudblz0027idd074d177v1","_id":"cmqyudbm00032idd028j6hx2h"},{"post_id":"cmqyudblx001midd0c8u7g9hr","tag_id":"cmqyudbm0002yidd0evl4hsbx","_id":"cmqyudbm00034idd08iocfnu6"},{"post_id":"cmqyudblx001midd0c8u7g9hr","tag_id":"cmqyudbm0002zidd0az7kfb8a","_id":"cmqyudbm00035idd0b90i9pfi"},{"post_id":"cmqyudbly001oidd04rcohmn6","tag_id":"cmqyudblz001vidd0ccy5ancj","_id":"cmqyudbm00037idd00vyc34so"},{"post_id":"cmqyudbly001ridd08t5dekob","tag_id":"cmqyudblz0022idd09l5u269h","_id":"cmqyudbm1003aidd0d9w5drw7"},{"post_id":"cmqyudbly001ridd08t5dekob","tag_id":"cmqyudblz0027idd074d177v1","_id":"cmqyudbm1003bidd0c8qw05kq"},{"post_id":"cmqyudbly001ridd08t5dekob","tag_id":"cmqyudblw0018idd0c291fgky","_id":"cmqyudbm1003cidd0ahz92jj9"},{"post_id":"cmqyudbly001ridd08t5dekob","tag_id":"cmqyudbm10038idd03kjzcpop","_id":"cmqyudbm1003didd06lo78vew"},{"post_id":"cmqyudbly001sidd0c1p4fvk6","tag_id":"cmqyudbm10039idd0b39m6ykq","_id":"cmqyudbm1003eidd0157a0ds5"},{"post_id":"cmqyudbly001sidd0c1p4fvk6","tag_id":"cmqyudblw0018idd0c291fgky","_id":"cmqyudbm1003fidd0g1y396a4"},{"post_id":"cmqyudbm1003gidd076wd2bca","tag_id":"cmqyudblw0018idd0c291fgky","_id":"cmqyudbm2003iidd0asde7w4m"},{"post_id":"cmqyudbm2003hidd077r6be6a","tag_id":"cmqyudblq0003idd031a3f6vs","_id":"cmqyudbm2003kidd065r9ca7h"},{"post_id":"cmqyudbm2003jidd022qvdidp","tag_id":"cmqyudblq0003idd031a3f6vs","_id":"cmqyudbm2003lidd06pd12rvw"}],"Tag":[{"name":"日志","_id":"cmqyudblq0003idd031a3f6vs"},{"name":"TEST","_id":"cmqyudblt000kidd0e0fu2e4j"},{"name":"音乐","_id":"cmqyudblu000sidd099rf4xuc"},{"name":"Vocaloid","_id":"cmqyudblv000yidd0d8oi9n0s"},{"name":"技术分享","_id":"cmqyudblw0018idd0c291fgky"},{"name":"竞赛","_id":"cmqyudblx001eidd0414tauy6"},{"name":"生活","_id":"cmqyudblx001kidd07cyg1ga8"},{"name":"学习","_id":"cmqyudbly001pidd04s69e3nx"},{"name":"ASC","_id":"cmqyudbly001tidd09ynhgci6"},{"name":"技术","_id":"cmqyudblz001vidd0ccy5ancj"},{"name":"Archlinux","_id":"cmqyudblz0022idd09l5u269h"},{"name":"系统优化","_id":"cmqyudblz0027idd074d177v1"},{"name":"Firefox","_id":"cmqyudblz002cidd0cfoo5ah4"},{"name":"板绘","_id":"cmqyudbm0002jidd0crga3gtk"},{"name":"HPC","_id":"cmqyudbm0002yidd0evl4hsbx"},{"name":"Linux内核","_id":"cmqyudbm0002zidd0az7kfb8a"},{"name":"SCX调度器","_id":"cmqyudbm10038idd03kjzcpop"},{"name":"开源工具","_id":"cmqyudbm10039idd0b39m6ykq"}]}} \ No newline at end of file diff --git a/source/_posts/firefox-global-menu.md b/source/_posts/firefox-global-menu.md new file mode 100644 index 0000000..ff35108 --- /dev/null +++ b/source/_posts/firefox-global-menu.md @@ -0,0 +1,84 @@ +--- +title: 为 Firefox 启用全局菜单集成到 KDE Plasma 任务栏 +date: 2026-06-29 11:32:26 +tags: [Firefox, 技术分享] +--- + +众所不周知, Chrome 菜单可以自动集成到 KDE Plasma 任务栏全局菜单组件,不管你喜不喜欢浏览器菜单至少看着很顺眼。然而,默认情况下,Firefox 并没有自动启用这个功能,需要进行一些配置。//本来我还以为是 Firefox 没有支持这个功能 TT + +本文将详细探讨 Linux 全局菜单的 D-Bus 工作原理,对比 Chrome 与 Firefox 的实现差异,并给出在 KDE Plasma 下完美启用 Firefox 全局菜单的配置方案(以下与 D-Bus 相关知识分享由 [Qwen 3.7](https://chat.qwen.ai)提供并由本人验证整理)。 + +## 什么是全局菜单?其背后的 D-Bus 工作原理 + +KDE Plasma 桌面的全局菜单(Global Menu)小组件可以把应用程序内部的菜单栏(如“文件”、“编辑”、“视图”等)提取出来,统一放置在桌面顶部的面板(Panel)中。这种机制实际上采用的是**“客户端-服务端”**的架构,其核心是基于 **D-Bus 消息总线系统** 与 **`dbusmenu` 协议**: + +1. **`dbusmenu` 协议**:最初由 Canonical 为 Ubuntu Unity 桌面环境设计,定义了一套标准的 D-Bus 接口,用来描述菜单的层次结构(菜单项、图标、快捷键、分隔符、启用状态等)以及用户交互事件。 +2. **应用端(D-Bus 导出者)**:支持全局菜单的应用启动时,会在 D-Bus 会话总线(Session Bus)上注册一个服务,把本应用的菜单树作为 D-Bus 对象暴露出来。此时,菜单的数据实质上依然保存在应用的内存中,只是通过 D-Bus 接口暴露给外部读取。 +3. **全局菜单小组件(D-Bus 消费者)**: + * **焦点追踪**:小组件通过窗口管理器(KWin)实时监听当前获得焦点的活动窗口。 + * **数据拉取**:当用户切换窗口时,小组件定位到该窗口对应的 D-Bus 服务地址,通过 `dbusmenu` 协议拉取菜单树,并在顶栏上进行渲染。 + * **事件回传**:用户在顶栏点击菜单项时,小组件通过 D-Bus 向目标应用发送“激活”(Activate)信号,应用接收后执行具体的操作。 + +--- + +## 为什么不同应用的支持情况不同? + +在 Linux 桌面中,不同工具包(Toolkit)的底层架构差异,导致了全局菜单集成度的不同: + +* **Qt 程序**:由于 KDE 深度基于 Qt 构建,Qt 5/6 已经原生内置了对 `dbusmenu` 的支持(通过 `libdbusmenu-qt` 等)。Qt 应用的 `QMenuBar` 会自动被全局菜单组件无缝接管,体验最完美。 +* **Electron / Chromium 应用**:Chromium 内核在 Linux 下原生集成了对 `libdbusmenu` 库的调用,只要没有使用 HTML 自绘标题栏,就会自动在 D-Bus 上注册原生菜单。 + +### Chrome 与 Firefox 的差异对比 + +* **Chrome/Chromium**:作为“优等生”,它直接在底层自带了 `dbusmenu` 支持,能自动检测 KDE 环境并向 D-Bus 注册菜单。 + + ![Chrome 全局菜单](/images/chrome-global-menu.png) + +* **Firefox**: + 1. Firefox 在 Linux 下主要基于 GTK3 构建,并不像 Chrome 那样天然“自带干粮”支持 D-Bus 菜单,需要依赖系统的 `appmenu-gtk-module` 翻译器。 + 2. 默认情况下,Firefox 倾向于“自己画界面”(使用 XUL/HTML 自绘机制)。当我们按下 `Alt` 键调出菜单栏时,它弹出的其实是**自绘的虚拟菜单**,而非标准的原生 `GtkMenuBar`。这就导致 `appmenu-gtk-module` 无法拦截到任何菜单数据。 + 3. 此外,如果启用了“自定义标题栏”(CSD,客户端装饰),Firefox 会完全接管窗口装饰,导致系统模块彻底与菜单脱钩。 + +--- + +## 完美解决:Firefox 启用全局菜单的完整步骤 + +要让 Firefox 的菜单能够正常进入全局菜单,我们需要: + +1. 让 Firefox 放弃自绘菜单,改用原生 GTK 菜单栏; +2. 开启 Firefox 内部的强制 D-Bus 导出开关; +3. 确保系统具备 GTK 菜单桥接模块。 + +具体步骤如下: + +### 步骤 1:确保安装并加载了 `appmenu-gtk-module` + +确保系统里存在 GTK 桥接模块: + +1. 根据发行版安装对应的模块: + * **Arch Linux**:`sudo pacman -S appmenu-gtk-module` + * **Ubuntu / Debian / KDE Neon**:`sudo apt install appmenu-gtk3-module` + * **Fedora**:`sudo dnf install gtk3-appmenu-gtk-module` +2. 重启你的桌面会话(或注销重新登录),以保证 `GTK_MODULES=appmenu-gtk-module` 环境变量在全局生效。 + +### 步骤 2:在 `about:config` 中强制开启全局菜单输出 + +由于 Firefox 默认隐藏菜单需要按 alt 键显示,我们需要手动强制其导出: + +1. 在 Firefox 地址栏输入 `about:config` 并回车,接受风险提示。 +2. 搜索并修改/新建以下两个键值: + * **`widget.gtk.global-menu.force`** 设置为 **`true`** (强制启用全局菜单) + * **`widget.gtk.global-menu.force-wayland`** 设置为 **`true`** (如果在 Wayland 会话下,强制通过 D-Bus 导出菜单,非常关键) +3. 重启 Firefox。 + +![Firefox 全局菜单配置](/images/firefox-global-menu-solve.png) + +--- + +## 最终效果 + +经过以上配置,Firefox 的菜单成功脱离了浏览器窗口,并无缝集成到了 KDE Plasma 的全局菜单面板中,整体桌面体验更加干净统一: + +![Firefox 默认菜单显示在标题栏下方](/images/firefox-global-menu.png) + +如此一来,无论是在 X11 还是 Wayland 会话下,我们都能在 Firefox 中享受到类似 macOS 的全局菜单体验了。 diff --git a/source/about/index.md b/source/about/index.md index ae42060..81fdfe6 100644 --- a/source/about/index.md +++ b/source/about/index.md @@ -4,7 +4,7 @@ date: 2024-11-04 22:46:12 layout: about --- -白茅铺高地玄院第N任非菌群主🐳,Linux六年牢用户🐧 +白茅铺高地玄院第四任非菌群主🐳,Linux七年牢用户🐧,ASC三年牢选手🤡(无人在意) 爱好编程,绘画,编曲,Vocaloid调教,Blender建模等😇 @@ -12,6 +12,6 @@ layout: about 联系方式: -- Email: +- Email: - Github: diff --git a/source/images/chrome-global-menu.png b/source/images/chrome-global-menu.png new file mode 100644 index 0000000000000000000000000000000000000000..cfc5350d199c37d19d9d50a9d38ec1474cc44b03 GIT binary patch literal 45048 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 literal 0 HcmV?d00001 diff --git a/source/images/firefox-global-menu-solve.png b/source/images/firefox-global-menu-solve.png new file mode 100644 index 0000000000000000000000000000000000000000..f727759438565925e6e8976425a69b0134d6765c GIT binary patch literal 50106 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 X=(b-e76|;EY17uwe_W~d?9KlHQa-*Z literal 0 HcmV?d00001 diff --git a/source/images/firefox-global-menu.png b/source/images/firefox-global-menu.png new file mode 100644 index 0000000000000000000000000000000000000000..9ef22fa2ec8042763d8aa963776db753ca7201b1 GIT binary patch literal 54145 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 literal 0 HcmV?d00001