[{"content":"","date":"July 24, 2026","externalUrl":null,"permalink":"/tags/blowfish/","section":"Tags","summary":"","title":"Blowfish","type":"tags"},{"content":"","date":"July 24, 2026","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","date":"July 24, 2026","externalUrl":null,"permalink":"/tags/hugo/","section":"Tags","summary":"","title":"Hugo","type":"tags"},{"content":"","date":"July 24, 2026","externalUrl":null,"permalink":"/tags/markdown/","section":"Tags","summary":"","title":"Markdown","type":"tags"},{"content":"","date":"July 24, 2026","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"这篇文章是我为本站写的一个备用写作手册，专门用来以后忘了有什么功能用法的一个备份。：以后新建 Markdown 文章时，当前这个博客到底支持什么，应该怎么写？\n本文依据当前仓库中的配置、布局、短代码和已有文章整理。当前站点使用 Hugo Extended v0.164.0、Blowfish v2.102.0、Goldmark Markdown、Waline 评论、Firebase 阅读量和点赞、KaTeX 数学公式，以及中文和日文两套内容。\n当前仓库自定义的短代码有：\nbilibili：B 站 BV 视频播放器； video-link：外部视频封面链接； gallery：图片网格和自动平衡画廊； carousel：图片轮播； link-card：官方链接卡片； promo-card 和 promo-grid：宣传卡片； mark：再次按 Markdown 渲染内部内容； callou：自定义提示框，名字是 callou，不是 callout。 一、最快开始：新建一篇文章 # 1. 最小文章模板 # 在 content/notes/、content/life/ 或 content/misc/ 下新建 Markdown 文件，例如：\ncontent/notes/tools/我的新文章.md 你也可以这样来新建笔记：\nhugo new 笔记目录/笔记名称.md 内容可以从下面开始：\n--- title: \u0026#34;我的新文章\u0026#34; date: 2026-07-24T22:00:00+08:00 description: \u0026#34;一句话说明这篇文章写了什么。\u0026#34; showTableOfContents: true tags: - Hugo - Markdown categories: - 博客 --- ## 第一部分 正文从这里开始。 保存后启动本地预览：\nhugo server 草稿也需要显示时：\nhugo server -D 生成生产文件：\nhugo --gc --minify 2. 当前栏目怎么选择 # 目录 用途 当前内容示例 content/notes/ 技术笔记、教程和知识总结 Linux、Git、Vim、数学 content/life/ 旅行、日常、动漫和生活记录 关西游记、观后感 content/misc/ 杂项和暂时不能归类的内容 Hexo 记录 首页和主要列表由 params.toml 中的 mainSections = [\u0026ldquo;notes\u0026rdquo;, \u0026ldquo;life\u0026rdquo;, \u0026ldquo;misc\u0026rdquo;] 控制。\n二、文件结构和图片资源 # 1. 单文件文章 # 适合没有专属图片的文章：\ncontent/ └── notes/ └── tools/ └── docker.md 2. Article Bundle：图文文章推荐使用 # 有很多只属于这篇文章的图片时，使用目录形式：\ncontent/ └── life/ └── travel/ └── my-trip/ ├── index.md ├── featured.jpg └── images/ ├── 01.jpg └── 02.jpg 正文中的图片可以直接引用：\n![第一天的风景](images/01.jpg) 这种方式最适合 figure、gallery 和 carousel，因为图片就是当前页面的 Page Resources。\n3. _index.md 和 index.md # 文件 Hugo 含义 用途 _index.md Branch Page 栏目页、列表页 index.md Leaf Page 具体文章页 name.md 单文件文章 没有专属资源的简单文章 栏目页示例：\n--- title: \u0026#34;工具\u0026#34; description: \u0026#34;命令行、Docker、网络和日常工具记录。\u0026#34; --- 4. 中文和日文文件 # 当前站点使用语言后缀：\ncontent/notes/tools/docker.md # 中文 content/notes/tools/docker.ja.md # 日文 同名的 md 和 ja.md 会被 Hugo 视为同一篇内容的两个语言版本。\n5. 图片放在哪里 # 位置 正文中常见写法 适用场景 当前文章目录 images/01.jpg 文章专属图片，最推荐 assets/ /img/logo.png 站点资源、可由 Hugo 处理 static/ /downloads/file.pdf 原样复制到网站根路径 不要写本机绝对路径，例如 /Users/ice/\u0026hellip;；文件名大小写必须完全一致。\n三、Front Matter：文章顶部配置 # Front Matter 位于文件最顶部两个 \u0026mdash; 之间，不会显示在正文中，但决定标题、日期、标签、目录、封面和页面功能。\n1. 常用字段 # 字段 用途 示例 title 文章标题 title: \u0026ldquo;Linux 文件描述符\u0026rdquo; date 发布时间和排序 date: 2026-07-24T22:00:00+08:00 lastmod 最近修改时间 lastmod: 2026-07-25T10:30:00+08:00 description 页面描述、搜索和分享摘要 description: \u0026ldquo;介绍 Linux 文件描述符。\u0026rdquo; summary 列表中的手动摘要 summary: \u0026ldquo;一篇入门文章。\u0026rdquo; tags 关键词标签 tags: [Linux, 内核] categories 较大的分类 categories: [Linux] draft 是否草稿 draft: true slug URL 最后一段 slug: linux-fd url 自定义完整地址 url: \u0026ldquo;/notes/linux/fd/\u0026rdquo; aliases 旧地址跳转 aliases: [\u0026quot;/old/fd/\u0026quot;] featureImage 封面图 featureImage: \u0026ldquo;/img/kumiko_jump.png\u0026rdquo; 完整示例：\n--- title: \u0026#34;Linux 文件描述符：fd、重定向与打开文件表\u0026#34; date: 2026-07-24T22:00:00+08:00 lastmod: 2026-07-25T10:30:00+08:00 description: \u0026#34;从 shell 重定向开始，解释文件描述符和打开文件表之间的关系。\u0026#34; summary: \u0026#34;一篇面向初学者的 Linux 文件描述符入门。\u0026#34; tags: - Linux - 文件描述符 - 重定向 categories: - Linux slug: linux-fd aliases: - /notes/linux/old-fd/ --- 2. 文章功能开关 # 当前 article 全局默认值：\n字段 默认值 作用 showTableOfContents true 显示目录 showMath true 加载数学公式支持 showDate true 显示发布日期 showDateUpdated true 显示更新时间 showBreadcrumbs true 显示面包屑 showTaxonomies true 显示标签和分类 showAuthor true 显示作者 showComments true 显示评论区 showViews true 显示阅读量 showLikes true 显示点赞 showReadingTime true 显示预计阅读时间 showWordCount true 显示字数 showZenMode true 显示专注阅读模式 showEdit true 显示 GitHub 编辑入口 showPagination true 显示上一篇和下一篇 showRelatedContent true 显示相关文章 showHero false 默认不显示大 Hero 图 单篇文章可以覆盖：\n--- showTableOfContents: true showMath: true showComments: false showViews: false showLikes: false showHero: true showEdit: false relatedContentLimit: 5 --- 3. 封面和 Hero 图 # 直接指定封面：\nfeatureImage: \u0026#34;/img/kumiko_jump.png\u0026#34; 或者在文章目录里放置以 feature 开头的图片：\ncontent/life/travel/my-trip/ ├── index.md └── featured.jpg 全局 showHero = false。想在当前文章顶部显示大图：\nshowHero: true heroStyle: \u0026#34;background\u0026#34; 4. 分类和标签 # 当前使用 Hugo 默认的 tags 和 categories：\ntags: - Docker - GitLab - 部署 categories: - DevOps - 计算机 建议 categories 表示大主题，tags 表示具体关键词；同一个词尽量保持拼写一致。\n四、标准 Markdown # 本站的 Goldmark 已开启表格、任务列表、删除线、自动链接、定义列表和数学公式 passthrough。\n1. 标题和段落 # # 一级标题 ## 二级标题 ### 三级标题 这是普通段落。段落之间空一行。 文章标题已经由 Front Matter 的 title 显示，正文一般从 ## 开始。目录范围是 h2 到 h4。\n2. 文本强调 # **粗体** *斜体* ~~删除线~~ 普通文本中的命令可以写成行内代码。 ***粗体斜体*** 3. 链接 # [Hugo 官网](https://gohugo.io/) \u0026lt;https://gohugo.io/\u0026gt; [站内另一篇文章](../../linux/Linux文件描述符-fd.md) [跳转到本文 Front Matter 部分](#三front-matter文章顶部配置) 文章移动后要检查相对链接。\n4. 列表 # - 第一项 - 第二项 - 第二项的子项 1. 第一步 2. 第二步 3. 第三步 5. 任务列表 # - [x] 已完成 - [ ] 尚未完成 6. 引用和提醒块 # 普通引用：\n\u0026gt; 这是普通引用。 \u0026gt; \u0026gt; 引用可以有多个段落。 主题支持 GitHub/Obsidian 风格的提醒块：\n\u0026gt; [!NOTE] \u0026gt; 这是说明。 \u0026gt; [!TIP] \u0026gt; 这是技巧。 \u0026gt; [!WARNING] \u0026gt; 这是警告。 常见类型有 NOTE、TIP、IMPORTANT、WARNING、CAUTION，也支持 INFO、SUCCESS、QUESTION、DANGER、BUG、EXAMPLE、QUOTE。\n7. 分割线 # --- 8. 表格 # | 命令 | 作用 | | --- | --- | | hugo server | 启动预览 | | hugo --gc | 生成并清理缓存 | | git status | 查看工作区状态 | 对齐方式：\n| 左对齐 | 居中 | 右对齐 | | :--- | :---: | ---: | | A | B | C | 9. 定义列表 # Hugo : 静态网站生成器。 Markdown : 纯文本标记语言。 10. 图片 # 普通图片：\n![大阪城公园](images/osaka-castle.jpg) 如果需要图片说明、外链或关闭放大，使用 figure。\n11. HTML # markup.toml 中设置了 goldmark.renderer.unsafe = true，可以使用 HTML：\n\u0026lt;details\u0026gt; \u0026lt;summary\u0026gt;点击展开\u0026lt;/summary\u0026gt; 这是折叠内容。 \u0026lt;/details\u0026gt; 不要把未经检查的用户输入、外部脚本或敏感 HTML 放入文章。\n五、代码块 # 1. 带语言的代码块 # ~~~bash mkdir -p content/notes/tools hugo server ~~~ 当前高亮主题是 Dracula，代码块支持复制按钮。\n常用语言名：bash、shell、python、javascript、typescript、go、rust、java、cpp、html、css、toml、yaml、json。\n2. 高亮指定行 # ~~~go {hl_lines=[\u0026#34;3\u0026#34;, \u0026#34;5-6\u0026#34;]} package main import \u0026#34;fmt\u0026#34; func main() { fmt.Println(\u0026#34;hello\u0026#34;) } ~~~ 如果行高亮不生效，先退回最基本的语言代码块。\n六、图片功能 # 1. figure：单张图片 # Blowfish 的 figure 支持：\n参数 用途 src 必填，图片路径或 URL alt 替代文本 caption 图片说明，支持 Markdown class 图片 CSS 类 figureClass 外层 figure 的 CSS 类 href 点击图片的链接 target 链接打开方式 nozoom true 时关闭放大 default true 时使用 Hugo 默认 figure 示例：\n{{\u0026lt; figure src=\u0026#34;featured.png\u0026#34; alt=\u0026#34;文章主视觉\u0026#34; caption=\u0026#34;这是一段 **支持 Markdown** 的说明。\u0026#34; \u0026gt;}} 带链接：\n{{\u0026lt; figure src=\u0026#34;map.png\u0026#34; alt=\u0026#34;路线图\u0026#34; caption=\u0026#34;点击查看原图。\u0026#34; href=\u0026#34;https://example.com/map\u0026#34; target=\u0026#34;_blank\u0026#34; nozoom=\u0026#34;true\u0026#34; \u0026gt;}} 2. gallery：图片网格 # 当前仓库覆盖了主题默认 gallery：\n{{\u0026lt; gallery \u0026gt;}} \u0026lt;img src=\u0026#34;01.jpg\u0026#34; class=\u0026#34;grid-w50\u0026#34; alt=\u0026#34;照片 1\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;02.jpg\u0026#34; class=\u0026#34;grid-w50\u0026#34; alt=\u0026#34;照片 2\u0026#34; /\u0026gt; {{\u0026lt; /gallery \u0026gt;}} 常见宽度：\n类名 效果 grid-w25 约四列 grid-w33 约三列 grid-w50 约两列 grid-w66 约三分之二宽 grid-w100 整行 响应式：\n{{\u0026lt; gallery \u0026gt;}} \u0026lt;img src=\u0026#34;01.jpg\u0026#34; class=\u0026#34;grid-w50 md:grid-w33\u0026#34; alt=\u0026#34;照片 1\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;02.jpg\u0026#34; class=\u0026#34;grid-w50 md:grid-w33\u0026#34; alt=\u0026#34;照片 2\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;03.jpg\u0026#34; class=\u0026#34;grid-w50 md:grid-w33\u0026#34; alt=\u0026#34;照片 3\u0026#34; /\u0026gt; {{\u0026lt; /gallery \u0026gt;}} 当前自定义 JavaScript 会在桌面端、图片数量较多且比例差异明显时自动平衡画廊高度。每张图片都要写 alt；同一组图片尽量统一宽度类。\ngallery 中也可以嵌套带说明的 figure：\n{{\u0026lt; gallery \u0026gt;}} {{\u0026lt; figure src=\u0026#34;01.jpg\u0026#34; alt=\u0026#34;第一张\u0026#34; caption=\u0026#34;第一天早晨。\u0026#34; figureClass=\u0026#34;grid-w33\u0026#34; \u0026gt;}} {{\u0026lt; figure src=\u0026#34;02.jpg\u0026#34; alt=\u0026#34;第二张\u0026#34; caption=\u0026#34;车站附近。\u0026#34; figureClass=\u0026#34;grid-w33\u0026#34; \u0026gt;}} {{\u0026lt; /gallery \u0026gt;}} 3. carousel：轮播图 # 当前仓库的 carousel 是自定义实现。\n参数 默认值 说明 images 无 必填，图片路径，用逗号分隔 aspectRatio 16-9 比例，例如 16-9、4-3 interval 2000 自动切换间隔，单位毫秒 captions 无 图片名和说明的映射 基础示例：\n{{\u0026lt; carousel images=\u0026#34;01.jpg,02.jpg,03.jpg\u0026#34; aspectRatio=\u0026#34;16-9\u0026#34; interval=\u0026#34;4000\u0026#34; \u0026gt;}} 带说明：\n{{\u0026lt; carousel images=\u0026#34;01.jpg,02.jpg,03.jpg\u0026#34; aspectRatio=\u0026#34;4-3\u0026#34; interval=\u0026#34;3000\u0026#34; captions=\u0026#34;{01.jpg:第一天,02.jpg:车站,03.jpg:夜景}\u0026#34; \u0026gt;}} 注意：\n当前实现使用逗号分隔，逗号后不要加多余空格； 图片推荐放在当前文章 Bundle； 支持左右按钮、指示器和自动播放； 点击图片可放大，放大时轮播暂停； interval = 4000 表示 4 秒。 七、视频功能 # 1. youtubeLite：YouTube # 参数是 id、label、params：\n{{\u0026lt; youtubeLite id=\u0026#34;5Ai4fRBZBbo\u0026#34; label=\u0026#34;作品官方 PV\u0026#34; \u0026gt;}} 带播放参数：\n{{\u0026lt; youtubeLite id=\u0026#34;5Ai4fRBZBbo\u0026#34; label=\u0026#34;作品官方 PV\u0026#34; params=\u0026#34;start=130\u0026amp;controls=0\u0026#34; \u0026gt;}} 2. video：本地或外部视频 # 常用参数是 src、poster、caption、autoplay、loop、muted、controls、playsinline、preload、start、end、ratio、fit。\n{{\u0026lt; video src=\u0026#34;movie.mp4\u0026#34; poster=\u0026#34;movie-poster.jpg\u0026#34; caption=\u0026#34;本地视频示例。\u0026#34; controls=\u0026#34;true\u0026#34; playsinline=\u0026#34;true\u0026#34; preload=\u0026#34;metadata\u0026#34; ratio=\u0026#34;16/9\u0026#34; \u0026gt;}} 自动播放一般需要同时静音：\n{{\u0026lt; video src=\u0026#34;demo.webm\u0026#34; autoplay=\u0026#34;true\u0026#34; muted=\u0026#34;true\u0026#34; loop=\u0026#34;true\u0026#34; \u0026gt;}} 3. bilibili：本站 B 站播放器 # 它会从 URL 中自动提取 BV 号。\n{{\u0026lt; bilibili url=\u0026#34;https://www.bilibili.com/video/BV13h411k7Zk/\u0026#34; label=\u0026#34;第一季 OP\u0026#34; \u0026gt;}} url 必须包含 BV 号。当前不会自动处理只有 av 号的链接，播放器默认关闭弹幕和自动播放。\n4. video-link：外部视频卡片 # 它只显示封面，点击后打开外部视频页面：\n参数 别名 用途 href url 外部地址 thumbnail image 封面图片 alt 无 图片替代文本 title label 标题 platform 无 平台名称 {{\u0026lt; video-link href=\u0026#34;https://www.example.com/video\u0026#34; thumbnail=\u0026#34;/img/video-cover.jpg\u0026#34; alt=\u0026#34;官方视频封面\u0026#34; platform=\u0026#34;官方网站\u0026#34; title=\u0026#34;点击打开官方视频页面\u0026#34; \u0026gt;}} 八、提示和折叠内容 # 1. alert：主题原生提示框 # 参数是 icon、iconColor、cardColor、textColor：\n{{\u0026lt; alert icon=\u0026#34;circle-info\u0026#34; \u0026gt;}} 这里是一条 **重要提示**，正文支持 Markdown。 {{\u0026lt; /alert \u0026gt;}} 已有文章中使用过的图标包括 circle-info、triangle-alert、graduation-cap、eye、comment、heart、star、plane-departure、tag、pencil。\n2. GitHub/Obsidian 提醒块 # \u0026gt; [!TIP] \u0026gt; 这是一条技巧提示。 \u0026gt; [!WARNING] \u0026gt; 这是一条警告。 这种写法比 alert 更容易复制到其他支持 GitHub Alerts 或 Obsidian Callout 的工具中。\n3. callou：本站自定义提示框 # 文件名是 layouts/shortcodes/callou.html，所以调用名必须是 callou。支持 note、tip、important、warning、caution。\n普通提示：\n{{\u0026lt; callou type=\u0026#34;tip\u0026#34; title=\u0026#34;写作建议\u0026#34; \u0026gt;}} 标题和正文都可以使用 Markdown。 {{\u0026lt; /callou \u0026gt;}} 可折叠：\n{{\u0026lt; callou type=\u0026#34;warning\u0026#34; title=\u0026#34;点击查看注意事项\u0026#34; foldable=\u0026#34;true\u0026#34; \u0026gt;}} 这里的内容默认折叠。 {{\u0026lt; /callou \u0026gt;}} 默认展开：\n{{\u0026lt; callou type=\u0026#34;note\u0026#34; title=\u0026#34;默认展开的说明\u0026#34; foldable=\u0026#34;true\u0026#34; open=\u0026#34;true\u0026#34; \u0026gt;}} 说明内容。 {{\u0026lt; /callou \u0026gt;}} 不需要折叠时直接省略 foldable，不要写 foldable=\u0026ldquo;false\u0026rdquo;。\n4. accordion：多面板折叠 # Blowfish 原生 accordion 适合 FAQ、不同系统步骤和长补充内容：\n{{\u0026lt; accordion mode=\u0026#34;open\u0026#34; separated=true \u0026gt;}} {{\u0026lt; accordionItem title=\u0026#34;Linux\u0026#34; open=true \u0026gt;}} Linux 安装步骤。 {{\u0026lt; /accordionItem \u0026gt;}} {{\u0026lt; accordionItem title=\u0026#34;macOS\u0026#34; \u0026gt;}} macOS 安装步骤。 {{\u0026lt; /accordionItem \u0026gt;}} {{\u0026lt; /accordion \u0026gt;}} mode=\u0026ldquo;collapse\u0026rdquo; 表示一次只展开一个，mode=\u0026ldquo;open\u0026rdquo; 表示可以同时展开多个。accordionItem 常用参数有 title、open、icon、align。\n九、卡片功能 # 1. link-card：官方链接卡片 # 参数：\n参数 别名 说明 href url，也支持第一个位置参数 必填，链接地址 image thumbnail 图片，可选 icon 无 小图标 URL title 无 标题 desc description 描述 eyebrow 无 顶部小标签 cta 无 按钮文案 alt 无 图片替代文本 {{\u0026lt; link-card title=\u0026#34;Hugo 官方文档\u0026#34; desc=\u0026#34;Hugo 的官方文档和配置参考。\u0026#34; href=\u0026#34;https://gohugo.io/documentation/\u0026#34; image=\u0026#34;/img/logo.png\u0026#34; eyebrow=\u0026#34;Official Documentation\u0026#34; cta=\u0026#34;打开官方文档\u0026#34; alt=\u0026#34;Hugo 文档\u0026#34; \u0026gt;}} 没有 image 时也可以显示纯文字卡片：\n{{\u0026lt; link-card title=\u0026#34;项目仓库\u0026#34; desc=\u0026#34;查看这个博客的源代码。\u0026#34; href=\u0026#34;https://github.com/ice345/my-hugo-blog\u0026#34; cta=\u0026#34;访问 GitHub\u0026#34; \u0026gt;}} 2. promo-grid 和 promo-card：宣传卡片 # promo-grid 是容器，内部放 promo-card。promo-card 必须有 image。\n参数：\n参数 别名 说明 href url，也支持第一个位置参数 点击地址，可选 image thumbnail 卡片主图，必填 title 无 标题 description 无 描述 alt 无 图片替代文本 badges 无 逗号分隔的标签 badgeColors colors 逗号分隔的颜色 {{\u0026lt; promo-grid \u0026gt;}} {{\u0026lt; promo-card href=\u0026#34;https://gohugo.io/\u0026#34; image=\u0026#34;/img/logo.png\u0026#34; title=\u0026#34;Hugo\u0026#34; description=\u0026#34;快速的静态网站生成器。\u0026#34; badges=\u0026#34;Static Site,Go\u0026#34; badgeColors=\u0026#34;violet,blue\u0026#34; \u0026gt;}} {{\u0026lt; /promo-grid \u0026gt;}} 默认颜色有 peach、magenta、lime、violet、blue、orange、green、yellow，当前 CSS 还提供 brown 和 pink。\n十、数学公式 # 1. katex # 一篇文章中调用一次 katex 即可：\n{{\u0026lt; katex \u0026gt;}} 行内公式：\n欧拉公式：\\(e^{i\\pi}+1=0\\)。 块级公式：\n$$ f(x) = \\int_{-\\infty}^{+\\infty} e^{-x^2}\\,dx $$ 也支持：\n\\[ a^2 + b^2 = c^2 \\] 2. 数学文章 Front Matter # --- title: \u0026#34;数论知识\u0026#34; showMath: true --- 当前全局 showMath = true，但数学文章仍建议显式写出来。\n3. 注意事项 # 美元符号既可能是金额，也可能参与公式解析； 公式中的反斜杠必须保留，例如 \\frac 和 \\sqrt； 一个页面通常只需要一次 katex； 公式不显示时检查 showMath、分隔符和浏览器控制台。 十一、Blowfish 其他常用短代码 # 下面是主题模块提供、当前内容中不一定已经使用的功能。第一次使用请先本地预览。\n1. badge、button、icon # badge：\n{{\u0026lt; badge \u0026gt;}} Beta {{\u0026lt; /badge \u0026gt;}} button：\n{{\u0026lt; button href=\u0026#34;https://gohugo.io/\u0026#34; target=\u0026#34;_blank\u0026#34; \u0026gt;}} 打开 Hugo {{\u0026lt; /button \u0026gt;}} 站内跳转可以使用 pageRef：\n{{\u0026lt; button pageRef=\u0026#34;notes/tools/\u0026#34; \u0026gt;}} 浏览工具笔记 {{\u0026lt; /button \u0026gt;}} icon：\n{{\u0026lt; icon \u0026#34;github\u0026#34; \u0026gt;}} {{\u0026lt; icon \u0026#34;openlist\u0026#34; \u0026gt;}} {{\u0026lt; icon \u0026#34;obsidian\u0026#34; \u0026gt;}} 自定义 SVG 放到 assets/icons/，调用时使用不带 .svg 的文件名。\n2. lead、keyword、swatches # lead：\n{{\u0026lt; lead \u0026gt;}} 这是一段放大的文章导语。 {{\u0026lt; /lead \u0026gt;}} keyword：\n{{\u0026lt; keywordList \u0026gt;}} {{\u0026lt; keyword icon=\u0026#34;code\u0026#34; \u0026gt;}}Markdown{{\u0026lt; /keyword \u0026gt;}} {{\u0026lt; keyword icon=\u0026#34;github\u0026#34; \u0026gt;}}GitHub{{\u0026lt; /keyword \u0026gt;}} {{\u0026lt; /keywordList \u0026gt;}} swatches：\n{{\u0026lt; swatches \u0026#34;#64748b\u0026#34; \u0026#34;#3b82f6\u0026#34; \u0026#34;#06b6d4\u0026#34; \u0026gt;}} 3. mermaid：流程图 # {{\u0026lt; mermaid \u0026gt;}} flowchart LR A[\u0026#34;写 Markdown\u0026#34;] --\u0026gt; B[\u0026#34;Hugo 构建\u0026#34;] B --\u0026gt; C[\u0026#34;生成 HTML\u0026#34;] C --\u0026gt; D[\u0026#34;发布网站\u0026#34;] {{\u0026lt; /mermaid \u0026gt;}} 图中文字有括号或标点时建议加引号。\n4. chart：图表 # {{\u0026lt; chart \u0026gt;}} type: \u0026#39;bar\u0026#39;, data: { labels: [\u0026#39;一月\u0026#39;, \u0026#39;二月\u0026#39;, \u0026#39;三月\u0026#39;], datasets: [{ label: \u0026#39;文章数\u0026#39;, data: [3, 5, 4] }] } {{\u0026lt; /chart \u0026gt;}} 内容使用 Chart.js 配置格式。\n5. tabs 和 tab：多系统步骤 # {{\u0026lt; tabs \u0026gt;}} {{\u0026lt; tab label=\u0026#34;macOS\u0026#34; \u0026gt;}} ~~~bash brew install hugo ~~~ {{\u0026lt; /tab \u0026gt;}} {{\u0026lt; tab label=\u0026#34;Linux\u0026#34; \u0026gt;}} ~~~bash sudo pacman -S hugo ~~~ {{\u0026lt; /tab \u0026gt;}} {{\u0026lt; /tabs \u0026gt;}} tabs 支持 group 和 default；tab 支持 label、icon、md。内部还要执行短代码时可以尝试 md=false。\n6. timeline 和 timelineItem # {{\u0026lt; timeline \u0026gt;}} {{\u0026lt; timelineItem icon=\u0026#34;code\u0026#34; header=\u0026#34;开始写博客\u0026#34; subheader=\u0026#34;Hugo + Markdown\u0026#34; badge=\u0026#34;2026\u0026#34; \u0026gt;}} 从 Markdown 开始记录学习过程。 {{\u0026lt; /timelineItem \u0026gt;}} {{\u0026lt; /timeline \u0026gt;}} timelineItem 常用参数是 icon、header、subheader、badge、md。\n7. typeit：打字机效果 # {{\u0026lt; typeit tag=\u0026#34;h3\u0026#34; speed=\u0026#34;50\u0026#34; loop=\u0026#34;true\u0026#34; \u0026gt;}} Hugo + Markdown {{\u0026lt; /typeit \u0026gt;}} 常用参数有 tag、classList、initialString、speed、lifeLike、startDelay、breakLines、waitUntilVisible、loop。动画应适量使用。\n8. list 和 article # 最近文章列表：\n{{\u0026lt; list limit=5 title=\u0026#34;最近更新\u0026#34; cardView=true \u0026gt;}} 按条件筛选：\n{{\u0026lt; list title=\u0026#34;最近的笔记\u0026#34; limit=6 where=\u0026#34;Section\u0026#34; value=\u0026#34;notes\u0026#34; \u0026gt;}} 嵌入另一篇文章摘要：\n{{\u0026lt; article link=\u0026#34;/notes/tools/docker/\u0026#34; showSummary=true compactSummary=true \u0026gt;}} 9. email、gist、mdimporter、codeimporter # 邮箱链接：\n{{\u0026lt; email email=\u0026#34;mailto:hello@example.com\u0026#34; text=\u0026#34;给我发邮件\u0026#34; subject=\u0026#34;关于博客的留言\u0026#34; \u0026gt;}} GitHub Gist：\n{{\u0026lt; gist \u0026#34;octocat\u0026#34; \u0026#34;6cad326836d38bd3a7ae\u0026#34; \u0026gt;}} 导入外部 Markdown：\n{{\u0026lt; mdimporter url=\u0026#34;https://raw.githubusercontent.com/example/project/main/README.md\u0026#34; \u0026gt;}} 导入外部代码：\n{{\u0026lt; codeimporter url=\u0026#34;https://raw.githubusercontent.com/example/project/main/main.go\u0026#34; type=\u0026#34;go\u0026#34; startLine=\u0026#34;10\u0026#34; endLine=\u0026#34;30\u0026#34; \u0026gt;}} 这些功能需要访问外部地址，网络失败时可能影响构建；重要内容建议保存到本仓库。\n10. 外部平台卡片 # Blowfish 提供多个平台卡片：\n{{\u0026lt; github repo=\u0026#34;ice345/my-hugo-blog\u0026#34; showThumbnail=true \u0026gt;}} {{\u0026lt; gitlab projectID=\u0026#34;278964\u0026#34; \u0026gt;}} {{\u0026lt; codeberg repo=\u0026#34;forgejo/forgejo\u0026#34; \u0026gt;}} {{\u0026lt; gitea server=\u0026#34;https://git.example.com\u0026#34; repo=\u0026#34;user/project\u0026#34; \u0026gt;}} {{\u0026lt; forgejo server=\u0026#34;https://forgejo.example.com\u0026#34; repo=\u0026#34;user/project\u0026#34; \u0026gt;}} {{\u0026lt; huggingface model=\u0026#34;google-bert/bert-base-uncased\u0026#34; \u0026gt;}} {{\u0026lt; huggingface dataset=\u0026#34;stanfordnlp/imdb\u0026#34; \u0026gt;}} {{\u0026lt; ansible role=\u0026#34;geerlingguy.docker\u0026#34; \u0026gt;}} gitlab 需要项目数字 ID；gitea 和 forgejo 需要服务地址；huggingface 的 model 和 dataset 二选一；ansible 的 role 和 collection 二选一。\n11. ltr 和 rtl：文字方向 # \u0026lt;div dir=\u0026#34;rtl\u0026#34; class=\u0026#34;text-right\u0026#34;\u0026gt; هذه فقرة من اليمين إلى اليسار \u0026lt;/div\u0026gt; 这里用百分号分隔符，是为了让里面的 Markdown 正常解析。\n十二、自定义短代码总表 # 短代码 作用 关键参数 bilibili B 站 BV 视频内嵌 url、label video-link 外部视频封面链接卡片 href、thumbnail、platform、title gallery 图片网格和自动平衡 内部 img 的 class、alt carousel 图片轮播 images、aspectRatio、interval、captions link-card 官方链接卡片 href、image、title、desc、cta promo-grid 宣传卡片容器 内部放 promo-card promo-card 产品或服务卡片 image、title、description、badges mark 再次按 Markdown 渲染 无 callou 自定义提示框和折叠 type、title、foldable、open mark 的特别说明 # 当前 mark.html 的实际实现只是把内部内容传给 markdownify，再直接输出。它没有输出真正的 mark 标签，也没有专门的高亮背景，因此不要把它当成黄色荧光笔功能。如果以后需要真正高亮，需要同时修改 layouts/shortcodes/mark.html 和 assets/css/custom.css。\n十三、站点级功能 # 1. 首页 # 当前首页配置：\n[homepage] layout = \u0026#34;background\u0026#34; homepageImage = \u0026#34;/img/background.svg\u0026#34; showRecent = true showRecentItems = 6 cardView = true layoutBackgroundBlur = true 效果是带背景图的首页，并显示最近 6 篇文章卡片。首页正文来自 content/_index.md。\n2. 搜索和输出 # 当前搜索已开启，首页输出 HTML、RSS 和 JSON：\n[outputs] home = [\u0026#34;HTML\u0026#34;, \u0026#34;RSS\u0026#34;, \u0026#34;JSON\u0026#34;] 文章标题、description、tags 写得清楚，会更容易被搜索到。\n3. 目录、代码复制和图片复制 # enableSearch = true enableCodeCopy = true enableImageCopy = true [tableOfContents] startLevel = 2 endLevel = 4 代码块有复制按钮，图片支持复制，文章目录默认取 h2 到 h4。\n4. 阅读量、点赞和评论 # 当前阅读量和点赞由 Firebase 提供：\n[firebase] lowerCasePath = true 当前实际启用 Waline：\n[comments] enable = true [comments.waline] enable = true serverURL = \u0026#34;waline-blowfish-blog.vercel.app\u0026#34; Giscus 配置保留但关闭：\n[comments.giscus] enable = false 同一篇文章不要随意更改 URL，否则统计路径会变化。\n5. 外观和编辑入口 # defaultAppearance = \u0026#34;light\u0026#34; autoSwitchAppearance = true [footer] showAppearanceSwitcher = true showScrollToTop = true [article] showEdit = true editURL = \u0026#34;https://github.com/ice345/my-hugo-blog/tree/master/content\u0026#34; editAppendPath = true 默认浅色，会根据系统切换；页脚有外观切换和回到顶部；文章页面有 GitHub 编辑入口。\n十四、推荐写作流程 # 先决定放在 notes、life 还是 misc。 有专属图片时建立文章 Bundle。 先写 title、date、description、tags、categories。 先用普通 Markdown 写清楚正文。 再补 figure、gallery、carousel、alert、卡片和视频。 本地运行 hugo server -D。 检查手机宽度、目录、图片、公式、链接和评论。 最后运行 hugo \u0026ndash;gc \u0026ndash;minify。 文章有很多图片时推荐结构：\n我的文章/ ├── index.md ├── featured.jpg └── images/ ├── 01.jpg └── 02.jpg 十五、常见问题 # 1. 文章不显示 # 检查：\n是否写了 draft: true； 日期是否是未来时间； 是否使用 hugo server -D -F； 文件是否在 content/ 下； 两个 Front Matter 分隔线是否成对； YAML 缩进是否正确。 2. 图片不显示 # 检查：\nBundle 图片路径是否相对 index.md； assets/ 图片是否使用正确的站点路径； static/ 文件是否从根路径访问； 文件名大小写； 是否误写成本机绝对路径。 3. gallery 异常 # 检查每个 img 是否有 grid-w25、grid-w33、grid-w50、grid-w66 或 grid-w100；检查 gallery 的开始和结束标签；不要把 gallery 嵌套在 gallery 中。\n4. carousel 没有图片 # 检查 images 是否用逗号分隔，是否存在多余空格，aspectRatio 是否写成 16-9，图片是否真的属于当前文章 Bundle。\n5. bilibili 报错 # bilibili 必须使用包含 BV 号的 URL：\nhttps://www.bilibili.com/video/BV13h411k7Zk/ 只有 av 号的链接当前不会自动转换。\n6. 短代码原样显示 # 可能是：\n你正好把它写在代码块里，这是展示代码时的正常现象； 少了闭合标签； 写错了名字，例如 callout 写成了 callou，或反过来； 嵌套短代码需要 md=false； Hugo 主题模块没有成功加载。 7. 数学公式不显示 # 检查是否调用 katex、公式分隔符是否成对、showMath 是否关闭、反斜杠是否被误删，并查看浏览器控制台。\n8. 外部卡片构建失败 # github、gitlab、huggingface、ansible、mdimporter 和 codeimporter 依赖外部网络。重要内容可以暂时改成普通 Markdown 链接，或复制到仓库内。\n十六、综合文章模板 # 下面是一份可以复制后修改的模板：\n--- title: \u0026#34;一篇示例文章\u0026#34; date: 2026-07-24T22:00:00+08:00 description: \u0026#34;这是一篇展示本站写作功能的示例文章。\u0026#34; showTableOfContents: true showMath: true featureImage: \u0026#34;/img/kumiko_jump.png\u0026#34; tags: - 示例 - Hugo categories: - 博客 --- ## 导语 这是一段普通正文，可以使用粗体、斜体、行内代码和链接。 \u0026gt; [!TIP] \u0026gt; 先写清楚内容，再补充复杂样式。 ## 图片 {{\u0026lt; figure src=\u0026#34;feature.jpg\u0026#34; alt=\u0026#34;示例图片\u0026#34; caption=\u0026#34;这是一段图片说明。\u0026#34; \u0026gt;}} ## 重点提示 {{\u0026lt; alert icon=\u0026#34;circle-info\u0026#34; \u0026gt;}} 这里是需要读者注意的信息。 {{\u0026lt; /alert \u0026gt;}} ## 图片画廊 {{\u0026lt; gallery \u0026gt;}} \u0026lt;img src=\u0026#34;01.jpg\u0026#34; class=\u0026#34;grid-w50\u0026#34; alt=\u0026#34;照片 1\u0026#34; /\u0026gt; \u0026lt;img src=\u0026#34;02.jpg\u0026#34; class=\u0026#34;grid-w50\u0026#34; alt=\u0026#34;照片 2\u0026#34; /\u0026gt; {{\u0026lt; /gallery \u0026gt;}} ## 数学公式 {{\u0026lt; katex \u0026gt;}} 行内公式：\\(a^2+b^2=c^2\\)。 $$ E = mc^2 $$ ## 代码 ~~~bash hugo server -D ~~~ ## 外部链接 {{\u0026lt; link-card href=\u0026#34;https://gohugo.io/\u0026#34; image=\u0026#34;/img/logo.png\u0026#34; title=\u0026#34;Hugo\u0026#34; desc=\u0026#34;静态网站生成器。\u0026#34; eyebrow=\u0026#34;Official Website\u0026#34; cta=\u0026#34;访问官网\u0026#34; \u0026gt;}} 十七、以后如何维护这份文档 # 增加新功能时同步更新：\n新增 layouts/shortcodes/xxx.html 后，在自定义短代码总表增加一行； 写清楚参数、默认值和一个完整例子； 修改 params.toml 后，更新站点级功能； 修改 markup.toml 后，更新 Markdown 或数学公式部分； 重命名短代码时记录迁移方式； 每次修改后运行 hugo \u0026ndash;gc \u0026ndash;minify。 十八、外部参考 # Blowfish Shortcodes Blowfish Front Matter Blowfish Getting Started Blowfish Thumbnails Hugo 官方文档 ","date":"July 24, 2026","externalUrl":null,"permalink":"/notes/tools/hugo%E5%8D%9A%E5%AE%A2markdown%E5%86%99%E4%BD%9C%E6%8C%87%E5%8D%97/","section":"笔记","summary":"这篇文章是我为本站写的一个备用写作手册，专门用来以后忘了有什么功能用法的一个备份。：以后新建 Markdown 文章时，当前这个博客到底支持什么，应该怎么写？\n本文依据当前仓库中的配置、布局、短代码和已有文章整理。当前站点使用 Hugo Extended v0.164.0、Blowfish v2.102.0、Goldmark Markdown、Waline 评论、Firebase 阅读量和点赞、KaTeX 数学公式，以及中文和日文两套内容。\n","title":"本站 Hugo + Markdown 写作功能参考","type":"notes"},{"content":"","date":"July 24, 2026","externalUrl":null,"permalink":"/notes/","section":"笔记","summary":"","title":"笔记","type":"notes"},{"content":"","date":"July 24, 2026","externalUrl":null,"permalink":"/categories/%E5%8D%9A%E5%AE%A2/","section":"Categories","summary":"","title":"博客","type":"categories"},{"content":"","date":"July 24, 2026","externalUrl":null,"permalink":"/tags/%E5%8D%9A%E5%AE%A2%E5%86%99%E4%BD%9C/","section":"Tags","summary":"","title":"博客写作","type":"tags"},{"content":"","date":"July 24, 2026","externalUrl":null,"permalink":"/notes/tools/","section":"笔记","summary":"","title":"工具与部署","type":"notes"},{"content":"Welcome to my blog!!!\n","date":"July 24, 2026","externalUrl":null,"permalink":"/","section":"響け！ユーフォニアム","summary":"Welcome to my blog!!!\n","title":"響け！ユーフォニアム","type":"page"},{"content":" 前文 # 本来在前一次写完这个关西游记 之后就是没有什么动力提笔写自己的 blog 了。因为感觉提笔写也没有什么人看，毕竟自己也羞于宣传自己的 blog 。因此，打算就这样作罢的。\n但是，想到自己的大学生涯即将结束，又是要开启新的一段征程，想到不写点什么，手又有点痒痒的。因此，就打算虽没人看，但是未来的自己看回这些又是一段回忆的涌现，也有可能想起那时的我在吹着夏天的风，写下的这篇文章。这何尝不是一种回忆。\n因此，在写回自己的大学 blog 文章前，先以这个刚好又涌上回忆的这本连夺三年「このライトノベルがすごい」首部荣登殿堂入り的轻小说「やはり俺の青春ラブコメはまちがっている」开写。\n我喜欢的一张宣传图：\n其实第一季这样的画风和轻小说的那种风格，我很喜欢的，就是感觉又那种10年代的那种并非旧时代又并非新时代的这种风格。 但是这张图片给出了一种亲近，又点名关系的图片，就是使得我很喜欢。 先晒出我的自己高中买的轻小说系列，可惜没有买日轻全系列的。\n相关视频和介绍 # 小学馆・ガガガ文库\n『やはり俺の青春ラブコメはまちがっている。』15周年記念特設サイト ガガガ文庫 15周年特設页面，回顾春物一路走来的轨迹(包含每一卷的轻小说的封面)与纪念企划。\n访问15周年特设网站 ↗ 小学馆・ガガガ文库\n『やはり俺の青春ラブコメはまちがっている。』原作特設サイト ガガガ文庫 官方原作特设 Portal 页。\n访问作品官方页面 ↗ 提示 我喜欢的第一季的 OP ，这个画面真的绝了\n作品信息 # 给不熟悉这部作品的做个简单介绍\n项目 内容 日文名 やはり俺の青春ラブコメはまちがっている。 English title My Teen Romantic Comedy SNAFU 原作 渡航《やはり俺の青春ラブコメはまちがっている。》 动画制作 Brain\u0026rsquo;s Base（第1季）、feel.（第2季与第3季） TV动画 第1季《我的青春恋爱物语果然有问题。》、第2季《续》、第3季《完》 核心角色 比企谷八幡、雪之下雪乃、由比滨结衣、一色彩羽 里面的主要几个关键词或者说是贯穿整部轻小说的核心：\n侍奉部 真物 共存 or 依赖 自爆 etc. 《我的青春恋爱物语果然有问题。》表面上是青春恋爱喜剧（Youth Romantic Comedy），但它真正让人感兴趣的或者说他能够连续三年霸榜「このライトノベルがすごい」的点并不是“最后谁和谁在一起”，而是大老师这种为了事件的顺利结束，不惜贬低牺牲自己(就是所谓的自爆)，但是雪乃和团子不希望大老师以这种方式来完成，其实就是都不希望大老师再继续让自己收到伤害，因为她们也心疼，这种我认为在渡航写这部轻小说的年代，就是妹控，傲娇横行的一个时代(渡航好像也是看了他的挚友平坂读写的「僕が友達が少ない」写的这部收到灵感而下笔写的吧。) 这种设定真的是令人无法不去读它。\n虽说让人感兴趣的点不是他们的党争恋爱，但是因为三人的性格鲜明，一个是傲娇黑长直，一个是不善言辞但温柔的自爆卡车，一个是察言观色团体为重体贴的🍡。他们每个人的日常发生的种种事件推进的关系的升温这些，也让人爱嗑。\n虽然我是雪乃党，但是最后一季团子的放手，等大老师走后自己蹲下哭泣😭这一幕，有人能够不共情吗？ 团子的失败，不是她的错，是这个世界的错，是没有存在两个大老师的错。\n这部作品最有魅力的地方，是它没有把“聪明”直接写成优点，也没有把“温柔”简单写成正确答案，而是让读者思考。 比企谷八幡擅长看穿问题，却习惯把自己放到最难看的位置；雪之下雪乃想靠自己的能力解决一切，却不擅长承认自己也需要别人；由比滨结衣看起来最会照顾气氛，却常常为了维持关系而把真正的想法藏起来。\n三个人的关系因此一直不稳定，因此也被阳乃说成是“共依存“的这种关系。直到后来，大老师在平塚 静老师的开导：感情这东西，一旦给了它名字，就会变得像是在强行套用模板一样。下才意识到，我们之间三人的感情关系不是简单一个名词就能够定义的。\n这部作品比普通的校园恋爱喜剧更像一部关于青春的沟通、孤独和自我防卫的成长长篇故事。虽然称为轻小说，但是渡航的这种功底和科班出身使得这部作品就是不单单是一部休闲放松看的一部类似解闷的小说，而是带有他的思考和能够让我们读者思考的一部小说。\n虽说，人人都尬黑自己的高中就像大老师那样。但是，人人都不是大老师，也无法成为大老师，也没办法遇到能够让大老师和雪乃以及团子一起改变的这样宛如只能在二次元存在的一个故事。可能，现实真的存在，毕竟现实更加魔幻的都有，但是我没有遇到，因此我不承认😭😭\n“真物”是什么？ # 日轻原著说的是：「本物　ほんもの」我认为中文直接说成“本物”更加好，也让读者去理解和思考。\n“本物”（本物，真正的东西）是整部作品里最重要、也最难一句话解释的关键词。我记得他是出在第八卷还是第九卷那里。\n大老师想要的本物是与伪物相对的。因此，我们展开这两个名次的解释让读者简单进行理解。\n伪物：为了维护表面和谐而互相说谎、客套、揣着明白装糊涂的“虚假关系”。表面平和，但是不会触及核心问题。\n真物：即使互相伤害，互相理解不了，也会尽力去理解，用语言无法理解，那就用行动。即使彼此了解对方最丑陋、最软弱的一面，哪怕会发生冲突、会伤害到彼此，也依然能够坦诚相待、不逃避、不会因此斩断羁绊的关系。\n一开始，大老师所理解的“真物”是没有被雪乃理解的，大老师自己也无法完全相信，因为无法用语言阐述那如何相互理解呢？这还是在团子的补充下：语言无法解释去理解，那么就用行动。因此，才得以在那次事件下，氛围得以修复。他们也可以说因此过上了一段“蜜月期”。\n推荐 # 如果你只把它当成党争（character faction debate）作品，喜欢看的是党争。如果是这样的话，我不建议你观看这一部作品。因为你在观看过程很容易变成“支持谁”的选择题。\n这部作品，我认为他是在青春这个框架下，探讨了何为真物何为伪物，在这个基础上，可以说是分为了叶山和大老师两个对照组这样。然后，大老师这个作为主要阐述，就是他和雪乃如此扭曲的性格，在认识了其他人的环境下，关系的变化，性格的变化，一个这样的文艺的需要思考的一个成长故事。\n如果你要入门，那么我建议你可以先观看番剧，看看是否合你胃口。如果这部番剧确实符合你的胃口了，那么恭喜你，你可以去看轻小说了，这小说的描写比番剧更佳详细并且更加有文艺更加能够让你代入去思考去体会。\n这部作品在我心中的评分如何说明呢？可以这样说吧，这部作品是我看了不说一百多本，可以说看了五十多本的轻小说来看，是否心中无法替代的存在。\n结语 # 这部作品时常能够让我回味起当初那个看这部作品时候的自己，那时的不理解，那时的懵懂，也逐渐变得好像能够理解了，自己也好像是成长了。但是，成长了又如何呢？我发生改变的好像只有自己的心境以及年龄，好像这样不如当初那个懵懂无知还在沉浸在这部作品时候的自己。\n","date":"July 17, 2026","externalUrl":null,"permalink":"/life/anime/oregairu/","section":"生活","summary":"前文 # 本来在前一次写完这个关西游记 之后就是没有什么动力提笔写自己的 blog 了。因为感觉提笔写也没有什么人看，毕竟自己也羞于宣传自己的 blog 。因此，打算就这样作罢的。\n","title":"《我的青春恋爱物语果然有问题。》观/读后感","type":"life"},{"content":"","date":"July 17, 2026","externalUrl":null,"permalink":"/ja/categories/%E3%82%A2%E3%83%8B%E3%83%A1/","section":"Categories","summary":"","title":"アニメ","type":"categories"},{"content":"","date":"July 17, 2026","externalUrl":null,"permalink":"/ja/tags/%E3%82%A2%E3%83%8B%E3%83%A1/","section":"Tags","summary":"","title":"アニメ","type":"tags"},{"content":"","date":"July 17, 2026","externalUrl":null,"permalink":"/ja/tags/%E3%83%A9%E3%82%A4%E3%83%88%E3%83%8E%E3%83%99%E3%83%AB/","section":"Tags","summary":"","title":"ライトノベル","type":"tags"},{"content":"","date":"July 17, 2026","externalUrl":null,"permalink":"/categories/%E7%95%AA%E5%89%A7/","section":"Categories","summary":"","title":"番剧","type":"categories"},{"content":"","date":"July 17, 2026","externalUrl":null,"permalink":"/life/anime/","section":"生活","summary":"","title":"番剧","type":"life"},{"content":"","date":"July 17, 2026","externalUrl":null,"permalink":"/tags/%E7%95%AA%E5%89%A7/","section":"Tags","summary":"","title":"番剧","type":"tags"},{"content":"","date":"July 17, 2026","externalUrl":null,"permalink":"/tags/%E9%9D%92%E6%98%A5/","section":"Tags","summary":"","title":"青春","type":"tags"},{"content":"","date":"July 17, 2026","externalUrl":null,"permalink":"/tags/%E8%BD%BB%E5%B0%8F%E8%AF%B4/","section":"Tags","summary":"","title":"轻小说","type":"tags"},{"content":"","date":"July 17, 2026","externalUrl":null,"permalink":"/life/","section":"生活","summary":"","title":"生活","type":"life"},{"content":"","date":"May 13, 2026","externalUrl":null,"permalink":"/tags/terminal/","section":"Tags","summary":"","title":"Terminal","type":"tags"},{"content":"","date":"May 13, 2026","externalUrl":null,"permalink":"/tags/tmux/","section":"Tags","summary":"","title":"Tmux","type":"tags"},{"content":" tmux 新手全指南：会话、窗口、分屏、复制、恢复一次搞定 # 从安装到进阶，把 tmux 的会话/窗口/窗格、复制模式、同步输入、配置与恢复完整讲清；覆盖 macOS、Linux、WSL，并配大量 SVG 示意图。\n你读完这篇，会从“只会开终端”变成“能稳定管理一堆后台任务”的人。tmux 的本质就一句话： 让你的终端永不下班 。最大的坑只有一个： 关窗口!= 退出会话 ，搞清楚“detach/attach”你就赢了一半。\n这篇是完整教程，带你从 0 建起可长期常驻的终端工作台，包含配置、恢复、同步输入、排错，以及大量图示。\ntmux 的四层结构 tmux 由 server 管理，内部是 session，再往下是 window，最底层是 pane。 tmux server session 会话 window 窗口 pane 窗格\n0. 准备：安装 tmux # 目的： 在你的系统上安装 tmux，并确认版本。\n操作：\nmacOS：\nbrew install tmux Ubuntu/Debian：\nsudo apt-get update \u0026amp;\u0026amp; sudo apt-get install -y tmux CentOS/Rocky：\nsudo yum install -y tmux Arch：\nsudo pacman -S tmux Windows（推荐 WSL2）：\nwsl --install 进入 WSL 后再用 apt 安装。\n你应该看到：\ntmux -V 输出类似 tmux 3.x 。\n失败怎么办：\ncommand not found ：确认包管理器是否存在，或在 WSL 内执行。 无权限：联系管理员或用有 sudo 的账户。 1. 先记住三件事：会话、窗口、窗格 # 目的： 知道 tmux 的三层结构，避免把“窗口”和“会话”混成一团。\nSession（会话） ：最大的容器，一般一个项目一个会话。 Window（窗口） ：会话里的多个“标签页”。 Pane（窗格） ：窗口里的分屏区域。 会话/窗口/窗格层级示意 一个 session 下包含多个 window，每个 window 下包含多个 pane。 Session: work Window 1: editor Pane A Pane B Window 2: server Pane A Session: lab 2. 启动你的第一个会话 # 目的： 创建一个会话，把任务放进 tmux。\n操作：\ntmux new -s work 你应该看到：\n进入一个新的终端界面 底部出现状态栏（默认绿色） 失败怎么办：\n提示已存在：换一个名字，或用 tmux attach -t work 直接进入。 会话启动流程 tmux new 创建会话，C-b d 退出，tmux attach 再次进入。 tmux new -s work 进入会话 C-b d detach 退出 tmux attach -t work 重新进入 3. 学会“detach / attach”这两个动作 # 目的： 让会话在后台继续跑，你断网/关机也不影响。\n操作：\n退出但不中断： Ctrl+b 然后 d 查看会话列表： tmux ls 重新进入： tmux attach -t work 你应该看到：\ndetach 后提示： [detached (from session work)] tmux ls 有一行 work: 1 windows (created ...) 失败怎么办：\nno sessions ：说明当前没有会话，重新 tmux new -s work 。 4. 窗口操作：像浏览器标签一样切换 # 目的： 把不同任务拆成不同窗口。\n操作：\n新建窗口： C-b c 列出窗口： C-b w 切换窗口： C-b n / C-b p 重命名窗口： C-b , 你也可以用命令：\ntmux new-window -n editor 你应该看到：\n状态栏里窗口列表发生变化 失败怎么办：\n快捷键没反应：先按 C-b ? 查看当前键位，再试一次。 窗口像标签页 一个会话里可以开多个窗口，每个窗口是一个任务面板。 editor server logs 当前窗口内容 5. 窗格分屏：把一个窗口切成多个视角 # 目的： 一边跑服务，一边看日志/编辑。\n操作：\n垂直分屏： C-b % 水平分屏： C-b \u0026quot; 在窗格间切换： C-b o 或 C-b 方向键 关闭当前窗格：在窗格里执行 exit 或 C-d 你也可以命令方式分屏：\ntmux split-window -h 你应该看到：\n一个窗口被分成多个区域 光标只在一个窗格里活动 失败怎么办：\n分屏后太小：在命令模式输入 C-b : resize-pane -L 10 之类进行调整。 窗格分屏示意 一个窗口可以分成左右或上下的窗格。 Pane A Pane B Pane C 6. 前缀键与快捷键的基本逻辑 # 目的： 理解为什么要先按 C-b ，避免“按了没反应”。\ntmux 的控制命令都需要先按 前缀键 。默认前缀是 C-b （Ctrl + b）。按下前缀后，tmux 等待下一个键，决定执行哪个命令。\n你可以用下面命令查看当前前缀表：\ntmux lsk -Tprefix -N 前缀键流程 按下前缀键后，tmux 才会识别下一次按键为命令。 你按键 C-b 下一键 命令\n7. 复制模式与滚屏 # 目的： 在 tmux 里像终端一样向上翻历史并复制。\n操作：\n进入复制模式： C-b [ 移动光标：方向键 / PageUp / PageDown 开始选择： Space 复制并退出： Enter 粘贴： C-b ] 你应该看到：\n进入复制模式后，光标可移动 选中区域高亮 失败怎么办：\n选不中：你的键位可能是 vi 模式，执行 tmux list-keys -Tcopy-mode 查看当前绑定。 复制模式示意 进入 copy-mode 后可以滚动历史并选区复制。 选中的文本区域 copy-mode 光标移动中 8. 鼠标与滚轮支持（推荐开启） # 目的： 用鼠标滚轮滚屏、点击切换窗格。\n操作： 在 ~/.tmux.conf 加一行：\nset -g mouse on 然后重载配置：\ntmux source-file ~/.tmux.conf 你应该看到：\n鼠标滚轮可以滚历史 点击窗格即可切换 失败怎么办：\n仍旧无效：确认你在 tmux 里（ [ -n \u0026quot;$TMUX\u0026quot; ] \u0026amp;\u0026amp; echo inside tmux ）。 鼠标滚动与切换 开启 mouse 后可以用滚轮和点击切换。 鼠标 滚轮滚屏 / 点击切换 9. 会话管理：用命令精准控制 # 目的： 管理多个会话，快速切换。\n操作：\ntmux ls tmux attach -t work tmux rename-session -t work ops 也可以用“存在即进入，不存在就创建”：\ntmux new -As work 你应该看到：\ntmux ls 显示所有会话 重命名后列表更新 失败怎么办：\n名字冲突：先 tmux ls 确认名称。 10. 同步输入：一条命令发给所有窗格 # 目的： 适合同时执行多台机器或多进程命令。\n操作：\n开启同步： C-b : 然后输入 setw -g synchronize-panes on 关闭同步： C-b : 然后输入 setw -g synchronize-panes off 你应该看到：\n输入的命令在多个窗格同时出现 失败怎么办：\n没反应：确认当前窗口有多个窗格。 同步输入示意 开启同步后，一条命令会广播到多个窗格。 Pane A Pane B Pane C 同一条命令广播 11. 配置文件：让 tmux 更顺手 # 目的： 固化你的常用习惯。\n操作： 新建 ~/.tmux.conf ，写入下面内容：\n# 允许鼠标 set -g mouse on # 更大的历史滚动 set -g history-limit 100000 # 把前缀从 C-b 改成 C-a（可选） set -g prefix C-a unbind C-b bind C-a send-prefix # 复制模式用 vi 键位（可选） setw -g mode-keys vi # 快速重载配置 bind r source-file ~/.tmux.conf \\; display-message \u0026#34;tmux reloaded\u0026#34; 重载配置：\ntmux source-file ~/.tmux.conf 你应该看到：\n状态栏弹出 “tmux reloaded” 失败怎么办：\n没提示：手动执行 tmux show -g history-limit 看配置是否生效。 配置加载流程 修改.tmux.conf 后通过 source-file 立即生效。 ~/.tmux.conf tmux source-file 立即生效 12. 进阶：多端同时连接同一会话 # 目的： 让两台设备同时看同一 session（适合远程协作或手机备份）。\n操作： 在另一台机器上执行：\ntmux attach -t work 你应该看到：\n两个终端同步显示 在一个端输入，另一个端实时更新 失败怎么办：\n连接后把别人踢下线：用 tmux attach -d -t work ，确保你是主动接管。 多客户端连接 多个终端可以连接同一个 tmux 会话。 终端 A 终端 B 会话 work 13. 进阶：会话恢复（重启机器也能回来） # 目的： 机器重启后恢复之前的 tmux 布局。\n操作： 使用 TPM + tmux-resurrect/continuum。\n安装 TPM： git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm 在 ~/.tmux.conf 加： set -g @plugin \u0026#39;tmux-plugins/tpm\u0026#39; set -g @plugin \u0026#39;tmux-plugins/tmux-resurrect\u0026#39; set -g @plugin \u0026#39;tmux-plugins/tmux-continuum\u0026#39; set -g @continuum-restore \u0026#39;on\u0026#39; 在 tmux 内按 C-b I 安装插件。 你应该看到：\n底部出现安装提示 重启后会话可恢复（需手动或自动） 失败怎么办：\n插件没装上：确认 ~/.tmux/plugins/tpm 目录存在。 会话恢复流程 tmux-resurrect 保存快照，continuum 自动恢复。 保存快照 系统重启 自动恢复 14. 常用快捷键速查（够你一周内上手） # 场景 快捷键 新建会话 tmux new -s name 退出但不中断 C-b d 新建窗口 C-b c 切换窗口 C-b n / C-b p 分屏（垂直/水平） C-b % / C-b \u0026quot; 切换窗格 C-b o 或方向键 复制模式 C-b [ 粘贴 C-b ] 查看快捷键 C-b ? 15. 排错清单（最常见的 6 个坑） # 快捷键没反应 ：确认先按 C-b ，再按目标键。 复制模式不好用 ：检查是否启用了 mode-keys vi 。 鼠标滚轮无效 ：确认 set -g mouse on 并重载。 分屏太小 ：用 resize-pane 调整，或把窗口拆成不同 window。 会话消失 ：确认是否误用了 kill-session 或 kill-server 。 WSL 无法滚动 ：更新 Windows Terminal/WSL，或开启 mouse。 如果你只记住一句话： tmux 是“会话常驻器” 。会话一旦建立，你的工作就不再依赖窗口是否开着。把它变成你的默认终端习惯，你就会自然进入长期高产的状态。\n","date":"May 13, 2026","externalUrl":null,"permalink":"/notes/tools/tmux_guide/","section":"笔记","summary":"tmux 新手全指南：会话、窗口、分屏、复制、恢复一次搞定 # 从安装到进阶，把 tmux 的会话/窗口/窗格、复制模式、同步输入、配置与恢复完整讲清；覆盖 macOS、Linux、WSL，并配大量 SVG 示意图。\n","title":"tmux 新手全指南：会话、窗口、分屏、复制、恢复","type":"notes"},{"content":"","date":"May 12, 2026","externalUrl":null,"permalink":"/categories/linux/","section":"Categories","summary":"","title":"Linux","type":"categories"},{"content":"","date":"May 12, 2026","externalUrl":null,"permalink":"/notes/linux/","section":"笔记","summary":"","title":"Linux","type":"notes"},{"content":"","date":"May 12, 2026","externalUrl":null,"permalink":"/tags/pty/","section":"Tags","summary":"","title":"PTY","type":"tags"},{"content":"","date":"May 12, 2026","externalUrl":null,"permalink":"/tags/shell/","section":"Tags","summary":"","title":"Shell","type":"tags"},{"content":" Shell、Terminal、TTY/PTY 的区别 # 简单来说就是如下关系：\n人 ↓ 输入命令 终端模拟器 Terminal Emulator ↓ 提供一个“终端窗口” TTY / PTY ↓ 传递输入输出 Shell ↓ 解释命令并调用程序 操作系统内核 / 程序 1. Shell 是什么？ # Shell 是命令解释器。\n你输入：\nls -l cd Desktop python main.py 真正理解这些命令并执行的是 shell。\n常见 shell 有：\nsh 最早期的 Unix shell bash Linux 中很常见 zsh macOS 现在默认使用 fish 更现代、交互体验更好 powershell Windows 上常见 提示 zsh,bash,fish 这些的不同之处就是它们的语法、功能、交互体验不同，但它们都是 shell，都是命令解释器。\nShell 的作用主要是：\n读取你输入的命令 解析命令 执行程序 处理变量、管道、重定向、脚本 例如：\ncat a.txt | grep hello \u0026gt; result.txt 这里的 |、\u0026gt; 这些符号不是 cat 或 grep 自己理解的，而是 shell 解析的。\n2. 终端模拟器是什么？ # 终端模拟器是你看到的那个窗口。\n比如：\nmacOS Terminal iTerm2 Windows Terminal GNOME Terminal Konsole Alacritty Kitty WezTerm 它本身不是 shell。\n它的作用是：\n显示文字界面 接收键盘输入 显示程序输出 模拟传统终端设备的行为 启动一个 shell 所以你打开 macOS 的“终端”后，里面默认启动的可能是：\nzsh 打开 Windows Terminal 后，里面可能启动：\nPowerShell 或者：\nUbuntu WSL bash 所以：\n终端模拟器 ≠ Shell 终端模拟器是“窗口”，shell 是窗口里面运行的“命令解释器”。\n3. TTY 是什么？ # TTY 原本是 Teletypewriter，电传打字机。\n早期计算机没有现代显示器，用户通过一种像打字机一样的设备和计算机交互：\n你在机器上打字 计算机返回文字 机器打印出来 这种设备就叫 TTY。\n后来虽然物理打字机没了，但 Unix/Linux 仍然保留了这个抽象：\nTTY = 一种终端设备接口 在 Linux 中你可以看到类似：\n/dev/tty /dev/tty1 /dev/tty2 /dev/pts/0 /dev/pts/1 其中：\n/dev/tty1, /dev/tty2 ... 通常是真正的虚拟控制台。\n例如在 Linux 里按：\nCtrl + Alt + F1 Ctrl + Alt + F2 进入的黑底登录界面，就是比较接近传统意义上的 TTY。\n4. PTY 是什么？ # 你现在在图形界面的终端模拟器里用 shell，通常用的不是传统 TTY，而是 PTY。\nPTY 全称是：\nPseudo Terminal 伪终端 它是一对虚拟设备：\nPTY master ←→ PTY slave 关系大概是：\n终端模拟器 ↓ PTY master ↓ PTY slave ↓ shell 终端模拟器通过 PTY master 和 shell 通信。\nshell 以为自己连接的是一个真正的终端设备，但其实是一个伪终端。\n例如你在终端里运行：\ntty 可能看到：\n/dev/pts/0 这个 /dev/pts/0 就是一个伪终端设备。\n5. 它们之间的关系 # 最常见的情况是：\n你打开 iTerm2 / Terminal / Windows Terminal ↓ 终端模拟器创建一个 PTY ↓ 终端模拟器启动 shell，比如 zsh/bash ↓ shell 绑定到这个 PTY ↓ 你输入命令 ↓ 终端模拟器把输入发给 PTY ↓ shell 收到命令并执行 ↓ 程序输出返回到 PTY ↓ 终端模拟器显示在窗口里 可以画成这样：\n+----------------+ | 用户 | +----------------+ | v +----------------+ | 终端模拟器 | | Terminal/iTerm2 | +----------------+ | v +----------------+ | PTY / TTY | | /dev/pts/0 | +----------------+ | v +----------------+ | Shell | | bash / zsh | +----------------+ | v +----------------+ | 命令 / 程序 | | ls, vim, python | +----------------+ 6. 举个具体例子 # 你打开 macOS Terminal，然后输入：\necho hello 实际发生的是：\n1. Terminal.app 打开一个窗口 2. Terminal.app 创建一个伪终端 PTY 3. Terminal.app 启动 zsh 4. 你输入 echo hello 5. zsh 解析这个命令 6. zsh 执行 echo 7. echo 输出 hello 8. 输出经过 PTY 返回 Terminal.app 9. Terminal.app 把 hello 显示出来 7. Shell 和终端最容易混淆的地方 # 很多人说：\n打开 shell 打开 terminal 打开命令行 日常说法里经常混着用，但严格来说：\n名称 本质 例子 Shell 命令解释器 bash, zsh, fish, PowerShell 终端模拟器 图形窗口程序 Terminal, iTerm2, Windows Terminal TTY 终端设备抽象 /dev/tty1 PTY 伪终端设备 /dev/pts/0 Console 控制台，语义较宽 Linux 虚拟控制台、系统控制台 CLI 命令行界面这种交互方式 git、npm、python 命令 8. Terminal、Console、Shell 的区别 # Terminal # 更偏向“输入输出设备”或“终端窗口”。\n你通过 terminal 和程序交互。 Console # 历史上指“系统控制台”，通常更接近直接连接到机器的主控输入输出设备。\n现在很多时候 console 和 terminal 也会混用。\n例如：\nbrowser console game console system console 含义会根据场景变化。\nShell # 是里面真正处理命令的程序。\nterminal 负责显示 shell 负责解释命令 9. 为什么 Vim、Top 这类程序需要 TTY？ # 有些程序不是简单输出一行文字，而是需要控制整个屏幕，比如：\nvim top htop less nano ssh tmux 它们需要知道：\n终端有多少行多少列 光标在哪里 颜色怎么显示 按键是什么 Ctrl+C 怎么处理 这些能力都是通过 TTY/PTY 这套终端接口完成的。\n所以你在某些环境里运行命令会看到：\nthe input device is not a TTY 意思就是：\n这个程序需要一个交互式终端，但当前环境没有提供真正的 TTY/PTY。 例如 Docker 里经常见到：\ndocker run -it ubuntu bash 这里：\n-i = interactive，保持标准输入 -t = 分配一个伪终端 TTY 没有 -t，有些交互程序就不好用。\n10. SSH 里的关系 # 当你 SSH 到服务器时：\nssh user@server 关系大概是：\n本地终端模拟器 ↓ 本地 ssh 客户端 ↓ 网络 远程 ssh 服务端 ↓ 远程 PTY ↓ 远程 shell 远程服务器上也会给你分配一个 PTY。\n所以你在远程服务器运行：\ntty 可能看到：\n/dev/pts/3 11. tmux / screen 又是什么？ # tmux 和 screen 可以理解成“终端复用器”。\n它们夹在终端模拟器和 shell 中间：\n终端模拟器 ↓ PTY ↓ tmux ↓ 多个 shell / 程序 它们的作用是：\n一个终端窗口里开多个会话 断开 SSH 后程序继续运行 分屏 恢复会话 所以 tmux 本质上也会大量使用 PTY。\n12. 总结一句话 # 终端模拟器是你看到的窗口； TTY/PTY 是连接窗口和命令程序的终端设备接口； Shell 是真正解释并执行命令的程序； 命令行程序通过这套机制和你交互。 最核心的关系是：\n用户 → 终端模拟器 → TTY/PTY → Shell → 程序 → 操作系统 ","date":"May 12, 2026","externalUrl":null,"permalink":"/notes/linux/shell_terminal_tty_diff/","section":"笔记","summary":"Shell、Terminal、TTY/PTY 的区别 # 简单来说就是如下关系：\n","title":"Shell、Terminal、TTY/PTY 的区别","type":"notes"},{"content":"","date":"May 12, 2026","externalUrl":null,"permalink":"/tags/tty/","section":"Tags","summary":"","title":"TTY","type":"tags"},{"content":" 关西游记⛺️ # 前言 # 根据之前做的理论准备关西游记攻略和关西游记实施攻略。2026年4月18日 我从广州坐大巴前往香港国际机场，然后准备明天的第一次飞机旅程。准备正式开始来到关西,游览了大阪、京都、奈良等地,体验了当地的文化、美食和风景。\nDay 0（2026年4月18日） # 值机和托运行李 # 18号的大概八点多，我就来到了深圳湾开始过关，过完关之后，因为有一个人也是和我一起从正佳出发来的，所以我就打算跟着她。结果，就是她在出完海关之后往左边靠，差点和司机错过。还好人不是很多使得司机可以找到我们，我们最终也是上了车。\n九点左右的时间，我就来到了香港国际机场。\n我首先就是随着人群进去，然后来到了机场的大厅。首先我是到处走走，观察机场的环境和设施，观察人群，了解都是一个怎么样的流程。然后我就观察了大概半个小时，就大概知道我们就是在这一层办理值机和托运行李的。通过一个悬空的设计，下面是一个乘坐飞机抵达香港的楼层，从大厅无法正常下去。\n我首先是遇到为什么我无法在当前的自动值机机器上没有找到自己的这个航空公司可以办理值机？这个问题。后面询问工作人员才知道要前往后面的列才行。 机场办理值机是按照航空公司来划分的，不同的航空公司会有不同的值机区域，所以需要根据自己的航空公司找到对应的值机区域进行办理。 随后我就来到了正确的列，然后就在自主值机机器上办理了值机和托运行李的手续。但是，我发现我的出现问题。工作人员就告诉我要前往人工值机柜台办理。于是我就前往人工值机柜台办理了值机和托运行李的手续。办理完之后，我就来到了安检口，准备进入安检。\n安检和登机 # 安检的流程和之前在国内机场的流程差不多，首先是要把随身携带的物品放到托盘里，然后通过安检门进行安检。安检的工作人员会对你的随身携带的物品进行检查，如果有需要的话，还会对你进行身体检查。安检的流程比较顺利，没有遇到什么问题。\n案件完之后，我就顺着指示牌的指引，来到了登机口。我来到的时候发现我的这个登机口几乎没有人，我还以为我的这趟航班可能没什么人坐。后来我才发现，几乎都是香港本地人，都是快出发的时候才陆续过来登机的。\n就这样，我在机场过了一晚，这一晚几乎没有睡着，因为没有什么舒适睡觉的地方，机场的温度在你慢慢入睡的时候会慢慢变冷，感觉有点冷。\n到了登记的早晨了，我本来想着在登记前就是办理香港机场退税的手续，来到这个Global Exchange的柜台办理退税的手续。 但是这个香港本地人工作人员不知道说的什么，告知我无法退税。我就问什么意思，结果给我一句英语差不多意思的翻译回答。 **我就请问，这样有什么意思？**我是在问你什么意思，你给我一句这样的话，有意义吗？解决问题了吗？这就是态度。😡\nDay 1（2026年4月19日） # 因为是第一次坐飞机，选择的是靠窗的位置。飞机开始起飞，飞机慢慢地升空，逐渐离开了地面，进入了云层。透过窗户，我看到了下面的城市和风景慢慢变小直至无法看清，感觉非常美丽。 飞机在空中废了大约三个多小时，睡觉也好像没有睡着，坐的很累。起初还感觉飞机好像就就这啊，也没有多快。直至我后面坐车才体会到这速度。 飞机进入到日本的空域之后，就是看到下面的海和城市环绕，并且这些城市好像还是环绕山和海的，感觉就是给我一种有种galgame里的回到那种日式小镇的感觉，感觉非常美丽。\nPrevious Next 落地关西机场 # 到达关西机场之后，就是随着人群走，先乘坐机场地铁到达大厅，跟着人群逐步走到入境的地方。 这里就会使用到我们之前网上办理的VJW QR Code，不断前进的途中就是有机器要扫描这个二维码，扫描之后就会有一个绿色的灯亮起，表示可以继续前进了。而且，途中还有一些显示屏扫描显示你要走的通道这样，感觉非常方便和发达。整个流程很顺利，没有遇到什么问题。\n然后，我们就是来到了入境的地方，排队等待入境检查。入境检查的流程和之前在国内机场的流程差不多，首先是要把随身携带的物品放到托盘里，然后通过安检门进行安检。安检的工作人员会对你的随身携带的物品进行检查，如果有需要的话，还会对你进行身体检查。安检的流程比较顺利，没有遇到什么问题。\n但是，因为是第一次的经验，随着人群走，也没有遇到什么问题，我在快要经过一个门走到外面的时候我忘记我是不是要在里面拿托运行李。然后我就问了一个工作人员，但是因为本人日语还是依托，就没有听明白。就直接走到了大厅，完全忘记要去拿托运行李了。后面，我就到处走，看看有没有走回去的路，发现没有。我就询问工作人员，工作人员告诉我直走然后左转。 但是，来到附近之后，发现没有相关咨询的地方啊。我就只能打开我的小红书🍠，看看有没有相关的攻略，最后也是找到了相关的攻略，其实是在一个门的后面的，我们要从这个门进去就能找到工作人员帮忙了。就这样，我找到了工作人员，通过工作人员帮我找到了我的托运行李，拿到了我的行李之后，我就着手随着寻找我的JR Haruka的车站了。\n就这样，我坐上了JR Haruka，准备前往京都站了。坐上车后，就经过大概一个多小时的车程，来到了京都站。\n到达京都站 # 来到京都站之后，我就想着跟着地图找到我的酒店办理入住的手续。结果就是这个京都站真的是，不知道一个怎么样的设计，真的是感觉有种迷宫的感觉，里面商店，电车指示牌，人群，给我搞晕了。我就这样，心想着就这样跟着地图走吧，不管这么多了，误打误撞走了出来。然后就赶去酒店办理入住了，办理入住的整个流程也比较顺利，没有遇到什么问题。\n因为，没有前一晚没有洗澡和也没有吃什么东西，实在太难受了，就先去便利店买了个桶面和在二楼看看贩卖机的使用。在趁放浴缸水的时候，吃了个方便面。跑了个澡，跑完后，感觉非常舒服。不过感觉少了个冰牛奶饮料，可惜，没有这样体验😂。 洗完澡之后，就想着吃点东西了。然后就问了一下gemini推荐了一个東洋亭的餐厅，去小红书搜了一下，发现这个评级还可以，也是点了一份小红书推荐的套餐。拍了几张照片，如下：\n还有一个汉堡肉的菜，但是没有拍照。整体吃完之后感觉还行，中规中矩吧，感觉没有很惊艳，感觉就是一个普通的餐厅的水平了。 可能是我看过了食劇のソーマ之后，对美食的期待值有点高了😂\n消费是： 不过，因为找这个餐厅，加上兜兜转转和等待的时间，吃完饭之后已经接近九点了。想要去找ユニクル买衣服的，但是又是找不到，兜兜转转了好久，看到有个药店就进去买了LION PAIR和ACNEIGE药膏，感觉这两款药膏的口碑都不错，想要试试看效果怎么样。\n买完之后，就走着回酒店休息了。回酒店的路上，感觉这个京都的夜晚还是挺安静的，街道上没有什么人，感觉有点冷清。这有点出乎我的意料，没想到这里到处游逛的不是本地人，而是欧美的游客，也颠覆了我对日本的一个初印象。刚好插一嘴，从机场到京都站再到酒店，发现来这里的游客大部分都是欧美的游客。\nDay 2（2026年4月20日） # 贵船神社 # 这一天按照之前做好的攻略，就是要早上前往贵船神社(きふねじんじゃ)。 这天早上起来的时候和昨天休息的时间也早，所以感觉还不错。在711便利店买了个400日元左右的🍙和一瓶矿泉水之后就前往京都站去坐地铁。 一开始我真以为是地铁的，后面我在到处转悠迷路之后，我问了工作人员随着指示牌走之后我也搞懂了，这不是地铁即地下鉄(ちかてつ)orメトロ，而是电车🚃。我以为日本是没有地铁的都是电车，不过我后来发现京都市内的也是有地铁线路的，而且就是京都站内什么都有。所以整个京都站，一开始给我的感觉就是混乱，可能是我没有理清导致的混乱吧。还有，这个地图和指示牌我因为习惯了国内的傻瓜式的指引，就给我弄混乱了。 总之，兜兜转转就又花了半个小时才坐上电车。我也开始踏上了前往贵船神社(きふねじんじゃ)的旅程了。\n在日本，国民素质还是比较高的，所以在公共交通工具上，大家都会自觉地排队等候，不会插队或者拥挤，以及会先下后上以及保持安静 乘坐的这个电车是叡山電鉄，其实这个电车会进入到一个让人感觉春意盎然的山路，会非常有感觉的。我见途中也有两个中国来的女生也在拿着相机拍照，但是毕竟我不想让人感觉到麻烦，我就没有拍照了。但是，感觉还在，记住就行了。\n到达贵船神社之后，我没有选择坐公交直接到达神社，而是选择了步行爬山的方式。 一方面是我才刚到这里，还不熟悉整个坐车流程，就是支付方式这些，有些胆怯;另一方面是我觉得爬山的方式更能体验到这个神社的氛围，感觉更有一种旅行的感觉。虽然爬山的路程比较长，但是沿途的风景非常美丽，感觉非常值得。\n整个路线是这样的：入口 → 本宫 →（步行山道约10–15分钟）→ 结社 →（再往上）→ 奥宫 标准的一个流程也是按照这个路线，逐步参拜的。但是我在途中遇到一个日本女生就是反着来，她是一口气先上到奥宫，然后再往下走到结社，最后再往下走到本宫的一个参拜路线。这个也是我觉得非常有意思的一个参拜方式，感觉也是一种不同的体验了，下次也这样体验一下。\n从图片中也可以看出，就是沿途风景非常美丽，感觉非常值得步行爬上来。\n首先到达的是神社⛩️的本宫，就是可以先看到的这个景色：\n听说这里接近傍晚的时候就会亮灯，会非常好看的。但是，这里傍晚时间来感觉还是有点太害怕了，毕竟之前日本经常有クマに注意的提示。\n这个本宫就是做参拜，水占卜的地方了。水占卜就是在这个神社里非常有名的一个占卜方式，就是把这个占卜纸放在水里，看看上面写的字会不会变淡或者变清晰，来判断这个占卜的结果。这个非常有趣，第一次看到就感觉很神奇。他这个纸上还有二维码这些显示网页和翻译的😂\n然后，我就继续向上走，就来到了结社。这里的人没有很多，所以我就观察和搜索参拜的步骤，就打算在这里尝试参拜了，毕竟下面本宫参拜的人太多了，我有点害怕自己参拜的步骤不对被人家看笑话了。\n单纯参拜，不洗手的步骤就是：鞠躬两次 → 拍手两次 → 鞠躬一次 接着，我就上来到了奥宫了。来到奥宫，发现和下面其实也是差不多，不过这里有个樱花树，但是毕竟我来的时候已经是四月中旬了，能看到地上谢了的樱花，很可惜没有看到盛开飘落的樱花。这里还看到有一个摆摊喝抹茶的小店，但是我想着后面也是要去宇治的，不如到宇治体验更加本地的感觉。\n琉璃光院 # 从贵船神社出来，其实已经接近十二点了，按照攻略应该是要去吃饭的。但是，因为琉璃光院还没有去逛，就问了一下AI应该怎么样安排不绕路。最终AI回答我们先去琉璃光院，然后再去一乘寺🍜。\n琉璃光院，这个其实早就在视频这些看到，略有耳闻了的。这个景点的开放季节好像没记错是春季和秋季的，分别是樱花和红叶的季节。这个景点看人流量决定是否要提前预约。\n我去的时候，价格2000日元，还可以逛下面的一个美术馆。\n买了票之后，我们也是跟着进去了，进去前要脱鞋，并自己拿一个袋子装鞋子。下面我直接给出自己拍的一些照片，自己体验这个景点怎么样了。\n琉璃光院本身是一个非常好看的景点，但是本人拍照技术，比较烂，所以可能拍不出这个景点的美丽，我先给出几张别人拍的照片，来体验这个景点的美丽了。 接下来就是我自己拍的，献丑了：\n逛完出来之后，就在门口遇到有个人问了すみません、写真を撮っていただけませんか。。我心想，这不就是我在来日本前就一直想着和别人说的话吗，怎么被别人先这样问了😂\n我就回了一句いいですよ。，然后就帮他拍了几张照片了。\n后面我给回手机，刚想走的时候，这个人就说了，要不要帮我也拍几张照片的意思的日语，我有点忘了原话是什么了。因为我本来是没想着拍照这样的，就犹豫了一会，但想到是别人的好意，我就说了いいですよ。，然后他就帮我拍了几张照片了。刚拍完，走下阶梯的时候，我就不小心有点踩空了，差点摔倒，连忙说了两句大丈夫、大丈夫，给别人都逗笑了(不是贬义的逗笑)。\n然后，我们就一起往下走了。我们路上简单聊了一下。一开始我问どこに住んでいますか。\n结果她说フランス，我感觉很震惊，毕竟日语听着像本地人，就疑惑问了日本人ですか，果不其然，她是日本人，她是和家里人从法国来日本旅游的。后面他问我来日本打算多久。这里我就忘了，九天要怎么说的来着，就凭着感觉和记忆来说了。结果，说错了，她也纠正了我，但是我毕竟也是因为有点紧张就没记起来也说的有点抽象。九天的日语就是：ここのか\n不知是为了表示歉意，没法好好交流，我就说了句すみません、私の日本語はあんまり上手です。(后面才后知后觉，这句话我又说错了。应该是:私の日本語はあんまり上手ではありません。) 不过，不知道是客气还是真的，她说了句いいえ、上手いよ。(一开始我还没想起うまい是什么意思😭，但我知道她是说我上手)，感觉很开心😊\n后面，她就进去美术馆里了，我本来想着一起再多聊聊的。但是我不知道有没有买票，就没一起进去了。后面我才意识到，我这个前面买琉璃光院的票的时候，工作人员说了是包含美术馆的门票的。我就打算进去再和这个妇女再聊一下的，就特意在进去前搜了一下又遇见了呢的日语说法的😂。但是，进去后发现没有看到她，我就自己再看了一下就出来了。\n但是，看到一个有意思的：\n一乘寺 # 那么，接下来我们就是前往吃饭的地方：一乘寺了。这里是一个比较有名的吃拉面🍜的地方了，因为我不想吃太油腻的面，我就问了gemini吃什么比较好。gemini就推荐了中華そば高安这个店了。这个店的口味算是比较清淡的，高汤不错，但是吃完之后柑橘就是会很渴。\n可惜的是，因为是第一天来，所以很多他们的习俗礼仪没有习惯，所以一些话也没有听懂也不知道吃饱之后要说ごちそうさまでした，感觉有点可惜了。\n吃饱喝足之后，我在附近到处转悠了一下，看到了惠文社，这个店确实我一眼就看到了，而且看到里面也有人在看商品，但是我没有进去逛一下，感觉有点可惜了。不过也有就是因为是第一天，不熟悉习俗礼仪而感觉到害怕导致的。\n然后就前往下一个目的地巡礼了: 鸭川乌龟跳石，这个其实就在出町柳站附近，出来车站大概十分钟左右就到了的。\n在等电车的时候，还看到一辆试运行的电车，一开始还疑惑，想着走上去，但见到没有人动，就看到是试运行的电车，原来日本的电车也是有试运行的电车的啊，感觉很有意思了。\n鸭川乌龟跳石(出町の飛び石) # 在台阶上看到有两个高中生一男一女在这练小号🎺和萨克斯🎷，真的太青春了，就以此记录一下：\nPrevious Next 看到这些景色，不禁想起玉子爱情故事里玉子和饼藏在河边的场景。\n玉子在跳石头，后面饼藏在旁边犹豫开不开口告白，后面告白玉子落水的情景了。\n接着，我就来到了玉子商店街\n我好像没有看到有一个很大的鲭鱼雕像这个标志。我在商店街这，没看到就是有好像小吃的东西，感觉比较少，在外面有一家买饼干这种的。然后，我就在这里的一个文具店买了一些笔，也算是纪念和感受日本的文具店。\n然后，我又回去跳石头那里找长椅坐了一下。发现这里真的很舒适，吹着风听着这里的吵闹声，感觉非常惬意，有种日漫里的感觉了，这才是青春！\n回到京都站之后，就去附近的Avanti的animate去看了看，我上到六楼，发现真的好小啊。 感觉和我想象中的完全不一样，，就是一个小轻小说店加上其他的买周边的以及买卡的店合起来的感觉。真的失望了的。 我就在轻小说店里看了一下，发现都找不到遊びの関係这本轻小说，最后问店员，她听不懂我说的，一直要我给图片，真的蚌埠住了。 最后，来了个男店员才从角落里拿给我。不过，最后拿了三本負けヒロインが多すぎる！和遊びの関係，但是居然凑不够免税额。 最后，我就随便拿了两本薬屋のひとりごと，本来我想拿游戏人生的。但是，因为后面有人买单，不想麻烦就这样了。\n但是，游戏人生这样的，不应该摆在比较显眼的位置的吗？？？ 实则，其实是这家animate店垃圾。我到了大阪的梅田店，发现这里的是一整栋楼都是，不同楼层是不同的区域，比京都的大多了。这才是我理想中的animate店啊。 这一天的流程也就差不多了，回去的时候，又在京都站里迷路了。还是感觉京都站的设计真的很迷宫了，感觉自己在里面走来走去都不知道自己在哪里了。没记错的话，今天的晚餐是在便利店和和小商铺买了一些吃的东西随便吃的。\nDay 3（2026年4月21日） # 这一天就是实打实的重头戏了，因为要前往宇治了。宇治这个地方，熟悉 京吹(響け！ユーフォニアム) 的应该都不陌生，这个地方就是久美子的主舞台了。\n首先，这天早上出发的时候相比实施攻略的时间略晚。所以，一开始还有点着急，但是时间其实算是来得及的。\n我们按照攻略，先前往伏见稻荷大社。来到这里的时候，人已经很多了，并且好像这时间段还是修学旅行的时间段。\n走到下面的时候，发现伏见稻荷大社在举行一个祭典，非常热闹。 不过，后面回想起来有点可惜是不是晚上也来看看比较好的。(可惜在之前到达京都站那一晚感受到的晚上没有什么人导致的害怕)\n买了一个10日元パン，吃了之后感觉非常好吃，很留恋这个味道。我也买了一个大福来吃，本来是想吃团子🍡的，可惜没了。\n接着，我们就准备前往宇治了。\n在等电车的时候，遇到一电车居然有女性专用车列的，感觉有点惊讶。\n来到宇治站，出来后就能够看到一个宇治观光服务中心，这个服务中心可以领取一份宇治观光Map的。 因为一开始我是不知道这个可以每人一份的，就是我问すみません、これ、写真を撮っていいですか。结果工作人员表达了，我可以拿走的意思。就简单交谈了一下。问我是哪里来的，还是对我说我的日本語は上手い、びっくりした，真的感觉太开心了，又收到这样的夸赞😊\n这张地图讲解了一些宇治的各个景点的分布，比较详细和全面的，不过好像没有讲京吹各个跑动的小人物的具体位置。\n接着，我就先按照地图🗺️去宇治桥，但是我实在看不懂纸质地图，我就还是打开了我的Google Map。今天宇治的风甚是喧嚣，感觉非常有日漫的感觉。 白天的宇治桥的照片没有拍，黄昏的就拍了几张：\n先是来到了宇治河堤附近，并去了久美子椅：\n后面就顺着去了朝雾桥(部长失格桥)\n接着就去了宇治神社，宇治上神社，平等院和大吉山这些著名的圣地了。\n在爬大吉山的时候，有一个日本女性又和我打招呼了，说了句こんにちは。，感觉心情非常愉悦😊，我也回了一句こんにちは。。\n然后就是，实在太心动了，这个京阪电车和京吹联动，所以我还是问了一下grok看看哪些地方还有的买这个联动一日券。 最后从宇治出发走了一大圈去到这个京阪电车的淀屋桥站买了这个1700日元的联动一日券了。最后，又坐电车回到宇治，坐jr回到京都站。\n接下来就是在去买这个联动一日券的路上，看到的这些联动立牌和自动贩卖机了。\n立牌：\n久美子的自动贩卖机： 丽奈的自动贩卖机： 小奏的自动贩卖机： 真由的自动贩卖机： 走去坐这个jr的路上还遇到典中典萨利亚以及小奏公交站：\n还有其他联动的相关元素：\n这一天虽然没有去到什么地方，也没有买到什么东西。因为家里基本就只有我喝抹茶🍵，所以我就没打算在中村藤吉本店这里买抹茶粉和喝抹茶巴菲(因为人实在太多了)了。但是，感觉还是有点可惜的，尽管人多，也应该体验一下的，毕竟来这里的机会也不多。\n后面就赶回京都站了，又在同一个店里harves买东西回去吃了。这里很抽象，因为前一天买的时候没有记住点名，就记住大概的店铺环境，就导致我又迷路了，导致我直接浪费半个多小时。最后也是买了个泡芙和一个面包吃，这泡芙非常好吃，可惜没有拍照。\n参考这个网图： 今天的晚餐就是这个泡芙，面包和在便利店买的这个酒了。\n这款酒感觉酒精还是太高了，就是我一开始以为是加上柠檬的话，就是有种酸感，应该会好像乳酸菌口味的ほろよい气泡酒那样好喝的，结果一般。\n喝了两次乳酸菌口味的ほろよい气泡酒，感觉这个口味的确是非常好喝的，可惜没有尝试一下桃子🍑口味的。\n还有晚上快睡觉前买京吹电影的票：\nDay 4（2026年4月22日） # 这天按照一开始的攻略是要去 豊郷町立豊郷小学校旧校舎群 进行 轻音少女(けいおん！) 圣地巡礼的。 但是，因为这个来回路程就是三个小时，我就开始犹豫，这样一来一回一天这样来逛好吗？就是去到那里就进行一个圣地巡礼，我要不要就是在日本都没有多少天就是进行一个体验日本当地文化比较好，就决定去了奈良了。毕竟车程比去 豊郷町 要近很多了。\n进行圣地巡礼才是最有体验感，更能体验到当地文化的方式了，如果觉得路程遥远，那你就输了。虽然是马后炮了，感觉真的好可惜啊😔😔 从奈良站出来一直走，就在一个小吃街上看到一个在类似中谷堂的那种高速餅つき，不确定我看到的那个店是不是就是中谷堂了，外面很多外国人在这看，拍视频，我也不想凑上前去看和买一些来迟了。\n感觉这天的奈良之旅也没有什么，不过，奈良的鹿确实多，就是已经有种不怕人的感觉了。\n这里的鹿确实会跪下来点头这样的，我看到真的太惊讶了，居然还能这样，但是没有拍下来。\n奈良这里也有学校来修学旅行，看到这一幕，真的是太青春了😭😭\n然后，中午吃饭的话，我因为还是有点害怕，所以就都尽量挑人流量没有这么大的店。因此，导致我走了很久。最后，我找到了一间来这里逛的旅客也不多的街道，人流量也不大的店。这个店铺是一对夫妇经营的。美食菜单如下：\n最后，我点了一份洋目カツ丼，吃完之后感觉还行，感觉就是在日本这应该是相当于正常的符合800日元的美食了。\n后面，我就坐上返回京都的电车了。回到京都之后，一开始犹豫要不要去京都塔上面看看和买一些京阿尼的周边。但是感觉自己的花销都已经算比较大了，毕竟买了一张1700日元的联动一日券了，所以就刷小红书看看值不值得推荐的帖子。最后，刷到是可以说上去买周边的。但是，因为我也只是打算看看再考虑买不买的，所以我就不敢上去了。最后就在附近的Avanti商场逛了一下GU。\n然后，我就又回到京都站里找一下有没有值得好吃的。最后就吃了つくもうどん，这个店是直接在外面的点餐机里点餐支付拿着票去取餐的。吃完之后就自己将餐盘放到回收处了。我点了一份鸡蛋咖喱热面加上炸鸡块，感觉这个店的口味还行，感觉就是在日本吃个面条的水平了。\n感觉日本的饮食真的是淀粉加淀粉这样，就是几乎都没有青菜🥬这样的，有点热量爆炸。\nDay 5（2026年4月23日） # 岚山竹林小径 # 这一天的行程就是要去看岚山的竹林小径。这个景点算是比较有名的，即使我去的那天下雨依然有很多人。这个景点是 春物大老师自爆 和 空之境界里的一个场景 的取景地了，也算是为了圣地巡礼了。\n以下就是我逛的时候随便拍的一些照片：\n就是没想到下雨的时候，而且这种雨挺烦人的，是那种不大但很密集的雨，居然还是有很多学生在修学旅行。不过，可能是习惯了日本就是这样的雨。\n看到的一个寺庙:\n然后，后面随便走，就来到一处地方，发现还有一个周总理纪念诗碑的：\n然后，就为了避雨，到处走，发现了湖。并且来到一个亭子处，结果发现这里好像就是春物中秋叶委托大老师的地点。不过，没有拍那个小道。其实就是在桥的下面，湖的旁边小路。\n拍到的湖:\n然后，其实这里也没有什么好逛的，并且由于下雨的关系，就又感觉心情又差了一些。然后我就到处走走，找到一个车站附近，有一些卖小吃的店。 我好像没记错我花了800日元还是100日元吃了一串四个那种肉团子(感觉可以说是大个的蒸饺，只是皮薄一点并且肉馅好吃)，感觉这个味道还不错。\n锦市场 # 后面，我就觉得这里没什么好玩的，就走了。但是，因为时间也还早，所以就为了更加体验，避免感觉自己好像来到日本没逛什么似的。 我就搜索，有什么好去的。最终，就决定去京都「祇園四條」的锦市场这里的小吃商店街逛一下。这里的人也是非常多，感觉这里很多都不是日本人，反而是那些欧美的外国人。\n在这个市场拍的一些图:\n在这里看到一个：\n好像这里也是比较出名的吧。\n最后吃了一个章鱼烧(たこ焼き)，感觉味道还行，就是我没想到它是这样来买的，800日元四个，但是1000日元8个。一开始以为这营销是不是有点抽象，但是吃过之后才知道，吃8个还是太腻了。\n吃完这个之后，我就又回到京都站这里了。然后，我就因为今天下雨就早早回到酒店看了番休息了。但是，现在想起来感觉自己真的是太浪费了，感觉自己来日本都没有好好逛一下，不应该这样的，毕竟来日本的机会也不多了。 不过，自己这样也是因为每天都这样大量的走动，就算休息8天也还是不能完全回复体力和精神的，所以之前这样坐也算是为了休息而这样的一个偷懒。不过，感觉还是太可惜了。\nDay 6（2026年4月24日） # 京都水族馆 # 今天是看最終楽章 響け！ユーフォニアム 前編的日子了。本当にワクワク 可以说，我这次来日本的主要目的就是为了看这个电影的(真的可以说是为了这碟醋包的饺子)。\n从酒店前往京都水族馆的路上:\n你会发现路上真的没有什么人，真的对日本的整体是怎么运行的感到好奇，没有人就是工作什么的吗？\n这天早上，我是按照攻略是要去京都水族馆的，这个我在小红书上搜到的评价是一般的。因为，他们都是对比这个大阪的海游馆的，说这里小很快就逛完了。但是，海游馆毕竟不是水族馆两者是不一样的。因此，我为了实现我的心愿，就是决定去这个水族馆。\n在这个水族馆前面，是一块很大的空地,有挺多人在这玩耍的。\n就是京都水族馆也有学生来修学旅行，没记错的话，好像有四个高校，可惜没有拍他们在集合的照片。\n京都水族馆的成人入场券是2600日元，是有点贵的，但是我后面逛完加上看海豚表演，感觉就是也没有很小。我觉得如果是在淡季来，自己欣赏真的很安静并且气氛很惬意的。最后逛完差不多接近两个小时吧。\n水族馆+日本jk的青春图片：\n下面是水族馆一系列的鱼图：\nPrevious Next 企鹅图:\n水母🪼图:\nPrevious Next 杂七杂八的图片:\n在中间看鱼的时候看到一个日本高中眼镜娘，感觉实在卡哇伊和青春就拍了两张：\n这才是青春啊😭😭😭\n5aaC5p6c5pyJ5Lq655yf55qE55+l6YGT5aW555qE6IGU57O75pa55byP77yM6IO95LiN6IO95bCx5piv5ZCR5oiR5LuL57uN5LiA5LiL77yM55yf55qE5piv5ouc5omY5LqG77yB77yB77yB 提示 base64\n一开始还犹豫要不要问个联系方式，因为认识一个日本高中jk还是不错的，也顺便拓展一下异性缘。 最后还是因为不好意思就不问了，后面看完海豚🐬因为没有再遇到，出去到草地也没有到处找，就为了去电影院看京吹就走了。\n不是吹的，如果真能够在电影院偶遇到，这就是缘分，不管怎么样死缠烂打我都会要到联系方式的。 最后，是海豚表演场地图和看完之后拍的外景图：\nPrevious Next MOVIX 京都 # 看完水族馆之后，我就立刻坐公交车去往四条的MOVIX 京都电影院了。 今天是我第一次坐公交车，还怕不会坐公交车，结果发现很简单，几乎只要你有苹果手机就很好办。最后，在小红书看一下攻略就可以了。\n在看电影前，找的一家店吃的：\n最后，我就前往这个电影院的store看看那个会发生的立牌有没有的卖。结果，我去到发现这里买的东西很少，都没有那个立牌。我就买了两个文件夹和一个钥匙扣。\n纪念：\nMOVIX 京都电影院附近:\n其实，就是我发现大多电影院对柯南的剧场版给足了很大排面，为什么呢？就是有很多海报和立牌展示。\n京吹真的因为第三季给我的感觉真的太复杂了，毕竟演奏画面少了很多，最后居然还改原著剧情，久美子不吹soli。 在这次的前篇中，有了大概八分钟的演奏画面，看的时候真的很激动，真的太棒了。\n京都大学 # 看完电影之后，还有很多时间，就乘坐公交来到了京都大学附近。\n我发现京都大学真的比华师还要旧，建筑分布也很分散，也好像不够华师大。\n我没有拍那个京都大学校内的百周年时钟记念馆那个。\n然后，我出去看，发现京都大学附近的一处地方在搞活动，拍了一些照片：\n然后又在附近溜达，拍的一些照片：\n最后，我就又回到京都站了，随便在便利店买了些东西然后就回酒店休息了。\nDay 7（2026年4月25日） # 今天的话，就是要从京都前往大阪的日子了。因为之前买了那个京阪电车的联动一日券了，所以今天就要使用这张券了。 从酒店出发的话，要坐京阪電車的话，我需要拖着一个行李走一公里多到东福寺站京到达大阪的淀屋桥站，然后再坐大阪市内地铁。\n从地铁站出来就是：\n然后我就前往我的东横inn酒店了。这个酒店其实属于大阪的商业区或者办公区附近，算是比较繁华的地带，不过可能还是比不上梅田的那个分店。\n酒店附近公园 # 酒店附近有个公园，就办理好入住寄存行李之后就去逛了一下。\n在这个公园的时候，我被隔壁的一个老大爷搭话了。不过，因为他的口音有点重，有点完全听不懂他在讲什么。只能听明白一些单词，比如说いっぱい，WIFI，携帯電話，データ。 后面我才明白一点：他看我有两台手机，就向我搭话，然后说他的手机为什么上不了网，问我这里有没有wifi，还有后面我看是它的这张卡流量已经用完了。然后，我就这样懵懵懂懂的和他交流了大概十多分钟，后面他向我感谢，就送了我一瓶桃子牛奶，也算是喝上了和番里面草莓牛奶的同款。\n大阪城公园 # 接下来，我就前往大阪城公园了。这个公园真的很大，本地人来运动走走的也很多。\n在路途看懂的大阪NHK大楼：\n大阪城公园的照片：\n在大阪城内还有柯南的联动商店，真的柯南这么大排面的吗？怎么这么多店铺贩卖的？\n丰臣秀吉的铜像：\n还看到了之前在b站还是什么看到江ノ島駅给小鳥穿衣服的同款小鸟：\n江ノ島駅 同款小鳥 点击查看 难波 # 因为之前在MOVIX 京都没有买到那个电影联动电影院的会发声的立牌，最后打算在大阪这里看看有没有的卖的。 最后，打算先在Namba Park Cienema这里看看有没有的卖。\n路上看到有在举行一个街头演唱活动：\n最后，上到电影院那里，发现这里也还是没有的卖。我最后真的是累了，就不想再去大阪其他电影院找了。就出去外卖呢的一个地方坐着休息一下，发现这里也有一个在举行演唱活动。我想，这样的活动应该和4.29TRUE在京都站举行的差不多的。然后，我就在这里听着音乐休息一下。\n休息了有二十多分钟，就打算进去再看看那个5500日元的衣服，犹豫要不要拿下。因为这个感觉真的太不值了(就是我的优衣库的也才1900日元)，所以犹豫。\n最后，在我进去再看一次的时候，发现这里又多了一个立牌，而且最后一个。因此，我就毫不犹豫的拿下了这个立牌了。虽然这个立牌4550日元，但是毕竟会发声嘛，真的太稀罕了。\n心斋桥 # 最后，应群友要求，就去附近的animate帮他看看CD。然后去到这里的大阪的animate，我才发现：之前京都那个真的拉垮。\n感受一下：\n然后，我发现这里的有那种站在街上给店铺宣传营销的女生。因为，我在京都的时候没有看到过，以为就是关西是没有的。可能这种就关东才有。 结果，应该是京都没有大阪这些发达，而且京都比较传统导致的。\n最后，因为中午也没有吃什么。所以必须尽快找个东西来迟了，问了一下AI，最后推荐吃这个Madras Curry (マドラスカレー)。 这个的量真的很大，肯定吃得饱。我没有拍照，但是如果要来吃，记住一般点份小份的都可以了的，真的吃得饱的。 我吃一半的时候，有个人来点份中份的，吃完看到他将咖喱也全部吃完，刨了个净干，就觉得震惊🤯\n最后，我就到处转悠，发现大阪真的和京都很大不一样，大阪的晚上也很多人，基本不会像京都那样这么少人的。\n最后，我就回酒店休息了。感觉这个酒店和京都比，发现这里的空间比京都的那个第一酒店大一点。但是感觉这个设施的崭新程度的话，还是京都的那个新一点。\nDay 8（2026年4月26日） # 这天的行程主要就是前往大阪大学去参观。\n酒店早餐 # 酒店的早餐都是一些小面包和饭团🍙，还有咖啡☕️，酸酸的一个橙色饮料和一个绿色的不知道是不是蔬菜饮料，还有味增汤。感觉这个酒店的早餐挺好吃的，真的挺推荐如果来大阪住的话就住东横inn的这个酒店。\n大阪大学 # 在去大阪的地铁上，这个地铁真的又差点绕晕我。在等电车的时候，又在后悔是不是应该奈良那天圣地巡礼那个豊郷町立豊郷小学校旧校舎群了，真的感觉好后悔😣😣\n后面，也是来到了大阪大学了。这个大学感觉就是在郊区，有点像我们的大学城。不过这一个学校的占地可能就是相当于大学城两个大学了。 来这个大学前，还会经过一段路，这路在真的很有日漫的感觉啊!\n大阪大学门口：\n公告栏🪧:\nご案内:\n景色和学生运动：\n最后，逛完大阪大学之后，本来是想去万博纪念公园看看那个太陽の塔的。 但是，我从大阪大学出来后，兜兜转转居然还没有找到路去这个万博纪念公园的入口。 并且好像看到这个是只支持本地人的(不确定)，并且还要收费，就索性不去了。\n最后，我就坐地铁打算去梅田还是哪里了。然后，坐地铁遇到一位感觉是大学生的：\n梅田 # 然后，看到这里好像有个日本女子偶像团体Lumi Cherie (ルミシェリー)在举行チェキ 拍立得互动，而且还有很多人。\n然后，我就在附近找了家鰻の成瀬吃了一顿鳗鱼饭，毕竟我老爸也要求我尝尝日本的鳗鱼饭了。\n吃完之后，就发现附近有个结缘的神社⛩️：\n在这里附近溜达完之后，我就前往梅田スカイビル 空中庭園展望台了。\n来的路上看到：\n去到那里之后，发现人也不是很多，并且还真看到印度佬了。\n因为这个我上去拍也只能拿手机拍，拍不出什么东西来的，而且上去也要钱，所以就没有上去了。 最后，我就在这坐一下就去MARUZEN \u0026amp; ジュンク堂書店 梅田店这个书店看看了。\n这个书店真的大，各种书籍都有，不管是杂志，小说，名著还是辅导书以及专业书籍都有。\n我看那个计算机专区的书籍，居然还有如何使用copilot, gpt这样的书籍，真的感觉有点抽象，不过考虑到有一些老年人什么的也能理解。 不过我没有想到，RUST学些圣经，Linux EBPF这些书籍都有，就是O\u0026rsquo;REILLY的书籍很多，感觉日本对于书籍的出版还是很重视的。\n而且，这个书店在最顶楼还有骏河屋，我属实没有想到。\n最后，就回到酒店休息并且又在便利店买东西来迟了。\n这个牛奶布丁🍮真的震惊到我了，日本的细节真的做到很到位。这个是不沾盖子的，所以我们就不用舔盖子了。 还有这个味道属实惊艳，真的太好吃了，真的有点后悔为什么没有从第一天吃到最后一天😔😔\nDay 9（2026年4月27日） # 今天就是启程回家的日子了。早上，继续在酒店吃了早餐，然后办理check out手续和寄存行李之后。 因为，我个人觉得大阪是没有什么好的地方逛的，除了买买买之外。所以我就打算就继续在附近的大阪城公园的外围绕一圈看看了。\n还是路途中的NHK大楼：\n附近的一个旧派建筑：\n附近的草地上幼稚园的老师带小朋友们玩耍：\n居然这个大阪城公园和一个高校有关系的吗？\n随手拍景色：\n最后，因为没有什么好逛的，我又回到了酒店附近的公园里坐着浪费我在日本的最后时光了。 其实，我这天也还在懊悔之前为什么不去豊郷町立豊郷小学校旧校舎群实现我的kon圣地巡礼，后悔😭😭 然后，我打算即使是四个小时，如果可以我也要今天去的。但是，一看推特的，人家今天星期一休息的。而且，昨天还是一个节日祭典。 早知道这样，我会这样后悔，我就昨天去kon圣地巡礼，今天去大阪大学进行一个参观了，又刚好周一大阪大学也多人。\n最后，我就在公园和酒店附近兜兜转转了很久，看一下这里的街道和有什么好吃的。最后，犹犹豫豫就决定在ごんべ吃了：\n这一家也是一位老夫妇开的，不过这对老夫妇已经上年纪了。我进去之后，发现都没有人在欢迎，我就有点怕了，到底是要怎么坐和点餐的。然后我就在门口的一个位置坐了下来，然后她问我吃什么。我就说吃个亲子盖饭。最后，她就端着茶水给我喝了。\n在等待上菜的时候，我看了看Google Map的评价，发现也是有个评价是说：没有欢迎，而且亲子盖饭的量很少，鸡肉才有几块。 不过，大多评价都是好评，说这里安静，气氛好，不用顾及等。\n上了饭菜之后，我一看确实亲子盖饭的鸡肉太少了吧，才四块，虽然这个鸡蛋挺好吃的，但是我800日元吃这个感觉还是有点不值。\n吃完饭之后，又回到酒店附近的公园坐着休息了。然后在一点左右，我就看到居然小学的他们已经放学了，我虽然知道会早放学，但也太早了吧。\n然后，我在这不久又看到三个小孩，两男一女在这拿着水枪玩耍。他们居然到处射的时候，射到了我。我一开始还以为是日本小孩，就不跟他们计较了。结果，后面听到说的是中文，我就蚌埠住了。\n可惜，还是没有和他们搭讪一下，问一下他们的生活，学校什么的。\n最后，又去别的公园逛了一下：\n最后，就在这逛了一下，然后就回酒店拿行李，前往机场了。\n但是，在我到达天王寺想要坐JR Haruka机场快线的时候，发现我携程买的这个指定票的座位没了，我只能去自由席去看看有没有座位，不然就站着。\n我怕到时去到机场不熟悉流程什么的，我就上了，不等下一趟了。不过，还好我这个价位不是亏的，就算没坐上座位。 上车之后，不久就来了乘务员和六七个外国人在交谈，不知道为什么她们要补车票钱，就在这讲了大概有三十多分钟了。 我看到车上还有两位中国人也是去机场，我就看到一位戴白色口罩的美女，真的可惜没有拍照，后面去到机场才发现她是坐春秋回上海的，真是有点可惜。\n到了上飞机的时候了：\nDay 10（2026年4月28日） # 过了三个多小时，我也在十二点多到达了香港机场了。然后，就在这里又度过一晚，真的难忘今宵了，根本难以入睡，很不舒服。\n如果你回到香港机场没有行李要拿的话，可以先不出关这么快(有行李的话，你如果不怕被拿走也可以不出去这么快)，然后就在下飞机走不久就会看到一个淋浴间，可以在这里洗澡。洗完澡也可以直接在这里休息，比外面的环境要好很多的。 到了早上五点多接近六点的时候，我就按照凌晨在小红书看到的攻略，到相应的公交车牌等公交车了。因为，我是没有流量的，而且时间也快到了，我之前看的那个攻略还是有点不确定下车的地方，所以我就向旁边一位人搭讪了。\n结果，刚好这个人也是会广州的，不过他是回到深圳湾口岸，按照之前的打算我是打算回到福田口岸然后坐高铁回学校的。但是，这个人挺友善的，我就决定跟着他一起回去了。 我在公交车上的时候，和他交谈了一下，然后打算眯一下眼的时候，发现我们刚好坐过一站，我们只能变更策略，去坐别的公交车回去了。\n在等公交车的时候，在旁边的一个写着有宠物厕所的公园： 香港的气候和广州真的差不多了，又闷又热，真的让人难受。\n最后，我们也是坐上了大巴回广州，大巴依然还是永东。\n恶心的事，我在回到广州的时候，在黄浦区这里塞住了，我真的是刚回到国内，又累知道这一幕真的难蚌。 毕竟，今天不是节假日，当前又不是下班的时候，居然还能塞车，我真的是难蚌。\n结语 # 最后，我也是在中午一点多回到了宿舍。至此，这次的关西之旅就结束了。\n虽然，这次的关西之旅有很多遗憾，毕竟我错过了4.29的TRUE在京都站的演唱活动和宇治・太陽が丘公園的sunfes活动，真的是可惜啊😭😭😭\n它们的公告都是分别4.13和3.31的时候发的，我那时候不管这么样，酒店和机票都买了啊，如果那个宇治・太陽が丘公園的sunfes活动的公告早发一点，我的机票就晚一点就又可以看到TRUE在京都站的演唱活动了，真的是太可惜了😭😭😭\n战利品：\n","date":"May 2, 2026","externalUrl":null,"permalink":"/life/travel/kansai-trip/","section":"生活","summary":"关西游记⛺️ # 前言 # 根据之前做的理论准备关西游记攻略和关西游记实施攻略。2026年4月18日 我从广州坐大巴前往香港国际机场，然后准备明天的第一次飞机旅程。准备正式开始来到关西,游览了大阪、京都、奈良等地,体验了当地的文化、美食和风景。\n","title":"关西游记⛺️","type":"life"},{"content":"","date":"May 2, 2026","externalUrl":null,"permalink":"/categories/%E6%97%85%E8%A1%8C/","section":"Categories","summary":"","title":"旅行","type":"categories"},{"content":"","date":"May 2, 2026","externalUrl":null,"permalink":"/life/travel/","section":"生活","summary":"","title":"旅行","type":"life"},{"content":"","date":"May 2, 2026","externalUrl":null,"permalink":"/tags/%E6%97%A5%E6%9C%AC/","section":"Tags","summary":"","title":"日本","type":"tags"},{"content":"","date":"May 2, 2026","externalUrl":null,"permalink":"/tags/%E6%97%A5%E6%9C%AC%E6%97%85%E8%A1%8C/","section":"Tags","summary":"","title":"日本旅行","type":"tags"},{"content":" 🎌 关西 9 天 8 夜圣地巡礼与文化巡游终极手册 # 📌 行前与抵达核心备忘录 # 交通卡充值： 落地前在 Apple Wallet 里给 ICOCA 充值到 15,000 日元（使用 Mastercard/Amex/JCB）。 HARUKA 提票： 关西机场 JR 站绿色售票机 ➔ 选繁体中文 ➔ 扫 QR 码 ➔ 扫护照原件 ➔ 拿纸质票进站。 电影首映划座（核心！）： 4月21日/22日午夜0:00，准时在 MOVIX 京都官网选 4.24 的首映座位。支付时选「ムビチケ」，输入实体卡背面的 10位购买番号和 4位密码。实体卡千万自己保留，现场只需用手机里的二维码在取票机扫码出票！ 免税原则： * “一般物品”（衣服、书、手机、风铃、明信片）满 5000 日元免税，可当场拆封使用。 “消耗品”（药妆、食品）免税后绝对不可拆封。 伴手礼大头全留到关西机场安检后买，不占行李额，直接免税。 Tips 终极免税与避坑指南 (Tips)\n1. 两种免税模式对比 # 模式 操作流程 常见场所 优缺点/注意事项 直接免税 (首选) 结账时店员直接扫护照，扣除 10% 消费税。你直接付尾款。 Uniqlo, GU, 大国药妆, 各种药妆店, Yodobashi 最省心，无额外费用。 售后退税 先付含税全额，然后拿着小票和物品去商场的“免税柜台 (Tax Free Counter)”排队领现金或退到卡里。 高岛屋, 大丸, 伊势丹等大型百货商场 商场会收取约 1.55% 的手续费，实际到手退税约 8.45%。人多时可能要排长队。 2. 免税核心门槛 (千万记住) # 金额： 单人在同一家店铺、同一天内消费金额必须满 5,000 日元（不含税）。 证件： 必须携带护照原件！店员要扫上面的入境贴纸。复印件、照片绝对不行。 3. “一般物品” vs “消耗品” 的致命区别 # 一般物品（衣服、轻小说、二手 iPhone、电子产品）： 结算免税后，可以直接拆封使用。买完衣服立马就能穿。 消耗品（药妆、零食、饮料）： 结算免税后，店员会将其装入印有免税字样的透明密封袋中。在日本境内绝对不能拆封！ 一旦拆封，出境查验时会被要求补缴 10% 消费税。 4. 二手 iPhone 避坑特记 # 日本销售的 iPhone 无论是否静音，拍照必定有快门声（防偷拍法律规定）。买之前要有这个心理预期，或者后续在国内使用特定方法规避。 确认机身无严重磕碰，屏幕边缘无老化发黄。 📅 逐日精细行程与实操指南 # Day 1 (4.19)：降落京都，安顿与装备升级 # 08:00 - 12:45： 香港起飞，抵达关西国际机场 (KIX)。 13:30 - 14:30（换票与乘车）： 跟着“铁道”指示牌来到 JR 关西机场站。 跟着这个链接 可以学习扫码进站 主要就是看红色闸机 16:00 入住： 京都第一ホテル (京都第一酒店) ➔ 放下行李，轻装上阵。 17:30 装备采购： * 京都アバンティ (Kyoto Avanti) ➔ 逛 GU 和 Workman。 * イオンモールKYOTO (永旺梦乐城 KYOTO) ➔ 去 Uniqlo 买 U 系列白 T 恤；去大垣书店买几本轻小说，结账时说 Cover onegaishimasu 拿书皮。 19:00 晚餐： 東洋亭 京都ポルタ店 (东洋亭 百年洋食) ➔ 必点招牌锡纸包汉堡排套餐。 20:30 日用品补给： 酒店附近的 ダイコクドラッグ (大国药妆) 买齐 狮王 PAIR Acne 祛痘膏 (ペアアクネクリームW) 等消耗品。 Day 2 (4.20)：叡山电车北线 (自然与日常番巡礼) # 警告 记得去到Movix 京都官网去选座: 优先 E10 or F10，选 4.24 的首映场次！支付时选「ムビチケ」，输入实体卡背面的 10 位购买番号和 4 位密码。实体卡千万自己保留，现场只需用手机里的二维码在取票机扫码出票！\n08:30 清晨森系： 貴船神社 (Kifune Jinja) ➔ 拍晨光中的红灯笼。 11:30 春季新绿： 瑠璃光院 (Rurikō-in)。 13:00 极客午餐： 麺屋 極鶏 (Men-ya Gokkei) 或 天天有 本店 (Tentenyu)。 14:30 🎐 文创采购： 恵文社一乗寺店 (Keibunsha) ➔ 在这家全球最美书店挑几张独立画师明信片和精美书签。 15:30 圣地巡礼： 鴨川デルタ (鸭川三角洲) ➔ K-ON! 跳石头。 出町桝形商店街 (Demachi Masugata Shotengai) ➔ 玉子市场原型。 必吃点心：出町ふたば (出町双叶 豆大福)。 18:00 晚餐： 鳥貴族 京都駅前店 (Torikizoku) ➔ 全场统一价居酒屋。 Day 3 (4.21)：京吹重头戏 (宇治深度巡礼) # 笔记 💡 联动任务： 在京阪车站购买“京阪電車×響け！ユーフォニアム2026春 1日乘车券”（1700日元）。全程开启手机 GPS，进入官方特设网页边走边收集数字印章！\n08:00 避开人流： 伏見稲荷大社 (Fushimi Inari Taisha)。 11:30 抵达宇治与午餐： 中村藤吉 本店 (Nakamura Tokichi) ➔ 抹茶荞麦面+芭菲。顺便买几包高级煎茶/抹茶粉送长辈。 14:00 - 17:00 吹奏部巡礼： 购买周边：宇治市観光センター (宇治市观光中心) ➔ 拿纸本“舞台探访MAP”并购买《京吹》限定明信片。 核心场景：宇治橋 (Uji Bridge) ➔ 宇治上神社 (Ujigami Shrine) ➔ 久美子ベンチ (久美子椅)。 17:30 黄昏打卡： 大吉山展望台 (Mt. Daikichi Observatory) ➔ 看日落名场面。 Tips 有什么觉得不够的，遗漏的就看手机上保存过的宇治有网图\n19:30 晚餐： 京都勝牛 京都駅前店 (Kyoto Katsugyu) ➔ 酥炸牛排。 Day 4 (4.22)：奈良千年古都 (文化遗产与境界的彼方) # 09:30 抵达奈良： 近鉄奈良駅 (Kintetsu Nara Station)。 10:00 圣地打卡： 猿沢池 (Sarusawa-ike) ➔ 栗山未来实景地。 10:30 世界遗产： 東大寺 (Todai-ji)。 12:00 特色午餐： 志津香 釜めし 公園店 (Shizuka Kamameshi) ➔ 奈良名物釜饭。 14:00 小鹿与神道： 奈良公園 (Nara Park) ➔ 春日大社 (Kasuga Taisha)。在春日大社求一个学业成就御守，祈愿7月的日语考试以及未来的修士入学考试顺利通过。 16:00 古街漫步： ならまち (Naramachi 奈良町) ➔ 逛逛改建的咖啡馆。 17:30 必吃点心： 中谷堂 (Nakatanidou 艾草麻薯)。 19:00 返回京都车站 ➔ ✨ 限定活动： ニデック京都タワー (京都塔) ➔ 展望室正在举办京吹联动，去拍角色等身大立牌，完成叠色印章。 Day 5 (4.23)：岚山漫步 (《春物》与风景) # 09:00 晨间漫步： 嵐山 竹林の小径 (Arashiyama Bamboo Forest) ➔ 天龍寺 (Tenryu-ji)。 11:00 🎐 寻找风铃： 嵐山 昇龍苑 (Arashiyama Syoryuen) ➔ 挑选一只声音清脆的金属风铃或清水烧风铃。注意：风铃随身带上飞机，切勿托运！ 12:00 豆腐料理： 嵯峨とうふ 稲 (Saga Tofu Ine)。 14:00 大老师打卡： 渡月橋 (Togetsukyo Bridge)。 Day 6 (4.24)：京都市内纵贯线 (剧场版之日) # 💡 票券提示：这一天在地铁站购入“地下铁一日券 (800 日元)”\n08:30 🚨 影院物贩抢夺战： 携带一日券直接冲去 MOVIX京都 (MOVIX Kyoto 电影院) 的周边贩卖区，抢购《最终乐章》场刊与限定周边！买完后在取票机扫手机二维码打出晚上的纸质电影票。 10:00 霙与希美： 京都水族館 (Kyoto Aquarium)。 12:00 ✨ 限定午餐： ホテル京阪 京都八条口 レストラン (京吹联动咖啡厅) ➔ 点一杯「久美子のレッドユーフォニアム」饮品，在等身大挂画包围下沉浸式就餐。 14:00 纵贯线： 京都御所 (Kyoto Imperial Palace) ➔ 京都大学 (Kyoto University) ➔ 禅林寺 永観堂 (Eikando Zenrin-ji)。 18:00 快速晚餐： 餃子の王将 三条店 (Gyoza no Ohsho) ➔ 吃一份热气腾腾的煎饺和天津饭。 18:45 影院补给： 返回 MOVIX京都 柜台购买附带原创提袋和迷你文件夹的京吹限定爆米花套餐。 19:00 终极浪漫： 带着爆米花和电影票进场，观赏《響け！ユーフォニアム》剧场版！ Day 7 (4.25)：转移大阪，拥抱天守阁 # 09:30 🎁 京都收尾采买： 在京都站内购买几盒生八桥 (生八ツ橋)。(保质期短，这时候买正好可以带回家分给朋友)。 10:30 退房转移： 入住 東横INN大阪谷四交差点 (Toyoko INN Osaka Tanimachi)。 13:00 历史名城： 大阪城 (Osaka Castle)。 14:00 咖喱乌冬： 得正 谷町店 (Tokumasa Curry Udon)。 18:00 繁华夜景： 道頓堀 (Dotonbori)。 19:30 大阪烧： 美津の (Mizuno)。 Day 8 (4.26)：硬核淘货与复古大阪 # 10:00 极客时间： 日本橋でんでんタウン (Nipponbashi Den Den Town)。 手机淘货：イオシス 日本橋店 (Iosis) / じゃんぱら (Janpara) ➔ 确认 SIM Free 与 Battery Health。 书籍淘货：周边的书店可以翻阅一下操作系统、内核技术、Rust 语言或 C 语言相关的底层日文图解书籍。 13:00 暴力回血： 天地人 日本橋店 (Tenchijin 炭火豚丼)。 17:00 昭和风情： 新世界 / 通天閣 (Shinsekai / Tsutenkaku)。 18:30 串炸晚餐： 八重勝 (Yaekatsu) ➔ 大阪地道串炸，酱汁严禁蘸第二次。 Day 9 (4.27)：终极扫货与满载返程 # 13:00 机场前哨： 退房，前往 りんくうプレミアム・アウトレット (Rinku Premium Outlets 临空城) 做最后闲逛。 16:00 抵达机场： 関西国際空港 (Kansai International Airport - KIX) ➔ 第一件事：办值机，把沉重的行李全部托运！ 17:00 🎁 终极伴手礼狂欢（免税区）： 过完安检后，在里面的 KIX Duty Free 免税店大肆采购。 必买清单：Royce\u0026rsquo; 生巧克力（加购冰袋）、白色恋人、神户风月堂法兰酥。 结账后直接提着免税购物袋登机，轻松完美收官。 *20:00 满载而归，起飞返航！ ","date":"April 16, 2026","externalUrl":null,"permalink":"/life/travel/kansai-trip-implementation-guide/","section":"生活","summary":"🎌 关西 9 天 8 夜圣地巡礼与文化巡游终极手册 # 📌 行前与抵达核心备忘录 # 交通卡充值： 落地前在 Apple Wallet 里给 ICOCA 充值到 15,000 日元（使用 Mastercard/Amex/JCB）。 HARUKA 提票： 关西机场 JR 站绿色售票机 ➔ 选繁体中文 ➔ 扫 QR 码 ➔ 扫护照原件 ➔ 拿纸质票进站。 电影首映划座（核心！）： 4月21日/22日午夜0:00，准时在 MOVIX 京都官网选 4.24 的首映座位。支付时选「ムビチケ」，输入实体卡背面的 10位购买番号和 4位密码。实体卡千万自己保留，现场只需用手机里的二维码在取票机扫码出票！ 免税原则： * “一般物品”（衣服、书、手机、风铃、明信片）满 5000 日元免税，可当场拆封使用。 “消耗品”（药妆、食品）免税后绝对不可拆封。 伴手礼大头全留到关西机场安检后买，不占行李额，直接免税。 Tips 终极免税与避坑指南 (Tips)\n","title":"关西之旅实施攻略","type":"life"},{"content":" 警告 已经决定 4.18 到机场, 4.19 飞去日本这样\n准备工作 # 签证 or 其他准备手续 # 去淘宝找人办理签证 (也就一两百块钱)\n要准备好电子版签证，去程值机要扫码验证\nvjw (Visit Japan Web) 要提前填写好，好快速出关\n教程🔗 VJW网站 机票 (提前三个月看即可) # 目的地: 关西 (关西国际机场)\n选择先坐高铁到达可以直飞并且费用没有这么贵的航空点 (这样一般不是在广州或者深圳起飞) 提前看好，买好票，价格差不多的就直飞 (一般能够在广深起飞) 也可以去香港然后再飞日本, 到时看情况 提示 香港出发的话 考虑一下 香港快速航空 和 乐桃航空, 不过注意行李这些要知道就是是可能会额外收费, 所以选机票的时候要看清楚\n提示 尽量选择早上航班，这样可以下午到达关西，有助于行程安排，不匆匆忙忙\n使用 天巡 和 东航 或者 携程 这种看看机票价钱即可\n住宿 # 警告 保留意见 (毕竟不同时间段会价格不一样) 京都选择这两个:==京都站前卓越酒店==和==樱花台画室饭店==/==京都乌丸五条酒店==\n笔记 京都日本第一酒店 (Kyoto Daiichi Hotel) 已经预订好住六天了\n可以 3+3 天安排\n可能要问一下学长转酒店这些是否能够提前寄存行李 (就是在下午入住前), 怕要提着行李到处走\n樱花台画室饭店\n小红书🍠 # 🏨在哪预定： 价格最低平台➡️booking.com\u0026lt;Agoda\u0026lt;Google map\u0026lt;美团\u0026lt;携程\u0026lt;爱彼迎。\n提示 一定要提前很长时间预定，否则没有房间还有价格偏高\n大阪难波道顿堀阿克罗胶囊旅馆1 (Acro Capsule Hotel Namba Dotonbori) 大阪东急 REI 酒店 (SHIN-OSAKA ESAKA TOKYU REI HOTEL) 京都—Wise Owl Hosteks Kyoto (京都明智猫头鹰青年旅馆) 京都日本第一酒店 (Kyoto Daiichi Hotel) 已经预订好住六天了 京都乌丸五条口袋酒店 (THE POCKET HOTEL Kyoto-Karasumagojo - Private Rooms) 京都-櫻花台畫室飯店 (Sakura Terrace The Gallery -Adult Only) 京都四条鸟丸索特图斯弗雷撒酒店 (Sotetsu Fresa Inn Kyoto-Shijokarasuma) 京都站八条口卓越酒店(Hotel Excellence Kyoto Station) ==住宿时间是多长根据到时讨论商量分别在这个地标居住多久而定==\n交通 # 最好就是问一下学长，地下铁是?\n摘抄自一位学长的攻略 # 携程上购买了 haruka 从关西——京都 (用于机场到京都)(提前 2-2.5 个月预订) 携程说明 在京都站旅客案内所买了两张京都一日地下铁（1100 日元，第一天和去三千院/贵船用）不坐巴士买一日地下铁券会便宜一点（800 日元） 地下铁 地下铁说明 购买西瓜卡 西瓜卡说明 个人出行的话建议试试 LUUP 的共享电车，真的蛮方便，护照和学习答题就好，不过界面是英文的 LUUP🔗 电话卡/流量卡 # 淘宝直接购买 softbank 流量卡 另一张卡开启国际漫游实现接受短信这样 (开启无忧行就可以使用流量这样，看个人选择) 下个无忧行 app 提示 建议先只插这一张卡，重启手机，成功搜索到信号后，再把自己的卡插为卡 2（禁止漫游联网），重启手机。 这样这个卡走流量，自己的卡可以收验证码短信等等。\n游玩路线 # 提示 记得买当地特色产品 (伴手礼)\nkon圣地巡礼\n琉璃光院(以最新消息为准)\n春季特别访问：2025 年 4 月 15 日（星期二）至 5 月 31 日（星期六） 夏季特别参观：2025 年 7 月 1 日（星期二）至 8 月 17 日（星期日） 秋季特别参观：2025 年 10 月 1 日星期三至 2025 年 12 月 14 日星期日 每个时段的参观时间为 10:00 至 17:00（最后入场时间为 16:30）。\n如何购买门票\n在春季和夏季的特殊观赏期，您无需预约即可亲自参观。\n关于秋季特别观赏的预约，详细信息将在决定后立即在官方网站上公布。\n入场费\n成人：2,000 日元 儿童（初中生以上，持学生证）：1,000日元 小学生及学龄前儿童：免费 DAY 0 (上海飞) 2026.04.18 # 提示 因为直飞都比较贵，所以我直接根据学长的攻略，先提前一天飞到上海玩一天，然后明天再坐飞机到关西国际机场 (可以找个有送机的酒店，在去哪儿找都行)\n广州/深圳\u0026ndash;\u0026gt;上海 # 可以简单游玩一下，先基本定在 唯庭轻居酒店(上海虹桥机场国家会展中心店)\n看到时是深圳飞上海便宜还是广州飞便宜 (记得提前一个月多看机票)\n落地先去酒店办理入住，放好东西就玩\n笔记 记得提前预约接送机，告诉行李，航班号，人数时间什么的\n上海\u0026ndash;\u0026gt;关西国际机场 (Day 1) 2026.04.19 # 提示 选择浦东机场🛫\n因为是早上飞机，所以我们是预定接近中午到达 关西国际机场\n到达 关西国际机场之后, 用提前买好的 携程haruka车票 在小绿屋人工兑换或者在绿色机器上兑换 (建议直接人工或者哪里人少去哪里)\n到达 京都站 之后就每个人买两张地下铁 (若决定在京都玩的时间较久可以买三张?!)\n大概是==下午两点半左右==可以到达酒店 (住在京都第一酒店\u0026lt;贵就住在京都乌丸五条先\u0026gt;)，就办理入住这些，就可以寄存行李就可以到处闲逛了\n办理好入住手续之后就先去吃个饭，然后就附近闲逛，看情况是否需要买伞 (雨 or 雪)\nDAY 0 (香港飞) # 提示 香港直飞关西国际机场相对方便，也可以考虑价格，如果直飞便宜就直接飞，否则可以结合广州/深圳方案。\n广州/深圳\u0026ndash;\u0026gt;香港 # 如果你人在广州或深圳，可以先搭高铁或者城际去香港机场（建议提前1–2小时出关），留足过关时间。 提前在 google travel / 携程 / Skyscanner 比价机票，看香港飞关西国际机场哪班更便宜。 建议选择上午或中午航班，这样到达关西后还有时间安排行程。 香港\u0026ndash;\u0026gt;关西国际机场 (Day 1) 2026.04.19 # 抵达香港机场\n办理登机手续（注意护照和港澳通行证/签证） 托运行李（如果有） 飞往关西国际机场\n建议直飞航班（约4小时左右） 如果是廉航，记得提前打印登机牌或下载电子登机牌 到达关西国际机场后\n使用提前购买的 携程Haruka车票 可选择 人工兑换窗口 或 自助绿色机器（人少的地方优先） 乘车前往 京都站（大约1小时15分钟–1小时30分钟车程） 到达京都站后\n每人购买2–3张地下铁票（根据停留时间和游玩计划决定） 前往 京都第一酒店 办理入住并寄存行李 根据时间和体力，可以先去附近吃饭，然后闲逛 若天气不佳（雨或雪），可以顺便购买雨伞或小件防寒物品 笔记 可能是会在机场过夜也说不定\n京都游玩 # 京阿尼实体店铺详情\n京阿尼实体店铺地址\n吃完饭之后就可以看看时间 (ASTY Kyoto 还营不营业，基本这里都有很多商店，可以到处逛逛), 还营业就去看京阿尼实体店，京吹我来了😋, 之后就是买买买了\n然后可以去京都塔逛逛了 (或者夜逛), 先吃了饭再去, 最后再通过附近的京都站乘坐地下铁到达这里附近的鸭川 (不是玉子市场的那里附近)\n最后，就是回酒店好好休息，准备好明天的游玩\nDAY2 2026.04.20 # 酒店\u0026ndash;\u0026gt;贵船神社 # 贵船神社\u0026ndash;\u0026gt;琉璃光院 # 琉璃光院\u0026ndash;\u0026gt;三千院 # 可以在途中到处逛逛\nDAY3 2026.04.21 # 警告 换酒店 (如果不可以先寄存行李的话)\n酒店\u0026ndash;\u0026gt;伏见稻荷神社 # 伏见稻荷神社\u0026ndash;\u0026gt;宇治 # 宇治市观光中心 平等院 宇治神社，宇治上神社 大吉山 宇治桥 久美子椅 莬兔高中看情况去吧 警告 记得买抹茶这些以及吃抹茶小点心😋\nDAY4 2026.04.22 (看推特情况调顺序) # 酒店\u0026ndash;\u0026gt;丰乡小学 (kon) # 笔记 这个其实和 DAY3 或者 DAY5 甚至其他调换顺序，因为要看 kon 圣地巡礼的开放时间\nkon圣地巡礼开放时间\n警告 注意去的是丰乡小学 (旧校舍)\n其实丰乡这个地方没什么太过特别的，这样我们就选择到处逛逛，然后就坐轻轨/地铁回京都\n回到京都之后可以看时间和情况安排到处逛逛\nDAY5 2026.04.23 # 酒店\u0026ndash;\u0026gt;岚山 # 岚山有竹林小径 (大老师自爆点，空之境界) # 岚山\u0026ndash;\u0026gt;天龙寺 # 天龙寺\u0026ndash;\u0026gt;御发神社 # 闲逛 # 回酒店 # DAY6 2026.04.24 # 重要 響け！ユーフォニアム剧场版, 重头戏\n新风馆\n京都水族馆\n京都御所\n京阿尼玉子市场\n鸭川三角洲\n酒店\u0026ndash;\u0026gt;京都水族馆 # 基本一些店什么的都是 10 点开门，看时间安排\n在京都水族馆买票后，看个一两个小时就 run, 坐地铁去下一站\n现在一个早上基本就没了, 吃了饭在出发\n京都水族馆\u0026ndash;\u0026gt;新风馆 # 地铁出发，买一些 Kyoto 当地特色商品就行 (就是逛商店😃)\n新风馆\u0026ndash;\u0026gt;京都御所 # 走路或者地铁都可以，看情况，是想要看看当地街道就走路\n京都御所\u0026ndash;\u0026gt;玉子市场 # 走路过去就可以，就可以看商店街以及三角洲 (玉子掉下水的场景😄)\n鸭川三角洲\u0026ndash;\u0026gt;京都大学 # 走着过去就到了，然后逛逛京都大学，感受下氛围\n可能现在也就五点多的感觉\n京都大学\u0026ndash;\u0026gt;禅林寺 # 禅林寺\u0026ndash;\u0026gt;酒店 # 30分钟 的路程，到了酒店之后就找东西吃\n后面看情况要不要夜行逛逛\nDAY7 2026.04.25 # 在早上10:00前离开京都第一酒店\n京都\u0026ndash;\u0026gt;大阪 # 以下暂定 (因为对大阪不是很熟)\n大阪站\u0026ndash;\u0026gt;心斋桥 # 心斋桥\u0026ndash;\u0026gt;道顿堀 # 吃吃吃😋\n道顿堀\u0026ndash;\u0026gt;大阪城公园/神社 # 夜间瞎逛 (买买买) # 回酒店睡觉，明天到机场回国 # Day8 2026.04.26 # 乘坐飞机离开大阪,返回国内\n参考文献 # 关西游记\n金色标记为首选\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"February 12, 2026","externalUrl":null,"permalink":"/life/travel/kansai-trip-guide/","section":"生活","summary":" 警告 已经决定 4.18 到机场, 4.19 飞去日本这样\n","title":"关西之旅旅行攻略","type":"life"},{"content":" 联系方式 # GitHub: 我的 GitHub 博客: 我的博客 QQ: 2145256133 邮箱： nni461904@gmail.com ","date":"August 11, 2025","externalUrl":null,"permalink":"/about/","section":"響け！ユーフォニアム","summary":"联系方式 # GitHub: 我的 GitHub 博客: 我的博客 QQ: 2145256133 邮箱： nni461904@gmail.com ","title":"About","type":"about"},{"content":"","date":"August 10, 2025","externalUrl":null,"permalink":"/categories/alist/","section":"Categories","summary":"","title":"Alist","type":"categories"},{"content":"sudo mount -t glusterfs 192.168.1.108:/myvolume /mnt/gluster 关于 GlusterFS，这是一个分布式文件系统，常用命令主要围绕卷（volume）的管理和查看状态。下面我整理了一些常用的查看卷信息和管理卷的命令。\nGluster 常用命令（卷信息及管理） # 1. 查看卷信息 # 查看所有卷列表： gluster volume list 查看某个卷的详细信息： gluster volume info \u0026lt;卷名\u0026gt; 查看某个卷的状态： gluster volume status \u0026lt;卷名\u0026gt; 查看卷的配置信息（例如副本数、分布等）： gluster volume get \u0026lt;卷名\u0026gt; all 查看所有节点状态： gluster peer status 2. 卷的基本管理命令 # 创建卷： gluster volume create \u0026lt;卷名\u0026gt; \u0026lt;brick1\u0026gt; \u0026lt;brick2\u0026gt; ... 启动卷： gluster volume start \u0026lt;卷名\u0026gt; 停止卷： gluster volume stop \u0026lt;卷名\u0026gt; 删除卷： gluster volume delete \u0026lt;卷名\u0026gt; 3. 节点管理相关 # 查看当前集群中的节点： gluster peer status 移除一个节点： gluster peer detach \u0026lt;hostname|uuid\u0026gt; 添加一个节点（节点本身需先安装好 glusterfs）： gluster peer probe \u0026lt;hostname\u0026gt; 4. 查看卷使用情况和配额 # 查看配额信息： gluster volume quota \u0026lt;卷名\u0026gt; list 启用配额： gluster volume quota \u0026lt;卷名\u0026gt; enable 设置配额： gluster volume quota \u0026lt;卷名\u0026gt; limit-usage /path \u0026lt;size\u0026gt; 5. 查看日志位置 # Gluster 的日志一般在：\n/var/log/glusterfs/ 可以用 tail -f 来实时查看日志：\ntail -f /var/log/glusterfs/glusterd.log 6. 其他实用命令 # 查看卷挂载点： mount | grep glusterfs 查看卷的brick信息： gluster volume status \u0026lt;卷名\u0026gt; detail 一次情况的处理 # 一、检查 brick 挂载及状态相关命令 # 查看 volume 详细状态，包括 brick 状态：\nsudo gluster volume status myvolume detail 查看 volume 简单状态：\nsudo gluster volume status myvolume 查看 brick 对应磁盘设备信息（文件系统、UUID 等）：\nsudo blkid /dev/sdb1 查看挂载点是否挂载成功：\nmount | grep extended_gluster 手动挂载所有 fstab 中定义的设备：\nsudo mount -a 编辑 fstab 文件配置自动挂载：\nsudo nano /etc/fstab 二、处理 brick 离线与恢复 # 强制启动 volume（重启 brick 服务）：\nsudo gluster volume start myvolume force （慎用）强制移除并重新添加 brick：\nsudo gluster volume remove-brick myvolume ice:/mnt/extended_gluster/extended_brick force sudo gluster volume add-brick myvolume ice:/mnt/extended_gluster/extended_brick 三、管理 rebalance 任务 # 查看 rebalance 任务状态：\nsudo gluster volume rebalance myvolume status 启动 rebalance 任务：\nsudo gluster volume rebalance myvolume start 停止（清理）rebalance 任务：\nsudo gluster volume rebalance myvolume stop 实时监控 rebalance 状态（循环查询）：\nwatch -n 2 sudo gluster volume rebalance myvolume status 四、日志排查相关命令 # 查看 GlusterFS 进程日志（全局）：\nsudo less /var/log/glusterfs/glusterfsd.log 查看指定 brick 的日志：\nsudo less /var/log/glusterfs/bricks/extended_brick.log 过滤日志中 rebalance 相关内容：\nsudo grep rebalance /var/log/glusterfs/glusterfsd.log 五、其它实用命令 # 查看 volume 信息（类型，副本数等）：\nsudo gluster volume info myvolume 查看挂载目录文件分布情况（判断数据是否正常）：\nls -lh /mnt/extended_gluster/extended_brick 检查文件系统挂载情况和容量：\ndf -h | grep extended_gluster ","date":"August 10, 2025","externalUrl":null,"permalink":"/notes/tools/alist%E4%BB%A5%E5%8F%8Agluster/","section":"笔记","summary":"sudo mount -t glusterfs 192.168.1.108:/myvolume /mnt/gluster 关于 GlusterFS，这是一个分布式文件系统，常用命令主要围绕卷（volume）的管理和查看状态。下面我整理了一些常用的查看卷信息和管理卷的命令。\n","title":"alist以及gluster","type":"notes"},{"content":"","date":"August 10, 2025","externalUrl":null,"permalink":"/categories/gluster/","section":"Categories","summary":"","title":"Gluster","type":"categories"},{"content":"","date":"August 10, 2025","externalUrl":null,"permalink":"/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E5%AD%98%E5%82%A8/","section":"Tags","summary":"","title":"分布式存储","type":"tags"},{"content":"","date":"August 10, 2025","externalUrl":null,"permalink":"/tags/%E6%8C%82%E8%BD%BD/","section":"Tags","summary":"","title":"挂载","type":"tags"},{"content":"","date":"August 10, 2025","externalUrl":null,"permalink":"/tags/%E6%96%87%E4%BB%B6%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"文件管理","type":"tags"},{"content":"","date":"August 10, 2025","externalUrl":null,"permalink":"/tags/brick/","section":"Tags","summary":"","title":"Brick","type":"tags"},{"content":" 节点（Node） 节点指的是参与 GlusterFS 集群的物理或虚拟服务器主机。每个节点上可以运行 GlusterFS 服务，并提供存储资源。\nPeer（对等体） Peer 指的是集群中彼此认识并通信的节点。一个 GlusterFS 集群由多个 peer 组成，peer 之间通过网络互联，形成分布式存储系统。\n在加入集群时，需要执行 gluster peer probe \u0026lt;hostname\u0026gt; 将节点加入集群，建立 peer 关系。 Brick（砖块） Brick 是 GlusterFS 中最小的存储单元，实际是节点上的一个目录或磁盘分区，作为卷（volume）的基本构建块。\n每个 brick 代表一个目录路径，比如 /data/brick1。 一个节点上可以有多个 brick。 Volume（卷） Volume 是对外暴露的逻辑存储池，由一个或多个 brick 组成。它是客户端挂载和使用的存储资源。\nVolume 可以有多种类型，如分布式（distributed）、复制（replicated）、分布式复制（distributed replicated）、条带（striped）等。 Volume 通过将多个 brick 组合起来，实现数据分布、冗余备份和性能提升。 它们之间的关系总结 # 节点 是物理服务器，承载着 GlusterFS 服务和存储资源。 Peer 是节点之间建立的集群关系，节点通过 peer 连接组成集群。 Brick 是节点上的目录或磁盘分区，提供实际的数据存储空间。 Volume 是由多个 brick 组成的逻辑存储单元，供客户端挂载使用。 示意：\n[节点1] -- peer连接 -- [节点2] -- peer连接 -- [节点3] 节点1: brick1 (/data/brick1), brick2 (/data/brick2) 节点2: brick3 (/data/brick3) 节点3: brick4 (/data/brick4), brick5 (/data/brick5) VolumeA = brick1 + brick3 + brick4 （组成分布式卷） 客户端挂载 VolumeA，通过集群访问分布在各节点上的brick数据。 ","date":"August 10, 2025","externalUrl":null,"permalink":"/notes/tools/gluster%E6%A6%82%E5%BF%B5/","section":"笔记","summary":" 节点（Node） 节点指的是参与 GlusterFS 集群的物理或虚拟服务器主机。每个节点上可以运行 GlusterFS 服务，并提供存储资源。\nPeer（对等体） Peer 指的是集群中彼此认识并通信的节点。一个 GlusterFS 集群由多个 peer 组成，peer 之间通过网络互联，形成分布式存储系统。\n","title":"gluster概念","type":"notes"},{"content":"","date":"August 10, 2025","externalUrl":null,"permalink":"/tags/volume/","section":"Tags","summary":"","title":"Volume","type":"tags"},{"content":"","date":"August 10, 2025","externalUrl":null,"permalink":"/categories/%E5%88%86%E5%B8%83%E5%BC%8F%E5%AD%98%E5%82%A8/","section":"Categories","summary":"","title":"分布式存储","type":"categories"},{"content":"","date":"August 10, 2025","externalUrl":null,"permalink":"/tags/%E9%9B%86%E7%BE%A4/","section":"Tags","summary":"","title":"集群","type":"tags"},{"content":"","date":"June 13, 2025","externalUrl":null,"permalink":"/tags/ssh/","section":"Tags","summary":"","title":"SSH","type":"tags"},{"content":" 一、在客户端生成 SSH 密钥对 # 在你的本地客户端（例如你的笔记本或开发机器）上：\nssh-keygen -t rsa -b 4096 -C \u0026#34;your_email@example.com\u0026#34; 说明：\n-t rsa：生成 RSA 类型的密钥； -b 4096：密钥长度，建议至少 4096； -C：注释，通常填邮箱方便识别。 系统会提示你保存文件位置，默认是：\n~/.ssh/id_rsa (私钥) ~/.ssh/id_rsa.pub (公钥) 也可以自定义文件名，方便区分不同用途。\n二、将公钥复制到服务器 # 假设你的服务器 IP 是 192.168.1.108，用户名是 ice345：\n方法一（推荐）使用 ssh-copy-id 自动复制： # 提示 这个通常都会无法成功 copy 到服务器上\nssh-copy-id -i ~/.ssh/id_rsa.pub ice345@192.168.1.108 系统会提示输入服务器密码，输入一次即可自动把公钥复制到服务器的 ~/.ssh/authorized_keys 里，并设置好权限。\n方法二：手动复制 # 如果没有 ssh-copy-id：\n查看公钥内容： cat ~/.ssh/id_rsa.pub 登录服务器（使用密码）： ssh ice345@192.168.1.108 在服务器上创建 ~/.ssh/ 目录（如果还没有）： mkdir -p ~/.ssh chmod 700 ~/.ssh 编辑 authorized_keys 文件： nano ~/.ssh/authorized_keys 将你刚才 cat 出来的公钥粘贴进去，保存。\n修改权限： chmod 600 ~/.ssh/authorized_keys 三、测试免密登录 # 回到客户端，执行：\nssh ice345@192.168.1.108 如果一切顺利，不需要输入密码就能登录。\n四、服务器配置 SSH 服务 # 在服务器上编辑 sshd 配置文件：\nsudo nano /etc/ssh/sshd_config 重点配置项：\n# 允许公钥认证（默认通常是yes） PubkeyAuthentication yes # 允许密码登录（如不想允许可以关闭） PasswordAuthentication yes # 或 no # 允许使用 authorized_keys 文件 AuthorizedKeysFile .ssh/authorized_keys # 禁止 root 直接登录（安全建议） PermitRootLogin no 修改后，重启 ssh 服务：\nsudo systemctl restart sshd 五、额外注意事项 # 如果你希望完全关闭密码登录，保证只允许密钥登录，可以把 PasswordAuthentication 改为 no。 确认目录权限正确： 目录/文件 权限 ~/.ssh/ 700 ~/.ssh/authorized_keys 600 防火墙确认开放 22 端口。 六、完整快速版流程总结 # # 1. 客户端生成密钥 ssh-keygen -t rsa -b 4096 # 2. 复制公钥到服务器 ssh-copy-id -i ~/.ssh/id_rsa.pub user@server_ip # 3. 测试免密登录 ssh user@server_ip # 4. 修改服务器 /etc/ssh/sshd_config（可选） sudo nano /etc/ssh/sshd_config # 确认或修改： PubkeyAuthentication yes PasswordAuthentication yes (或 no) # 5. 重启 SSH 服务 sudo systemctl restart sshd ","date":"June 13, 2025","externalUrl":null,"permalink":"/notes/tools/ssh%E7%AE%80%E5%8D%95%E9%85%8D%E7%BD%AE/","section":"笔记","summary":"一、在客户端生成 SSH 密钥对 # 在你的本地客户端（例如你的笔记本或开发机器）上：\n","title":"SSH简单配置","type":"notes"},{"content":"","date":"June 13, 2025","externalUrl":null,"permalink":"/tags/%E5%AF%86%E9%92%A5%E8%AE%A4%E8%AF%81/","section":"Tags","summary":"","title":"密钥认证","type":"tags"},{"content":"","date":"June 13, 2025","externalUrl":null,"permalink":"/categories/%E6%B7%B1%E5%85%A5%E5%AD%A6%E4%B9%A0/","section":"Categories","summary":"","title":"深入学习","type":"categories"},{"content":"","date":"June 13, 2025","externalUrl":null,"permalink":"/tags/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/","section":"Tags","summary":"","title":"网络安全","type":"tags"},{"content":"","date":"June 13, 2025","externalUrl":null,"permalink":"/tags/%E8%BF%9C%E7%A8%8B%E8%BF%9E%E6%8E%A5/","section":"Tags","summary":"","title":"远程连接","type":"tags"},{"content":" 问题描述 # 因为通过上一节的描述, 我们可以通过 cloudflare tunnel 和 alist 结合实现加速访问以及稳定加载, 但是这又导致了一个问题:alist 上传出现了问题.\n经过分析, 有以下原因:\ncloudflare 的 tunnel 的免费服务无法支持不稳定的连接和长时间的连接支持 家里的服务器因为被墙, 所以无法稳定连接 (因为 cloudflare 是在国外) 解决办法 # 所以, 我们想有一种办法能够同时解决这两种问题: 不稳定加载和访问 和 稳定上传 这是无法免费的解决办法可以解决的.\n因此, 我们只好设置两个口来访问 alist 就行.\n通过上一节的办法为 alistip.050626.xyz 再申请一个域名, 然后配置在 /opt/alist/data/config.json 中即可, 然后设置 cloudflare 的 DNS 记录\n这样就可以有域名访问 alist 了, 这个就是直接通过服务器的 ipv6 链路直连的, 稳定性相比配置 cloudflare 的 tunnel 稳定不少\n提示 /opt/alist/data/config.json 里的 force_https 这里仍然是 false, 不然 cloudflare 的 tunnel 绑定的域名会发生问题, 无法访问\n所以, 现在我们就会有两个域名来访问 alist\n但是这两种访问的用途就不一样.\nalist.050626.xyz 就是用来提升下载和访问速度的 alistip.050626.xyz 就是用来上传的 ","date":"June 11, 2025","externalUrl":null,"permalink":"/notes/tools/alist%E9%85%8D%E7%BD%AE%E5%8F%8C%E8%AE%BF%E9%97%AE/","section":"笔记","summary":"问题描述 # 因为通过上一节的描述, 我们可以通过 cloudflare tunnel 和 alist 结合实现加速访问以及稳定加载, 但是这又导致了一个问题:alist 上传出现了问题.\n","title":"alist配置双访问","type":"notes"},{"content":"","date":"June 11, 2025","externalUrl":null,"permalink":"/tags/cloudflare-tunnel/","section":"Tags","summary":"","title":"Cloudflare Tunnel","type":"tags"},{"content":"","date":"June 11, 2025","externalUrl":null,"permalink":"/tags/%E4%B8%8A%E4%BC%A0%E4%BC%98%E5%8C%96/","section":"Tags","summary":"","title":"上传优化","type":"tags"},{"content":"","date":"June 11, 2025","externalUrl":null,"permalink":"/tags/%E5%8F%8C%E5%9F%9F%E5%90%8D/","section":"Tags","summary":"","title":"双域名","type":"tags"},{"content":"","date":"June 11, 2025","externalUrl":null,"permalink":"/tags/%E7%BD%91%E7%BB%9C%E9%85%8D%E7%BD%AE/","section":"Tags","summary":"","title":"网络配置","type":"tags"},{"content":" 先前做法 # 先前, 我们是通过脚本来和 cloudflare 配合来不断将域名和 ipv6 进行一个绑定来修改, 但是这样我的服务器就是一个直接的出站, 并没有经过什么加速. 所以相比通过代理的方式, 速度会慢很多.\n脚本如下:\n#!/bin/bash # CHANGE THESE auth_email=\u0026#34;nni461904@gmail.com\u0026#34; #你的CloudFlare注册账户邮箱,your cloudflare account email address auth_key=\u0026#34;188f5f3f1ba73b37fd2a0\u0026#34; #你的cloudflare账户Globel ID ,your cloudflare Globel ID（就是我的个人资料里的global api key，应该就是用来做身份识别用的） zone_name=\u0026#34;050626.xyz\u0026#34; #你的域名,your root domain address record_name=\u0026#34;alist.050626.xyz\u0026#34; #完整域名,your full domain address record_type=\u0026#34;AAAA\u0026#34; #A or AAAA,ipv4 或 ipv6解析 ip_index=\u0026#34;local\u0026#34; #use \u0026#34;internet\u0026#34; or \u0026#34;local\u0026#34;,使用本地方式还是网络方式获取地址 eth_card=\u0026#34;enp1s0\u0026#34; #使用本地方式获取ip绑定的网卡，默认为eth0，仅本地方式有效,the default ethernet card is eth0 ip_file=\u0026#34;ip.txt\u0026#34; #保存地址信息,save ip information in the ip.txt id_file=\u0026#34;cloudflare.ids\u0026#34; log_file=\u0026#34;cloudflare.log\u0026#34; if [ $record_type = \u0026#34;AAAA\u0026#34; ];then if [ $ip_index = \u0026#34;internet\u0026#34; ];then ip=$(curl -6 ip.sb) elif [ $ip_index = \u0026#34;local\u0026#34; ];then if [ \u0026#34;$user\u0026#34; = \u0026#34;root\u0026#34; ];then ip=$(ifconfig $eth_card | grep \u0026#39;inet6\u0026#39; | cut -f2 | awk \u0026#39;{ print $2}\u0026#39; | grep -v \u0026#39;^::1$\u0026#39; | grep -v \u0026#39;^fe80\u0026#39; | grep -v \u0026#39;^f[d|c]\u0026#39; | head -1) else ip=$(/sbin/ifconfig $eth_card | grep \u0026#39;inet6\u0026#39; | cut -f2 | awk \u0026#39;{ print $2}\u0026#39; | grep -v \u0026#39;^::1$\u0026#39; | grep -v \u0026#39;^fe80\u0026#39; | grep -v \u0026#39;^f[d|c]\u0026#39; | head -1) fi else echo \u0026#34;Error IP index, please input the right type\u0026#34; exit 0 fi elif [ $record_type = \u0026#34;A\u0026#34; ];then if [ $ip_index = \u0026#34;internet\u0026#34; ];then ip=$(curl -4 ip.sb) elif [ $ip_index = \u0026#34;local\u0026#34; ];then if [ \u0026#34;$user\u0026#34; = \u0026#34;root\u0026#34; ];then ip=$(ifconfig $eth_card | grep \u0026#39;inet\u0026#39;| grep -v \u0026#39;127.0.0.1\u0026#39; | grep -v \u0026#39;inet6\u0026#39;|cut -f2 | awk \u0026#39;{ print $2}\u0026#39;) else ip=$(/sbin/ifconfig $eth_card | grep \u0026#39;inet\u0026#39;| grep -v \u0026#39;127.0.0.1\u0026#39; | grep -v \u0026#39;inet6\u0026#39;|cut -f2 | awk \u0026#39;{ print $2}\u0026#39;) fi else echo \u0026#34;Error IP index, please input the right type\u0026#34; exit 0 fi else echo \u0026#34;Error DNS type\u0026#34; exit 0 fi # 日志 log file log() { if [ \u0026#34;$1\u0026#34; ]; then echo -e \u0026#34;[$(date)] - $1\u0026#34; \u0026gt;\u0026gt; $log_file fi } # SCRIPT START log \u0026#34;Check Initiated\u0026#34; #判断ip是否发生变化,check the ip had been changed? if [ -f $ip_file ]; then old_ip=$(cat $ip_file) if [ $ip == $old_ip ]; then echo \u0026#34;IP has not changed.\u0026#34; exit 0 fi fi #获取域名和授权 get the domain and authentic if [ -f $id_file ] \u0026amp;\u0026amp; [ $(wc -l $id_file | cut -d \u0026#34; \u0026#34; -f 1) == 2 ]; then zone_identifier=$(head -1 $id_file) record_identifier=$(tail -1 $id_file) else zone_identifier=$(curl -s -X GET \u0026#34;https://api.cloudflare.com/client/v4/zones?name=$zone_name\u0026#34; \\ -H \u0026#34;X-Auth-Email: $auth_email\u0026#34; \\ -H \u0026#34;X-Auth-Key: $auth_key\u0026#34; \\ -H \u0026#34;Content-Type: application/json\u0026#34; | grep -Po \u0026#39;(?\u0026lt;=\u0026#34;id\u0026#34;:\u0026#34;)[^\u0026#34;]*\u0026#39; | head -1 ) record_identifier=$(curl -s -X GET \u0026#34;https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records?type=${record_type}\u0026amp;name=$record_name\u0026#34; \\ -H \u0026#34;X-Auth-Email: $auth_email\u0026#34; \\ -H \u0026#34;X-Auth-Key: $auth_key\u0026#34; \\ -H \u0026#34;Content-Type: application/json\u0026#34; | grep -Po \u0026#39;(?\u0026lt;=\u0026#34;id\u0026#34;:\u0026#34;)[^\u0026#34;]*\u0026#39;) echo \u0026#34;$zone_identifier\u0026#34; \u0026gt; $id_file echo \u0026#34;$record_identifier\u0026#34; \u0026gt;\u0026gt; $id_file fi #更新DNS记录 update the dns update=$(curl -s -X PUT \u0026#34;https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records/$record_identifier\u0026#34; \\ -H \u0026#34;X-Auth-Email: $auth_email\u0026#34; \\ -H \u0026#34;X-Auth-Key: $auth_key\u0026#34; \\ -H \u0026#34;Content-Type: application/json\u0026#34; \\ --data \u0026#34;{\\\u0026#34;type\\\u0026#34;:\\\u0026#34;$record_type\\\u0026#34;,\\\u0026#34;name\\\u0026#34;:\\\u0026#34;$record_name\\\u0026#34;,\\\u0026#34;content\\\u0026#34;:\\\u0026#34;$ip\\\u0026#34;,\\\u0026#34;ttl\\\u0026#34;:1,\\\u0026#34;proxied\\\u0026#34;:false}\u0026#34;) #反馈更新情况 gave the feedback about the update statues if [[ $update == *\u0026#34;\\\u0026#34;success\\\u0026#34;:true\u0026#34;* ]]; then message=\u0026#34;IP changed to: $ip\u0026#34; echo \u0026#34;$ip\u0026#34; \u0026gt; $ip_file log \u0026#34;$message\u0026#34; echo \u0026#34;$message\u0026#34; else message=\u0026#34;API UPDATE FAILED. DUMPING RESULTS:\\n$update\u0026#34; log \u0026#34;$message\u0026#34; echo -e \u0026#34;$message\u0026#34; exit 1 fi 然后, 在 cloudflare 中配置一条 DNS 记录 (先设为 AAAA, 然后就是先输入一次 ipv6, 之后更改就会通过脚本自动推送的了, 不设置代理即云朵为灰色)\n但是, 显然这样的方法, 速度会很慢. 所以就有了以下改进.\n通过 Cloudflare 的 Tunnel 和 alist 绑定 # 提示 因为 alist 一开始是直接通过本服务器的自身 ipv6 链路出站的, 所以速度很慢. 现在换成通过 cloudflare 节点加速的话, 配置就改变一下. (其实就是通过 cloudflare 代理), 所以要删除原先的 DNS记录 才行\n所以, 先设置 /opt/alist/data 的 config.yaml 中的 force_https 为 false\n还有就是因为已经通过 cloudflare 代理了, 所以我们不用自己设置端口号为 443, 我们可以通过设置 http://localhost:5244 这个本地的端口就行, 免得发生冲突\n用 cloudflared 将 Alist 暴露为 https://yourname.cloudflare.workers.dev\nCloudflare 代理可自动走优质线路，无需暴露真实 IPv6\n几乎所有地区都有高质量访问速度\n适合你的场景（远端用户多，原生 IPv6 差）\n非常好，你已经使用了 IPv6 + DDNS 访问 https://alist.050626.xyz，接下来我们要做的是：\n使用 Cloudflare Tunnel 给你的 Alist 提速（绕过 IPv6 出口限制） # 目标： # 通过 Cloudflare Tunnel 实现：\n不暴露真实 IP（保护隐私） 自动走 Cloudflare 高质量 CDN 节点 提升远端访问速度（避免你当前的“IPv6 出站带宽被限速”问题） 步骤一：在服务器上安装 cloudflared # 以 Arch 为例（你是 Arch 用户）：\nsudo pacman -S cloudflared 若是其他系统：\n# Debian/Ubuntu sudo apt install cloudflared # RedHat/CentOS sudo yum install cloudflared 若没有, 就要通过 wget 下载 deb 包然后 dpkg 解压安装\n步骤二：登录并授权 cloudflared # cloudflared tunnel login 这会打开浏览器登录 Cloudflare，授权后你可以创建 tunnel。\n步骤三：创建 tunnel 并绑定 Alist 服务 # cloudflared tunnel create alist-tunnel 会输出一个 tunnel ID，例如：\nCreated tunnel alist-tunnel with ID 12345678-aaaa-bbbb-cccc-123456abcdef 步骤五：创建配置文件 # 默认目录在 ~/.cloudflared/config.yml, 你也要配置在 /etc/cloudflared/ 目录中：\ntunnel: 12345678-aaaa-bbbb-cccc-123456abcdef # 你的 tunnel ID credentials-file: /home/\u0026lt;user\u0026gt;/.cloudflared/12345678-aaaa-bbbb-cccc-123456abcdef.json # 你的json文件 ingress: - hostname: alist.050626.xyz service: http://localhost:5244 # Alist 默认端口 - service: http_status:404 确保 localhost:5244 是 Alist 的监听地址和端口。\n步骤六：创建 DNS 记录 + 启动 tunnel # 将 tunnel 与 Cloudflare DNS 绑定：\ncloudflared tunnel route dns alist-tunnel alist.044454.xyz 然后运行 tunnel：\ncloudflared tunnel run alist-tunnel 如需后台运行，可以用 systemd 或 --no-autoupdate 参数。\n成功后你可以： # 使用 https://alist.044454.xyz 高速访问 Alist（通过 Cloudflare 节点加速） 客户端访问走的是 Cloudflare 的全球加速网络，而不是你服务器自身出站 IPv6 链路 可选：设置为 systemd 后台服务 # sudo cloudflared service install 这样服务器重启后自动启动 tunnel。\n验证： # 用浏览器打开 https://alist.044454.xyz 使用 curl -v https://alist.044454.xyz，应看到 Cloudflare 代理标识 远程测速明显比直接 IPv6 快，尤其是下载时 通过 systemd 来自动运行 # 一键启用后台服务 # sudo cloudflared service install 这个命令会自动为你生成 systemd 服务文件并启用开机启动。\n然后启动服务： # sudo systemctl start cloudflared 设置开机自动运行（通常默认启用）： # sudo systemctl enable cloudflared 检查运行状态： # systemctl status cloudflared 会看到 tunnel 正常运行，输出中包含 alist-tunnel 和你的域名。\n验证 # 浏览器访问你的域名 https://alist.050626.xyz 用命令查看： curl -I https://alist.050626.xyz 看是否有响应头包含：\nHTTP/1.1 200 HTTP/2 200 cf-ray: xxxx server: cloudflare 说明已通过 Cloudflare Tunnel 代理 ✅\n完整示例:\nHTTP/1.1 200 Connection established HTTP/2 200 date: Fri, 06 Jun 2025 05:12:42 GMT content-type: text/html cf-cache-status: DYNAMIC speculation-rules: \u0026#34;/cdn-cgi/speculation\u0026#34; report-to: {\u0026#34;group\u0026#34;:\u0026#34;cf-nel\u0026#34;,\u0026#34;max_age\u0026#34;:604800,\u0026#34;endpoints\u0026#34;:[{\u0026#34;url\u0026#34;:\u0026#34;https://a.nel.cloudflare.com/report/v4?s=UOD6%2Fislw4MqZ951XaKh0jczFwySa4dshAcWMzNleVhuay3NDTkvlRwkqr%2BRv5PBb%2FPMW7aQ2mZFerZSiEZxvZpKE4CPVIpvxGXTdWO5ph4%3D\u0026#34;}]} nel: {\u0026#34;report_to\u0026#34;:\u0026#34;cf-nel\u0026#34;,\u0026#34;success_fraction\u0026#34;:0.0,\u0026#34;max_age\u0026#34;:604800} server: cloudflare cf-ray: 94b56a8e293909f0-HKG alt-svc: h3=\u0026#34;:443\u0026#34;; ma=86400 ❗ 常见问题提示 # 如果你配置了多个 tunnel，可以指定要运行的 tunnel，比如编辑 /etc/default/cloudflared，或手动创建一个 service 文件（进阶使用）。 如果你使用 --config ~/.cloudflared/config.yml，确保 cloudflared 安装路径和 config 路径匹配 ","date":"June 7, 2025","externalUrl":null,"permalink":"/notes/tools/alist%E5%8A%A0%E9%80%9F/","section":"笔记","summary":"先前做法 # 先前, 我们是通过脚本来和 cloudflare 配合来不断将域名和 ipv6 进行一个绑定来修改, 但是这样我的服务器就是一个直接的出站, 并没有经过什么加速. 所以相比通过代理的方式, 速度会慢很多.\n","title":"alist加速","type":"notes"},{"content":"","date":"June 7, 2025","externalUrl":null,"permalink":"/tags/cdn/","section":"Tags","summary":"","title":"CDN","type":"tags"},{"content":"","date":"June 7, 2025","externalUrl":null,"permalink":"/tags/cloudflare/","section":"Tags","summary":"","title":"Cloudflare","type":"tags"},{"content":"","date":"June 7, 2025","externalUrl":null,"permalink":"/tags/ipv6/","section":"Tags","summary":"","title":"IPv6","type":"tags"},{"content":"","date":"June 7, 2025","externalUrl":null,"permalink":"/tags/%E7%BD%91%E7%BB%9C%E5%8A%A0%E9%80%9F/","section":"Tags","summary":"","title":"网络加速","type":"tags"},{"content":" 在 LazyVim 中自定义插件的快捷键 # 要自定义 LazyVim 插件使用的快捷键，有几种不同的方法，具体取决于你希望快捷键在哪些地方生效以及如何应用。LazyVim 是基于 Neovim 并由 lazy.nvim 插件管理器驱动的，提供了灵活的方式来管理插件的快捷键，同时保持高效的懒加载机制。下面，我将详细介绍几种主要的方法，并提供完整的示例代码，同时解答是否需要手动加载相关文件。\n自定义快捷键的方法 # 1. 在插件规范文件中定义快捷键 (lua/plugins/\u0026lt;plugin\u0026gt;.lua) # 最常见和推荐的方法是在插件的规范文件中使用 keys 字段直接定义快捷键。这种方法可以让 LazyVim 在按下快捷键时才加载插件，从而优化启动时间。\n工作原理： # keys 字段可以指定快捷键，同时设置按键映射，并触发插件加载。 适用于插件提供的命令或功能（例如，切换文件浏览器或打开 Telescope 搜索）。 示例：自定义 nvim-tree 的快捷键 # 假设你想将 \u0026lt;C-n\u0026gt; 绑定为 nvim-tree 文件浏览器的切换键，而不是它的默认快捷键。\n创建或编辑 lua/plugins/nvim-tree.lua： -- lua/plugins/nvim-tree.lua return { { \u0026#34;kyazdani42/nvim-tree.lua\u0026#34;, dependencies = { \u0026#34;kyazdani42/nvim-web-devicons\u0026#34; }, config = function() require(\u0026#34;nvim-tree\u0026#34;).setup({ view = { width = 30 }, -- 可选配置 }) end, keys = { { \u0026#34;\u0026lt;C-n\u0026gt;\u0026#34;, \u0026#34;\u0026lt;cmd\u0026gt;NvimTreeToggle\u0026lt;cr\u0026gt;\u0026#34;, desc = \u0026#34;Toggle NvimTree\u0026#34; }, }, }, } 解释： \u0026lt;C-n\u0026gt;（Ctrl+n）被映射为 NvimTreeToggle 命令。 desc 字段提供了快捷键的描述（可在 WhichKey 等工具中可见）。 当你按下 \u0026lt;C-n\u0026gt; 时，LazyVim 会加载 nvim-tree（如果尚未加载），并切换文件浏览器。 添加多个快捷键： 如果你还希望 \u0026lt;leader\u0026gt;e 也能切换 nvim-tree，可以这样做： keys = { { \u0026#34;\u0026lt;C-n\u0026gt;\u0026#34;, \u0026#34;\u0026lt;cmd\u0026gt;NvimTreeToggle\u0026lt;cr\u0026gt;\u0026#34;, desc = \u0026#34;Toggle NvimTree\u0026#34; }, { \u0026#34;\u0026lt;leader\u0026gt;e\u0026#34;, \u0026#34;\u0026lt;cmd\u0026gt;NvimTreeToggle\u0026lt;cr\u0026gt;\u0026#34;, desc = \u0026#34;Toggle NvimTree\u0026#34; }, }, 解释：现在 \u0026lt;C-n\u0026gt; 和 \u0026lt;leader\u0026gt;e 都可以触发相同的操作，并在需要时加载插件。 2. 在插件的 config 函数中自定义快捷键 # 有些插件会在其 setup 配置中定义快捷键（例如 nvim-cmp 这样的补全插件）。对于这些插件，可以直接在 config 函数中修改快捷键。\n工作原理： # config 函数在插件加载后执行，因此可以覆盖插件的默认按键映射。 适用于有自己内部映射系统的插件（例如，补全菜单的导航）。 示例：自定义 nvim-cmp 的快捷键 # 假设你想用 \u0026lt;Tab\u0026gt; 和 \u0026lt;S-Tab\u0026gt;（Shift+Tab）来导航补全菜单，而不是默认的 \u0026lt;C-n\u0026gt; 和 \u0026lt;C-p\u0026gt;。\n编辑 lua/plugins/nvim-cmp.lua： -- lua/plugins/nvim-cmp.lua return { { \u0026#34;hrsh7th/nvim-cmp\u0026#34;, dependencies = { \u0026#34;hrsh7th/cmp-nvim-lsp\u0026#34;, \u0026#34;L3MON4D3/LuaSnip\u0026#34; }, config = function() local cmp = require(\u0026#34;cmp\u0026#34;) cmp.setup({ -- 其他配置（如补全来源、代码片段设置等） mapping = { [\u0026#34;\u0026lt;Tab\u0026gt;\u0026#34;] = cmp.mapping.select_next_item(), [\u0026#34;\u0026lt;S-Tab\u0026gt;\u0026#34;] = cmp.mapping.select_prev_item(), [\u0026#34;\u0026lt;C-y\u0026gt;\u0026#34;] = cmp.mapping.confirm({ select = true }), }, }) end, }, } 解释： mapping 表定义了补全菜单的快捷键。 \u0026lt;Tab\u0026gt; 选择下一个补全项，\u0026lt;S-Tab\u0026gt; 选择上一个，\u0026lt;C-y\u0026gt; 确认补全。 这些快捷键仅在补全菜单打开时生效，并在 nvim-cmp 加载时设置。 3. 在 lua/config/keymaps.lua 中定义快捷键 # 如果你希望所有快捷键集中管理，可以在 lua/config/keymaps.lua 文件中定义它们。但如果插件是懒加载的，必须确保插件在按下快捷键时已加载。\n工作原理： # keymaps.lua 中的快捷键会在 Neovim 启动时生效。 对于懒加载的插件，如果插件尚未加载，命令可能不可用，除非插件已被触发加载。 示例：为 nvim-tree 添加快捷键 # 假设你想使用 \u0026lt;leader\u0026gt;nt 来切换 nvim-tree，并希望在 keymaps.lua 中定义它。\n方案 A：立即加载插件： 在插件配置中设置 lazy = false，确保插件在启动时加载： -- lua/plugins/nvim-tree.lua return { { \u0026#34;kyazdani42/nvim-tree.lua\u0026#34;, lazy = false, -- 启动时加载 config = function() require(\u0026#34;nvim-tree\u0026#34;).setup({ view = { width = 30 }, }) end, }, } 然后，在 lua/config/keymaps.lua 中： -- lua/config/keymaps.lua vim.keymap.set(\u0026#34;n\u0026#34;, \u0026#34;\u0026lt;leader\u0026gt;nt\u0026#34;, \u0026#34;\u0026lt;cmd\u0026gt;NvimTreeToggle\u0026lt;cr\u0026gt;\u0026#34;, { desc = \u0026#34;Toggle NvimTree\u0026#34; }) 解释：由于 nvim-tree 在启动时已加载，\u0026lt;leader\u0026gt;nt 可以立即生效。 方案 B：保持懒加载（推荐）： 在插件规范文件的 keys 字段中定义快捷键： -- lua/plugins/nvim-tree.lua return { { \u0026#34;kyazdani42/nvim-tree.lua\u0026#34;, keys = { { \u0026#34;\u0026lt;leader\u0026gt;nt\u0026#34;, \u0026#34;\u0026lt;cmd\u0026gt;NvimTreeToggle\u0026lt;cr\u0026gt;\u0026#34;, desc = \u0026#34;Toggle NvimTree\u0026#34; }, }, config = function() require(\u0026#34;nvim-tree\u0026#34;).setup({ view = { width = 30 }, }) end, }, } 解释：这样可以保持懒加载，仅在按下快捷键时加载插件，提高启动速度。 注意：如果在 keymaps.lua 中为懒加载插件定义快捷键，且没有手动触发插件加载，可能会遇到命令不可用的情况。因此，最佳做法是将快捷键放在插件规范文件的 keys 字段中。\n需要手动加载文件吗？ # 不需要！LazyVim 的 lazy.nvim 会自动管理插件加载：\n在 keys 字段中定义快捷键：按下键时加载插件并执行命令。 在 config 函数中定义快捷键：插件加载后按键才会生效。 在 keymaps.lua 中定义快捷键：适用于已加载的插件，否则可能需要 lazy = false。 总结 # 最佳实践：在 lua/plugins/\u0026lt;plugin\u0026gt;.lua 中的 keys 字段定义快捷键，以便懒加载插件。 对于内部映射：在 config 函数中修改插件的默认按键（如 nvim-cmp）。 集中管理快捷键：如果插件已加载（lazy = false），可以使用 keymaps.lua。 按照这些方法，你可以轻松、高效地为任何 LazyVim 插件自定义快捷键\n","date":"June 6, 2025","externalUrl":null,"permalink":"/notes/vim/lazyvim-keymap/","section":"笔记","summary":"在 LazyVim 中自定义插件的快捷键 # 要自定义 LazyVim 插件使用的快捷键，有几种不同的方法，具体取决于你希望快捷键在哪些地方生效以及如何应用。LazyVim 是基于 Neovim 并由 lazy.nvim 插件管理器驱动的，提供了灵活的方式来管理插件的快捷键，同时保持高效的懒加载机制。下面，我将详细介绍几种主要的方法，并提供完整的示例代码，同时解答是否需要手动加载相关文件。\n","title":"lazyvim_keymap","type":"notes"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/categories/neovim/","section":"Categories","summary":"","title":"NeoVim","type":"categories"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/categories/vim/","section":"Categories","summary":"","title":"Vim","type":"categories"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/notes/vim/","section":"笔记","summary":"","title":"Vim / Neovim","type":"notes"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/tags/%E7%BC%96%E8%BE%91%E5%99%A8/","section":"Tags","summary":"","title":"编辑器","type":"tags"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/tags/%E6%8F%92%E4%BB%B6%E9%85%8D%E7%BD%AE/","section":"Tags","summary":"","title":"插件配置","type":"tags"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/tags/%E5%BF%AB%E6%8D%B7%E9%94%AE/","section":"Tags","summary":"","title":"快捷键","type":"tags"},{"content":" 如何在 LazyVim 中配置插件？ # LazyVim 使用 lazy.nvim 作为插件管理器，允许以模块化和灵活的方式定义和配置插件。要配置插件，您需要创建或修改 Lua 文件来指定要安装的插件及其设置和加载条件。这些文件通常位于 LazyVim 配置的 lua/plugins/ 目录中。\n每个插件配置都写作一个 Lua 表，包含插件的仓库地址、依赖项、配置函数以及加载条件（如立即加载或在特定事件/按键时延迟加载）。LazyVim 的 lazy.nvim 设置（通常在 lua/config/lazy.lua 中定义）会自动发现并加载这些插件配置。\n配置插件的步骤： # 在 lua/plugins/ 中创建或修改文件：通过在此目录中创建新的 .lua 文件或编辑现有文件来添加插件。 定义插件规格：返回一个表（或表列表）来指定插件详细信息。 保存文件：保存后，lazy.nvim 会根据您的配置自动管理插件。 插件配置文件保存在哪里？ # 根据您的目录结构：\n. ├── init.lua ├── lazy-lock.json ├── lazyvim.json ├── LICENSE ├── lua │ ├── config │ │ ├── autocmds.lua │ │ ├── keymaps.lua │ │ ├── lazy.lua │ │ ├── options.lua │ │ └── plugins.lua │ └── plugins │ ├── example.lua │ ├── nvim-cmp.lua │ ├── nvim-tree.lua │ └── treesistter.lua ├── README.md └── stylua.toml 插件配置文件保存在 lua/plugins/ 目录中。该目录中的每个文件（如 nvim-cmp.lua、nvim-tree.lua、treesistter.lua、example.lua）通常定义一个或多个插件。例如：\nnvim-cmp.lua 可能配置 nvim-cmp 自动补全插件 nvim-tree.lua 配置 nvim-tree 文件管理器 treesistter.lua（可能是 treesitter.lua 的拼写错误）可能配置 nvim-treesitter 这些文件会被 lazy.nvim 自动加载，由 lua/config/lazy.lua 文件统筹管理（该文件会导入 lua/plugins/ 的所有模块）。\n注意：您的结构中包含 lua/config/plugins.lua，这不是 LazyVim 的默认配置。通常插件配置应放在 lua/plugins/，而 lua/config/ 存放通用配置文件（如 lazy.lua、options.lua 等）。除非 lua/config/plugins.lua 被显式导入，否则默认不会使用。建议遵循 LazyVim 惯例，将插件配置放在 lua/plugins/。\n详细示例：配置 telescope.nvim 插件 # 以添加强大的模糊搜索插件 nvim-telescope/telescope.nvim 为例：\n步骤 1：创建新文件 # 在 lua/plugins/ 中创建 telescope.lua 文件（路径：~/.config/nvim/lua/plugins/telescope.lua）。\n步骤 2：定义插件规格 # 将以下内容添加到 lua/plugins/telescope.lua：\nreturn { { \u0026#34;nvim-telescope/telescope.nvim\u0026#34;, -- 指定依赖项 dependencies = { \u0026#34;nvim-lua/plenary.nvim\u0026#34; }, -- 插件加载后运行的配置函数 config = function() require(\u0026#34;telescope\u0026#34;).setup({ defaults = { -- 自定义Telescope行为 layout_strategy = \u0026#34;horizontal\u0026#34;, layout_config = { prompt_position = \u0026#34;top\u0026#34;, }, sorting_strategy = \u0026#34;ascending\u0026#34;, }, pickers = { find_files = { hidden = true, -- 显示隐藏文件 }, }, }) end, -- 定义触发插件加载的快捷键 keys = { { \u0026#34;\u0026lt;leader\u0026gt;ff\u0026#34;, \u0026#34;\u0026lt;cmd\u0026gt;Telescope find_files\u0026lt;cr\u0026gt;\u0026#34;, desc = \u0026#34;查找文件\u0026#34; }, { \u0026#34;\u0026lt;leader\u0026gt;fg\u0026#34;, \u0026#34;\u0026lt;cmd\u0026gt;Telescope live_grep\u0026lt;cr\u0026gt;\u0026#34;, desc = \u0026#34;实时搜索\u0026#34; }, }, -- 当执行Telescope命令时加载插件 cmd = { \u0026#34;Telescope\u0026#34; }, }, } 配置详解： # nvim-telescope/telescope.nvim：插件的 GitHub 仓库 dependencies：Telescope 需要 plenary.nvim 作为依赖 config：插件加载后调用的配置函数，设置布局策略和显示隐藏文件 keys：定义快捷键 \u0026lt;leader\u0026gt;ff（查找文件）和 \u0026lt;leader\u0026gt;fg（实时搜索） cmd：运行 :Telescope 命令时加载插件 步骤 3：保存并同步 # 保存文件后，在 Neovim 中运行 :Lazy 并选择 \u0026ldquo;Sync\u0026rdquo;（或重启 Neovim）来安装插件。LazyVim 会自动处理下载和配置。\n如何使用这些插件？ # 在 lua/plugins/ 中配置插件后，lazy.nvim 会根据您指定的条件（如 lazy = false、keys、cmd、event）管理加载。使用方式如下：\n自动功能：许多插件加载后即用。例如：\n配置 telescope.nvim 后，按 \u0026lt;leader\u0026gt;ff 即可打开文件搜索 nvim-cmp.lua 配置后会在插入模式自动激活补全 命令：插件常提供命令。例如 Telescope 的 :Telescope find_files，配置中的 cmd = { \u0026quot;Telescope\u0026quot; } 确保执行命令时加载插件\n快捷键：使用插件规格中定义的快捷键（如 \u0026lt;leader\u0026gt;ff），或在 lua/config/keymaps.lua 添加自定义快捷键：\nvim.keymap.set(\u0026#34;n\u0026#34;, \u0026#34;\u0026lt;leader\u0026gt;ff\u0026#34;, \u0026#34;\u0026lt;cmd\u0026gt;Telescope find_files\u0026lt;cr\u0026gt;\u0026#34;, { desc = \u0026#34;查找文件\u0026#34; }) 插件文档：通过 :help 插件名（如 :help telescope）查看具体用法\n延迟加载注意事项： # 默认插件会延迟加载（按需加载）以加快启动速度 在 Telescope 示例中，插件会在按下 \u0026lt;leader\u0026gt;ff 或运行 :Telescope 时加载。如需立即加载，可在规格中添加 lazy = false 需要在其他配置文件中加载插件吗？ # 通常不需要。lua/plugins/ 中的插件配置会被 lazy.nvim 自动加载（通过 lua/config/lazy.lua 的设置，通常包含）：\nrequire(\u0026#34;lazy\u0026#34;).setup({ spec = { { import = \u0026#34;plugins\u0026#34; }, -- 导入 lua/plugins/ 所有文件 }, -- 其他性能设置等 }) 这意味着您无需在 init.lua、options.lua 或 keymaps.lua 中手动加载插件。但以下情况需注意：\n在 keymaps.lua 定义快捷键：\n如果在 keymaps.lua 中定义依赖插件的快捷键（如 \u0026lt;leader\u0026gt;ff），lazy.nvim 会在按键时通过 cmd 或 keys 设置自动加载插件 示例：在 keymaps.lua 添加 Telescope 快捷键仍会正常触发加载 选项或自动命令：\n如果插件需要 Neovim 选项或自动命令（如设置 vim.g.some_option），建议将配置放在插件的 config 函数中 或放在 lua/config/options.lua/autocmds.lua，但需确保插件已加载（如设置 lazy = false） 插件间依赖：\n在插件规格中使用 dependencies 字段确保先加载依赖项 示例：配置 nvim-tree 的快捷键 # 在 lua/plugins/nvim-tree.lua 中：\nreturn { { \u0026#34;kyazdani42/nvim-tree.lua\u0026#34;, dependencies = { \u0026#34;kyazdani42/nvim-web-devicons\u0026#34; }, config = function() require(\u0026#34;nvim-tree\u0026#34;).setup({ view = { width = 30 }, }) end, keys = { { \u0026#34;\u0026lt;leader\u0026gt;e\u0026#34;, \u0026#34;\u0026lt;cmd\u0026gt;NvimTreeToggle\u0026lt;cr\u0026gt;\u0026#34;, desc = \u0026#34;切换文件树\u0026#34; }, }, }, } 按下 \u0026lt;leader\u0026gt;e 时会加载插件，无需在其他地方额外配置。\n总结 # 配置插件：在 lua/plugins/ 中添加/修改 .lua 文件，返回包含插件详情的表 配置文件位置：保存在 lua/plugins/（如 telescope.lua） 使用插件：通过命令、快捷键或插件自动功能访问 其他文件加载：一般不需要，lazy.nvim 通过 lazy.lua 自动管理。如需在快捷键/选项中引用插件，确保配置加载顺序 ","date":"June 6, 2025","externalUrl":null,"permalink":"/notes/vim/lazyvim-plugins/","section":"笔记","summary":"如何在 LazyVim 中配置插件？ # LazyVim 使用 lazy.nvim 作为插件管理器，允许以模块化和灵活的方式定义和配置插件。要配置插件，您需要创建或修改 Lua 文件来指定要安装的插件及其设置和加载条件。这些文件通常位于 LazyVim 配置的 lua/plugins/ 目录中。\n","title":"lazyvim_plugins","type":"notes"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/tags/%E7%BC%96%E8%BE%91%E5%99%A8%E9%85%8D%E7%BD%AE/","section":"Tags","summary":"","title":"编辑器配置","type":"tags"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/tags/%E6%8F%92%E4%BB%B6%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"插件管理","type":"tags"},{"content":"Neovim 的「宏编程」就是通过录制并使用宏（macro）来快速执行重复性操作。它是 Vim/Neovim 中非常实用、效率极高的功能之一。\n✅ 什么是 Neovim 的宏？ # 宏本质上是：你在 Normal 模式下的操作序列的录音回放。\n你可以：\n录制一系列操作 保存到某个寄存器（比如 a） 通过 @a 来重复执行 还可以用 @@ 重复上一次宏调用 📌 宏的基本使用步骤： # 动作 命令 开始录制宏 q\u0026lt;寄存器\u0026gt; 例如 qa 开始录到寄存器 a 执行操作 你正常地输入命令（比如 dw, j, iHello\u0026lt;Esc\u0026gt; 等） 停止录制 q 执行宏 @a 执行寄存器 a 的宏 重复执行上一个宏 @@ 🛠 示例：删除每一行的第一个单词 # qa → 开始录制到 a ^dwj → 移动到行首 (^)，删除一个词 (dw)，跳到下一行 (j) q → 结束录制 @a → 执行一遍宏 @@ → 再次执行刚才的宏 也可以：10@a → 执行 10 次 📎 技巧和注意事项： # 宏只能录制 Normal/Insert/Visual 模式下的操作，不能直接录制 Lua 或 Vimscript。 宏操作受当前光标位置、寄存器内容、编辑器状态影响，操作前最好保证状态一致。 如果你在 Insert 模式下输入内容，宏也会录进去。 宏中的移动建议用固定逻辑（例如 0, $, ^）而非手动左右移动（如 l, h），避免光标状态不同导致行为异常。 🚀 想更强？可以结合： # :normal 批量应用宏到多个行，比如：\n:10,20 normal @a Lua 中调用宏或用 Lua 实现更强的批处理（如使用 vim.api.nvim_feedkeys）\n示例 # test1 1.txt test2 2.txt test3 3.txt ... test10 10.txt 你想借助 Neovim 宏编程（比如使用 @a）自动生成这些行。我们来分两种情况：\n✅ 情况一：你只有第一行 test1 1.txt，想生成接下来的行 # 这种情况，我们可以借助宏 + Ctrl-a（数字加一命令）来自动生成每一行！\n🪄 操作步骤如下： # 手动输入第一行：\ntest1 1.txt 移动光标到行首，执行：\nqa \u0026#34; 开始录制宏到寄存器 a yyp \u0026#34; 复制整行并粘贴 w\u0026lt;C-a\u0026gt; \u0026#34; 移动到数字1，加1（变成2） w\u0026lt;C-a\u0026gt; \u0026#34; 再往后移动到 1.txt 中的1，再加1（变成2.txt） q \u0026#34; 停止录制 然后执行：\n@a \u0026#34; 执行宏一次 @@ \u0026#34; 重复上一次宏（可以多次按） 或者批量执行 9 次（再加9行）：\n9@a 👀 结果你将得到： # test1 1.txt test2 2.txt test3 3.txt test4 4.txt ... test10 10.txt 💡 补充说明 # yyp：复制整行并粘贴\nw\u0026lt;C-a\u0026gt;：先跳到第一个数字（1），然后 Ctrl-a 加1\n再 w\u0026lt;C-a\u0026gt;：继续跳到下一个数字加1（即 1.txt 中的 1）\n只要你的格式是 英文单词+空格+数字.txt，这套宏就能自动推进\n如果你之后格式变复杂，比如：\nprefix-test01 result_001.txt 这种带前导零的情况，也可以用 g\u0026lt;C-a\u0026gt;（更高级的 Vim 自增命令）或 Lua 批量生成\n","date":"June 6, 2025","externalUrl":null,"permalink":"/notes/vim/neovim-macro/","section":"笔记","summary":"Neovim 的「宏编程」就是通过录制并使用宏（macro）来快速执行重复性操作。它是 Vim/Neovim 中非常实用、效率极高的功能之一。\n✅ 什么是 Neovim 的宏？ # 宏本质上是：你在 Normal 模式下的操作序列的录音回放。\n","title":"neovim_macro","type":"notes"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/tags/%E7%BC%96%E8%BE%91%E6%95%88%E7%8E%87/","section":"Tags","summary":"","title":"编辑效率","type":"tags"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/tags/%E5%AE%8F%E7%BC%96%E7%A8%8B/","section":"Tags","summary":"","title":"宏编程","type":"tags"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/tags/%E8%87%AA%E5%8A%A8%E5%8C%96/","section":"Tags","summary":"","title":"自动化","type":"tags"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/tags/dhcpcd/","section":"Tags","summary":"","title":"Dhcpcd","type":"tags"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/tags/dns/","section":"Tags","summary":"","title":"DNS","type":"tags"},{"content":"是的，你可以在 /etc/resolv.conf 文件中修改 DNS 服务器。该文件用于配置 Linux 系统使用的 DNS 解析服务器。通过编辑该文件，你可以手动指定 DNS 服务器（例如 Google 的 8.8.8.8 或 Cloudflare 的 1.1.1.1 等）。\n警告 最好使用的是 dhcpcd 服务，这个最容易修改，就在 /etc/dhcpcd.conf 中修改就行\n如何修改 /etc/resolv.conf 文件来更改 DNS 服务器 # 编辑 /etc/resolv.conf 文件\n使用文本编辑器（例如 nano、vim 或 gedit）编辑 /etc/resolv.conf 文件：\nsudo nano /etc/resolv.conf 修改 DNS 服务器\n在文件中，你可以添加或修改 nameserver 行来指定 DNS 服务器。例如：\nnameserver 8.8.8.8 # Google Public DNS nameserver 8.8.4.4 # Google Public DNS (备用) nameserver 2001:4860:4860::8888 nameserver 2001:4860:4860::8844 或者，你可以使用其他 DNS 服务提供商的 IP 地址，例如：\nnameserver 1.1.1.1 # Cloudflare DNS nameserver 1.0.0.1 # Cloudflare DNS (备用) 保存文件并退出编辑器。\n检查 DNS 设置\n编辑完成后，可以使用以下命令检查 DNS 配置是否生效：\ncat /etc/resolv.conf 你应该看到你添加的 DNS 服务器 IP 地址。\n注意事项 # 网络管理工具的干扰\n在某些系统中，/etc/resolv.conf 文件可能会被网络管理工具（如 NetworkManager、systemd-resolved 或 dhclient）覆盖或自动生成。如果你手动修改了该文件，但网络管理工具恢复了默认的 DNS 设置，修改将会被覆盖。\n禁用自动生成的 /etc/resolv.conf\n如果你希望手动管理 DNS 配置并防止它被覆盖，你可以采取以下方法：\n禁用 systemd-resolved（如果在使用 systemd）：\n如果你的系统使用 systemd-resolved 管理 DNS 配置，可以禁用它，然后手动修改 /etc/resolv.conf。\nsudo systemctl disable systemd-resolved.service sudo systemctl stop systemd-resolved.service sudo rm /etc/resolv.conf sudo ln -s /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf 修改 NetworkManager 设置：\n如果你使用的是 NetworkManager，可以通过修改其配置文件来防止它覆盖 /etc/resolv.conf。编辑 /etc/NetworkManager/NetworkManager.conf 并添加以下行：\n[main] dns=none 然后重新启动 NetworkManager 服务：\nsudo systemctl reload NetworkManager 然后要重新编辑 resolv.conf 文件\n然后再继续执行\nsudo systemctl reload NetworkManager 通过 DHCP 自动获取 DNS\n如果你的网络通过 DHCP 自动获取 IP 配置，DHCP 服务器也可能会向 /etc/resolv.conf 写入 DNS 配置。你可以禁用 DHCP 配置 DNS 设置来防止这种情况，具体方法依赖于你的网络管理工具。 使用的是 dhcpcd # 编辑 /etc/dhcpcd. conf 的, 找到/添加: static domain_name_server=.....\n使用的是 systemd-networkd # 这个可能就要在 /etc/systemd/network/ 中编辑文件，创建一个文件 ,以. network 结尾例如: 20-wired. network\n[Match] Name=eth0 [Network] Address=192.168.1.100/24 Gateway=192.168.1.1 DNS=8.8.8.8 DNS=1.1.1.1 总结 # 你确实可以手动编辑 /etc/resolv.conf 来设置 DNS 服务器，但要注意一些网络管理工具可能会自动覆盖该文件。如果你想持久化更改，可能需要禁用某些自动管理 DNS 的服务或工具。\n","date":"June 6, 2025","externalUrl":null,"permalink":"/notes/linux/linux-dns%E7%BD%91%E7%BB%9C%E7%AE%A1%E7%90%86/","section":"笔记","summary":"是的，你可以在 /etc/resolv.conf 文件中修改 DNS 服务器。该文件用于配置 Linux 系统使用的 DNS 解析服务器。通过编辑该文件，你可以手动指定 DNS 服务器（例如 Google 的 8.8.8.8 或 Cloudflare 的 1.1.1.1 等）。\n警告 最好使用的是 dhcpcd 服务，这个最容易修改，就在 /etc/dhcpcd.conf 中修改就行\n","title":"Linux-DNS网络管理","type":"notes"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/tags/resolv.conf/","section":"Tags","summary":"","title":"Resolv.conf","type":"tags"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/ja/tags/%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"ネットワーク管理","type":"tags"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/tags/%E7%BD%91%E7%BB%9C%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"网络管理","type":"tags"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/tags/hyprland/","section":"Tags","summary":"","title":"Hyprland","type":"tags"},{"content":" 提示 首先声明，并不是所有功能都是通过快捷键打开的，就是比如说: Kool_Quick_Settings.sh 这个就是先通过 yad 或者 rofi 这些工具图形化功能栏目，然后就可以通过命令打开文件\n目录结构 # 省流版 # .config/hypr/ . ├── animations ├── application-style.conf ├── configs ├── hypridle.conf ├── hyprland.conf ├── hyprlock-2k.conf ├── hyprlock.conf ├── hyprpaper.conf ├── initial-boot.sh ├── Monitor_Profiles ├── monitors.conf ├── scripts ├── UserConfigs ├── UserScripts ├── v2.3.15 ├── wallpaper_effects ├── wallust └── workspaces.conf 大概说明 # 最重要文件: hyprland.conf, 这个文件就是一个相当于 hyprland 的根配置文件，窗口管理(WM)的根基配置\nanimations 是控制窗口、工作区、图层等 UI 元素的动画效果的一个目录的配置文件. (可以通过 SUPER SHIFT A 来选择)\napplication-style.conf: hyprland-qt-support provides a QML style for hypr qt6 apps\nconfigs 是项目 Default config 目录, 最好不要改动. scripts 同样是\nUserConfigs 则不一样，是表示用户级别的配置目录，可以自定义设置. UserScripts 同样\nhypridle.conf :\nhyprlock.conf 是与锁屏有关的配置文件\nhyprpaper.conf 是显示器 wallpaper 设置的配置文件\ninitial-boot.sh 是 hyprland 启动的时候，如何渲染就是通过这个脚本文件来配置的\nmonitors.conf 是由 Monitor_Profile 这个与monitor配置有关的目录里的 default.conf 生成的\nwallpaper_effects 是 wallpaper 改变之后生成的一个目录，有两个隐藏文件: .wallpaper_current 和 .wallpaper_modified\nworkspaces.conf 是一个由 Userconfigs 里的 WorkSpaceRules 文件生成的一个文件，是一个工作区如何定义和管理的配置文件\nwallust 是颜色定义目录，里面有个文件 (相当于一个常量头文件)\nUserConfigs/WindowRules.conf 这个文件就是管理窗口的，定义规则看如何给窗口贴标签，分配 workspace 这些等1\n例如:\nwindowrulev2 = tag +projects, class:^(codium|codium-url-handler|VSCodium)$ windowrulev2 = tag +projects, class:^(VSCode|code-url-handler)$ windowrulev2 = tag +projects, class:^(jetbrains-.+)$ # 这个可以实现在规定workspace启动应用窗口 windowrulev2 = workspace 4, tag:projects 详细版目录结构 # .config/hypr . ├── animations │ ├── 00-default.conf │ ├── 01-default - v2.conf │ ├── 03- Disable Animation.conf │ ├── END-4.conf │ ├── HYDE - default.conf │ ├── HYDE - minimal-1.conf │ ├── HYDE - minimal-2.conf │ ├── HYDE - optimized.conf │ ├── HYDE - Vertical.conf │ ├── Mahaveer - me-1.conf │ ├── Mahaveer - me-2.conf │ ├── ML4W - classic.conf │ ├── ML4W - dynamic.conf │ ├── ML4W - fast.conf │ ├── ML4W - high.conf │ ├── ML4W - moving.conf │ └── ML4W - standard.conf ├── application-style.conf ├── configs │ └── Keybinds.conf ├── hypridle.conf ├── hyprland.conf ├── hyprlock-2k.conf ├── hyprlock.conf ├── hyprpaper.conf ├── initial-boot.sh ├── Monitor_Profiles │ ├── default.conf │ └── README ├── monitors.conf ├── scripts │ ├── AirplaneMode.sh │ ├── Animations.sh │ ├── BrightnessKbd.sh │ ├── Brightness.sh │ ├── ChangeBlur.sh │ ├── ChangeLayout.sh │ ├── ClipManager.sh │ ├── DarkLight.sh │ ├── Distro_update.sh │ ├── GameMode.sh │ ├── Hypridle.sh │ ├── KeyBinds.sh │ ├── KeyHints.sh │ ├── KillActiveProcess.sh │ ├── Kitty_themes.sh │ ├── Kool_Quick_Settings.sh │ ├── KooLsDotsUpdate.sh │ ├── LockScreen.sh │ ├── MediaCtrl.sh │ ├── MonitorProfiles.sh │ ├── Polkit-NixOS.sh │ ├── Polkit.sh │ ├── PortalHyprland.sh │ ├── RefreshNoWaybar.sh │ ├── Refresh.sh │ ├── RofiEmoji.sh │ ├── RofiSearch.sh │ ├── RofiThemeSelector-modified.sh │ ├── RofiThemeSelector.sh │ ├── ScreenShot.sh │ ├── Sounds.sh │ ├── SwitchKeyboardLayout.sh │ ├── TouchPad.sh │ ├── UptimeNixOS.sh │ ├── Volume.sh │ ├── WallustSwww.sh │ ├── WaybarCava.sh │ ├── WaybarLayout.sh │ ├── WaybarScripts.sh │ ├── WaybarStyles.sh │ └── Wlogout.sh ├── UserConfigs │ ├── 00-Readme │ ├── 01-UserDefaults.conf │ ├── ENVariables.conf │ ├── LaptopDisplay.conf │ ├── Laptops.conf │ ├── Startup_Apps.conf │ ├── UserAnimations.conf │ ├── UserDecorations.conf │ ├── UserKeybinds.conf │ ├── UserSettings.conf │ ├── WindowRules.conf │ ├── WindowRules-new.conf │ └── WorkSpaceRules ├── UserScripts │ ├── 00-Readme │ ├── RainbowBorders.sh │ ├── RofiBeats.sh │ ├── RofiCalc.sh │ ├── WallpaperAutoChange.sh │ ├── WallpaperEffects.sh │ ├── WallpaperRandom.sh │ ├── WallpaperSelect.sh │ ├── Weather.py │ ├── Weather.sh │ └── ZshChangeTheme.sh ├── v2.3.15 ├── wallpaper_effects ├── wallust │ └── wallust-hyprland.conf └── workspaces.conf 配置 # 应用自启动配置 # 在 hyprland.conf 这个文件里自定义软件的自启动2\nexamples:\nexec-once = [workspace 1 silent] kitty exec-once = [workspace 1 silent] subl exec-once = [workspace 3 silent] mailspring exec-once = [workspace 4 silent] firefox exec VS exec-once # You can execute a shell script on:3\nstartup of the compositor every time the config is reloaded. shutdown of the compositor exec-once = command will execute only on launch support rules\nexecr-once = command will execute only on launch\nexec = command will execute on each reload support rules\nexecr = command will execute on each reload\nexec-shutdown = command will execute only on shutdown\n工作区 (Workspace) # 就是如何自定义和管理 workspaces 的，所以都是在 UserConfigs/WorkspaceRules 来设置来管理(不过这个一旦设置就是全部通用)，或者通过 workspaces.conf 这个文件来定义4\n显示器 (Monitor) # 可以直接通过 nwg-displays 来快速设定 (可以 SUPER SHIFT E 打开功能栏目然后搜索就可以了)\n也可以直接自定义 monitors.conf 文件5\n可以通过 nwg-display 工具来设定显示器\n快捷键 (keybinds) # rules: bind = MODS, key, dispatcher, params6\nexamples: bind = SUPER, B, exec, firefox\n根据上面目录结构所说的，可以在 UserConfigs/UserKeybinds.conf 里定义，也有 UserScripts/UserKeybinds.sh 里定义脚本，还有一个 UserScripts/UserKeyHints.sh 有常用快捷键提示\n这里说到快捷键要说明一下，渲染问题 (有 yad 和 rofi 这两个实现脚本的 GUI)\n简单对比:\n功能 yad rofi 图形风格 GTK，传统 GUI 样式 极简、dmenu 风格 多列显示 ✅ 原生支持 🚫 不支持多列（需手动 hack） 自定义交互复杂度 高（按钮、表单、进度条等） 中（菜单交互 + 自定义脚本） 外观主题支持 GTK 主题 rofi 主题（通过 config 设置） 使用场景 配置工具、提示菜单、设置界面 启动器、搜索菜单、快速选择器 环境变量 ENV # 在 /etc/environment 加载 fcitx5\n笔记 下面的注释是 fcitx5 的 wayland 前端好像是会自动加载这些，所以还添加的话，这样每次启动都会有提示消息\n#GTK_IM_MODULE=fcitx #QT_IM_MODULE=fcitx XMODIFIERS=@im=fcitx SDL_IM_MODULE=fcitx GLFW_IM_MODULE=ibus 加载 nvidia (还是怕 nvidia 驱动没有加载，所以在系统级别的 evn 中添加)\n__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia Nvidia # 警告 首先禁用 nouveau 这个开源驱动，fedora 和 arch 方法不一样，arch 可以直接在 /etc/mkinitcpio.conf 里的 HOOK 字段修改 (删除 kms 就行), 然后就mkinitcpio -P\n禁用 nouveau 驱动 # 在 /etc/modprobe.d 这个目录里编写一个文件:blacklist-nouveau. conf(文件名可以自己自定义)\nblacklist nouveau options nouveau modeset=0 然后, 我们要加载这个禁用的文件配置\n/etc/default/grub 文件中:\nGRUB_CMDLINE_LINUX=\u0026#34;rd.driver.blacklist=nouveau modprobe.blacklist=nouveau\u0026#34; 然后就要: sudo dracut --force 重新生成 grub 引导文件\n以防 nvidia 驱动没有启动，我们在很多方面都写了相关环境变量\n在 /etc/modprobe.d 编写一个nvidia. conf文件7\noptions nvidia_drm modeset=1 UserConfigs/ENVariables.conf 定义各种 env 包括nvidia的 (所以基本我们为了项目化，都在这个改动 env)\n输入法设置 # Fcitx5 各种配置以及问题 # Rime 的大写模式下的问题 # 在 Rime 的中文输入法下按下 capslock键 你就处于大写模式无法输入中文了,然后就各种文档参看, 找到2013 年的的 issue8, 这种应该按道理不管在哪个 DE 或者 WM 下都应该修复了才对的啊!!🤔\nI\u0026rsquo;ll look into this problem tonight.\nYou can temporarily disable mode switching with Caps Lock by patching default.yaml:\n# default.custom.yaml patch: { ascii_composer/switch_key/Caps_Lock: noop } 软件窗口里的输入法 # 笔记 不知道是 electron 应用的问题还是 hyprland 和 fcitx5 还不是很兼容的问题。我们需要在 desktop file 里的Exec字段手动定义实现可以用 fcitx5 的中文输入\n补充如下配置:\n--enable-wayland-ime 如果是用户级别的软件，我们需要在 .local/share/applications 里的 desktop file 里定义。否则，我们要在 /usr/share/applications 里查找 desktop file 来修改\n因为有些应用会出现输入中文的拼音会漏词 (就是明明想要输入 shuru 但是可能会漏出 h 到 fcitx5 的候选词栏中), 有些博客说可以:\n在 ~/.config/gtk-3.0/config 文件中添加:9\n[Settings] gtk-im-module = fcitx Electron 相关应用参考官方链接 # 比如软件:obsidian,vscode,chrome等10\n因为在 linux+nvidia+wayland 这种情况下，nvidia 这个 gpu 加速不一定适配，所以可能就无法实现很流畅地渲染，但是为了进可能地渲染\n我们在 desktop file 里也定义:\n--enable-features=UseOzonePlatform --ozone-platform=wayland StartupWMClass=obsidian # 这个field就是添加窗口class属性(可以更好在窗口管理管理) 代理问题: 这个也是要在 .desktop 里根据各个 applications 的要求设置 proxy\n奇奇怪怪的问题 # firefox 的 class 问题 # 目的: 想要实现在打开某些窗口的时候打开到固定的工作区\n.config/hypr/UserConfigs/WindowRules.conf 这个文件里定义窗口规则的时候，发现如下配置:\nwindowrule2 = tag +browser, class:^([Ff]irefox|org.mozilla.firefox|[Ff]irefox-esr|[Ff]irefox-bin) windowrule2 = workspace 4, tag: browser 这样的配置根本没有在 workspace 4 打开 firefox, 我使用 hyprctl clients 命令查看属性的时候，发现是正常的.\n但是听从 gpt 意见写成:\nwindowrele2= workspace 4, tag +browser, class:^([Ff]irefox|org.mozilla.firefox|[Ff]irefox-esr|[Ff]irefox-bin) 这样的配置却可以生效，问了一下 gpt, 就说是如下原因:\n窗口创建时机：某些应用程序在启动时可能会先创建窗口，然后再设置类名（class）等属性。如果 tag 是基于 class 设置的，而 class 尚未被识别，tag 就不会被应用，导致依赖于该 tag 的规则无法生效。(比如 firefox)\nwaybar 的 icon 显示问题: 比如 waybar 的 rose 的主题，有时候会出现蓝牙图标没有显示(==我想到的可能性是蓝牙启动比 waybar 慢，所以无法显示==)，只有重新 refresh waybar 才行，所以可以尝试在 hyprland.conf 中设置 exec-once = blueman-applet 我们手动设置让蓝牙启动\n结论:\nHyprland 的 windowrulev2 规则在处理标签（tag）和工作区（workspace）时，应用顺序和窗口创建时机非常关键。​如果窗口在创建时尚未被赋予标签，那么依赖于该标签的工作区规则可能不会生效。​ spotify 也是这样，wiki 里也有提及11\n为确保窗口在创建时立即被分配到正确的工作区，建议将标签和工作区的设置合并为一条规则，并确保窗口在创建时就匹配该规则。\n参考🔗 # hyprland wiki workspace rules\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nhyprland wiki autostartup apps\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nhyprland wiki executing\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nhyprland wiki workspaces\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nhyprland wiki monitors\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nhyprland wiki keybinds\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nhyprland wiki nvidia\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nrime-issue\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nfcitx5漏字\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nhyprland wiki electron\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nhyprland wiki spotify\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"June 6, 2025","externalUrl":null,"permalink":"/notes/tools/hyprland%E7%AE%80%E6%98%8E%E6%8C%87%E5%8C%97/","section":"笔记","summary":" 提示 首先声明，并不是所有功能都是通过快捷键打开的，就是比如说: Kool_Quick_Settings.sh 这个就是先通过 yad 或者 rofi 这些工具图形化功能栏目，然后就可以通过命令打开文件\n","title":"Hyprland简明指北","type":"notes"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/tags/wayland/","section":"Tags","summary":"","title":"Wayland","type":"tags"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/tags/%E7%AA%97%E5%8F%A3%E7%AE%A1%E7%90%86%E5%99%A8/","section":"Tags","summary":"","title":"窗口管理器","type":"tags"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/tags/%E6%A1%8C%E9%9D%A2%E7%8E%AF%E5%A2%83/","section":"Tags","summary":"","title":"桌面环境","type":"tags"},{"content":"","date":"June 6, 2025","externalUrl":null,"permalink":"/categories/%E6%A1%8C%E9%9D%A2%E8%AE%BE%E8%AE%A1/","section":"Categories","summary":"","title":"桌面设计","type":"categories"},{"content":" 杂谈 # alist 的相关目录配置全部都在 /opt/alist 目录下，这里可以配置相关的比如:\nhttps 设置 (即证书的设置) 端口设置 密码设置 (高版本已经不行了) 警告 这个脚本是可以使用缓存的文件的, 所以配置新的域名或者什么的, 请先删除 ip.txt, cloudflare.log, cloudflare.ids 文件\n谈到有关证书的事情，我这个 alist 服务器有关 https 访问，就要申请 ssl 证书。那么就要将相关的情况分类说明:\n情况一 # 你的 cloudflare 代理不是仅 DNS 的 (即你的云朵是橙色的), 这就要看你的加密方式是什么了 (在 边缘证书 那里可以看到相关设置)\nCloudflare 提供三种主要的 SSL/TLS 模式，决定了 Cloudflare 与源站之间的连接方式：\n灵活（Flexible）：​浏览器与 Cloudflare 之间使用 HTTPS，但 Cloudflare 与源站之间使用 HTTP。​\n完全（Full）：​浏览器与 Cloudflare 之间使用 HTTPS，Cloudflare 与源站之间也使用 HTTPS，但不验证源站证书的有效性。​\n完全（严格）（Full (Strict)）：​浏览器与 Cloudflare 之间使用 HTTPS，Cloudflare 与源站之间也使用 HTTPS，并且验证源站证书的有效性（必须是受信任的 CA 签发的有效证书）。\n在“完全（严格）”模式下，Cloudflare 会验证你服务器上的 SSL 证书是否有效且由受信任的证书颁发机构（CA）签发。​如果你使用 Let’s Encrypt 证书，并确保其未过期，Cloudflare 将接受该证书。​\n所以，如果你想设置 cloudflare 的证书，你就要自己手动设置相关的还有续期也要分不同情况来决定是否会自动续期.\n情况二 # 你的 cloudflare 代理是仅 DNS 的 (即你的云朵是灰色的)\n我的服务器是通过 cerbot 这个来申请 ssl 证书的，他的相关配置文件存储在 /etc/letsencrypt 下\n/etc/letsencrypt/live/yourdomain/：包含实际使用的证书文件的符号链接。​ 以下是先骨干目录结构内容:\nfullchain.pem：完整的证书链，供服务器使用。​\nprivkey.pem：私钥文件。​\n/etc/letsencrypt/archive/yourdomain/：存储所有历史版本的证书和私钥。​\n/etc/letsencrypt/renewal/yourdomain.conf：包含续期所需的配置信息。​\n/var/log/letsencrypt/letsencrypt.log：Certbot 的日志文件，记录操作过程和错误信息。\n以下是通过 cerbot 申请 ssl 证书步骤:\n申请 SSL 证书: 通过 standalone 模式申请证书：(因为我不是使用 nginx 的，所以是 standalone 模式) sudo cerbot certonly --standalone -d alist.050626.xyz 验证过程: Certbot 会启动一个临时的 web 服务器，向 Let\u0026rsquo;s Encrypt 证明你拥有该域名。验证完成后，它会生成证书并保存在 /etc/letsencrypt/live/yourdomain.com/ 目录下。 证书自动续期: Certbot 会自动配置一个 cron 任务来定期检查并续期证书。验证证书是否会自动续期: sudo cerbot renew --dry-run 配置定时任务: sudo crontab -e 每天检查证书并进行续期 (如果需要), 然后重启 alist\n0 0 * * * certbot renew --quiet \u0026amp;\u0026amp; systemctl restart alist 然后有了证书之后，就在 /opt/alist/data 目录下:\n在 cert_file 以及 key_file 上放置相关配置文件\n例如:\n\u0026#34;cert_file\u0026#34;: \u0026#34;/etc/letsencrypt/live/alist.050626.xyz/fullchain.pem\u0026#34;, \u0026#34;key_file\u0026#34;: \u0026#34;/etc/letsencrypt/live/alist.050626.xyz/privkey.pem\u0026#34;, ","date":"April 20, 2025","externalUrl":null,"permalink":"/notes/tools/alist%E9%85%8D%E7%BD%AE/","section":"笔记","summary":"杂谈 # alist 的相关目录配置全部都在 /opt/alist 目录下，这里可以配置相关的比如:\nhttps 设置 (即证书的设置) 端口设置 密码设置 (高版本已经不行了) 警告 这个脚本是可以使用缓存的文件的, 所以配置新的域名或者什么的, 请先删除 ip.txt, cloudflare.log, cloudflare.ids 文件\n","title":"alist配置","type":"notes"},{"content":"","date":"April 20, 2025","externalUrl":null,"permalink":"/tags/https/","section":"Tags","summary":"","title":"HTTPS","type":"tags"},{"content":"","date":"April 20, 2025","externalUrl":null,"permalink":"/tags/ssl%E8%AF%81%E4%B9%A6/","section":"Tags","summary":"","title":"SSL证书","type":"tags"},{"content":"","date":"April 20, 2025","externalUrl":null,"permalink":"/tags/%E9%85%8D%E7%BD%AE%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"配置管理","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/python/","section":"Tags","summary":"","title":"Python","type":"tags"},{"content":"零宽断言（lookahead 和 lookbehind）、负向断言、后向引用以及平衡组是正则表达式中的高级特性，能够处理更复杂的匹配需求。以下是它们的功能和在 Python 中的具体应用。\n1. 零宽断言 # 正向零宽断言（Positive Lookahead） # 匹配某个位置后面紧跟着特定内容，但不会把这个内容包括在结果中。\n语法： # (?=pattern) 示例： # 提取所有后面跟着数字的单词：\nimport re text = \u0026#34;apple 123, banana456, cherry 789.\u0026#34; # 匹配后面紧跟数字的单词 pattern = r\u0026#39;\\b\\w+(?=\\d)\u0026#39; matches = re.findall(pattern, text) print(matches) # 输出：[\u0026#39;banana\u0026#39;] 负向零宽断言（Negative Lookahead） # 匹配某个位置后面不跟特定内容。\n语法： # (?!pattern) 由感叹号都知道这是表示非\n示例： # 提取所有后面不跟数字的单词：\nimport re text = \u0026#34;apple 123, banana456, cherry 789.\u0026#34; # 匹配后面不跟数字的单词 pattern = r\u0026#39;\\b\\w+(?!\\d)\u0026#39; matches = re.findall(pattern, text) print(matches) # 输出：[\u0026#39;apple\u0026#39;, \u0026#39;cherry\u0026#39;] 2. 后向零宽断言 # 正向后向断言（Positive Lookbehind） # 匹配某个位置前面紧跟着特定内容。\n语法： # (?\u0026lt;=pattern) 示例： # 判断条件是 \u0026ldquo;@\u0026rdquo; ：\nimport re text = \u0026#34;Contact us at support@example.com or sales@company.com.\u0026#34; # 匹配 \u0026#34;@\u0026#34; 后的部分 pattern = r\u0026#39;(?\u0026lt;=@)\\w+\u0026#39; matches = re.findall(pattern, text) print(matches) # 输出：[\u0026#39;example\u0026#39;, \u0026#39;company\u0026#39;] 负向后向断言（Negative Lookbehind） # 匹配某个位置前面不紧跟特定内容。\n语法： # (?\u0026lt;!pattern) 示例： # import re text = \u0026#34;Contact us at support@example.com or sales@company.com.\u0026#34; # 匹配前面不是 @ 的单词 pattern = r\u0026#39;(?\u0026lt;!@)\\b\\w+\u0026#39; matches = re.findall(pattern, text) print(matches) # 输出：[\u0026#39;Contact\u0026#39;, \u0026#39;us\u0026#39;, \u0026#39;at\u0026#39;, \u0026#39;support\u0026#39;, \u0026#39;or\u0026#39;, \u0026#39;sales\u0026#39;] 3. 后向引用 # 后向引用是指在正则表达式中引用之前捕获的内容，可以用 \\1, \\2, 等等表示。\n示例： # 匹配文本中的重复单词：\nimport re text = \u0026#34;I saw a dog and a cat, but I didn\u0026#39;t see the dog dog.\u0026#34; # 匹配重复单词 pattern = r\u0026#39;\\b(\\w+)\\b\\s+\\1\\b\u0026#39; matches = re.findall(pattern, text) print(matches) # 输出：[\u0026#39;dog\u0026#39;] 4. 平衡组 # 平衡组主要用于匹配嵌套结构（例如括号对等结构）。Python 的标准库 re 不直接支持平衡组操作，但可以通过复杂的逻辑来实现。例如，使用 pyparsing 或 regex 模块可以更好地处理。\n示例：使用 regex 匹配嵌套括号：\nimport regex text = \u0026#34;(a(b(c)d)e)f(g(h)i)\u0026#34; # 匹配嵌套括号 pattern = r\u0026#39;\\((?\u0026gt;[^\\(\\)]+|(?R))*\\)\u0026#39; matches = regex.findall(pattern, text) print(matches) # 输出：[\u0026#39;(a(b(c)d)e)\u0026#39;, \u0026#39;(b(c)d)\u0026#39;, \u0026#39;(c)\u0026#39;, \u0026#39;(g(h)i)\u0026#39;, \u0026#39;(h)\u0026#39;] 解释：\n(?R) 是递归调用整个正则表达式本身。 (?\u0026gt;...) 是原子组，确保括号内的内容是完整匹配。 组合练习：解析复杂数据 # 任务： # 提取所有括号中的内容，但要求括号内不含特定关键词（如 \u0026ldquo;skip\u0026rdquo;）。\n示例代码： # import re text = \u0026#34;Match (this), (skip this), and (not skip).\u0026#34; # 匹配括号中的内容，但不包含 \u0026#34;skip\u0026#34; pattern = r\u0026#39;\\((?!.*skip).*?\\)\u0026#39; matches = re.findall(pattern, text) print(matches) # 输出：[\u0026#39;(this)\u0026#39;, \u0026#39;(not skip)\u0026#39;] 任务：验证嵌套括号结构是否匹配 # 例如：\n输入 \u0026quot;(a(b)c)d\u0026quot; 是有效的。 输入 \u0026quot;a(b(c)d\u0026quot; 是无效的。 示例代码： # import regex def is_valid_parentheses(s): # 匹配嵌套括号 pattern = r\u0026#39;^\\((?\u0026gt;[^\\(\\)]+|(?R))*\\)$\u0026#39; return bool(regex.match(pattern, s)) print(is_valid_parentheses(\u0026#34;(a(b)c)d\u0026#34;)) # False print(is_valid_parentheses(\u0026#34;(a(b)c)d)\u0026#34;)) # False print(is_valid_parentheses(\u0026#34;(a(b)c)\u0026#34;)) # True 总结 # 零宽断言：用于检查前后内容是否满足条件，但不消耗匹配的内容。 后向引用：引用前面的捕获内容，特别适合处理重复和结构化数据。 平衡组：用于复杂嵌套结构匹配（需借助 regex 模块）。 通过练习这些场景，你可以熟悉它们的强大功能，并灵活应用到数据处理、文件操作和复杂匹配任务中！\n","date":"February 4, 2025","externalUrl":null,"permalink":"/notes/regex/regrex%E8%A1%A5%E5%85%85/","section":"笔记","summary":"零宽断言（lookahead 和 lookbehind）、负向断言、后向引用以及平衡组是正则表达式中的高级特性，能够处理更复杂的匹配需求。以下是它们的功能和在 Python 中的具体应用。\n","title":"regrex补充","type":"notes"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E5%90%8E%E5%90%91%E5%BC%95%E7%94%A8/","section":"Tags","summary":"","title":"后向引用","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E9%9B%B6%E5%AE%BD%E6%96%AD%E8%A8%80/","section":"Tags","summary":"","title":"零宽断言","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E5%B9%B3%E8%A1%A1%E7%BB%84/","section":"Tags","summary":"","title":"平衡组","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/ja/tags/%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE/","section":"Tags","summary":"","title":"正規表現","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/notes/regex/","section":"笔记","summary":"","title":"正则表达式","type":"notes"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/","section":"Tags","summary":"","title":"正则表达式","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/categories/regrex/","section":"Categories","summary":"","title":"Regrex","type":"categories"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E7%BB%83%E4%B9%A0%E9%A2%98/","section":"Tags","summary":"","title":"练习题","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E5%AE%9E%E6%88%98%E5%BA%94%E7%94%A8/","section":"Tags","summary":"","title":"实战应用","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E6%96%87%E4%BB%B6%E6%93%8D%E4%BD%9C/","section":"Tags","summary":"","title":"文件操作","type":"tags"},{"content":"以下是一些精心设计的正则表达式练习，结合了 Python 编程 和 文件操作，帮助你熟悉正则表达式在不同场景中的实际应用。\n练习一：基本字符匹配 # 目标：掌握正则表达式的基础功能。\n任务：\n提取字符串中的所有电话号码（格式如：123-456-7890 或 1234567890）。 提取所有以 \u0026ldquo;test\u0026rdquo; 开头的单词。 代码模板：\nimport re text = \u0026#34;\u0026#34;\u0026#34; 联系人1：123-456-7890 联系人2：9876543210 测试数据：test1, test2, testing \u0026#34;\u0026#34;\u0026#34; # 匹配电话号码 phone_pattern = r\u0026#39;\\d{3}-\\d{3}-\\d{4}|\\d{10}\u0026#39; phones = re.findall(phone_pattern, text) print(\u0026#34;电话号码列表：\u0026#34;, phones) # 匹配以 test 开头的单词 test_pattern = r\u0026#39;\\btest\\w*\\b\u0026#39; test_words = re.findall(test_pattern, text) print(\u0026#34;以 test 开头的单词：\u0026#34;, test_words) 练习二：处理文件名 # 目标：熟悉正则表达式操作文件名。\n任务：\n在指定文件夹中，找到所有以 test_ 开头的文件。 将这些文件重命名为去掉 test_ 的新名字。 代码模板：\nimport os import re folder_path = r\u0026#39;F:\\your_folder\u0026#39; # 替换为你的文件夹路径 # 正则匹配以 test_ 开头的文件 pattern = re.compile(r\u0026#39;^test_(.+)$\u0026#39;) for root, dirs, files in os.walk(folder_path): for file in files: match = pattern.match(file) if match: new_name = match.group(1) os.rename(os.path.join(root, file), os.path.join(root, new_name)) print(f\u0026#34;重命名：{file} -\u0026gt; {new_name}\u0026#34;) 练习三：文本清洗 # 目标：使用正则表达式清洗文本数据。\n任务：\n移除文本中的所有 HTML 标签。 替换所有连续的空白字符为单个空格。 代码模板：\nimport re text = \u0026#34;\u0026#34;\u0026#34; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt;\u0026lt;title\u0026gt;测试页面\u0026lt;/title\u0026gt;\u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1\u0026gt;标题\u0026lt;/h1\u0026gt; \u0026lt;p\u0026gt;这是一个段落。\u0026lt;/p\u0026gt; \u0026lt;p\u0026gt; 还有一些 多余的空格。\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; \u0026#34;\u0026#34;\u0026#34; # 移除 HTML 标签 html_pattern = r\u0026#39;\u0026lt;[^\u0026gt;]+\u0026gt;\u0026#39; clean_text = re.sub(html_pattern, \u0026#39;\u0026#39;, text) print(\u0026#34;去掉 HTML 标签后的文本：\\n\u0026#34;, clean_text) # 替换连续空格为单个空格 space_pattern = r\u0026#39;\\s+\u0026#39; clean_text = re.sub(space_pattern, \u0026#39; \u0026#39;, clean_text) print(\u0026#34;清理空格后的文本：\\n\u0026#34;, clean_text) 练习四：验证用户输入 # 目标：使用正则表达式对用户输入进行格式验证。\n任务：\n验证一个输入是否是有效的电子邮件地址。 验证一个输入是否是有效的 URL。 代码模板：\nimport re # 验证电子邮件 email = input(\u0026#34;请输入一个电子邮件地址：\u0026#34;) email_pattern = r\u0026#39;^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$\u0026#39; if re.match(email_pattern, email): print(\u0026#34;有效的电子邮件地址\u0026#34;) else: print(\u0026#34;无效的电子邮件地址\u0026#34;) # 验证 URL url = input(\u0026#34;请输入一个 URL：\u0026#34;) url_pattern = r\u0026#39;^https?://[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}(/.*)?$\u0026#39; if re.match(url_pattern, url): print(\u0026#34;有效的 URL\u0026#34;) else: print(\u0026#34;无效的 URL\u0026#34;) 练习五：提取日志中的 IP 地址 # 目标：从日志文件中提取所有的 IP 地址。\n任务：\n打开一个日志文件。 提取其中的所有 IPv4 地址。 代码模板：\nimport re # 假设日志内容 log_data = \u0026#34;\u0026#34;\u0026#34; 192.168.0.1 - - [24/Nov/2024:14:15:22] \u0026#34;GET /index.html HTTP/1.1\u0026#34; 200 10.0.0.5 - - [24/Nov/2024:14:16:00] \u0026#34;POST /form.html HTTP/1.1\u0026#34; 404 Invalid log entry 123.456.789.0 \u0026#34;\u0026#34;\u0026#34; # 匹配 IPv4 地址 ip_pattern = r\u0026#39;((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\u0026#39; # 使用 finditer 获取迭代器 matches = re.finditer(ip_pattern, log_data) ips = [match.group(0) for match in matches] print(\u0026#34;提取的 IP 地址：\u0026#34;, ips) 练习六：复杂文件名处理 # 目标：掌握文件名匹配和高级正则表达式。\n任务：\n查找文件夹中的所有符合以下模式的文件名：课程编号_章节名_说明_时间戳.ext。 从文件名中提取 课程编号 和 章节名，生成新的文件名格式：课程编号-章节名.ext。 代码模板：\nimport os import re folder_path = r\u0026#39;F:\\your_folder\u0026#39; # 替换为你的文件夹路径 # 匹配复杂文件名 pattern = re.compile(r\u0026#39;^(课程\\d+)_(章节\\d+)_.+?_(\\d+)\\.(.+)$\u0026#39;) for root, dirs, files in os.walk(folder_path): for file in files: match = pattern.match(file) if match: course = match.group(1) chapter = match.group(2) ext = match.group(4) new_name = f\u0026#34;{course}-{chapter}.{ext}\u0026#34; os.rename(os.path.join(root, file), os.path.join(root, new_name)) print(f\u0026#34;重命名：{file} -\u0026gt; {new_name}\u0026#34;) 练习七：动态生成正则表达式 # 目标：掌握在代码中动态构造正则表达式。\n任务：\n根据用户输入的关键词列表，生成一个动态正则表达式，用来查找文本中包含任意关键词的句子。 代码模板：\nimport re text = \u0026#34;\u0026#34;\u0026#34; 今天我学习了正则表达式，它非常有用。 Python 编程语言让我感觉很强大。 我还想学习更多关于人工智能的知识。 \u0026#34;\u0026#34;\u0026#34; # 用户输入关键词 keywords = input(\u0026#34;请输入关键词，用空格分隔：\u0026#34;).split() keyword_pattern = \u0026#39;|\u0026#39;.join(map(re.escape, keywords)) # 动态构造正则表达式 sentence_pattern = fr\u0026#39;\\b.*?({keyword_pattern}).*?\\.\u0026#39; sentences = re.findall(sentence_pattern, text, re.IGNORECASE) print(\u0026#34;匹配的句子：\u0026#34;, sentences) 通过以上练习，你可以逐步熟悉 正则表达式的匹配规则，以及如何在 Python 文件操作 中使用正则表达式。完成每个任务后，试着改动规则或扩展功能，进一步加深理解！\n","date":"February 4, 2025","externalUrl":null,"permalink":"/notes/regex/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E7%BB%83%E4%B9%A0/","section":"笔记","summary":"以下是一些精心设计的正则表达式练习，结合了 Python 编程 和 文件操作，帮助你熟悉正则表达式在不同场景中的实际应用。\n练习一：基本字符匹配 # 目标：掌握正则表达式的基础功能。\n","title":"正则表达式练习","type":"notes"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E5%88%86%E7%BB%84/","section":"Tags","summary":"","title":"分组","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E5%8C%B9%E9%85%8D%E6%A8%A1%E5%BC%8F/","section":"Tags","summary":"","title":"匹配模式","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B/","section":"Tags","summary":"","title":"入门教程","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E5%85%83%E5%AD%97%E7%AC%A6/","section":"Tags","summary":"","title":"元字符","type":"tags"},{"content":"版本：v2.4.1 (2019-11-15) 作者：deerchao 转载请注明来源\n目录 # 本文目标 如何使用本教程 正则表达式到底是什么东西？ 入门 测试正则表达式 元字符 字符转义 重复 字符类 分枝条件 反义 分组 后向引用 零宽断言 负向零宽断言 注释 贪婪与懒惰 处理选项 平衡组/递归匹配 还有些什么东西没提到 联系作者 网上的资源及本文参考文献 更新纪录 本文目标 # 30分钟内让你明白正则表达式是什么，并对它有一些基本的了解，让你可以在自己的程序或网页里使用它。\n如何使用本教程 # 别被下面那些复杂的表达式吓倒，只要跟着我一步一步来，你会发现正则表达式其实并没有想像中的那么困难。当然，如果你看完了这篇教程之后，发现自己明白了很多，却又几乎什么都记不得，那也是很正常的——我认为，没接触过正则表达式的人在看完这篇教程后，能把提到过的语法记住80%以上的可能性为零。这里只是让你明白基本的原理，以后你还需要多练习，多使用，才能熟练掌握正则表达式。\n除了作为入门教程之外，本文还试图成为可以在日常工作中使用的正则表达式语法参考手册。就作者本人的经历来说，这个目标还是完成得不错的——你看，我自己也没能把所有的东西记下来，不是吗？\n清除格式 文本格式约定：专业术语 元字符/语法格式 正则表达式 正则表达式中的一部分(用于分析) 对其进行匹配的源字符串 对正则表达式或其中一部分的说明\n隐藏边注 本文右边有一些注释，主要是用来提供一些相关信息，或者给没有程序员背景的读者解释一些基本概念，通常可以忽略。\n本文介绍的大部分正则语法，在不同的正则表达式引擎中都可以使用，但也有一些会有所差异。本文介绍的是 .Net 下的正则表达式，其它环境下的具体情况可以在读完本文后去参考官方文档，或者查看正则表达式引擎特性对比。\n最重要的是——请给我30分钟，如果你没有使用正则表达式的经验，请不要试图在30秒内入门——除非你是超人 :)\n正则表达式到底是什么东西？ # 在编写处理字符串的程序或网页时，经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说，正则表达式就是记录文本规则的代码。\n很可能你使用过Windows/Dos下用于文件查找的通配符(wildcard)，也就是*和?。如果你想查找某个目录下的所有的Word文档的话，你会搜索*.doc。在这里，*会被解释成任意的字符串。和通配符类似，正则表达式也是用来进行文本匹配的工具，只不过比起通配符，它能更精确地描述你的需求——当然，代价就是更复杂——比如你可以编写一个正则表达式，用来查找所有以0开头，后面跟着2-3个数字，然后是一个连字号“-”，最后是7或8位数字的字符串(像010-12345678或0376-7654321)。\n字符是计算机软件处理文字时最基本的单位，可能是字母，数字，标点符号，空格，换行符，汉字等等。字符串是0个或更多个字符的序列。文本也就是文字，字符串。说某个字符串匹配某个正则表达式，通常是指这个字符串里有一部分（或几部分分别）能满足表达式给出的条件。\n入门 # 学习正则表达式的最好方法是从例子开始，理解例子之后再自己对例子进行修改，实验。下面给出了不少简单的例子，并对它们作了详细的说明。\n假设你在一篇英文小说里查找hi，你可以使用正则表达式hi。\n这几乎是最简单的正则表达式了，它可以精确匹配这样的字符串：由两个字符组成，前一个字符是h,后一个是i。通常，处理正则表达式的工具会提供一个忽略大小写的选项，如果选中了这个选项，它可以匹配hi,HI,Hi,hI这四种情况中的任意一种。\n不幸的是，很多单词里包含hi这两个连续的字符，比如him,history,high等等。用hi来查找的话，这里边的hi也会被找出来。如果要精确地查找hi这个单词的话，我们应该使用\\bhi\\b。\n\\b是正则表达式规定的一个特殊代码（好吧，某些人叫它元字符，metacharacter），代表着单词的开头或结尾，也就是单词的分界处。虽然通常英文的单词是由空格，标点符号或者换行来分隔的，但是\\b并不匹配这些单词分隔字符中的任何一个，它只匹配一个位置。\n如果需要更精确的说法，\\b匹配这样的位置：它的前一个字符和后一个字符不全是(一个是,一个不是或不存在)\\w。\n假如你要找的是hi后面不远处跟着一个Lucy，你应该用\\bhi\\b.*\\bLucy\\b。\n这里，.是另一个元字符，匹配除了换行符以外的任意字符。*同样是元字符，不过它代表的不是字符，也不是位置，而是数量——它指定*前边的内容可以连续重复使用任意次以使整个表达式得到匹配。因此，.*连在一起就意味着任意数量的不包含换行的字符。现在\\bhi\\b.*\\bLucy\\b的意思就很明显了：先是一个单词hi,然后是任意个任意字符(但不能是换行)，最后是Lucy这个单词。\n换行符就是\u0026rsquo;\\n\u0026rsquo;,ASCII编码为10(十六进制0x0A)的字符。\n如果同时使用其它元字符，我们就能构造出功能更强大的正则表达式。比如下面这个例子：\n0\\d\\d-\\d\\d\\d\\d\\d\\d\\d\\d匹配这样的字符串：以0开头，然后是两个数字，然后是一个连字号“-”，最后是8个数字(也就是中国的电话号码。当然，这个例子只能匹配区号为3位的情形)。\n这里的\\d是个新的元字符，匹配一位数字(0，或1，或2，或……)。-不是元字符，只匹配它本身——连字符(或者减号，或者中横线，或者随你怎么称呼它)。\n为了避免那么多烦人的重复，我们也可以这样写这个表达式：0\\d{2}-\\d{8}。这里\\d后面的{2}({8})的意思是前面\\d必须连续重复匹配2次(8次)。\n测试正则表达式 # 如果你不觉得正则表达式很难读写的话，要么你是一个天才，要么，你不是地球人。正则表达式的语法很令人头疼，即使对经常使用它的人来说也是如此。由于难于读写，容易出错，所以找一种工具对正则表达式进行测试是很有必要的。\n不同的环境下正则表达式的一些细节是不相同的，本教程介绍的是微软 .Net Framework 4.x 下正则表达式的行为，所以，我向你推荐我编写的.Net下的工具 Regester。请参考该页面的说明来安装和运行该软件。\n下面是Regester运行时的截图：\n你也可以试试这个在线测试工具：Wegester, JavaScript正则表达式测试器。\n现在你已经知道几个很有用的元字符了，如\\b,.,*，还有\\d.正则表达式里还有更多的元字符，比如\\s匹配任意的空白符，包括空格，制表符(Tab)，换行符，中文全角空格等。\\w匹配字母或数字或下划线或汉字等。\n对中文/汉字的特殊处理是由.Net提供的正则表达式引擎支持的，其它环境下的具体情况请查看相关文档。\n下面来看看更多的例子：\n\\ba\\w*\\b匹配以字母a开头的单词——先是某个单词开始处(\\b)，然后是字母a,然后是任意数量的字母或数字(\\w*)，最后是单词结束处(\\b)。\n\\d+匹配1个或更多连续的数字。这里的+是和*类似的元字符，不同的是*匹配重复任意次(可能是0次)，而+则匹配重复1次或更多次。\n\\b\\w{6}\\b 匹配刚好6个字符的单词。\n好吧，现在我们说说正则表达式里的单词是什么意思吧：就是不少于一个的连续的\\w。不错，这与学习英文时要背的成千上万个同名的东西的确关系不大 :)\n代码 说明 . 匹配除换行符以外的任意字符 \\w 匹配字母或数字或下划线或汉字 \\s 匹配任意的空白符 \\d 匹配数字 \\b 匹配单词的开始或结束 ^ 匹配字符串的开始 $ 匹配字符串的结束 元字符^（和数字6在同一个键位上的符号）和$都匹配一个位置，这和\\\\b有点类似。^匹配你要用来查找的字符串的开头，$匹配结尾。这两个代码在验证输入的内容时非常有用，比如一个网站如果要求你填写的QQ号必须为5位到12位数字时，可以使用：^\\d{5,12}$。\n这里的{5,12}和前面介绍过的{2}是类似的，只不过{2}匹配只能不多不少重复2次，{5,12}则是重复的次数不能少于5次，不能多于12次，否则都不匹配。\n因为使用了^和$，所以输入的整个字符串都要用来和\\d{5,12}来匹配，也就是说整个输入必须是5到12个数字，因此如果输入的QQ号能匹配这个正则表达式的话，那就符合要求了。\n和忽略大小写的选项类似，有些正则表达式处理工具还有一个处理多行的选项。如果选中了这个选项，^和$的意义就变成了匹配行的开始处和结束处。\n正则表达式引擎通常会提供一个“测试指定的字符串是否匹配一个正则表达式”的方法，如JavaScript里的RegExp.test()方法或.NET里的Regex.IsMatch()方法。这里的匹配是指是字符串里有没有符合表达式规则的部分。如果不使用^和$的话，对于\\d{5,12}而言，使用这样的方法就只能保证字符串里包含5到12连续位数字，而不是整个字符串就是5到12位数字。\n字符转义 # 如果你想查找元字符本身的话，比如你查找.,或者*,就出现了问题：你没办法指定它们，因为它们会被解释成别的意思。这时你就得使用\\来取消这些字符的特殊意义。因此，你应该使用\\.和\\*。当然，要查找\\本身，你也得用\\\\.\n例如：deerchao\\.cn匹配deerchao.cn，C:\\\\Windows匹配C:\\Windows。\n重复 # 你已经看过了前面的*,+,{2},{5,12}这几个匹配重复的方式了。下面是正则表达式中所有的限定符(指定数量的代码，例如*,{5,12}等)：\n代码/语法 说明 * 重复零次或更多次 + 重复一次或更多次 ? 重复零次或一次 {n} 重复n次 {n,} 重复n次或更多次 {n,m} 重复n到m次 下面是一些使用重复的例子：\nWindows\\d+匹配Windows后面跟1个或更多数字\n^\\w+匹配一行的第一个单词(或整个字符串的第一个单词，具体匹配哪个意思得看选项设置)\n字符类 # 要想查找数字，字母或数字，空白是很简单的，因为已经有了对应这些字符集合的元字符，但是如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办？\n很简单，你只需要在方括号里列出它们就行了，像\n\\[aeiou\\]就匹配任何一个英文元音字母，\n\\[.?!\\]匹配标点符号(.或?或!)。\n我们也可以轻松地指定一个字符范围，像\n\\[0-9\\]代表的含意与\\d就是完全一致的：一位数字；同理\n\\[a-z0-9A-Z\\_\\]也完全等同于\\w（如果只考虑英文的话）。\n下面是一个更复杂的表达式：\\(?0\\d{2}\n\\[) -\\]?\\d{8}。\n这个表达式可以匹配几种格式的电话号码，像(010)88886666，或022-22334455，或02912345678等。我们对它进行一些分析吧：首先是一个转义字符\\(,它能出现0次或1次(?),然后是一个0，后面跟着2个数字(\\d{2})，然后是)或-或空格中的一个，它出现1次或不出现(?)，最后是8个数字(\\d{8})。\n“(”和“)”也是元字符，后面的分组节里会提到，所以在这里需要使用转义。\n分枝条件 # 不幸的是，刚才那个表达式也能匹配010)12345678或(022-87654321这样的“不正确”的格式。要解决这个问题，我们需要用到分枝条件。正则表达式里的分枝条件指的是有几种规则，如果满足其中任意一种规则都应该当成匹配，具体方法是用|把不同的规则分隔开。听不明白？没关系，看例子：\n0\\d{2}-\\d{8}|0\\d{3}-\\d{7}这个表达式能匹配两种以连字号分隔的电话号码：一种是三位区号，8位本地号(如010-12345678)，一种是4位区号，7位本地号(0376-2233445)。\n\\(0\\d{2}\\)\n\\[- \\]?\\d{8}|0\\d{2}\n\\[- \\]?\\d{8}这个表达式匹配3位区号的电话号码，其中区号可以用小括号括起来，也可以不用，区号与本地号间可以用连字号或空格间隔，也可以没有间隔。你可以试试用分枝条件把这个表达式扩展成也支持4位区号的。\n\\d{5}-\\d{4}|\\d{5}这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字，或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题：使用分枝条件时，要注意各个条件的顺序。如果你把它改成\\d{5}|\\d{5}-\\d{4}的话，那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时，将会从左到右地测试每个条件，如果满足了某个分枝的话，就不会去再管其它的条件了。\n分组 # 我们已经提到了怎么重复单个字符（直接在字符后面加上限定符就行了）；但如果想要重复多个字符又该怎么办？你可以用小括号来指定子表达式(也叫做分组)，然后你就可以指定这个子表达式的重复次数了，你也可以对子表达式进行其它一些操作(后面会有介绍)。\n(\\d{1,3}\\.){3}\\d{1,3}是一个简单的IP地址匹配表达式。要理解这个表达式，请按下列顺序分析它：\\d{1,3}匹配1到3位的数字，(\\d{1,3}\\.){3}匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复3次，最后再加上一个一到三位的数字(\\d{1,3})。\n不幸的是，它也将匹配256.300.888.999这种不可能存在的IP地址。如果能使用算术比较的话，或许能简单地解决这个问题，但是正则表达式中并不提供关于数学的任何功能，所以只能使用冗长的分组，选择，字符类来描述一个正确的IP地址：((2\n\\[0-4\\]\\d|25\n\\[0-5\\]|\n\\[01\\]?\\d\\d?)\\.){3}(2\n\\[0-4\\]\\d|25\n\\[0-5\\]|\n\\[01\\]?\\d\\d?)。\n理解这个表达式的关键是理解2\n\\[0-4\\]\\d|25\n\\[0-5\\]|\n\\[01\\]?\\d\\d?，这里我就不细说了，你自己应该能分析得出来它的意义。\nIP地址中每个数字都不能大于255. 经常有人问我, 01.02.03.04 这样前面带有0的数字, 是不是正确的IP地址呢? 答案是: 是的, IP 地址里的数字可以包含有前导 0 (leading zeroes).\n反义 # 有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外，其它任意字符都行的情况，这时需要用到反义：\n代码/语法 说明 \\W 匹配任意不是字母，数字，下划线，汉字的字符 \\S 匹配任意不是空白符的字符 \\D 匹配任意非数字的字符 \\B 匹配不是单词开头或结束的位置 \\[^x\\] 匹配除了x以外的任意字符 \\[^aeiou\\] 匹配除了aeiou这几个字母以外的任意字符 例子：\\S+匹配不包含空白符的字符串。\n\u0026lt;a\n\\[^\u003e\\]+\u0026gt;匹配用尖括号括起来的以a开头的字符串。\n以下是将你提供的内容修改成适合 Markdown 格式的笔记版本：\n后向引用 # 使用小括号指定一个子表达式后，匹配这个子表达式的文本（也就是此分组捕获的内容）可以在表达式或其它程序中作进一步的处理。默认情况下，每个分组会自动拥有一个组号，规则是：从左向右，以分组的左括号为标志，第一个出现的分组的组号为 1，第二个为 2，以此类推。\n实际上，组号分配过程要从左向右扫描两遍：第一遍只给未命名组分配，第二遍只给命名组分配——因此所有命名组的组号都大于未命名的组号。你可以使用 (?:exp) 这样的语法来剥夺一个分组对组号分配的参与权。\n后向引用示例 # 后向引用用于重复搜索前面某个分组匹配的文本。例如，\\\\1 代表分组 1 匹配的文本。以下正则表达式可以用来匹配重复的单词：\n\\\\b(\\\\w+)\\\\b\\\\s+\\\\1\\\\b 这个表达式首先匹配一个单词，然后是 1 个或几个空白符，最后是分组 1 中捕获的内容（也就是前面匹配的那个单词）。\n你也可以自己指定子表达式的组名。要指定一个子表达式的组名，请使用这样的语法：\n(?\u0026lt;Word\u0026gt;\\\\w+) 然后可以使用 \\\\k\u0026lt;Word\u0026gt; 来反向引用这个分组捕获的内容，所以上一个例子可以改写为：\n\\\\b(?\u0026lt;Word\u0026gt;\\\\w+)\\\\b\\\\s+\\\\k\u0026lt;Word\u0026gt;\\\\b 常用分组语法 # 分类 代码/语法 说明 捕获 (exp) 匹配 exp 并捕获文本到自动命名的组里 捕获命名组 (?\u0026lt;name\u0026gt;exp) 匹配 exp 并捕获文本到名称为 name 的组里 非捕获分组 (?:exp) 匹配 exp，不捕获匹配的文本，也不给此分组分配组号 零宽断言 (?=exp) 匹配 exp 前面的位置 零宽回顾 (?\u0026lt;=exp) 匹配 exp 后面的位置 负向零宽断言 (?!exp) 匹配后面跟的不是 exp 的位置 负向零宽回顾 (?\u0026lt;!exp) 匹配前面不是 exp 的位置 注释 (?#comment) 这类分组不会影响正则表达式的处理，用于注释 零宽断言 # 零宽断言用于查找在某些内容（但并不包括这些内容）之前或之后的东西。它们像 \\\\b, ^, $ 那样用于指定一个位置，这个位置应该满足一定的条件（即断言），因此它们也被称为零宽断言。\n正向先行断言 # (?=exp) 也叫零宽度正预测先行断言，它断言当前位置后面能匹配 exp。\n例如：\n\\\\b\\\\w+(?=ing\\\\b) 它会匹配以 ing 结尾的单词的前部分。\n正回顾后发断言 # (?\u0026lt;=exp) 也叫零宽度正回顾后发断言，它断言当前位置前面能匹配 exp。\n例如：\n(?\u0026lt;=\\\\bre)\\\\w+\\\\b 它会匹配以 re 开头的单词的后半部分。\n负向零宽断言 # 如果你想要确保某个字符没有出现，可以使用负向零宽断言。例如，匹配包含字母 q 且后面不是字母 u 的单词：\n\\\\b\\\\w*q[^u]\\\\w*\\\\b 如果 q 是单词的最后一个字符，可以使用负向先行断言解决问题：\n\\\\b\\\\w*q(?!u)\\\\w*\\\\b 示例 # \\\\d{3}(?!\\\\d) 匹配三位数字，且后面不能是数字。 (?\u0026lt;=\u0026lt;(\\\\w+)\u0026gt;).*?(?=\u0026lt;\\\\/\\\\1\u0026gt;) 匹配 HTML 标签内的内容。 贪婪与懒惰 # 贪婪匹配表示尽可能多地匹配字符，而懒惰匹配表示尽可能少地匹配字符。\n贪婪匹配 # 例如：\na.*b 它会匹配以 a 开头，以 b 结尾的最长字符串。\n懒惰匹配 # 懒惰匹配通过在量词后面加上问号 ? 来实现：\na.*?b 它会匹配以 a 开头，以 b 结尾的最短字符串。\n处理选项 # 在正则表达式中，你可以使用一些选项来改变处理方式，例如忽略大小写、处理多行等。\n名称 说明 IgnoreCase 匹配时不区分大小写 Multiline 使 ^ 和 $ 匹配任意一行的行首和行尾 Singleline 使 . 匹配所有字符（包括换行符） IgnorePatternWhitespace 忽略表达式中的空白符并启用注释 ExplicitCapture 仅捕获显式命名的组 平衡组/递归匹配 # 平衡组语法用于匹配嵌套的层次结构，如括号之间的内容。以下是一个使用平衡组的正则表达式示例：\n\u0026lt; # 最外层的左括号 [^\u0026lt;\u0026gt;]* # 非括号的内容 ( ( (?\u0026#39;Open\u0026#39;\u0026lt;) # 左括号，压入 \u0026#34;Open\u0026#34; [^\u0026lt;\u0026gt;]* # 左括号后面的内容 )+ ( (?\u0026#39;-Open\u0026#39;\u0026gt;) # 右括号，弹出 \u0026#34;Open\u0026#34; [^\u0026lt;\u0026gt;]* # 右括号后面的内容 )+ )* (?(Open)(?!)) # 检查是否存在未弹出的 \u0026#34;Open\u0026#34; \u0026gt; # 最外层的右括号 这个示例可以匹配嵌套的 \u0026lt;div\u0026gt; 标签。\n其他未提到的内容 # 代码/语法 说明 \\\\a 报警字符（电脑发出嘀声） \\\\b 单词边界（在字符类里是退格符） \\\\t 制表符（Tab） \\\\r 回车符 \\\\n 换行符 \\\\xnn 十六进制表示的 ASCII 字符 \\\\unnnn Unicode 字符 \\\\A 字符串开头（类似 ^） \\\\Z 字符串结尾或行尾（不受多行选项影响） ","date":"February 4, 2025","externalUrl":null,"permalink":"/notes/regex/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F30%E5%88%86%E9%92%9F%E5%85%A5%E9%97%A8/","section":"笔记","summary":"版本：v2.4.1 (2019-11-15) 作者：deerchao 转载请注明来源\n目录 # 本文目标 如何使用本教程 正则表达式到底是什么东西？ 入门 测试正则表达式 元字符 字符转义 重复 字符类 分枝条件 反义 分组 后向引用 零宽断言 负向零宽断言 注释 贪婪与懒惰 处理选项 平衡组/递归匹配 还有些什么东西没提到 联系作者 网上的资源及本文参考文献 更新纪录 本文目标 # 30分钟内让你明白正则表达式是什么，并对它有一些基本的了解，让你可以在自己的程序或网页里使用它。\n","title":"正则表达式30分钟入门","type":"notes"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/pip/","section":"Tags","summary":"","title":"Pip","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/categories/python/","section":"Categories","summary":"","title":"Python","type":"categories"},{"content":" 1. 基础结构 # setup.py 是 Python 打包的核心配置文件，需使用 setuptools 库定义包的元数据和构建规则。\n基本模板 # from setuptools import setup, find_packages setup( name=\u0026#34;your-package-name\u0026#34;, # 包名（PyPI唯一标识） version=\u0026#34;0.1.0\u0026#34;, # 版本号 author=\u0026#34;Your Name\u0026#34;, author_email=\u0026#34;your@email.com\u0026#34;, description=\u0026#34;Short description\u0026#34;, long_description=open(\u0026#34;README.md\u0026#34;).read(), # 长描述（README内容） long_description_content_type=\u0026#34;text/markdown\u0026#34;, # README格式 url=\u0026#34;https://github.com/yourusername/your-package\u0026#34;, # 项目地址 packages=find_packages(), # 自动发现包目录 install_requires=[], # 依赖库列表 entry_points={}, # 命令行工具入口 classifiers=[], # PyPI分类标签 python_requires=\u0026#34;\u0026gt;=3.6\u0026#34;, # Python版本要求 ) 2. 关键参数详解 # (1) name # 作用：包在 PyPI 上的唯一标识符。 规则： 必须全小写，可用连字符（-），如 my-package。 需在 PyPI 上未被占用（上传前检查）。 (2) version # 作用：包的版本号，每次更新必须递增。 格式：推荐语义化版本（如 1.0.0, 0.2.5）。 (3) packages # 作用：指定包含的 Python 包目录。 自动发现： packages=find_packages(), # 自动搜索所有含 __init__.py 的目录 手动指定： packages=[\u0026#34;my_package\u0026#34;, \u0026#34;my_package.submodule\u0026#34;], (4) install_requires # 作用：定义包的依赖库及版本约束。 示例： install_requires=[ \u0026#34;requests\u0026gt;=2.25.1\u0026#34;, # 最低版本 \u0026#34;numpy\u0026lt;1.22,\u0026gt;=1.20\u0026#34;, # 版本范围 \u0026#34;pandas\u0026#34;, # 不限制版本 ] (5) entry_points # 作用：定义命令行工具入口，生成可执行命令。 示例： entry_points={ \u0026#34;console_scripts\u0026#34;: [ \u0026#34;my-command=my_package.module:main\u0026#34;, # 格式：命令名=包.模块:函数 ], } 用户安装后可直接运行 my-command。 (6) package_data # 作用：包含包内的非代码文件（如数据、配置文件）。 示例： package_data={ \u0026#34;my_package\u0026#34;: [\u0026#34;data/*.csv\u0026#34;, \u0026#34;templates/*.html\u0026#34;], }, (7) include_package_data # 作用：自动包含 MANIFEST.in 中指定的文件。 用法： include_package_data=True, # 需配合 MANIFEST.in 文件 (8) classifiers # 作用：为 PyPI 提供包的分类标签（便于搜索）。 常用选项： classifiers=[ \u0026#34;Programming Language :: Python :: 3\u0026#34;, \u0026#34;License :: OSI Approved :: MIT License\u0026#34;, \u0026#34;Operating System :: OS Independent\u0026#34;, \u0026#34;Topic :: Software Development :: Libraries\u0026#34;, ] 完整列表见：PyPI Classifiers 3. 完整示例 # 假设项目结构如下：\nmy_project/ ├── my_package/ │ ├── __init__.py │ ├── main.py │ └── data/ │ └── config.json ├── README.md ├── LICENSE └── setup.py 对应的 setup.py：\nfrom setuptools import setup, find_packages setup( name=\u0026#34;my-awesome-tool\u0026#34;, version=\u0026#34;0.1.0\u0026#34;, author=\u0026#34;Jane Doe\u0026#34;, author_email=\u0026#34;jane@example.com\u0026#34;, description=\u0026#34;A tool to do awesome things.\u0026#34;, long_description=open(\u0026#34;README.md\u0026#34;).read(), long_description_content_type=\u0026#34;text/markdown\u0026#34;, url=\u0026#34;https://github.com/janedoe/my-awesome-tool\u0026#34;, packages=find_packages(), install_requires=[ \u0026#34;requests\u0026gt;=2.25.1\u0026#34;, \u0026#34;click\u0026gt;=8.0.0\u0026#34;, # 命令行库 ], entry_points={ \u0026#34;console_scripts\u0026#34;: [ \u0026#34;awesome-tool=my_package.main:cli\u0026#34;, # 假设 main.py 有 cli() 函数 ], }, package_data={ \u0026#34;my_package\u0026#34;: [\u0026#34;data/*.json\u0026#34;], # 包含 data 目录下的 JSON 文件 }, classifiers=[ \u0026#34;Programming Language :: Python :: 3\u0026#34;, \u0026#34;License :: OSI Approved :: MIT License\u0026#34;, \u0026#34;Operating System :: OS Independent\u0026#34;, ], python_requires=\u0026#34;\u0026gt;=3.7\u0026#34;, ) 4. 包含额外文件：MANIFEST.in # 如果需要包含包外的文件（如文档、测试数据），需创建 MANIFEST.in 文件：\ninclude LICENSE README.md recursive-include docs * recursive-include tests * 5. 验证与打包 # (1) 生成分发包 # pip install setuptools wheel # 确保工具已安装 python setup.py sdist bdist_wheel # 生成源码包和二进制包 生成文件在 dist/ 目录。 (2) 本地测试安装 # pip install . # 在当前目录安装包 awesome-tool # 测试命令行工具 6. 关键注意事项 # 包名唯一性：在 PyPI 搜索确认包名未被占用。 版本号递增：每次更新代码后需修改 version。 依赖管理：精确指定依赖版本，避免未来更新导致兼容性问题。 许可证：必须包含 LICENSE 文件，并在 classifiers 中声明。 入口函数：确保 entry_points 指向的函数已正确定义。 通过以上步骤，你可以编写一个完整的 setup.py 文件，为发布到 PyPI 做好准备。\n7. 提一嘴之生成可执行文件 # 使用工具如 PyInstaller 或 cx_Freeze 将Python脚本转换为独立的可执行文件：\n1. 使用 PyInstaller # 安装： pip install pyinstaller 生成可执行文件： pyinstaller --onefile your_script.py --onefile 生成单个可执行文件。\n输出在 dist/ 目录中。\n跨平台：需在目标操作系统下分别打包。\n2. 注意事项 # 依赖处理：PyInstaller会自动分析脚本依赖，但某些复杂情况（如动态导入）可能需要手动配置（使用 .spec 文件）。\n文件大小：因包含Python解释器和依赖库，生成文件较大。\n","date":"February 4, 2025","externalUrl":null,"permalink":"/notes/tools/python%E4%BD%BF%E7%94%A8pip%E4%B9%8Bsetup-py%E7%BC%96%E5%86%99/","section":"笔记","summary":"1. 基础结构 # setup.py 是 Python 打包的核心配置文件，需使用 setuptools 库定义包的元数据和构建规则。\n基本模板 # from setuptools import setup, find_packages setup( name=\"your-package-name\", # 包名（PyPI唯一标识） version=\"0.1.0\", # 版本号 author=\"Your Name\", author_email=\"your@email.com\", description=\"Short description\", long_description=open(\"README.md\").read(), # 长描述（README内容） long_description_content_type=\"text/markdown\", # README格式 url=\"https://github.com/yourusername/your-package\", # 项目地址 packages=find_packages(), # 自动发现包目录 install_requires=[], # 依赖库列表 entry_points={}, # 命令行工具入口 classifiers=[], # PyPI分类标签 python_requires=\"\u003e=3.6\", # Python版本要求 ) 2. 关键参数详解 # (1) name # 作用：包在 PyPI 上的唯一标识符。 规则： 必须全小写，可用连字符（-），如 my-package。 需在 PyPI 上未被占用（上传前检查）。 (2) version # 作用：包的版本号，每次更新必须递增。 格式：推荐语义化版本（如 1.0.0, 0.2.5）。 (3) packages # 作用：指定包含的 Python 包目录。 自动发现： packages=find_packages(), # 自动搜索所有含 __init__.py 的目录 手动指定： packages=[\"my_package\", \"my_package.submodule\"], (4) install_requires # 作用：定义包的依赖库及版本约束。 示例： install_requires=[ \"requests\u003e=2.25.1\", # 最低版本 \"numpy\u003c1.22,\u003e=1.20\", # 版本范围 \"pandas\", # 不限制版本 ] (5) entry_points # 作用：定义命令行工具入口，生成可执行命令。 示例： entry_points={ \"console_scripts\": [ \"my-command=my_package.module:main\", # 格式：命令名=包.模块:函数 ], } 用户安装后可直接运行 my-command。 (6) package_data # 作用：包含包内的非代码文件（如数据、配置文件）。 示例： package_data={ \"my_package\": [\"data/*.csv\", \"templates/*.html\"], }, (7) include_package_data # 作用：自动包含 MANIFEST.in 中指定的文件。 用法： include_package_data=True, # 需配合 MANIFEST.in 文件 (8) classifiers # 作用：为 PyPI 提供包的分类标签（便于搜索）。 常用选项： classifiers=[ \"Programming Language :: Python :: 3\", \"License :: OSI Approved :: MIT License\", \"Operating System :: OS Independent\", \"Topic :: Software Development :: Libraries\", ] 完整列表见：PyPI Classifiers 3. 完整示例 # 假设项目结构如下：\n","title":"python使用pip之setup.py编写","type":"notes"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/setup.py/","section":"Tags","summary":"","title":"Setup.py","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/setuptools/","section":"Tags","summary":"","title":"Setuptools","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/ja/categories/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0/","section":"Categories","summary":"","title":"プログラミング","type":"categories"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E5%8C%85%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"包管理","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/categories/%E7%BC%96%E7%A8%8B/","section":"Categories","summary":"","title":"编程","type":"categories"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/android/","section":"Tags","summary":"","title":"Android","type":"tags"},{"content":" 3. Add a provider that offers secure DNS # This is where it can get a bit tricky. You need to have the address of a provider that offers Private DNS. Here\u0026rsquo;s my provider of choice: 1dot1dot1dot1.cloudflare-dns.com Some other possible hostnames you can use: # Google DNS: dns.google\nQuad9: dns.quad9.net\nCleanbrowsing DNS: security-filter-dns.cleanbrowsing.org\nOpen DNS: 208.67.222.222\nNextDNS: 45.90.28.0\nComodo Secure 8.26.56.26\nOpenNIC: 192.95.54.3\nNote: Although the above free DNS services are all worth trying, I recommend going with Cloudflare (1dot1dot1dot1.cloudflare-dns.com). I find it the fastest and the most secure of the bunch. On top of the speed, Cloudflare adds DNS filtering into the mix, which can help prevent email from being sent from malicious IP addresses.\n","date":"February 4, 2025","externalUrl":null,"permalink":"/notes/tools/android-privatedns/","section":"笔记","summary":"3. Add a provider that offers secure DNS # This is where it can get a bit tricky. You need to have the address of a provider that offers Private DNS. Here’s my provider of choice: 1dot1dot1dot1.cloudflare-dns.com Some other possible hostnames you can use: # Google DNS: dns.google\n","title":"Android-PrivateDNS","type":"notes"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/","section":"Categories","summary":"","title":"计算机","type":"categories"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E7%BD%91%E7%BB%9C/","section":"Tags","summary":"","title":"网络","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E7%A7%BB%E5%8A%A8%E8%AE%BE%E5%A4%87/","section":"Tags","summary":"","title":"移动设备","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E9%9A%90%E7%A7%81/","section":"Tags","summary":"","title":"隐私","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/categories/%E6%9D%82/","section":"Categories","summary":"","title":"杂","type":"categories"},{"content":"You can find my dotfiles here.\nOpen a terminal window # Open a terminal window on your macOs or linux machine.\nI’m using Alacritty on macOs and I’m using the zsh shell.\nInstall Homebrew # Run the following command:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; If necessary, when prompted, enter your password here and press enter. If you haven’t installed the XCode Command Line Tools, when prompted, press enter and homebrew will install this as well.\nAdd To Path (Only Apple Silicon Macbooks) # After installing, add it to the path. This step shouldn’t be necessary on Intel macs.\nRun the following two commands to do so:\necho \u0026#39;eval \u0026#34;$(/opt/homebrew/bin/brew shellenv)\u0026#34;\u0026#39; \u0026gt;\u0026gt; ~/.zprofile eval \u0026#34;$(/opt/homebrew/bin/brew shellenv)\u0026#34; #1: fzf (command line fuzzy finder) # fzf is an amazing fuzzy finder for the command line.\nInstall fzf with homebrew:\nThis will vary slightly depending on the shell that you’re using, I’m using zsh.\nOpen ~/.zshrc file with your favorite editor. I personally use Neovim.\nWith TextEdit:\nWith VSCode (you have to have it installed):\nWith Neovim (what I use):\nTo the bottom of the file add:\n# ---- FZF ----- # Set up fzf key bindings and fuzzy completion eval \u0026#34;$(fzf --zsh)\u0026#34; After saving, in your terminal reload the zsh configuration:\nNow you can use fzf!\nExamples of what you can do with it:\nExample Description CTRL-t Look for files and directories CTRL-r Look through command history Enter Select the item Ctrl-j or Ctrl-n or Down arrow Go down one result Ctrl-k or Ctrl-p or Up arrow Go up one result Tab Mark a result Shift-Tab Unmark a result cd **Tab Open up fzf to find directory export **Tab Look for env variable to export unset **Tab Look for env variable to unset unalias **Tab Look for alias to unalias ssh **Tab Look for recently visited host names kill -9 **Tab Look for process name to kill to get pid any command (like nvim or code) + **Tab Look for files \u0026amp; directories to complete command Use fd with fzf # fzf by default uses the find command to look for files and directories.\nLet’s replace that with fd for better functionality, like ignoring files ignored by git (with .gitignore).\nInstall fd:\nOpen ~/.zshrc with preferred text editor.\nAdd to the bottom of this file:\n# -- Use fd instead of fzf -- export FZF_DEFAULT_COMMAND=\u0026#34;fd --hidden --strip-cwd-prefix --exclude .git\u0026#34; export FZF_CTRL_T_COMMAND=\u0026#34;$FZF_DEFAULT_COMMAND\u0026#34; export FZF_ALT_C_COMMAND=\u0026#34;fd --type=d --hidden --strip-cwd-prefix --exclude .git\u0026#34; # Use fd (https://github.com/sharkdp/fd) for listing path candidates. # - The first argument to the function ($1) is the base path to start traversal # - See the source code (completion.{bash,zsh}) for the details. _fzf_compgen_path() { fd --hidden --exclude .git . \u0026#34;$1\u0026#34; } # Use fd to generate the list for directory completion _fzf_compgen_dir() { fd --type=d --hidden --exclude .git . \u0026#34;$1\u0026#34; } After saving, in your terminal do:\nSetup fzf-git # fzf-git is a really nice script to look for git related things (commits, hashes, files and more) with fzf.\nNavigate to your home directory:\nClone the repository:\ngit clone https://github.com/junegunn/fzf-git.sh.git Open ~/.zshrc with preferred text editor and add to the bottom:\nsource ~/fzf-git.sh/fzf-git.sh Save and in terminal run:\nNow you can use the following:\nKeybind Description CTRL-GF Look for git files with fzf CTRL-GB Look for git branches with fzf CTRL-GT Look for git tags with fzf CTRL-GR Look for git remotes with fzf CTRL-GH Look for git commit hashes with fzf CTRL-GS Look for git stashes with fzf CTRL-GL Look for git reflogs with fzf CTRL-GW Look for git worktrees with fzf CTRL-GE Look for git for-each-ref with fzf Hold down CTRL and then press GF or one of the others consecutively and quickly.\nSetup a custom theme for fzf # You can also setup a custom theme for fzf.\nTo use mine you can add this to ~/.zshrc:\n# --- setup fzf theme --- fg=\u0026#34;#CBE0F0\u0026#34; bg=\u0026#34;#011628\u0026#34; bg_highlight=\u0026#34;#143652\u0026#34; purple=\u0026#34;#B388FF\u0026#34; blue=\u0026#34;#06BCE4\u0026#34; cyan=\u0026#34;#2CF9ED\u0026#34; export FZF_DEFAULT_OPTS=\u0026#34;--color=fg:${fg},bg:${bg},hl:${purple},fg+:${fg},bg+:${bg_highlight},hl+:${purple},info:${blue},prompt:${cyan},pointer:${cyan},marker:${cyan},spinner:${cyan},header:${cyan}\u0026#34; If you want to make your own check out the theme generator\n#2: bat (better cat) # bat is a really nice alternative to cat to display file contents in the terminal with syntax highlighting.\nInstall with homebrew:\nNow you can start using it!\nExample:\nThe example above would print out the contents of a file called filename.txt.\nTo see examples of all of the themes you can use:\nbat --list-themes | fzf --preview=\u0026#34;bat --theme={} --color=always /path/to/file\u0026#34; Make sure to replace /path/to/file with the path to an actual file.\nInstall a custom theme # If you find a theme on github for example, you can follow these steps.\nThe first time you do this create the following directory:\nmkdir -p \u0026#34;$(bat --config-dir)/themes\u0026#34; Then move into it:\ncd \u0026#34;$(bat --config-dir)/themes\u0026#34; Then you can download the file for the theme you want to use into this directory.\nThe file should use the .tmTheme extension.\nFor example:\ncurl -O https://raw.githubusercontent.com/folke/tokyonight.nvim/main/extras/sublime/tokyonight_night.tmTheme This will install the night version of the tokyonight theme.\nThen to use it run:\nAnd then add the following to your ~/.zshrc:\n# ----- Bat (better cat) ----- export BAT_THEME=tokyonight_night Replace tokyonight_night with the name of the theme you would like to use.\nThen save this file and run:\n#3: delta (better git diff) # delta is a great way to produce better git diffs using the syntax highlighting functionality of bat.\nInstall it:\nThen open your ~/.gitconfig file.\nWith TextEdit:\nWith VSCode (you have to have it installed):\nWith Neovim (what I use):\nAdd the following to it:\n[core] pager = delta [interactive] diffFilter = delta --color-only [delta] navigate = true # use n and N to move between diff sections side-by-side = true # delta detects terminal colors automatically; set one of these to disable auto-detection # dark = true # light = true [merge] conflictstyle = diff3 [diff] colorMoved = default You can remove side-by-side=true if you don’t want side by side diffs.\nAfter saving this file your git diffs should look a lot nicer.\nSetup fzf previews # Now that you have eza and bat setup you can setup some really nice previews for fzf.\nIn your ~/.zshrc, where you’re configuring fzf, add the following:\nexport FZF_CTRL_T_OPTS=\u0026#34;--preview \u0026#39;bat -n --color=always --line-range :500 {}\u0026#39;\u0026#34; export FZF_ALT_C_OPTS=\u0026#34;--preview \u0026#39;eza --tree --color=always {} | head -200\u0026#39;\u0026#34; # Advanced customization of fzf options via _fzf_comprun function # - The first argument to the function is the name of the command. # - You should make sure to pass the rest of the arguments to fzf. _fzf_comprun() { local command=$1 shift case \u0026#34;$command\u0026#34; in cd) fzf --preview \u0026#39;eza --tree --color=always {} | head -200\u0026#39; \u0026#34;$@\u0026#34; ;; export|unset) fzf --preview \u0026#34;eval \u0026#39;echo $\u0026#39;{}\u0026#34; \u0026#34;$@\u0026#34; ;; ssh) fzf --preview \u0026#39;dig {}\u0026#39; \u0026#34;$@\u0026#34; ;; *) fzf --preview \u0026#34;bat -n --color=always --line-range :500 {}\u0026#34; \u0026#34;$@\u0026#34; ;; esac } This will setup file previews with bat as well as directory previews with eza and some other previews as well.\nThere is one slight issue with this code though. When we are looking for files and directories with Ctrl-T or doing something like nvim **Tab, we are using bat to generate the preview but this won’t work for directories and we’ll get an error.\nWe can fix this by using an if statement that checks whether the path to what we should preview is a directory or not and use eza instead for the preview in this case.\nTo do this you can replace the previous code with something like the following:\nshow_file_or_dir_preview=\u0026#34;if [ -d {} ]; then eza --tree --color=always {} | head -200; else bat -n --color=always --line-range :500 {}; fi\u0026#34; export FZF_CTRL_T_OPTS=\u0026#34;--preview \u0026#39;$show_file_or_dir_preview\u0026#39;\u0026#34; export FZF_ALT_C_OPTS=\u0026#34;--preview \u0026#39;eza --tree --color=always {} | head -200\u0026#39;\u0026#34; # Advanced customization of fzf options via _fzf_comprun function # - The first argument to the function is the name of the command. # - You should make sure to pass the rest of the arguments to fzf. _fzf_comprun() { local command=$1 shift case \u0026#34;$command\u0026#34; in cd) fzf --preview \u0026#39;eza --tree --color=always {} | head -200\u0026#39; \u0026#34;$@\u0026#34; ;; export|unset) fzf --preview \u0026#34;eval \u0026#39;echo ${}\u0026#39;\u0026#34; \u0026#34;$@\u0026#34; ;; ssh) fzf --preview \u0026#39;dig {}\u0026#39; \u0026#34;$@\u0026#34; ;; *) fzf --preview \u0026#34;$show_file_or_dir_preview\u0026#34; \u0026#34;$@\u0026#34; ;; esac } #4: eza (better ls) # eza is a better version of ls with lots of different options.\nInstall it:\nNow you can start using it!\nYou can create an alias for it in ~/.zshrc like so:\n# ---- Eza (better ls) ----- alias ls=\u0026#34;eza --color=always --long --git --no-filesize --icons=always --no-time --no-user --no-permissions\u0026#34; #5: tldr (user-friendly man pages) # tldr is a huge collection of community maintained help pages with helpful summaries for cli tools. You can use this as an alternative to man pages.\nInstall the rust client with homebrew:\nNow you can use it just like man to learn more about a tool.\nFor example to learn more about eza:\n#6: thefuck # thefuck is a really nice tool to auto correct mistyped commands.\nInstall it like so:\nThen open your ~/.zshrc file and add:\n# thefuck alias eval $(thefuck --alias) This will setup the alias which is fuck.\nYou can also add your own custom alias:\n# thefuck alias eval $(thefuck --alias) eval $(thefuck --alias fk) Save your zshrc file and run:\nNow if mistype a command you can run fuck afterwards.\nIf there are is more than one result you can use your up and down arrow keys and then enter to select the one you want.\n#7: zoxide (better cd) # zoxide is an amazing alternative to cd.\nIt will remember the directories you’ve visited in the past and make it really easy to navigate back to them by just typing out a portion of the name of the directory you want to visit.\nInstall it like so:\nThen add the following to ~/.zshrc:\n# ---- Zoxide (better cd) ---- eval \u0026#34;$(zoxide init zsh)\u0026#34; If you want to keep using cd then create an alias in ~/.zshrc:\n# ---- Zoxide (better cd) ---- eval \u0026#34;$(zoxide init zsh)\u0026#34; alias cd=\u0026#34;z\u0026#34; Save and then run:\nNow you can use z as a much smarter replacement to cd.\nIf you want to trigger fzf when you’re moving to a directory with z you can do space + tab.\nThat’s it! 🚀 # ","date":"February 4, 2025","externalUrl":null,"permalink":"/notes/tools/7-amazing-cli-tools/","section":"笔记","summary":"You can find my dotfiles here.\nOpen a terminal window # Open a terminal window on your macOs or linux machine.\nI’m using Alacritty on macOs and I’m using the zsh shell.\nInstall Homebrew # Run the following command:\n","title":"7-Amazing-CLI-Tools","type":"notes"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/cli%E5%B7%A5%E5%85%B7/","section":"Tags","summary":"","title":"CLI工具","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/categories/cli%E7%9B%B8%E5%85%B3/","section":"Categories","summary":"","title":"CLI相关","type":"categories"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/homebrew/","section":"Tags","summary":"","title":"Homebrew","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/ja/tags/%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%A9%E3%82%A4%E3%83%B3/","section":"Tags","summary":"","title":"コマンドライン","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E5%91%BD%E4%BB%A4%E8%A1%8C/","section":"Tags","summary":"","title":"命令行","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E7%BB%88%E7%AB%AF/","section":"Tags","summary":"","title":"终端","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/gnome/","section":"Tags","summary":"","title":"GNOME","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/kde/","section":"Tags","summary":"","title":"KDE","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/xorg/","section":"Tags","summary":"","title":"Xorg","type":"tags"},{"content":"Wayland、Xorg、GNOME、KDE 这些术语在 Linux 图形系统和桌面环境中扮演着不同的角色。它们分别负责不同的功能，彼此之间相互协作，提供完整的图形用户体验。下面我将解释它们的关系以及各自的定义：\n笔记 省流:\n用户与桌面环境(KDE,GNOME)交互 桌面环境和显示服务器协议(wayland,xrog)交互 显示服务器和内核交互 内核与硬件交互 1. Xorg 和 Wayland: 显示服务器协议 # Xorg 和 Wayland 是两种不同的 显示服务器协议，它们负责在 Linux 系统上管理图形显示、处理输入设备（如鼠标、键盘）的交互，协调窗口的绘制。\nXorg（X Window System 或 X 11）:\n概念: Xorg 是最早的、也是最广泛使用的显示服务器协议，它在 Linux 和类 Unix 系统上运行，已经有几十年的历史。 功能: Xorg 提供了客户端-服务器架构，应用程序通过 X 协议与显示服务器通信，显示服务器负责处理输入设备、管理窗口绘制、以及将图形内容显示在屏幕上。 优缺点: 优点: 经过多年开发，兼容性好，支持广泛的硬件和软件。 缺点: 架构较为老旧，性能问题和安全性不足（如通过网络远程访问图形界面时可能有性能损失）。 使用场景: Xorg 仍然是大多数 Linux 系统的默认显示服务器，特别是在不支持 Wayland 的情况下。 Wayland:\n概念: Wayland 是一个较新的显示服务器协议，旨在替代 Xorg，它简化了架构，提供更现代化的性能和安全性。 功能: Wayland 将显示管理和窗口管理的许多任务移交给应用程序，由窗口管理器（即 Wayland Compositor）来完成，减少了传统 Xorg 的复杂性和延迟问题。 优缺点: 优点: 架构简洁，性能更好，延迟更低，尤其是针对现代硬件的支持更完善，提供更好的图形效果和安全性。 缺点: 虽然已经发展了多年，但在某些方面的兼容性（如某些老应用程序或图形驱动程序）可能不如 Xorg 完善。 使用场景: GNOME 和 KDE 等现代桌面环境越来越多地采用 Wayland，尤其在高性能、高安全性要求的场景下。 2. GNOME 和 KDE: 桌面环境 # GNOME 和 KDE 是 Linux 上的 桌面环境（Desktop Environment），它们提供了图形用户界面（GUI）以及与用户交互的各种工具和应用程序。\nGNOME:\n概念: GNOME 是一个广泛使用的桌面环境，以简洁、高效、现代的设计为特点。 功能: GNOME 提供了窗口管理器、文件管理器、应用程序启动器、系统设置等图形界面工具，帮助用户管理系统和应用程序。它默认使用 Wayland 作为显示协议（但也兼容 Xorg），并且在一些 Linux 发行版（如 Fedora、Ubuntu GNOME）中默认采用。 特性: 用户界面简洁，强调简化操作，减少复杂的设置选项。 默认使用 Wayland，提供现代图形处理和显示性能。 窗口管理器: GNOME 使用 Mutter 作为窗口管理器，它同时也是 Wayland 的合成器。 KDE Plasma:\n概念: KDE Plasma 是另一个流行的桌面环境，以高度可定制化和丰富的功能著称。它适合那些希望更多自定义选项和控制的用户。 功能: KDE Plasma 提供了丰富的桌面定制工具和应用程序，用户可以根据需要调整外观、行为和设置。KDE Plasma 也支持 Wayland 和 Xorg。 特性: 提供丰富的配置选项，用户可以对桌面环境的每个部分进行自定义。 支持 Wayland，但在某些情况下仍使用 Xorg，尤其是在 Wayland 兼容性不足的情况下。 窗口管理器: KDE Plasma 使用 KWin 作为窗口管理器，它同时支持 Xorg 和 Wayland 协议。 3. 关系总结 # 显示服务器协议（Xorg 和 Wayland）: 负责处理系统的图形输出和输入设备。它们是桌面环境的底层技术，直接与操作系统的内核和硬件交互。\nXorg 是老式的、功能成熟的显示服务器协议，兼容性广泛。 Wayland 是现代的显示服务器协议，提供更好的性能和安全性，正逐步取代 Xorg。 桌面环境（GNOME 和 KDE）: 是在显示服务器之上运行的图形用户界面，它们提供了一整套交互工具和应用程序，让用户可以方便地操作和管理系统。\nGNOME 提倡简洁、现代的界面设计，默认支持 Wayland。 KDE 强调可定制性和丰富功能，同时支持 Xorg 和 Wayland。 4. 工作方式举例： # 当你启动一个 Linux 系统时，显示服务器协议（如 Xorg 或 Wayland）首先负责启动和管理显示设备。 然后，桌面环境（如 GNOME 或 KDE Plasma）会通过显示服务器协议与操作系统交互，提供你所看到的图形界面、窗口管理、任务栏等。 桌面环境的具体表现、功能和风格由其内部的组件（如 Mutter 和 KWin）决定，而这些组件则通过 Xorg 或 Wayland 来与硬件互动。 你目前使用的 GNOME 桌面环境 是基于 Wayland 的，但也可以切换回 Xorg。如果你有其他桌面环境（如 KDE Plasma），可以根据自己的需求选择不同的图形协议和桌面环境。\n","date":"February 4, 2025","externalUrl":null,"permalink":"/notes/tools/%E5%9B%BE%E5%BD%A2%E5%8C%96%E7%9B%B8%E5%85%B3%E7%A7%91%E6%99%AE/","section":"笔记","summary":"Wayland、Xorg、GNOME、KDE 这些术语在 Linux 图形系统和桌面环境中扮演着不同的角色。它们分别负责不同的功能，彼此之间相互协作，提供完整的图形用户体验。下面我将解释它们的关系以及各自的定义：\n","title":"图形化相关科普","type":"notes"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/inode/","section":"Tags","summary":"","title":"Inode","type":"tags"},{"content":" 文件描述符 # 文件在 Linux 系统中通过文件描述符与内核进行交互。打开文件时，Linux 内核会执行以下步骤：\n系统调用：当用户程序调用 open() 打开文件时，它会触发一个系统调用，通过内核与硬件进行交互。 文件系统操作：内核会查找文件路径对应的 inode 结构，inode 存储文件的元数据（如文件大小、权限、数据块位置等）。 缓存与内存映射：内核会将文件数据从磁盘加载到内存中的缓存（page cache）中，以加速后续的文件读取操作。如果文件很大，内核可能会使用内存映射（mmap()）将文件映射到进程的虚拟地址空间。 在 Linux 中，文件共享主要指多个进程或多个线程对同一文件的并发访问。文件共享不仅仅包括并发访问文件内容，还涉及对文件描述符和文件锁定的管理。\n3.1 文件描述符和共享 # 每个进程在打开文件时，都会获取一个文件描述符（file descriptor）。文件描述符是内核中的一个索引，它指向一个 file 结构，这个结构包含了文件的状态和偏移量等信息。多个进程如果打开相同的文件，它们会共享文件描述符，指向同一个文件结构。\n内核在处理文件描述符时会采用 引用计数，即多个进程或线程共享文件描述符时，只要有一个进程关闭文件描述符，内核才会真正释放相关资源。\n3.2 文件锁定与并发访问 # 为了避免文件的并发写入产生冲突，Linux 提供了文件锁（flock）机制，进程可以对文件加锁，确保在某一时刻只有一个进程对文件进行写操作。\n文件锁的类型： 共享锁（Shared Lock）：多个进程可以同时获得共享锁，适用于只读操作。 独占锁（Exclusive Lock）：只有一个进程可以获得独占锁，适用于写操作。 通过 flock() 或 fcntl() 系统调用，程序可以请求对文件的共享或独占锁。\n3 .3 内核中的文件共享实现 # 内核通过 struct file 结构来实现文件共享。每当一个进程打开一个文件时，内核会为该文件分配一个 file 结构体，并将该文件的描述符映射到进程的文件表中。如果多个进程打开相同的文件，它们共享这个 file 结构体。进程间共享文件时，内核会确保数据的同步性和一致性，避免数据竞争和冲突。\n内存映射文件（mmap）：如果多个进程需要访问同一个文件的同一部分，Linux 提供了内存映射文件机制（mmap()），这使得进程可以将文件内容映射到进程的地址空间，进程之间可以直接通过共享内存区域访问文件数据。内存映射文件适合高效的文件共享和进程间通信（IPC）。\n读写锁（Read-Write Locks）：内核也提供了一些同步机制，如读写锁，确保对文件的并发访问不会引起数据不一致。\nindoe # 是的，正如你所说，在 Linux 中，几乎所有的资源都被视为文件，这包括普通文件、目录、设备文件、管道、套接字等。这种“万物皆文件”的设计理念使得 Linux 操作系统对文件的管理变得统一且高效。与文件紧密相关的一个重要概念是 inode。\n什么是 inode？ # inode 是 Linux 文件系统中的一个重要数据结构，它存储了文件的元数据（metadata），即与文件内容无关的信息。每个文件（或目录）在文件系统中都有一个唯一的 inode 来描述该文件的属性。inode 并不包含文件的名称或文件的实际数据内容，而是包含了指向数据块的指针、文件的大小、权限、创建时间、修改时间等重要信息。\ninode 的主要内容 # 一个 inode 通常包含以下内容：\n文件类型：例如普通文件、目录、符号链接、设备文件等。 文件权限：文件的读、写、执行权限（例如 755、644 等）。 文件拥有者：文件的所有者 UID 和 GID（用户 ID 和组 ID）。 文件大小：文件的字节数。 时间戳： 创建时间（ctime）：inode 被创建或修改的时间。 修改时间（mtime）：文件内容最后一次修改的时间。 访问时间（atime）：文件最后一次被访问的时间。 数据块指针：指向文件数据的指针，表示该文件存储的数据位于磁盘的哪个位置。这些指针分为直接指针、间接指针等。 硬链接计数：指示指向此 inode 的硬链接数量。 文件与 inode 的关系 # 每个文件（无论是普通文件还是目录文件）都有一个 唯一的 inode，这是文件的核心标识。文件名只是文件系统中某个目录项（directory entry）中的一部分，它与 inode 关联起来。在 Linux 中，文件名和 inode 之间的映射关系是通过 目录项（directory entry）来实现的。\n目录项（如 readdir() 返回的内容）将文件名与其对应的 inode 号关联起来。文件名是目录中的一个条目，而 inode 号则指向存储文件元数据的 inode 结构。 inode 如何工作 # 当你执行以下操作时，操作系统如何利用 inode 来实现文件访问：\n打开文件：当你打开一个文件时，操作系统会先查找该文件的目录项，找到文件名对应的 inode 号。\n读取文件内容：一旦找到 inode，操作系统就知道文件数据的存储位置（通过 inode 中的块指针）。它会使用这些指针从磁盘中加载文件内容到内存中。\n文件写入：当文件内容被修改时，内核会更新 inode 中的时间戳，并可能修改数据块指针。如果文件增长或减少，内核会根据需要更新 inode 的大小字段。\n文件和 inode 的生命周期 # 文件创建：当你创建一个新文件时，内核会为该文件分配一个新的 inode，并在磁盘上为文件分配数据块。文件名会被存储在某个目录中，并与 inode 号关联。\n文件删除：当删除文件时，文件名会从目录中移除，内核会减少 inode 的硬链接计数。当硬链接计数为零时，inode 和数据块会被释放。\n硬链接：硬链接是一个文件名和一个 inode 号的额外映射。硬链接创建后，多个文件名可以指向同一个 inode。这时，删除某个文件名不会立即释放 inode 或数据块，只有当所有硬链接都删除后，inode 才会被释放。\n软链接（符号链接）：符号链接（symlink）是一种特殊类型的文件，它包含指向另一个文件路径的字符串，而不是直接指向 inode。通过符号链接，可以间接访问文件。\ninode 与目录的关系 # 目录也是文件。在 Linux 中，目录实际上是一个特殊的文件，它的内容是目录项，每个目录项包含一个文件名和其对应的 inode 号。因此，目录与文件的关系也是通过 inode 来建立的。\n目录的 inode：目录本身也有一个 inode，它包含了目录的元数据（如权限、拥有者等）。然而，目录的内容并不是文件数据，而是目录项，这些目录项将文件名映射到其对应的 inode。\n目录项与 inode：当你查看一个目录时，内核会通过目录项中的 inode 号来访问每个文件的 inode，进而获得文件的元数据和实际数据。\ninode 的限制与优化 # 文件系统的 inode 数量：每个文件系统在格式化时会指定一个 inode 的数量。这个数量决定了文件系统最多能创建多少个文件。通常情况下，inode 的数量与磁盘的容量相关，但它也受到文件系统设计的影响。\n文件系统优化：为了解决 inode 数量不足的问题，许多现代文件系统（如 ext4、XFS 等）采用了动态 inode 分配机制，也就是说，在文件系统创建后可以根据需要扩展 inode 的数量。\n总结 # inode 是 Linux 文件系统中每个文件（或目录）都有的一个数据结构，存储了文件的元数据（如权限、大小、时间戳等）以及指向文件数据块的指针。 文件名 和 inode 是通过 目录项 关联起来的。文件名只是一个标识符，而 inode 存储了实际的文件信息。 目录文件 本身也是特殊的文件，包含了文件名和 inode 号的映射关系，目录中的每个条目指向一个文件的 inode。 硬链接 允许多个文件名指向同一个 inode，而 符号链接 则是指向另一个文件路径的引用，不直接指向 inode。 通过 inode，Linux 文件系统能够高效地管理文件的存储和访问，提供了文件的元数据和数据的分离，极大地提升了文件管理的灵活性和性能。\n","date":"February 4, 2025","externalUrl":null,"permalink":"/notes/linux/linux%E6%96%87%E4%BB%B6file/","section":"笔记","summary":"文件描述符 # 文件在 Linux 系统中通过文件描述符与内核进行交互。打开文件时，Linux 内核会执行以下步骤：\n","title":"Linux文件FILE","type":"notes"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/ja/tags/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0/","section":"Tags","summary":"","title":"ファイルシステム","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E6%96%87%E4%BB%B6%E5%85%B1%E4%BA%AB/","section":"Tags","summary":"","title":"文件共享","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E6%96%87%E4%BB%B6%E6%8F%8F%E8%BF%B0%E7%AC%A6/","section":"Tags","summary":"","title":"文件描述符","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F/","section":"Tags","summary":"","title":"文件系统","type":"tags"},{"content":"作者: 杰克小麻雀\n原文链接: https://blog.csdn.net/yushuaigee/article/details/107883964\n1、从一个最常见的例子说起 # 在使用Linux的过程中， 我们平时经常看到下面这样的用法：\n1 echo log \u0026gt; /dev/null 2\u0026gt;\u0026amp;1 \u0026gt; ：表示将输出结果重定向到哪里，例如：echo “123” \u0026gt; /home/123.txt /dev/null ：表示空设备文件 所以 echo log \u0026gt; /dev/null 表示把日志输出到空文件设备，也就是将打印信息丢弃掉，屏幕上什么也不显示。\n1 ：表示stdout标准输出 2 ：表示stderr标准错误 \u0026amp; ：表示等同于的意思 所以 2\u0026gt;\u0026amp;1 表示2的输出重定向等同于1，也就是标准错误输出重定向到标准输出。因为前面标准输出已经重定向到了空设备文件，所以标准错误输出也重定向到空设备文件。\n这个用法平时很常见，重点是为什么这里是用 2 和 1 ，不是3456什么的呢？这要从 Linux 中的文件描述符说起。\n2、Linux中的文件描述符（file descriptor） # 我们知道在Linux系统中一切皆可以看成是文件，文件又可分为：普通文件、目录文件、链接文件和设备文件。在操作这些所谓的文件的时候，我们每操作一次就找一次名字，这会耗费大量的时间和效率。所以Linux中规定每一个文件对应一个索引，这样要操作文件的时候，我们直接找到索引就可以对其进行操作了。\n文件描述符（file descriptor）就是内核为了高效管理这些已经被打开的文件所创建的索引，其是一个非负整数（通常是小整数），用于指代被打开的文件，所有执行I/O操作的系统调用都通过文件描述符来实现。同时还规定系统刚刚启动的时候，0是标准输入，1是标准输出，2是标准错误。这意味着如果此时去打开一个新的文件，它的文件描述符会是3，再打开一个文件文件描述符就是4……\nLinux内核对所有打开的文件有一个文件描述符表格，里面存储了每个文件描述符作为索引与一个打开文件相对应的关系，简单理解就是下图这样一个数组，文件描述符（索引）就是文件描述符表这个数组的下标，数组的内容就是指向一个个打开的文件的指针。\n上面只是简单理解，实际上关于文件描述符，Linux内核维护了3个数据结构：\n进程级的文件描述符表 系统级的打开文件描述符表 文件系统的i-node表 一个 Linux 进程启动后，会在内核空间中创建一个 PCB 控制块，PCB 内部有一个文件描述符表（File descriptor table），记录着当前进程所有可用的文件描述符，也即当前进程所有打开的文件。进程级的描述符表的每一条记录了单个进程所使用的文件描述符的相关信息，进程之间相互独立，一个进程使用了文件描述符3，另一个进程也可以用3。除了进程级的文件描述符表，系统还需要维护另外两张表：打开文件表、i-node 表。这两张表存储了每个打开文件的打开文件句柄（open file handle）。一个打开文件句柄存储了与一个打开文件相关的全部信息。\n系统级的打开文件描述符表：\n当前文件偏移量（调用read()和write()时更新，或使用lseek()直接修改） 打开文件时的标识（open()的flags参数） 文件访问模式（如调用open()时所设置的只读模式、只写模式或读写模式） 与信号驱动相关的设置 对该文件i-node对象的引用，即i-node 表指针 文件系统的i-node表：\n文件类型（例如：常规文件、套接字或FIFO）和访问权限 一个指针，指向该文件所持有的锁列表 文件的各种属性，包括文件大小以及与不同类型操作相关的时间戳 文件描述符、打开的文件句柄以及i-node之间的关系如下图：\n在进程 A 中，文件描述符 1 和 20 都指向了同一个打开文件表项，标号为 23（指向了打开文件表中下标为 23 的数组元素），这可能是通过调用 dup()、dup2()、fcntl() 或者对同一个文件多次调用了 open() 函数形成的。 进程 A 的文件描述符 2 和进程 B 的文件描述符 2 都指向了同一个文件，这可能是在调用 fork() 后出现的（即进程 A、B 是父子进程关系），或者是不同的进程独自去调用 open() 函数打开了同一个文件，此时进程内部的描述符正好分配到与其他进程打开该文件的描述符一样。 进程 A 的描述符 0 和进程 B 的描述符 3 分别指向不同的打开文件表项，但这些表项均指向 i-node 表的同一个条目（标号为 1976）；换言之，它们指向了同一个文件。发生这种情况是因为每个进程各自对同一个文件发起了 open() 调用。同一个进程两次打开同一个文件，也会发生类似情况。 这就说明：同一个进程的不同文件描述符可以指向同一个文件；不同进程可以拥有相同的文件描述符；不同进程的同一个文件描述符可以指向不同的文件（一般也是这样，除了 0、1、2 这三个特殊的文件）；不同进程的不同文件描述符也可以指向同一个文件。\n3、Linux上打开文件举例 # 比如在Linux上用 vim test.py 打开一个文件，保持打开状态，再新打开一个新的shell，输入命令pidof vim 获取vim进程的pid号，然后 ll /proc/$pid/fd 查看vim 进程所使用的文件描述符列表。\n/dev/pts是远程登陆(telnet,ssh等)后创建的控制台设备文件所在的目录。因为我是通过Xshell远程登录的，所以标准输入0，标准输出1，标准错误2的文件描述符都指向虚拟终端控制台 /dev/pts/6 。 再看下面是新打开的 test.py 的文件描述符，竟然是4，说好的从3开始呢？\n这个我也困扰了好久，查了各种资料，终于在一个大佬的帮助下在一个论坛找到原因，有时候中文查不到还是要试试英文搜索啊。因为vim这种编辑器的原理是先打开源文件并拷贝，然后关闭源文件再打开自己的副本，修改完文件保存的时候直接将副本重命名覆盖源文件。所以打开源文件的时候用的文件描述符3，然后打开自己的副本是时候就该用文件描述符4了，然后关闭源文件，文件描述符3就被释放了，我们查看的时候就只剩下了4，这里它指向的是vim创建的副本文件。这里只是说个大概意思，具体深究要去深入了解一下 vim的实现原理——奥尔特星云大使，下面是当时我看到的论坛上的资料截图，链接在这：StackOverFlow。\n如果不相信可以试一试别的进程，比如 tail。\n在Linux上用 tail -f test.py 打开一个文件，保持打开状态，再新打开一个新的shell，输入命令pidof tail 获取tail进程的pid号，然后ll /proc/$pid/fd查看tail进程所使用的文件描述符列表，可以看到文件描述符确实是从3开始使用的。tail不是编辑器不存在修改文件的情况，所以直接文件描述符直接打开的源文件。实际上可以使用 ll /proc/$pid/fd命令获取当前运行的任意进程的文件描述符使用情况。\n4、C语言中文件描述符的使用 # C语言中可以通过 open 函数返回一个文件的文件描述符，首先创建一个 test.py 文件用于打开，然后创建一个 test.c 文件，输入下面代码保存。 编译后执行，发现新打开文件的文件描述符是3。\n1 2 3 4 5 6 7 8 9 10 11 12 13 #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;fcntl.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main(int argc, char* argv[]) { int fd = open(\u0026quot;test.py\u0026quot;, O_RDONLY); if (fd == -1) { return -1; } printf(\u0026quot;test.py fd = %d \\n\u0026quot;, fd); close(fd); return 0; } 5、Python中文件描述符的使用 # Python中通过 sys 模块封装了标准输入、标准输出和错误输出。通过我们平时常用的内建函数 open 可以获取一个文件的文件描述符，首先创建一个 test.py 文件用于打开，然后创建一个 test2.py 文件，输入下面代码保存。 执行，发现新打开文件的文件描述符是3。\n1 2 3 4 5 6 7 8 import sys print('stdin fd = ', sys.stdin.fileno()) print('stdout fd = ', sys.stdout.fileno()) print('stderr fd = ', sys.stderr.fileno()) with open(\u0026quot;test.py\u0026quot;, \u0026quot;w\u0026quot;) as f: print('test.py fd = ', f.fileno()) 6、Linux配置系统最大打开文件描述符个数 # （1）系统级限制\n理论上系统内存有多少就可以打开多少的文件描述符，但是在实际中内核是会做相应的处理，一般最大打开文件数会是系统内存的10%（以KB来计算），称之为系统级限制。这个数字可以通过 cat /proc/sys/fs/file-max 或者 sysctl -a | grep fs.file-max 命令查看。\n更改系统级限制有临时更改和永久更改两种方式：\n临时更改：session断开或者系统重启后会恢复原来的设置值。使用命令 sysctl -w fs.file-max=xxxx，其中xxxx就是要设置的数字。 永久更改：vim编辑 /etc/sysctl.conf 文件，在后面添加 fs.file-max=xxxx，其中xxxx就是要设置的数字。保存退出后还要使用sysctl -p 命令使其生效。 （2）用户级限制\n同时为了控制每个进程消耗的文件资源，内核也会对单个进程最大打开文件数做默认限制，即用户级限制。32位系统默认值一般是1024，64位系统默认值一般是65535，可以使用 ulimit -n 命令查看。\n7、参考链接 # 每天进步一点点——Linux中的文件描述符与打开文件之间的关系——cywosp Linux文件描述符到底是什么？——C语言中文网 句柄和文件描述符（FD）——阳光丶不锈 带你破案：文件描述符到底是什么？——vran Linux配置调优：最大打开文件描述符个数——Idea Buffer 修改Linux系统下的最大文件描述符限制——BlueguyChui ","date":"February 4, 2025","externalUrl":null,"permalink":"/notes/linux/linux%E6%96%87%E4%BB%B6%E6%8F%8F%E8%BF%B0%E7%AC%A6-fd/","section":"笔记","summary":"作者: 杰克小麻雀\n原文链接: https://blog.csdn.net/yushuaigee/article/details/107883964\n1、从一个最常见的例子说起 # 在使用Linux的过程中， 我们平时经常看到下面这样的用法：\n","title":"Linux文件描述符(fd)","type":"notes"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/stderr/","section":"Tags","summary":"","title":"Stderr","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/stdout/","section":"Tags","summary":"","title":"Stdout","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E9%87%8D%E5%AE%9A%E5%90%91/","section":"Tags","summary":"","title":"重定向","type":"tags"},{"content":" 需求 # 最近我们在开发一个基于树莓派的小盒子，我们会采购一些树莓派，装好外盒，装好arm版本的【Ubuntu Server 20.04】系统，并且装上我们开发的配套软件，对接我们云端的服务，最终把小盒子卖给用户并提供一些收费的服务。我们在安装系统的时候使用了Ubuntu自带的磁盘加密功能，确保机器在不开机的情况下不能被轻易的取出TF卡读取数据。\n另一方面，我们创建了一个低权限的Linux用户【user】，方便用户进行一些基本操作。\n默认情况下【Ubuntu Server 20.04】系统开机后，会要求你输入账号和密码来登录，但我们希望用户开机后，系统自动以【user】身份登录，并且自动运行一些脚本。\n实现 # 在网络搜索了一下，资料很多，但是坑也很多，最终总结出一个最简单的办法。\n先创建一个用户【user】\nsudo adduser user\n然后一路回车，这时候发现无法给这个用户指定空密码，提示\nNo password supplied\n不用担心，先胡乱设一个密码，一路回车，创建完毕后，删除该用户的密码\nsudo passwd -d user\n接下来就是配置终端的自动登录\nsudo vim /etc/systemd/system/getty.target.wants/getty@tty1.service\n修改【getty@tty1.service】文件的【ExecStart】这一行内容，从\nExecStart=-/sbin/agetty -o ‘-p – \\u’ \u0026ndash;noclear %I $TERM\n修改为\nExecStart=-/sbin/agetty -a user -o ‘-p – \\u’ \u0026ndash;noclear %I $TERM\n其实是就是加了-a \\[用户名\\]最后让systemd重新加载一下配置文件\nsudo systemctl daemon-reload\n然后重启系统\nsudo reboot\n便可实现Ubuntu Server的无密码开机自动登录\n","date":"February 4, 2025","externalUrl":null,"permalink":"/notes/linux/linux%E6%97%A0%E9%9C%80%E5%AF%86%E7%A0%81%E7%99%BB%E9%99%86-%E9%80%82%E5%90%88%E6%9C%8D%E5%8A%A1%E5%99%A8/","section":"笔记","summary":"需求 # 最近我们在开发一个基于树莓派的小盒子，我们会采购一些树莓派，装好外盒，装好arm版本的【Ubuntu Server 20.04】系统，并且装上我们开发的配套软件，对接我们云端的服务，最终把小盒子卖给用户并提供一些收费的服务。我们在安装系统的时候使用了Ubuntu自带的磁盘加密功能，确保机器在不开机的情况下不能被轻易的取出TF卡读取数据。\n","title":"Linux无需密码登陆-适合服务器","type":"notes"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/ubuntu-server/","section":"Tags","summary":"","title":"Ubuntu Server","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"服务器管理","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E7%94%A8%E6%88%B7%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"用户管理","type":"tags"},{"content":"","date":"February 4, 2025","externalUrl":null,"permalink":"/tags/%E8%87%AA%E5%8A%A8%E7%99%BB%E5%BD%95/","section":"Tags","summary":"","title":"自动登录","type":"tags"},{"content":"","date":"January 27, 2025","externalUrl":null,"permalink":"/tags/hexo/","section":"Tags","summary":"","title":"Hexo","type":"tags"},{"content":"你提供的代码中已经添加了显示网站存活时间的功能，接下来可以根据你的需求来添加访问人数统计的功能。以下是详细步骤：\n1. 不蒜子访问量统计的代码添加 # 不蒜子是一个非常简单、免费且无需配置的访问量统计工具，可以直接嵌入你的 Hexo 网站。\n步骤： # 在你的 \u0026lt;div class=\u0026quot;footer-inner\u0026quot;\u0026gt; 中，找到合适的位置（例如网站存活时间下方），然后添加以下不蒜子的统计代码： \u0026lt;!-- 访问人数统计 --\u0026gt; \u0026lt;span id=\u0026#34;busuanzi_container_site_pv\u0026#34; style=\u0026#34;display: none;\u0026#34;\u0026gt; 本站总访问量：\u0026lt;span id=\u0026#34;busuanzi_value_site_pv\u0026#34;\u0026gt;\u0026lt;/span\u0026gt; 次 \u0026lt;/span\u0026gt; \u0026lt;script async src=\u0026#34;https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 代码解释： # id=\u0026quot;busuanzi_container_site_pv\u0026quot; 和 id=\u0026quot;busuanzi_value_site_pv\u0026quot; 是不蒜子提供的 ID，它们会在页面加载后自动统计并显示站点的总访问量。 通过 busuanzi.pure.mini.js 引入不蒜子统计脚本，它会自动计算并更新访问量。 修改后的文件： # \u0026lt;div class=\u0026#34;footer-inner\u0026#34;\u0026gt; \u0026lt;% if (theme.footer.content) { %\u0026gt; \u0026lt;div class=\u0026#34;footer-content\u0026#34;\u0026gt; \u0026lt;%- theme.footer.content %\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;% } %\u0026gt; \u0026lt;% if (theme.footer.statistics.enable) { %\u0026gt; \u0026lt;%- partial(\u0026#39;_partials/footer/statistics.ejs\u0026#39;) %\u0026gt; \u0026lt;% } %\u0026gt; \u0026lt;% if(theme.footer.beian.enable) { %\u0026gt; \u0026lt;!-- 备案信息 ICP for China --\u0026gt; \u0026lt;%- partial(\u0026#39;_partials/footer/beian.ejs\u0026#39;) %\u0026gt; \u0026lt;% } %\u0026gt; \u0026lt;% if(theme.web_analytics.cnzz) { %\u0026gt; \u0026lt;!-- cnzz Analytics Icon --\u0026gt; \u0026lt;span id=\u0026#34;cnzz_stat_icon_\u0026lt;%= theme.web_analytics.cnzz %\u0026gt;\u0026#34; style=\u0026#34;display: none\u0026#34;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;% } %\u0026gt; \u0026lt;!-- 网站存活时间计时功能 --\u0026gt; \u0026lt;div id=\u0026#34;site-lifetime\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;script\u0026gt; // 设置网站的创建日期 const creationDate = new Date(\u0026#39;2024-04-21\u0026#39;); // 请根据实际情况修改日期 // 计算当前日期和网站创建日期之间的天数差 function calculateDaysSinceCreation() { const now = new Date(); const timeDifference = now - creationDate; const daysSinceCreation = Math.floor(timeDifference / (1000 * 60 * 60 * 24)); return daysSinceCreation; } // 页面加载完成后执行 document.addEventListener(\u0026#39;DOMContentLoaded\u0026#39;, function() { // 计算存活天数 const daysSinceCreation = calculateDaysSinceCreation(); // 更新存活时间 const lifetimeElement = document.getElementById(\u0026#39;site-lifetime\u0026#39;); lifetimeElement.textContent = `网站存活了 ${daysSinceCreation} 天`; }); \u0026lt;/script\u0026gt; \u0026lt;!-- 访问人数统计 --\u0026gt; \u0026lt;span id=\u0026#34;busuanzi_container_site_pv\u0026#34; style=\u0026#34;display: none;\u0026#34;\u0026gt; 本站总访问量：\u0026lt;span id=\u0026#34;busuanzi_value_site_pv\u0026#34;\u0026gt;\u0026lt;/span\u0026gt; 次 \u0026lt;/span\u0026gt; \u0026lt;script async src=\u0026#34;https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/div\u0026gt; 2. 使用插件方式显示访问量 # 如果你想使用插件（例如 hexo-wordcount）来统计和显示页面访问量，你可以按照以下步骤进行配置。\n步骤： # 安装插件：\n首先，安装 hexo-wordcount 插件：\nnpm install hexo-wordcount --save 修改模板文件：\n找到你的主题文件夹中的页面模板（例如 themes/your-theme/layout/_partial/footer.ejs 或 footer.ejs），然后在适当位置（例如网站存活时间和备案信息之后）添加以下代码：\n\u0026lt;!-- 访问人数统计 --\u0026gt; 本站总访问量：\u0026lt;%= page.total_count %\u0026gt; 注意： 这个插件可以统计字数、阅读时间等信息，也可以结合其他统计功能使用。\n更新 Hexo 并生成静态文件：\n完成后，执行以下命令生成更新后的静态文件：\nhexo clean hexo generate 然后部署网站：\nhexo deploy 3. Moe-Counter 动漫式计数 # github🔗\n主要思路就是跟着文档操作就行了的，先生成代码 (就是一个 img 格式的代码)\n然后，如果你想在底部 (footer) 插入，你就要在 /home/ice/my-blog/themes/fluid(your-theme)/layout/_partials/footer.ejs 插入代码，具体示例如下:\n\u0026lt;!-- Moe-Counter 访问人数统计 --\u0026gt; \u0026lt;div style=\u0026#34;text-align: center; margin-top: 10px;\u0026#34;\u0026gt; \u0026lt;span style=\u0026#34;font-size: 16px; font-weight: bold;\u0026#34;\u0026gt;访问人数统计: \u0026lt;/span\u0026gt; \u0026lt;img src=\u0026#34;https://count.getloli.com/@:ice345?name=%3Aice345\u0026amp;theme=gelbooru\u0026amp;padding=7\u0026amp;offset=0\u0026amp;align=bottom\u0026amp;scale=1\u0026amp;pixelated=1\u0026amp;darkmode=auto\u0026#34; alt=\u0026#34;visitor count\u0026#34;\u0026gt; \u0026lt;/div\u0026gt; 这个你想在底部生成邮箱，github 链接，QQ这些就仿照如下:\n\u0026lt;!-- 添加社交链接 --\u0026gt; \u0026lt;div style=\u0026#34;font-size: 13px; font-weight: bold; text-align: center;\u0026#34;\u0026gt; \u0026lt;div style=\u0026#34;display: flex; justify-content: center; gap: 20px;\u0026#34;\u0026gt; \u0026lt;span class=\u0026#34;nav-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;nav-link\u0026#34; href=\u0026#34;https://github.com/ice345\u0026#34; target=\u0026#34;_blank\u0026#34;\u0026gt; \u0026lt;i class=\u0026#34;fab fa-github\u0026#34; style=\u0026#34;color:#409EFF;\u0026#34; aria-hidden=\u0026#34;true\u0026#34;\u0026gt;\u0026lt;/i\u0026gt; GitHub \u0026lt;/a\u0026gt; \u0026lt;/span\u0026gt; \u0026lt;span class=\u0026#34;nav-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;nav-link\u0026#34; href=\u0026#34;mailto:nni461904@gmail.com\u0026#34; target=\u0026#34;_blank\u0026#34;\u0026gt; \u0026lt;i class=\u0026#34;fa fa-envelope\u0026#34; style=\u0026#34;color:#409EFF\u0026#34; aria-hidden=\u0026#34;true\u0026#34;\u0026gt;\u0026lt;/i\u0026gt; 邮箱 \u0026lt;/a\u0026gt; \u0026lt;/span\u0026gt; \u0026lt;span class=\u0026#34;nav-item\u0026#34;\u0026gt; \u0026lt;a class=\u0026#34;nav-link\u0026#34; href=\u0026#34;https://alist.050626.xyz\u0026#34; target=\u0026#34;_blank\u0026#34;\u0026gt; \u0026lt;i class=\u0026#34;fa-solid fa-cloud-download\u0026#34; style=\u0026#34;color:#409EFF;\u0026#34; aria-hidden=\u0026#34;true\u0026#34;\u0026gt;\u0026lt;/i\u0026gt; Alist \u0026lt;/a\u0026gt; \u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;br /\u0026gt; \u0026lt;/div\u0026gt; 4. 存活时间 # 这个的添加就仿照访问人数即可\n","date":"January 27, 2025","externalUrl":null,"permalink":"/misc/hexo%E6%B7%BB%E5%8A%A0%E5%AD%98%E6%B4%BB%E6%97%B6%E9%97%B4%E5%92%8C%E8%AE%BF%E9%97%AE%E4%BA%BA%E6%95%B0/","section":"杂项","summary":"你提供的代码中已经添加了显示网站存活时间的功能，接下来可以根据你的需求来添加访问人数统计的功能。以下是详细步骤：\n1. 不蒜子访问量统计的代码添加 # 不蒜子是一个非常简单、免费且无需配置的访问量统计工具，可以直接嵌入你的 Hexo 网站。\n","title":"hexo添加存活时间和访问人数","type":"misc"},{"content":"","date":"January 27, 2025","externalUrl":null,"permalink":"/ja/tags/%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E7%B5%B1%E8%A8%88/","section":"Tags","summary":"","title":"アクセス統計","type":"tags"},{"content":"","date":"January 27, 2025","externalUrl":null,"permalink":"/ja/tags/%E3%83%96%E3%83%AD%E3%82%B0%E6%94%B9%E5%96%84/","section":"Tags","summary":"","title":"ブログ改善","type":"tags"},{"content":"","date":"January 27, 2025","externalUrl":null,"permalink":"/tags/%E5%8D%9A%E5%AE%A2%E4%BC%98%E5%8C%96/","section":"Tags","summary":"","title":"博客优化","type":"tags"},{"content":"","date":"January 27, 2025","externalUrl":null,"permalink":"/ja/tags/%E4%B8%8D%E8%92%9C%E5%AD%90/","section":"Tags","summary":"","title":"不蒜子","type":"tags"},{"content":"","date":"January 27, 2025","externalUrl":null,"permalink":"/tags/%E4%B8%8D%E7%AE%97%E5%AD%90/","section":"Tags","summary":"","title":"不算子","type":"tags"},{"content":"","date":"January 27, 2025","externalUrl":null,"permalink":"/tags/%E8%AE%BF%E9%97%AE%E7%BB%9F%E8%AE%A1/","section":"Tags","summary":"","title":"访问统计","type":"tags"},{"content":"","date":"January 27, 2025","externalUrl":null,"permalink":"/categories/%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2/","section":"Categories","summary":"","title":"个人博客","type":"categories"},{"content":"","date":"January 27, 2025","externalUrl":null,"permalink":"/ja/categories/%E5%80%8B%E4%BA%BA%E3%83%96%E3%83%AD%E3%82%B0/","section":"Categories","summary":"","title":"個人ブログ","type":"categories"},{"content":"","date":"January 27, 2025","externalUrl":null,"permalink":"/misc/","section":"杂项","summary":"","title":"杂项","type":"misc"},{"content":"","date":"December 20, 2024","externalUrl":null,"permalink":"/tags/.desktop%E6%96%87%E4%BB%B6/","section":"Tags","summary":"","title":".Desktop文件","type":"tags"},{"content":" 笔记 好用的工具: desktop-file-validate 这个来检查编写的错误\n例子 # [Desktop Entry] Version=1.0 Name=Todoist Comment=This is balabala(填任何东西都可以) Exec=/home/ice/.... Icon=/home/ice/..... Terminal=false Type=Application Categories=Utility 这个就是典型的 .desktop 的编写例子, 其余什么的 .desktop 文件都可以参考这个来编写\n错误 # 最近在 arch 中编写这个发现出错，但是想破了脑袋都没有想明白哪里出了问题，问 chatgpt, 我就简单进行比对，发现一模一样就没有想清楚。\n我就只好总结询问 Google, 功夫不负有心人，在 reddit 中，看到这个 question 发现可以通过 desktop-file-validate \u0026lt;.desktop file name\u0026gt; 来检查编写出现的错误.\n最后，给我这样的信息:\ntodoist.desktop: error: value \u0026#34;Application \u0026#34; for key \u0026#34;Type\u0026#34; in group \u0026#34;Desktop Entry\u0026#34; is not a registered type value (\u0026#34;Application\u0026#34;, \u0026#34;Link\u0026#34; and \u0026#34;Directory\u0026#34;) 但是我看我的 Type 行没有发现有什么空格，我就不能理解.\n但是最后我在 Application 后发现这个后面有个空格导致了出现这样的错误\nTips 我会在这个后面出现空格，是因为 nvim 编写时，如果你不加空格，你按 enter 就会接受提示词，因此才会导致这样的问题的😠\n参考 # reddit\n","date":"December 20, 2024","externalUrl":null,"permalink":"/notes/linux/linux%E4%B8%8B-desktop%E5%88%9B%E5%BB%BA%E6%97%B6%E5%8F%91%E7%94%9F%E7%9A%84%E9%94%99%E8%AF%AF/","section":"笔记","summary":" 笔记 好用的工具: desktop-file-validate 这个来检查编写的错误\n","title":"Linux下.desktop创建时发生的错误","type":"notes"},{"content":"","date":"December 20, 2024","externalUrl":null,"permalink":"/ja/tags/%E3%82%A8%E3%83%A9%E3%83%BC%E8%AA%BF%E6%9F%BB/","section":"Tags","summary":"","title":"エラー調査","type":"tags"},{"content":"","date":"December 20, 2024","externalUrl":null,"permalink":"/tags/%E9%94%99%E8%AF%AF%E6%8E%92%E6%9F%A5/","section":"Tags","summary":"","title":"错误排查","type":"tags"},{"content":"","date":"December 20, 2024","externalUrl":null,"permalink":"/tags/%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6/","section":"Tags","summary":"","title":"配置文件","type":"tags"},{"content":"","date":"December 20, 2024","externalUrl":null,"permalink":"/tags/%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87/","section":"Tags","summary":"","title":"桌面图标","type":"tags"},{"content":"","date":"December 13, 2024","externalUrl":null,"permalink":"/tags/cpu%E6%BC%8F%E6%B4%9E/","section":"Tags","summary":"","title":"CPU漏洞","type":"tags"},{"content":"","date":"December 13, 2024","externalUrl":null,"permalink":"/tags/meltdown/","section":"Tags","summary":"","title":"Meltdown","type":"tags"},{"content":"","date":"December 13, 2024","externalUrl":null,"permalink":"/tags/spectre/","section":"Tags","summary":"","title":"Spectre","type":"tags"},{"content":"就利用到了 cache 的缓存的时间不一样 (访问某些位置时会发生访问时间快的问题使得可以通过这个来得到其他进程的数据内容)\n分支预测 (speculative execution) 侧信道攻击 (side-channel attack) 访问时间差异获取数据内容 通过下面的这张图基本也可以理解个大概了\n","date":"December 13, 2024","externalUrl":null,"permalink":"/notes/system/spectre%E5%92%8Cmeltdown/","section":"笔记","summary":"就利用到了 cache 的缓存的时间不一样 (访问某些位置时会发生访问时间快的问题使得可以通过这个来得到其他进程的数据内容)\n分支预测 (speculative execution) 侧信道攻击 (side-channel attack) 访问时间差异获取数据内容 通过下面的这张图基本也可以理解个大概了\n","title":"spectre和meltdown","type":"notes"},{"content":"","date":"December 13, 2024","externalUrl":null,"permalink":"/tags/%E4%BE%A7%E4%BF%A1%E9%81%93%E6%94%BB%E5%87%BB/","section":"Tags","summary":"","title":"侧信道攻击","type":"tags"},{"content":"","date":"December 13, 2024","externalUrl":null,"permalink":"/tags/%E5%88%86%E6%94%AF%E9%A2%84%E6%B5%8B/","section":"Tags","summary":"","title":"分支预测","type":"tags"},{"content":"","date":"December 13, 2024","externalUrl":null,"permalink":"/notes/system/","section":"笔记","summary":"","title":"系统底层","type":"notes"},{"content":"","date":"December 13, 2024","externalUrl":null,"permalink":"/tags/cpu%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"CPU管理","type":"tags"},{"content":" 00:00 - ⏰ 进程调度 # 总结： # 进程调度的目的是在多个程序或进程之间合理分配 CPU 时间。最初，程序通过主动调用 API 函数交出 CPU，但有时进程不合作，比如陷入死循环或长时间占用 CPU，导致其他进程无法执行。\n为了避免这些问题，系统引入了时钟中断机制，在每个时钟周期结束时，强制切换进程，这就是抢占式调度的基础。每个进程被分配一个固定的时间片（比如 100 毫秒），时间片用完时，调度器会选择其他进程执行。\n随着进程数量增加，系统通过就绪队列管理进程（即正在等待 CPU 执行的进程）。此外，将阻塞的进程（如等待 I/O 或锁的进程）移到等待队列，避免 CPU 空转。\n示例： # 假设你有 5 个进程在运行，每个进程执行 100ms，调度器每 100ms 中断一次。如果进程 A 被选中运行，但它一直在等待 I/O，系统就会把它移入等待队列，调度下一个进程。这样，CPU 就不会空闲，而是去执行其他进程。\n2:09 - 🤔 进程过多导致系统卡顿 # 总结： # 初始时系统进程较少，运行流畅，但随着进程增多（达到数百个），即使每个进程的执行时间很短（例如 10 毫秒），每个进程上下文切换的开销依然很大。\n上下文切换包括保存和加载进程的状态，这些操作有较高的开销。如果进一步缩短每个进程的执行时间，虽然每个进程的 CPU 时间减少了，但上下文切换的开销却增加了，反而导致系统卡顿。\n示例： # 假设有 1000 个进程，每个进程分配 10ms 执行时间。理论上每个进程执行 10ms 后进行一次上下文切换，但上下文切换本身需要时间（假设每次切换花费 1ms），如果系统做过多上下文切换，反而会导致效率低下，系统变得卡顿。\n2:47 - ⏳ 双队列进程调度 # 总结： # 为了解决高进程数量带来的效率问题，系统采用了双队列调度： active 队列：用于存放正在运行的进程。 expi 队列：用于存放高优先级的进程，暂时不执行。 这种方法的关键是避免高优先级进程长期占用 CPU 资源，导致低优先级进程**“饿死”。在某一轮调度后，系统将高优先级进程移到 expi 队列**，然后继续执行 active 队列中的进程，直到所有进程都执行完后，交换两个队列指针，确保所有优先级的进程都有机会执行。 示例： # 假设有两个队列：\nactive 队列：[进程 A, 进程 B, 进程 C] expi 队列：[进程 D, 进程 E] 调度时，高优先级进程（D, E）会被暂时放入 expi 队列，只有 active 队列中的进程（A, B, C）能被调度。待 active 队列中的进程执行完后，交换队列指针，使得 expi 队列中的进程得以执行。\n5:33 - 优先级调度改进 # 总结： # 为了解决优先级调度中出现的问题，采用了动态时间片分配算法。该算法根据进程的优先级来分配时间片：优先级较高的进程拥有较短的时间片，优先级较低的进程则拥有较长的时间片。\n举例来说，优先级为 20 的进程的时间片是 100 毫秒，每升高或降低一级，时间片增加或减少 5 毫秒。然而，存在缺陷：如果两个进程优先级相差 1，CPU 占用的差距可能非常大，有时能达到几倍的差异。\n示例： # 假设有两个进程，进程 A 和进程 B，优先级分别为 20 和 19：\n进程 A 时间片是 100ms，进程 B 时间片是 105ms。理论上，进程 B 会占用更多的 CPU 时间，但如果系统频繁切换进程，B 却可能因为时间片过长而阻塞过久，A 反而会更频繁地被调度，影响公平性。 7:01 - ⏳ 权重优先调度 # 总结： # 权重优先调度算法基于进程优先级的权重来分配 CPU 时间。每个进程的时间片根据其权重进行调整，权重越大的进程获得更多的 CPU 时间。\n例如，进程 A 权重为 1，进程 B 权重为 2，进程 A 每次获得 100 毫秒的时间片，而进程 B 则获得 200 毫秒的时间片。为了避免过度切换，算法还设置了最小时间片。\n如果进程的时间片没有完全用完，调度器会考虑优先执行那些运行时间最短的进程。通过引入虚拟运行时间，算法能够补偿权重差异所导致的时间片分配不均，确保系统公平。\n示例： # 假设进程 A 的权重是 1，进程 B 的权重是 2。假如它们的时间片分别为 100ms 和 200ms。 如果进程 A 没有完全用完 100ms，它会较快地再次获得 CPU，进程 B 则较慢。为了补偿进程 B 因为权重较大而执行时间过长，调度器会让进程 A 在某些时刻得到更多的 CPU 时间。 9:02 - ⏱️ 虚拟时间调度 # 总结： # 这种调度算法的核心思想是通过进程的虚拟时间来决定其执行顺序。虚拟时间是指进程已消耗的“公平时间”，进程的虚拟时间越短，它会越早被选中执行。\n为了高效管理这些进程，系统使用红黑树来维护进程。通过缓存最左节点（虚拟时间最短的进程），调度器能够快速找到下一个应该执行的进程。\n这种方式与 Linux 的 O(1) 调度算法和 CFS 调度算法类似，但做了一些简化，仍然能保证高效且公平的调度。\n示例： # 假设有 3 个进程 A、B 和 C，它们的虚拟时间分别为 5ms、10ms 和 15ms。调度器会根据虚拟时间来选择最短的进程（A）执行。 红黑树用于快速查找最短虚拟时间的进程，进程 A 会被选中执行。 ","date":"December 13, 2024","externalUrl":null,"permalink":"/notes/linux/linux%E8%BF%9B%E7%A8%8B%E8%B0%83%E5%BA%A6/","section":"笔记","summary":"00:00 - ⏰ 进程调度 # 总结： # 进程调度的目的是在多个程序或进程之间合理分配 CPU 时间。最初，程序通过主动调用 API 函数交出 CPU，但有时进程不合作，比如陷入死循环或长时间占用 CPU，导致其他进程无法执行。\n","title":"linux进程调度","type":"notes"},{"content":"","date":"December 13, 2024","externalUrl":null,"permalink":"/tags/%E8%BF%9B%E7%A8%8B%E8%B0%83%E5%BA%A6/","section":"Tags","summary":"","title":"进程调度","type":"tags"},{"content":"","date":"December 13, 2024","externalUrl":null,"permalink":"/tags/%E6%8A%A2%E5%8D%A0%E5%BC%8F%E8%B0%83%E5%BA%A6/","section":"Tags","summary":"","title":"抢占式调度","type":"tags"},{"content":"","date":"December 13, 2024","externalUrl":null,"permalink":"/tags/%E6%97%B6%E9%97%B4%E7%89%87/","section":"Tags","summary":"","title":"时间片","type":"tags"},{"content":"","date":"December 13, 2024","externalUrl":null,"permalink":"/tags/%E5%B9%B6%E5%8F%91/","section":"Tags","summary":"","title":"并发","type":"tags"},{"content":"","date":"December 13, 2024","externalUrl":null,"permalink":"/tags/%E5%B9%B6%E8%A1%8C/","section":"Tags","summary":"","title":"并行","type":"tags"},{"content":"","date":"December 13, 2024","externalUrl":null,"permalink":"/tags/%E5%A4%9A%E8%BF%9B%E7%A8%8B/","section":"Tags","summary":"","title":"多进程","type":"tags"},{"content":" 1. 多进程 (Multiprocessing) # 示例：假设你需要进行大量的文件处理任务，比如读取多个文件、处理内容并写入新文件。\n如何使用多进程： 你可以为每个文件创建一个独立的进程，每个进程处理一个文件。 每个进程在操作系统层面上拥有独立的内存空间，互不干扰。 如果你有四核 CPU，操作系统可以将这些进程分配给不同的核心进行并行处理，提高效率。 实际应用：如图像处理、视频编码等计算密集型任务，它们可以通过多进程来分摊计算负载，每个进程执行不同的计算任务。\n2. 多线程 (Multithreading) # 示例：你正在开发一个 web 服务器，服务器需要同时处理多个用户的请求（比如访问不同的网页）。\n如何使用多线程： 每一个用户请求可以由一个线程来处理。所有线程共享服务器的内存和资源，例如共享缓存数据。 线程间的通信和数据共享更容易（通过共享内存），但也需要注意同步，以避免并发问题。 如果是多核 CPU，多个线程可以同时在不同核心上执行，增强服务器的响应能力。 实际应用：如网络服务、数据库连接池等场景，线程之间需要频繁的资源共享，适合使用多线程来处理高并发请求。\n3. 并发 (Concurrency) # 示例：你有一个任务是同时进行多个 I/O 操作（如网络请求、文件读写等），但系统只有一个 CPU 核心。\n如何使用并发： 虽然系统只有一个核心，但通过操作系统的调度，可以在短时间内交替执行多个任务。例如，任务 1 正在等待网络响应，操作系统可以切换到任务 2，继续执行文件操作。这样看起来像是同时进行，但实际上是任务间的切换。 操作系统通过时间片轮转等机制，实现任务的“并发”，即在一个 CPU 上交替执行多个任务。 实际应用：如高并发的网络爬虫，爬虫需要同时发送多个请求，虽然只有一个 CPU 核心，但通过任务调度（并发）能有效地提高 I/O 操作的效率。\n4. 并行 (Parallelism) # 示例：假设你需要处理一个大规模的图像处理任务，比如对大量图片进行滤镜处理。\n如何使用并行： 如果你的系统有多个 CPU 核心（例如四核），你可以将任务分成 4 个子任务，每个子任务分别在一个核心上处理不同的图片。 这样所有图片的处理是同时进行的，充分利用了多核 CPU 的计算能力，显著提高处理速度。 实际应用：如深度学习模型训练、科学计算、视频渲染等场景，这些任务往往计算密集型，并且可以通过并行化加速处理过程。\nTips 并发和并行的区别就是: 并行就是多个任务同时进行;并发并不是多个任务同时进行，而是多个任务轮流进行，因为调度得好，从而看上去是同步进行的. (==并发就是多进程通过进程调度算法调度从而实现的==)\n总结：具体例子对比 # 任务类型 举例：图片处理任务 举例：Web 服务器 举例：网络爬虫 举例：科学计算 多进程 每个进程处理一张图片（独立进程） 每个用户请求一个进程处理（多个用户请求并行） 不常见，进程间通信开销大 每个进程处理一个计算任务（分布式计算） 多线程 不适用（图片处理通常不需要共享资源） 每个请求一个线程处理，线程共享数据 合适，线程共享内存、频繁I/O 适合计算任务的拆分（如大规模模拟） 并发 不常见（计算任务通常不能并发） 多个请求轮流处理（在一个核心上交替执行） 多个网络请求交替执行 适合 I/O 密集型任务（单核） 并行 同时处理多个图片（多核 CPU） 多个请求同时处理（多核 CPU） 适合在多核机器上同时发起请求 多个计算任务同时执行（多核 CPU） 每种方法都适用于不同的场景：多进程适合任务独立且计算密集型；多线程适合任务间需要频繁交互的场景；并发强调任务的调度和交替执行；而并行则适合计算任务的加速和负载分配。\n","date":"December 13, 2024","externalUrl":null,"permalink":"/notes/linux/%E5%A4%9A%E8%BF%9B%E7%A8%8B%E5%92%8C%E5%A4%9A%E7%BA%BF%E7%A8%8B-%E5%B9%B6%E5%8F%91%E5%92%8C%E5%B9%B6%E8%A1%8C/","section":"笔记","summary":"1. 多进程 (Multiprocessing) # 示例：假设你需要进行大量的文件处理任务，比如读取多个文件、处理内容并写入新文件。\n","title":"多进程和多线程,并发和并行","type":"notes"},{"content":"","date":"December 13, 2024","externalUrl":null,"permalink":"/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/","section":"Tags","summary":"","title":"多线程","type":"tags"},{"content":"","date":"November 18, 2024","externalUrl":null,"permalink":"/tags/aes/","section":"Tags","summary":"","title":"AES","type":"tags"},{"content":"","date":"November 18, 2024","externalUrl":null,"permalink":"/categories/algorithm/","section":"Categories","summary":"","title":"Algorithm","type":"categories"},{"content":"","date":"November 18, 2024","externalUrl":null,"permalink":"/tags/rsa/","section":"Tags","summary":"","title":"RSA","type":"tags"},{"content":"","date":"November 18, 2024","externalUrl":null,"permalink":"/notes/security/","section":"笔记","summary":"","title":"安全与加密","type":"notes"},{"content":"","date":"November 18, 2024","externalUrl":null,"permalink":"/tags/%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86/","section":"Tags","summary":"","title":"对称加密","type":"tags"},{"content":"","date":"November 18, 2024","externalUrl":null,"permalink":"/tags/%E9%9D%9E%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86/","section":"Tags","summary":"","title":"非对称加密","type":"tags"},{"content":"","date":"November 18, 2024","externalUrl":null,"permalink":"/tags/%E6%B7%B7%E5%90%88%E5%8A%A0%E5%AF%86/","section":"Tags","summary":"","title":"混合加密","type":"tags"},{"content":"在处理大文件或大量数据时，流行的加密方法通常是采用 混合加密方案，即结合了 对称加密（如 AES）和 非对称加密（如 RSA）的方法。这样可以在保证安全性的同时，提高效率，避免直接对大文件进行加密所带来的性能瓶颈。\n1. 混合加密方案 # 混合加密方案利用了对称加密和非对称加密各自的优势：\n对称加密（如 AES）：处理速度快，适合加密大数据。 非对称加密（如 RSA）：安全性高，适合用来加密对称加密的密钥。 典型流程 # 生成对称密钥：首先，生成一个随机的对称密钥（如 AES 密钥），这个密钥用来加密文件。对称加密通常很快，适用于处理大量数据。\n用对称密钥加密文件：使用 AES 或其他对称加密算法将大文件或大量数据加密。因为对称加密效率高，可以迅速加密大文件。\n加密对称密钥：用接收方的公钥（RSA）加密对称密钥。这样，只有持有私钥的接收方才能解密对称密钥，从而解密文件。\n传输加密数据：将加密后的文件和加密后的对称密钥一起发送给接收方。\n接收方解密：接收方首先用自己的私钥解密对称密钥，然后使用解密得到的对称密钥解密文件。\n这种方案的优点：\n效率高：对称加密（AES）非常快速，适合处理大文件。 安全性高：非对称加密（RSA）保证了对称密钥的安全传输。 灵活性：适用于大文件、流媒体、云存储等场景。 2. 分块加密 # 对于一些特殊应用，分块加密也是一种流行的方法，尤其是在直接使用非对称加密时：\n将大文件分成多个较小的块（通常每个块的大小限制在 RSA 加密能够处理的范围内，比如 256 字节）。 对每个块使用 RSA 或其他加密算法进行加密。 这种方法适用于一些小型系统或者需要直接加密而没有对称加密需求的场景，但效率比混合加密方案低。 3. 端到端加密 (End-to-End Encryption) # 端到端加密也在加密大量数据时广泛使用，特别是用于云存储或消息传递服务中。以下是典型的端到端加密方案：\n数据加密：客户端对数据进行加密，然后发送到服务器。 密钥管理：密钥管理是关键，通常使用公私钥对来加密通信密钥。 解密：只有授权的接收方能够解密数据。 在很多应用场景中，端到端加密结合了对称加密和非对称加密（类似混合加密方案），确保通信的安全性和效率。\n4. 使用专用加密工具 # 许多加密工具和协议已经优化了大文件的加密过程，例如：\nGPG (GNU Privacy Guard)：基于 OpenPGP 标准，常用于文件加密，支持对称和非对称加密。 SSL/TLS：用于保护网络传输中的数据，特别适合流媒体、实时通信等大数据量传输。 5. 硬件加速 # 为了进一步提高加密效率，很多系统使用硬件加速：\n硬件加速的对称加密算法：如 AES-NI（Intel 提供的 AES 加速指令），可以显著加速 AES 加密过程，尤其是在加密大文件时。 硬件安全模块 (HSM)：用于生成和存储密钥，确保加密操作的安全性和高效性。 6. 云服务加密 # 对于云存储和云计算中的大文件加密，很多云服务提供商会自动处理加密：\n例如，Amazon S 3、Google Cloud Storage 等会自动对存储在云中的数据进行加密，通常使用 AES-256（对称加密算法）。 同时，密钥管理服务（如 AWS KMS 或 Google Cloud KMS）提供了加密密钥的管理和访问控制。 7. 流式加密 # 对于实时数据流的加密（如视频流、音频流、实时通信数据），流式加密算法（如 RC 4、ChaCha 20）常常被使用。这些算法专门针对数据流进行加密，能够在流数据传输过程中保证安全性。\n总结 # 处理大文件或大量数据时，最流行的加密方法是采用 混合加密方案，即结合 对称加密（如 AES）和 非对称加密（如RSA）的优点。对称加密用于高效地加密数据，而非对称加密用于加密对称密钥，确保密钥的安全性。这种方法广泛应用于云存储、文件加密、数据传输等场景，同时，使用硬件加速、分块加密、端到端加密等技术可以进一步提高效率和安全性。\n","date":"November 18, 2024","externalUrl":null,"permalink":"/notes/security/%E6%B5%81%E8%A1%8C%E7%9A%84%E5%A4%A7%E9%87%8F%E6%95%B0%E6%8D%AE%E6%88%96%E8%80%85%E5%A4%A7%E6%96%87%E4%BB%B6%E5%8A%A0%E5%AF%86/","section":"笔记","summary":"在处理大文件或大量数据时，流行的加密方法通常是采用 混合加密方案，即结合了 对称加密（如 AES）和 非对称加密（如 RSA）的方法。这样可以在保证安全性的同时，提高效率，避免直接对大文件进行加密所带来的性能瓶颈。\n","title":"流行的大量数据或者大文件加密","type":"notes"},{"content":"","date":"November 18, 2024","externalUrl":null,"permalink":"/categories/%E5%AF%86%E7%A0%81%E5%AD%A6/","section":"Categories","summary":"","title":"密码学","type":"categories"},{"content":"","date":"November 18, 2024","externalUrl":null,"permalink":"/tags/hash%E7%AE%97%E6%B3%95/","section":"Tags","summary":"","title":"Hash算法","type":"tags"},{"content":"","date":"November 18, 2024","externalUrl":null,"permalink":"/tags/rsa%E5%8A%A0%E5%AF%86/","section":"Tags","summary":"","title":"RSA加密","type":"tags"},{"content":" 情境概述 # Alice 和 Bob 是通信双方。 目标：确保消息 \\( M \\) 的机密性、完整性和来源可验证。 使用的技术：RSA 加密、数字签名和 Hash 算法。 双方应持有的密钥 # Alice:\n私钥$( d_{A})$ ：用来签名消息。 公钥 $(e_{A})$：用来加密消息（如果 Alice 要接收加密消息）。 Bob:\n私钥 $( d_{B})$ ：用来解密消息。 公钥 $(e_{B})$：用来加密消息（如果 Bob 要接收加密消息）。 注意：双方的公钥是公开的，任何人都可以获取，而私钥则是保密的，只能由对应的密钥持有者使用。\n已知信息 # Alice 和 Bob 都知道对方的 公钥。即： Alice 知道 Bob 的公钥 $(e_{B})$。 Bob 知道 Alice 的公钥 $(e_{A})$。 过程说明 # 1. Alice 准备消息 # Alice 要向 Bob 发送一条消息 \\( M \\)，比如： $$ M = \\text{\"Hello, Bob!\"} $$ 2. Alice 对消息进行 RSA 加密 # Alice 使用 Bob 的公钥 $(e_{B})$ 对消息 \\( M \\) 进行加密，得到密文 \\( C \\)： $$ C = M^{e_{B}} \\mod n_{B} $$这里，$( n_{B} )$ 是 Bob 公钥的一个组成部分。\n密文 \\( C \\) 是加密后的消息，只有拥有 Bob 私钥 $( d_{B})$ 的 Bob 才能解密。\n3. Alice 计算消息的 Hash 值并生成数字签名 # 为了确保消息的完整性和来源，Alice 使用 Hash 算法（如 SHA-256）计算消息 \\( M \\) 的 Hash 值 \\( H (M) \\)： $$ H(M) = \\text{SHA-256}(M) $$ 然后，Alice 使用 自己的私钥$( d_{A})$ 对 Hash 值 \\( H (M) \\) 进行签名，生成数字签名 \\( S \\)： $$ S = H(M)^{d_{A}} \\mod n_{A} $$这里，$( n_{A} )$ 是 Alice 私钥的一个组成部分。\n4. Alice 发送加密消息和签名 # Alice 将加密后的消息 \\( C \\) 和数字签名 \\( S \\) 一同发送给 Bob。 $$ \\text{发送数据} = (C, S) $$ 5. Bob 接收消息和签名 # Bob 收到 Alice 发送的加密消息 \\( C \\) 和签名 \\( S \\)。 6. Bob 使用私钥解密消息 # Bob 使用 自己的私钥 $( d_{B})$ 解密密文 \\( C \\)，得到原始消息 \\( M \\)： $$ M = C^{d_{B}} \\mod n_{B} $$ 解密后，Bob 得到原始的消息 $( M = \\text{\"Hello, Bob!\"} )$。\n7. Bob 使用 Alice 公钥验证签名 # Bob 使用 Alice 的公钥 $(e_{A})$ 对签名 \\( S \\) 进行验证： $$ H'(M) = S^{e_{A}} \\mod n_{A} $$这里，\\( H' (M) \\) 是通过公钥解密签名后得到的 Hash 值。\n然后，Bob 对收到的消息 \\( M \\) 使用相同的 Hash 算法计算 Hash 值 \\( H (M) \\)，即： $$ H(M) = \\text{SHA-256}(M) $$ 8. Bob 验证签名的有效性 # 如果计算得到的 Hash 值 \\( H (M) \\) 与通过公钥解密得到的 Hash 值 \\( H' (M) \\) 相同： $$ H(M) = H'(M) $$那么消息 \\( M \\) 没有被篡改，且确实是由 Alice 发送的，签名有效。\n如果 $( H (M) \\neq H' (M) )$，则说明消息可能已被篡改，或者签名无效。\n9. Bob 进一步处理消息 # 如果签名验证成功，Bob 知道消息完整且真实，接下来可以继续处理消息内容（比如显示消息：“Hello, Bob!”）。 总结 # 在这个过程中，RSA 加密和数字签名共同作用：\nRSA 加密：保护消息的机密性，确保只有 Bob（拥有私钥）才能解密消息。 数字签名：确保消息的完整性和来源，只有 Alice（拥有私钥）才能生成有效的签名，Bob 可以用 Alice 的公钥验证签名。 双方持有的密钥 # Alice： 私钥$( d_{A})$ 公钥 $(e_{A})$ Bob： 私钥 $( d_{B})$ , 公钥 $( e_{B} )$ 双方应知的内容 # Alice 知道：Bob 的公钥 $(e_{B})$（用于加密消息） Bob 知道：Alice 的公钥 $(e_{A})$（用于验证签名） 通过这种方式，RSA 加密保证了消息的机密性，数字签名确保了消息的完整性和来源的真实性。\n","date":"November 18, 2024","externalUrl":null,"permalink":"/notes/security/rsa%E5%8A%A0%E5%AF%86-%E6%95%B0%E5%AD%97%E7%AD%BE%E5%90%8D%E5%92%8Chas%E7%AE%97%E6%B3%95%E7%BB%93%E5%90%88%E4%BE%8B%E5%AD%90/","section":"笔记","summary":"情境概述 # Alice 和 Bob 是通信双方。 目标：确保消息 \\( M \\) 的机密性、完整性和来源可验证。 使用的技术：RSA 加密、数字签名和 Hash 算法。 双方应持有的密钥 # Alice:\n","title":"RSA加密,数字签名和HAS算法结合例子","type":"notes"},{"content":"","date":"November 18, 2024","externalUrl":null,"permalink":"/tags/%E5%AE%89%E5%85%A8%E9%80%9A%E4%BF%A1/","section":"Tags","summary":"","title":"安全通信","type":"tags"},{"content":"","date":"November 18, 2024","externalUrl":null,"permalink":"/tags/%E6%95%B0%E5%AD%97%E7%AD%BE%E5%90%8D/","section":"Tags","summary":"","title":"数字签名","type":"tags"},{"content":" RSA 加密算法解释 # 一、RSA 加密算法的数学原理 # 1. 生成密钥对 # 步骤如下：\n选择两个大素数：\\( p \\) 和 \\( q \\)。 计算 \\( n \\)： $$ n = p \\times q $$ 计算欧拉函数 ($\\varphi(n)$)： $$ \\varphi (n) = (p-1) \\times (q-1) $$ 选择一个公钥指数 ( e )，要求 $1","date":"November 18, 2024","externalUrl":null,"permalink":"/notes/security/rsa%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95/","section":"笔记","summary":"RSA 加密算法解释 # 一、RSA 加密算法的数学原理 # 1. 生成密钥对 # 步骤如下：\n","title":"RSA加密算法","type":"notes"},{"content":"sudo mount /dev/sdXX /mnt sudo mount /dev/sdaXX /mnt/home(有这个分区的话) sudo mount --bind /dev /mnt/dev sudo mount --bind /dev/pts /mnt/dev/pts sudo mount --bind /proc /mnt/proc sudo mount --bind /sys /mnt/sys sudo mount --bind /run /mnt/run sudo mount --bind /etc/resolv. conf /mnt/etc/resolv. conf sudo mount --bind /tmp /mnt/tmp sudo chroot /mnt 什么是 chroot？ # chroot（Change Root）是 Linux/Unix 系统中的一种工具，它允许你将当前的根文件系统切换到指定的目录，使该目录成为虚拟机或类似于容器的环境。简单来说，chroot 可以将系统的根目录 / 改变为某个其他的目录，这样你可以在这个隔离的环境下操作，不影响主系统。这种方式通常用于恢复系统、测试环境、构建软件或者运行独立的系统。\n但是，chroot 环境并不自动包含宿主机所有功能。为了确保虚拟机能够正常使用类似宿主机的功能，比如设备访问、进程管理和网络功能，你需要手动挂载一些关键文件系统。\n挂载的原理： # 这些挂载操作是通过 --bind 选项来实现的，它允许将宿主机的某个目录或文件系统“绑定”到 chroot 环境的某个位置。这样一来，虚拟机就可以直接访问宿主机上的这些资源，达到正常运行系统和网络功能的目的。\n这些文件系统本身大多是虚拟的（例如 /proc 和 /sys），因此它们不是实际的存储内容，而是动态生成的系统信息。通过将它们挂载到虚拟机，虚拟机就可以和宿主机共享这些动态系统信息。\n挂载的文件系统有哪些？为什么挂载？ # 为了让 chroot 环境有完整的系统功能，你需要挂载以下文件系统或文件：\n/proc （进程文件系统） # /proc （进程文件系统） 作用：提供系统和进程的信息，如 CPU、内存使用情况、挂载点等。 为什么挂载：许多命令依赖 /proc 来获取系统状态（如 ps, top 等）。挂载后，chroot 可以访问宿主机的系统和进程信息。 挂载命令： sudo mount --bind /proc /mnt/proc /sys （系统文件系统） # /sys （系统文件系统） 作用：提供关于系统硬件设备的信息，包括驱动程序、设备状态等。 为什么挂载：系统管理工具（如 udevadm）和硬件相关的功能需要访问 /sys。 挂载命令： sudo mount --bind /sys /mnt/sys /dev （设备文件系统） # /dev （设备文件系统） 作用：提供设备节点，比如硬盘、终端设备、USB 等。 为什么挂载：/dev 让 chroot 环境可以访问实际的硬件设备，包括硬盘、鼠标、键盘等，以及管理这些设备。 挂载命令： sudo mount --bind /dev /mnt/dev /run （运行时信息） # /run （运行时信息） 作用：包含系统运行时生成的临时文件，比如 PID 文件、socket 文件等。 为什么挂载：一些系统进程和服务需要 /run 来存储它们的运行状态或通信文件。 挂载命令： sudo mount --bind /run /mnt/run /dev/pts （伪终端 # /dev/pts （伪终端） 作用：提供伪终端（pseudo-terminal），用于创建多个终端会话，比如 ssh 或 sudo 需要使用它。 为什么挂载：在 chroot 中执行需要伪终端的命令时（如 sudo），必须挂载 /dev/pts。 挂载命令： sudo mount --bind /dev/pts /mnt/dev/pts /etc/resolv.conf （DNS 配置） # /etc/resolv.conf （DNS 配置） 作用：配置系统如何解析域名，通过 DNS 服务器转换域名为 IP 地址。 为什么挂载：如果 chroot 环境中需要访问网络（如更新软件），必须能解析域名，因此要挂载 /etc/resolv.conf。 挂载命令： sudo mount --bind /etc/resolv.conf /mnt/etc/resolv.conf /tmp （临时文件） # /tmp （临时文件） 作用：用于存储临时文件和目录。 为什么挂载：某些程序可能需要 /tmp 目录来存储临时数据。 挂载命令： sudo mount --bind /tmp /mnt/tmp 挂载顺序 # 一般来说，挂载这些目录的顺序不是特别严格，只要你确保所有必要的目录都已经挂载。可以按照如下步骤进行：\nsudo mount --bind /dev /mnt/dev sudo mount --bind /dev/pts /mnt/dev/pts sudo mount --bind /proc /mnt/proc sudo mount --bind /sys /mnt/sys sudo mount --bind /run /mnt/run sudo mount --bind /etc/resolv. conf /mnt/etc/resolv. conf sudo mount --bind /tmp /mnt/tmp 然后进入 chroot：\nsudo chroot /mnt ","date":"October 14, 2024","externalUrl":null,"permalink":"/notes/system/chroot/","section":"笔记","summary":"sudo mount /dev/sdXX /mnt sudo mount /dev/sdaXX /mnt/home(有这个分区的话) sudo mount --bind /dev /mnt/dev sudo mount --bind /dev/pts /mnt/dev/pts sudo mount --bind /proc /mnt/proc sudo mount --bind /sys /mnt/sys sudo mount --bind /run /mnt/run sudo mount --bind /etc/resolv. conf /mnt/etc/resolv. conf sudo mount --bind /tmp /mnt/tmp sudo chroot /mnt 什么是 chroot？ # chroot（Change Root）是 Linux/Unix 系统中的一种工具，它允许你将当前的根文件系统切换到指定的目录，使该目录成为虚拟机或类似于容器的环境。简单来说，chroot 可以将系统的根目录 / 改变为某个其他的目录，这样你可以在这个隔离的环境下操作，不影响主系统。这种方式通常用于恢复系统、测试环境、构建软件或者运行独立的系统。\n","title":"chroot","type":"notes"},{"content":"","date":"October 14, 2024","externalUrl":null,"permalink":"/tags/chroot/","section":"Tags","summary":"","title":"Chroot","type":"tags"},{"content":"","date":"October 14, 2024","externalUrl":null,"permalink":"/tags/%E9%9A%94%E7%A6%BB%E7%8E%AF%E5%A2%83/","section":"Tags","summary":"","title":"隔离环境","type":"tags"},{"content":"","date":"October 14, 2024","externalUrl":null,"permalink":"/tags/%E7%B3%BB%E7%BB%9F%E6%81%A2%E5%A4%8D/","section":"Tags","summary":"","title":"系统恢复","type":"tags"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/tags/bios/","section":"Tags","summary":"","title":"BIOS","type":"tags"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/categories/boot/","section":"Categories","summary":"","title":"BOOT","type":"categories"},{"content":" 笔记 A bootloader is a program responsible for loading the operating system kernel into memory and starting the operating system. It operates at the low level, interfacing directly with the system\u0026rsquo;s firmware (e.g., BIOS/UEFI).\n笔记 A boot manager is a higher-level program that allows users to select from multiple operating systems or kernels to boot into. ==It operates after the system firmware has selected a boot device but before the operating system is loaded.==\n引导加载程序 和 引导管理器 是系统引导过程中密切相关的组件，但它们的用途不同。下面详细介绍了两者之间的关系和区别：\n1. 引导加载程序(bootloader) # 引导加载程序 是一个负责将操作系统内核加载到内存中并启动操作系统的程序。它在低级运行，直接与系统的固件（例如 BIOS/UEFI）交互。\n引导加载程序的主要功能： # 低级功能：引导加载程序的主要功能是将操作系统加载到内存中并将控制权移交给它。它是系统固件（BIOS/UEFI）完成硬件初始化后运行的第一个软件。 位置：在 MBR 系统中，引导加载程序存储在主引导记录（磁盘的前 446 个字节）中。在 GPT 系统中，引导加载程序存储在 EFI 系统分区 (ESP) 中。 通常没有用户界面：引导加载程序的作用主要是功能性的。其主要任务是加载操作系统，而不一定提供用户选择（除非它还充当引导管理器）。 示例： GRUB（GRand Unified Bootloader）是 Linux 系统中最常用的引导加载程序之一。 Windows Bootloader 从磁盘加载 Windows。 LILO（Linux Loader）是 Linux 引导加载程序的另一个示例。 2. 引导管理器（boot manager） # 引导管理器是一个高级程序，允许用户从多个操作系统或内核中进行选择以进行引导。它在系统固件选择引导设备之后但在加载操作系统之前运行。\n引导管理器的主要功能： # 提供操作系统选择菜单：引导管理器允许用户在安装了多个操作系统时选择从哪个操作系统或内核进行引导。它为此显示菜单或界面。 可与多个引导加载程序配合使用：引导管理器可以指示系统为不同的操作系统加载特定的引导加载程序。例如，GRUB 可以加载 Linux 和 Windows 的引导加载程序，让用户可以选择要引导的引导加载程序。 位于 EFI 系统分区或 MBR 中：在 UEFI 系统中，引导管理器文件存储在 EFI 系统分区 (ESP) 中。在 MBR 系统中，像 GRUB 这样的引导管理器可以驻留在 MBR 中。 可以成为引导加载程序的一部分：某些引导加载程序（如 GRUB）也可用作引导管理器。GRUB 不仅加载操作系统，还提供菜单供用户在多个操作系统或内核配置之间进行选择。 示例： GRUB：GRUB 既是引导加载程序又是引导管理器，让用户可以从不同的操作系统或内核版本中进行选择。 Windows 引导管理器：Windows 系统的默认引导管理器，如果安装了多个版本的 Windows，则会显示菜单。 rEFInd：UEFI 系统的独立启动管理器，可以直接从其引导加载程序加载操作系统。 3. 引导加载程序和引导管理器之间的关系 # 相互依赖：引导管理器与引导加载程序协同工作。引导管理器负责向用户提供选项（例如要引导哪个操作系统），而引导加载程序则执行实际加载所选操作系统的任务。例如，GRUB（可充当引导加载程序和引导管理器）将显示一个菜单，供用户在 Linux 和 Windows 之间进行选择。一旦用户做出选择，GRUB 将加载相应的引导加载程序以引导到所选操作系统。\n引导顺序：\n系统固件 (BIOS/UEFI) 启动引导过程，并根据配置的引导设备顺序查找引导加载程序或引导管理器。 引导管理器（如果存在）向用户提供操作系统选择。 执行所选操作系统的 引导加载程序，并将该操作系统的内核加载到内存中。 控制权移交给操作系统，操作系统完成启动过程。 4. 引导加载程序和引导管理器之间的主要区别： # 功能 引导加载程序 引导管理器 主要角色 将操作系统内核加载到内存中 管理用户选择要运行的操作系统或引导加载程序 运行时 在系统固件 (BIOS/UEFI) 之后立即运行 在引导加载程序之前运行以提供操作系统选择 功能 直接加载并启动操作系统 显示用于选择要运行的操作系统或引导加载程序的菜单 用户界面 通常没有用户界面（除非集成） 提供用于选择操作系统的菜单 示例 GRUB（引导加载程序功能）、Windows 引导加载程序 GRUB（启动管理器功能）、Windows 启动管理器、rEFInd 存储位置 MBR 或 EFI 系统分区 (ESP) EFI 系统分区 (ESP) 或有时在 MBR 中 多操作系统支持 直接加载一个操作系统 管理多个引导加载程序或操作系统选择 5. 摘要： # 引导加载程序：加载操作系统并负责启动引导过程。它是系统开始加载操作系统所需的关键低级程序。 引导管理器：允许用户在不同的操作系统或配置之间进行选择。它提供了可以在安装多个操作系统时提供选择的用户界面。 在现代系统中，GRUB 是一种常用工具，它结合了引导管理器和引导加载程序的角色，提供操作系统选择和操作系统的实际加载。但是，某些系统可能会为这两个角色使用单独的程序，尤其是在更复杂的多引导环境中。\n","date":"October 10, 2024","externalUrl":null,"permalink":"/notes/system/bootloader-%E5%92%8C-boot-manager%E7%9A%84%E5%8C%BA%E5%88%AB%E4%B8%AD%E6%96%87/","section":"笔记","summary":" 笔记 A bootloader is a program responsible for loading the operating system kernel into memory and starting the operating system. It operates at the low level, interfacing directly with the system’s firmware (e.g., BIOS/UEFI).\n","title":"bootloader 和 boot manager的区别（中文）","type":"notes"},{"content":" 笔记 A bootloader is a program responsible for loading the operating system kernel into memory and starting the operating system. It operates at the low level, interfacing directly with the system\u0026rsquo;s firmware (e.g., BIOS/UEFI).\n笔记 A boot manager is a higher-level program that allows users to select from multiple operating systems or kernels to boot into. ==It operates after the system firmware has selected a boot device but before the operating system is loaded.==\nThe bootloader and the boot manager（不仅可以引导多系统，还可以引导多个 bootloader） are closely related components in the system boot process, but they serve different purposes. Here\u0026rsquo;s a breakdown of the relationship and differences between the two:\n1. Bootloader # A bootloader is a program responsible for loading the operating system kernel into memory and starting the operating system. It operates at the low level, interfacing directly with the system\u0026rsquo;s firmware (e.g., BIOS/UEFI).\nKey Features of a Bootloader: # Low-level function: The bootloader\u0026rsquo;s primary function is to load the operating system into memory and hand over control to it. It is the first software that runs after the system firmware (BIOS/UEFI) has done its hardware initialization. Location: In MBR systems, the bootloader is stored in the Master Boot Record (the first 446 bytes of the disk). In GPT systems, bootloaders are stored in the EFI System Partition (ESP). Doesn’t typically have a user interface: The bootloader’s role is primarily functional. Its main task is to load the OS without necessarily providing user choices (unless it also serves as a boot manager). Examples: GRUB (GRand Unified Bootloader) is one of the most common bootloaders used in Linux systems. Windows Bootloader loads Windows from the disk. LILO (Linux Loader) is another example of a Linux bootloader. 2. Boot Manager # A boot manager is a higher-level program that allows users to select from multiple operating systems or kernels to boot into. It operates after the system firmware has selected a boot device but before the operating system is loaded.\nKey Features of a Boot Manager: # Presents a menu for OS selection: A boot manager allows users to choose which operating system or kernel to boot from when multiple OSes are installed. It displays a menu or interface for this purpose. Works with multiple bootloaders: The boot manager can direct the system to load specific bootloaders for different operating systems. For example, GRUB can load the bootloaders for both Linux and Windows, allowing the user to choose which one to boot. Located in the EFI System Partition or MBR: In UEFI systems, the boot manager files are stored in the EFI System Partition (ESP). In MBR systems, a boot manager like GRUB can reside in the MBR. Can be part of a bootloader: Some bootloaders, like GRUB, also serve as boot managers. GRUB not only loads the OS but also presents a menu to choose between multiple operating systems or kernel configurations. Examples: GRUB: GRUB is both a bootloader and a boot manager, allowing users to choose from different operating systems or kernel versions. Windows Boot Manager: The default boot manager for Windows systems that presents a menu if multiple versions of Windows are installed. rEFInd: A standalone boot manager for UEFI systems that can load operating systems directly from their bootloaders. 3. Relationship Between Bootloader and Boot Manager # Interdependency: The boot manager works with bootloaders. The boot manager is responsible for presenting the user with options (like which OS to boot), while the bootloader executes the task of actually loading the chosen OS. For example, GRUB (which can act as both a bootloader and a boot manager) will present a menu to choose between Linux and Windows. Once the user makes a selection, GRUB will load the corresponding bootloader to boot into the chosen operating system.\nBoot Sequence:\nSystem firmware (BIOS/UEFI) starts the boot process and looks for a bootloader or boot manager based on the configured boot device order. The boot manager (if present) presents the user with a choice of operating systems. The bootloader for the selected OS is executed, and it loads the kernel of that OS into memory. Control is handed over to the OS, which completes the boot process. 4. Key Differences Between Bootloader and Boot Manager: # Feature Bootloader Boot Manager Primary Role Loads the operating system kernel into memory Manages user selection of which OS or bootloader to run When It Runs Runs immediately after system firmware (BIOS/UEFI) Runs before the bootloader to offer OS selection Functionality Directly loads and starts the OS Presents a menu for choosing which OS or bootloader to run User Interface Typically no user interface (unless integrated) Provides a menu for OS selection Examples GRUB (bootloader function), Windows Bootloader GRUB (boot manager function), Windows Boot Manager, rEFInd Storage Location MBR or EFI System Partition (ESP) EFI System Partition (ESP) or sometimes in the MBR Multiple OS Support Directly loads one OS Manages multiple bootloaders or OS choices 5. Summary: # Bootloader: Loads the operating system and is responsible for starting the boot process. It’s a critical low-level program that the system needs to begin loading the OS. Boot Manager: Allows the user to select between different operating systems or configurations. It provides the user interface that can offer choices when there are multiple OSes installed. In modern systems, GRUB is a common tool that combines the roles of boot manager and bootloader, offering both OS selection and actual loading of the OS. However, some systems may use separate programs for these two roles, especially in more complex multi-boot environments.\n","date":"October 10, 2024","externalUrl":null,"permalink":"/notes/system/bootloader%E5%92%8Cboot-manager%E7%9A%84%E5%8C%BA%E5%88%ABenglish/","section":"笔记","summary":" 笔记 A bootloader is a program responsible for loading the operating system kernel into memory and starting the operating system. It operates at the low level, interfacing directly with the system’s firmware (e.g., BIOS/UEFI).\n","title":"BOOTLOADER和BOOT MANAGER的区别（English）","type":"notes"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/tags/gpt/","section":"Tags","summary":"","title":"GPT","type":"tags"},{"content":" GPT（GUID 分区表） 分区方案在结构、功能以及与引导加载程序和系统引导的交互方式方面与 MBR（主引导记录） 方案有很大不同。让我们比较一下这两者，并了解 GPT 在系统引导环境中的工作原理，尤其是对于双引导场景。\n1. GPT 和 MBR 分区方案之间的差异： # 方面 MBR GPT 分区表大小 64 字节（限制为 4 个主分区） 将多个分区存储在更大的表中 分区数 最多 4 个主分区（或 3 个主分区 + 1 个带逻辑分区的扩展分区） 默认支持最多 128 个分区（可以添加更多分区） 磁盘大小支持 最大 2 TB 支持大于 2 TB 的磁盘（最多 9.4 ZB） 引导代码 包含 446 字节的引导加载程序代码（主引导代码） GPT 不以相同的方式存储引导加载程序。引导加载程序存储在 EFI 系统分区 (ESP) 中 冗余 无冗余，分区表仅存储在磁盘的开头 GPT 存储分区表的多个副本以实现冗余（一个在开头，一个在磁盘的末尾） 损坏恢复 更容易损坏；没有自动恢复 更强大，可以使用备份表从损坏中恢复 兼容性 适用于旧式 BIOS 启动模式 专为 UEFI 设计，但可以模拟 MBR 以实现向后兼容（通过“保护性 MBR”） 2. 使用 GPT 启动： # 使用 GPT 启动与 MBR 不同，因为 UEFI（统一可扩展固件接口） 是传统 BIOS 的现代替代品。让我们来探索一下启动差异：\n使用 GPT 和 UEFI 启动： # EFI 系统分区 (ESP)：\n在使用 UEFI 的基于 GPT 的系统中，引导加载程序存储在称为 EFI 系统分区 (ESP) 的特殊分区中。\nESP 包含各种操作系统的引导加载程序文件。这些引导加载程序文件采用 EFI 可执行文件 (. efi) 的形式（可以用 c语言 来写），UEFI 可以直接加载。\n您安装的每个操作系统都可以将其自己的引导加载程序放置在 ESP 中。例如，GRUB 可能位于 /EFI/grub 中，而 Windows 启动管理器位于 /EFI/Microsoft/Boot 中。\n没有主引导记录 (MBR) 代码：\n与 MBR 不同，GPT 中没有单个“主引导代码”。相反，UEFI 根据引导顺序设置或用户输入（通过引导菜单 grub）从 ESP 加载适当的引导加载程序。\n引导管理器：\nUEFI 本身可以充当基本引导管理器。它可以提供一个菜单（取决于 uefi 的固件支不支持），列出存储在 ESP 中的可用操作系统或引导加载程序。这允许您选择从哪个操作系统引导，而无需单独的引导管理器（如 GRUB）。\n但是，对于更复杂的设置（例如，使用 Linux 和 Windows 双引导），仍然可以安装 GRUB 或其他引导管理器以提供更多灵活性（例如，自定义引导选项、在不同的 Linux 内核之间进行选择等）。\n使用 GPT 和传统 BIOS（CSM 模式）启动： # 兼容性支持模块 (CSM)： 一些支持 UEFI 的系统还提供兼容性支持模块 (CSM)，允许它们在传统 BIOS 模式下启动。 如果您使用的是 GPT 磁盘，但想要在传统 BIOS 模式下启动，则可以将系统配置为通过 CSM 模拟 BIOS。在这种情况下，您可能需要“保护性 MBR”以避免与不理解 GPT 的旧工具发生兼容性问题。 但是，使用 GPT 在传统模式下启动通常不太常见，并且不是 GPT 的预期设计，GPT 旨在与 UEFI 配合使用。 3. 使用 GPT 进行双启动： # 在使用 GPT 的双启动场景中，所涉及的过程和工具与基于 MBR 的系统略有不同：\n安装多个操作系统：\n两个操作系统（例如 Linux 和 Windows）都将其引导加载程序文件放在 EFI 系统分区 (ESP) 中。每个操作系统都会在 ESP 中创建一个目录来存储其引导加载程序。\nUEFI 启动菜单：\nUEFI 可以识别 ESP 中的多个引导加载程序条目，并且通常提供基本启动菜单。您可以在启动期间按下某个键（通常是 F 12、ESC 或根据制造商的不同而不同的其他键）来访问该菜单，以手动选择要加载哪个引导加载程序（例如 Windows 启动管理器、GRUB）。\nGPT 上的 GRUB：\n如果您在具有 UEFI 的 GPT 磁盘上安装 GRUB，它仍将用作启动管理器，允许您在多个操作系统之间进行选择。GRUB 将检测 Windows 启动管理器和其他可启动条目，并在其启动菜单中显示它们。\nGRUB 的引导加载程序安装在 EFI 系统中分区（不在 MBR 中，因为 GPT 不使用 MBR 引导代码），UEFI 将从 ESP 加载 GRUB。\n没有 GRUB？：\n如果您不安装 GRUB 或任何其他引导管理器，UEFI 将从 ESP 启动默认操作系统引导加载程序（例如，Windows 引导管理器）。如果只有一个操作系统的引导加载程序存在或配置为默认，则不会有任何引导菜单，系统将直接启动到该操作系统。\n4. MBR 和 GPT 之间双引导设置的主要区别： # MBR：\n引导加载程序存储在 MBR 中。\n有限的分区支持。\n需要在 MBR 中安装 GRUB（或其他引导管理器）以提供双引导系统的引导菜单。\nGPT：\n引导加载程序存储在 EFI 系统分区中。\n没有像 MBR 中的单个“主引导代码”； UEFI 直接从 ESP 加载引导加载程序。\n如果 ESP 中存在多个引导加载程序，UEFI 可以提供基本启动菜单，但可以安装 GRUB 或其他启动管理器以实现更高级的控制。\n支持更多分区和更大的磁盘大小。\n摘要： # MBR 依赖于主引导记录中的单个引导加载程序，并支持有限的分区和较小的磁盘大小。 GPT 专为 UEFI 系统设计，使用 EFI 系统分区 (ESP) 来存储多个引导加载程序，并允许使用许多分区和更大的磁盘进行灵活的启动管理。 使用 GPT，UEFI 可以提供启动菜单以在不同的操作系统之间进行选择（如果已配置），但更高级的启动管理（如 GRUB）仍可用于复杂的双启动设置。 ","date":"October 10, 2024","externalUrl":null,"permalink":"/notes/system/gpt%E5%88%86%E5%8C%BA%E6%96%B9%E6%A1%88/","section":"笔记","summary":" GPT（GUID 分区表） 分区方案在结构、功能以及与引导加载程序和系统引导的交互方式方面与 MBR（主引导记录） 方案有很大不同。让我们比较一下这两者，并了解 GPT 在系统引导环境中的工作原理，尤其是对于双引导场景。\n","title":"GPT分区方案","type":"notes"},{"content":"是的，GRUB 不会将其所有引导加载程序代码都存储在 /boot/grub 中。它使用 多阶段架构，其中引导加载程序的不同部分存储在不同的位置，这对于使用 MBR 和 GPT 分区方案的系统至关重要，尤其是在 BIOS 模式 下启动时。\nGRUB 的多阶段架构说明 # GRUB 的启动过程分为几个阶段，每个阶段都存储在不同的位置，具体取决于分区方案（MBR 或 GPT）和启动模式（BIOS 或 UEFI）。以下是细分：\n1. GRUB 第 1 阶段 # 位置：存储在 MBR（主引导记录）或 GPT 系统中的 保护性 MBR（protective MBR） 中。 大小：仅 446 字节（磁盘前 512 字节的一部分）。 功能：阶段 1 的唯一目的是加载 GRUB 的下一阶段（阶段 1.5 或阶段 2）。由于阶段 1 非常小，因此它无法执行读取文件系统等复杂任务。 2. GRUB 阶段 1.5 # 位置： 在 MBR 磁盘 上：存储在 后 MBR 间隙（post-mbr gap）（MBR 和磁盘上第一个分区之间的小空间）中。 在 GPT 磁盘 上：存储在专用的 BIOS 启动分区（通常大小为 1-2 MB）中。 功能：阶段 1.5 包含必要的 文件系统驱动程序 和从分区（例如 /boot/grub）读取所需的其他代码。它弥补了阶段 1（最小）和阶段 2（功能齐全）之间的差距。 如果没有阶段 1.5，GRUB 将无法找到并读取存储在 /boot/grub 目录中的实际文件，因为阶段 1 本身缺乏理解文件系统（例如 ext 4、Btrfs 等）的能力。 3. GRUB 阶段 2 # 位置：存储在分区（通常是 /boot 分区或操作系统的根分区）上的 /boot/grub/ 目录中。 功能：阶段 2 是完整的引导加载程序，它： 显示 GRUB 菜单（如果有多个操作系统或内核）。 允许用户选择要引导的操作系统或内核。 将 操作系统内核 加载到内存中（例如 Linux 内核）。 将控制权传递给操作系统。 当我们在多启动系统上看到 GRUB 菜单时，我们经常与 Stage 2 交互。它可以自定义，可以提供图形界面，并处理启动操作系统的更复杂任务。\nGRUB 为什么需要 Stage 1.5？ # Stage 1.5 的主要原因是 Stage 1 太小，无法包含对不同文件系统的支持。如果没有 Stage 1.5，GRUB 的 Stage 1 将无法理解从分区中加载 Stage 2 的位置或方式，因为分区通常使用复杂的文件系统（如 ext 4、XFS 等）。Stage 1.5 包括文件系统驱动程序，允许 GRUB 在分区上找到 /boot/grub 目录，并从中加载 Stage 2。\n带有 Stage 1.5 的 MBR：在 MBR 系统上，Stage 1.5 存储在 MBR 后间隙中，这为其提供了足够的空间来存储必要的文件系统驱动程序。 带有 Stage 1.5 的 GPT：在 GPT 系统上，不存在 MBR 后间隙，因此 Stage 1.5 存储在 BIOS 启动分区中。这是 **GRUB 在 GPT 上以 BIOS 模式运行所必需的。 摘要 # Stage 1（存储在 MBR 中）非常小，仅用于加载 Stage 1.5 或 Stage 2。 Stage 1.5（存储在 MBR 的 MBR 后间隙中或 GPT 的 BIOS 启动分区中）是加载文件系统驱动程序和继续启动过程所必需的。 第 2 阶段（存储在 /boot/grub/ 中）是功能齐全的引导加载程序，它显示 GRUB 菜单、允许选择操作系统并加载操作系统内核。 因此，GRUB 不仅将引导代码存储在 /boot/grub/ 中；它还将关键的引导加载程序代码存储在 MBR（第 1 阶段）中，如果是 GPT 磁盘上的 BIOS 模式，则存储在 BIOS 引导分区（第 1.5 阶段）中。这种架构使 GRUB 能够正确加载和引导操作系统。\n","date":"October 10, 2024","externalUrl":null,"permalink":"/notes/system/grub/","section":"笔记","summary":"是的，GRUB 不会将其所有引导加载程序代码都存储在 /boot/grub 中。它使用 多阶段架构，其中引导加载程序的不同部分存储在不同的位置，这对于使用 MBR 和 GPT 分区方案的系统至关重要，尤其是在 BIOS 模式 下启动时。\n","title":"GRUB","type":"notes"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/tags/grub/","section":"Tags","summary":"","title":"GRUB","type":"tags"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/tags/mbr/","section":"Tags","summary":"","title":"MBR","type":"tags"},{"content":" 笔记 MBR（主引导记录） 结构就是主引导代码+分区表（+post-mbr gap），主引导代码 就是专门负责用来加载系统到内存中的，分区表 就是一个磁盘的整体布局。post-mbr gap 就是用来过渡引导的阶段 1 和阶段 2 的。如果就是，主引导记录用来引导 GRUB，==则就会出现 grub 的代码覆盖了原本的主引导的原先代码。==\n1. MBR 结构： # 主引导记录 (MBR) 由两个主要部分组成： 主引导代码 (446 字节)：此部分包含负责将操作系统或其他引导加载程序（如 GRUB）加载到内存中的引导加载程序代码。 分区表 (64 字节)：此部分包含磁盘分区的布局，包括其起始和结束位置。 在 MBR（主引导记录） 设置中，磁盘布局在 MBR（占用前 512 个字节）和磁盘上的第一个分区之间留下一小块未使用的空间。此空间通常称为 后 MBR 间隙（post-mbr gap），通常约为 31 KB。此间隙用于存储 BIOS 在启动过程中加载的额外引导加载程序代码。(==所以从这里可知: 单纯靠 mbr 无法完整引导系统，所以要靠 post-mbr gap 来将阶段 1 过渡到阶段 2) 如果 MBR 的主引导代码部分为空或已损坏，则系统将无法加载任何操作系统，因为没有代码来指示系统如何继续引导过程。分区表可能仍然完好无损，但如果没有引导代码，系统将不知道如何从这些分区启动引导。\n2. 启动具有双操作系统（GRUB 和 MBR）的系统： # 当您拥有双系统（例如 Linux 和 Windows）时，引导加载程序是必需的，以便提供选择要启动哪个操作系统的方法。 如果 MBR 包含标准引导加载程序（如默认的 Windows 引导加载程序），它将仅启动默认操作系统（在本例中为 Windows），而不提供选择其他操作系统的菜单。 另一方面，GRUB 是一种更高级的引导加载程序，可以检测多个操作系统。如果 MBR 中安装了 GRUB，它将加载并提供启动菜单，允许您选择要启动哪个操作系统。 如果 MBR 不包含 GRUB（或任何启动管理器）： # 没有启动管理器菜单：如果 MBR 中没有安装 GRUB（或其他启动管理器，如 rEFInd 或 LILO），您将没有菜单来选择要启动哪个操作系统。系统将直接启动到与已安装的引导加载程序关联的默认操作系统。 Windows 引导加载程序：例如，如果 Windows 引导加载程序 位于 MBR 中，它将直接启动到 Windows，而不提供启动 Linux 的选项。 3. GRUB 和引导管理器角色： # 当 GRUB 安装在 MBR 中时，它会用自己的代码替换默认引导代码，该代码可以： 检测多个操作系统安装（例如 Linux 和 Windows）。 在启动期间显示启动菜单，允许您选择要启动的操作系统。 如果您在设置双系统后安装 GRUB，GRUB 通常会扫描其他已安装的操作系统（如 Windows）并将其添加到其启动菜单中。如果 MBR 不包含 GRUB 或任何其他引导管理器，您将只能启动与 MBR 中存在的引导加载程序关联的操作系统（例如，如果 Windows 引导加载程序存在，则为 Windows）。\n结论： # 如果您的 MBR 为空，您将根本无法启动系统。 如果您有 双系统，但 GRUB（或其他启动管理器） 未安装在 MBR 中，您将无法获得启动菜单来选择要启动的操作系统。系统将默认使用现有的任何引导加载程序（例如，Windows 引导加载程序），这可能会自动加载一个操作系统而不显示另一个系统的任何选项。 要启用双启动功能，在 MBR 中安装像 GRUB 这样的启动管理器是必不可少的。\n补充说明 post-mbr gap # 流程 # 第 1 阶段引导加载程序执行： 第 1 阶段引导加载程序（MBR 中的小引导代码）非常有限。它的工作是定位和加载引导加载程序的下一阶段，通常称为第 1.5 阶段或直接称为第 2 阶段。 由于 MBR 只有 446 字节的空间，因此它无法容纳完整的引导加载程序（如 GRUB 或其他引导管理器），也无法容纳直接引导操作系统所需的复杂代码。 MBR 后间隙（也称为“嵌入区域”）： 在 MBR 之后，MBR 与磁盘上的第一个分区之间通常存在间隙。此间隙通常称为 MBR 后间隙 或 嵌入区域。 在 传统 BIOS-MBR 引导 中，此间隙为引导加载程序的 第 1.5 阶段 代码提供了额外的空间。第 1.5 阶段代码是必需的，因为： 它包含理解磁盘上文件系统的代码。 它提供加载完整 第 2 阶段引导加载程序 所需的额外功能。 此间隙通常介于 30 KB 到 60 KB 之间，具体取决于分区方案和引导加载程序。 阶段 1.5 引导加载程序： 阶段 1.5 是 MBR 中最小 阶段 1 引导加载程序 与完整 阶段 2 引导加载程序 之间的桥梁。 它位于 MBR 后间隙中，包含理解存储 阶段 2 引导加载程序的文件系统（例如 ext 4、NTFS 等）所需的基本驱动程序或代码。 例如，在 GRUB 的情况下，阶段 1.5 允许 GRUB 访问包含完整 /boot/grub 目录的分区。 阶段 2 引导加载程序： 一旦加载 阶段 1.5，它就会找到完整的 阶段 2 引导加载程序，该程序通常存储在操作系统分区上的 /boot 目录中。 第 2 阶段 是完整的引导加载程序（例如 GRUB 或 LILO），它： 显示引导菜单（如果配置了多个操作系统）。 将所选操作系统的内核加载到内存中。 将控制权移交给内核以引导系统。 操作系统内核： 一旦 第 2 阶段引导加载程序 将内核加载到内存中，它就会开始执行内核，从而接管引导过程、初始化硬件、加载驱动程序并最终引导系统。 为什么后 MBR 间隙很重要？ # MBR 引导代码 只有 446 个字节，非常小，不足以加载完整的引导加载程序（如 GRUB）。 后 MBR 间隙 为引导加载程序的代码（第 1.5 阶段）提供了额外的空间，而这些空间无法容纳在 MBR 本身中。 使用 MBR 磁盘 时，此间隙对于 基于 BIOS 的启动 至关重要，因为它包含读取磁盘文件系统和加载完整引导加载程序所需的代码。 ","date":"October 10, 2024","externalUrl":null,"permalink":"/notes/system/mbr%E5%88%86%E5%8C%BA%E6%96%B9%E6%A1%88/","section":"笔记","summary":" 笔记 MBR（主引导记录） 结构就是主引导代码+分区表（+post-mbr gap），主引导代码 就是专门负责用来加载系统到内存中的，分区表 就是一个磁盘的整体布局。post-mbr gap 就是用来过渡引导的阶段 1 和阶段 2 的。如果就是，主引导记录用来引导 GRUB，==则就会出现 grub 的代码覆盖了原本的主引导的原先代码。==\n","title":"MBR分区方案","type":"notes"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/tags/post/","section":"Tags","summary":"","title":"POST","type":"tags"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/tags/sysfs/","section":"Tags","summary":"","title":"Sysfs","type":"tags"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/tags/uefi/","section":"Tags","summary":"","title":"UEFI","type":"tags"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/","section":"Tags","summary":"","title":"操作系统","type":"tags"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/tags/%E5%A4%9A%E7%B3%BB%E7%BB%9F%E5%90%AF%E5%8A%A8/","section":"Tags","summary":"","title":"多系统启动","type":"tags"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/tags/%E5%88%86%E5%8C%BA/","section":"Tags","summary":"","title":"分区","type":"tags"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/tags/%E5%88%86%E5%8C%BA%E5%AF%B9%E6%AF%94/","section":"Tags","summary":"","title":"分区对比","type":"tags"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/tags/%E5%9B%BA%E4%BB%B6/","section":"Tags","summary":"","title":"固件","type":"tags"},{"content":" 固件 # 定义：固件是嵌入硬件或由系统加载以直接控制硬件内部操作的低级软件。它通常存储在硬件设备本身的非易失性存储器（如 ROM、EEPROM 或闪存）中。\n角色：固件提供允许硬件正常运行的基本控制指令。它非常接近硬件并且通常在最基本的层面上运行。这意味着它控制硬件的基本行为和特性，例如网卡如何处理数据传输或显卡如何处理渲染。\n直接控制：在某些情况下，固件直接与硬件通信，而不依赖于操作系统 (OS)。例如，BIOS/UEFI 固件甚至在操作系统启动之前就控制硬件。某些设备可以仅使用其固件（例如嵌入式系统）运行，但如果没有来自操作系统的高级控制，它们可能会受到限制。\n示例：\nBIOS/UEFI，用于在计算机启动时初始化和配置系统硬件。\n显卡固件（如 VBIOS），用于设置 GPU 以进行基本渲染和与操作系统通信。\nSSD 固件，用于管理存储设备的内部磨损均衡、垃圾收集和错误更正过程。\n驱动程序 # 定义：驱动程序是一种软件，允许操作系统（如 Linux、Windows 或 macOS）与硬件设备通信。驱动程序充当操作系统和硬件之间的中介。\n角色：驱动程序使操作系统和更高级别的软件能够控制和使用硬件。固件控制硬件的内部操作，而驱动程序解释来自操作系统的命令并将其转换为硬件可以理解和执行的指令。驱动程序还抽象硬件复杂性，允许操作系统和应用程序与硬件交互，而无需了解其低级细节。\n操作系统控制：与固件不同，驱动程序由操作系统在运行时加载。如果没有驱动程序，操作系统就无法直接控制硬件，但一旦安装了驱动程序，操作系统就可以通过驱动程序向硬件发送请求（例如，读取/写入数据、显示图形、发送网络数据包）。驱动程序还使操作系统能够以标准化的方式访问硬件资源。\n示例：\n图形驱动程序（如 NVIDIA、AMD 或 Intel 驱动程序）允许操作系统和应用程序渲染复杂的图形并访问 GPU 功能。 网络驱动程序使操作系统能够与系统的网卡通信并处理网络上的数据传输。 存储驱动程序使操作系统能够与硬盘驱动器、SSD 和 USB 设备交互。 固件和驱动程序之间的主要区别 # 方面 固件 驱动程序 位置 嵌入硬件或存储在 /lib/firmware 安装在操作系统中（例如，在 Linux 上存储在 /lib/modules/ 中） 控制级别 直接控制硬件的内部工作 为操作系统提供与硬件通信的接口 加载时间 在操作系统启动前加载（例如，BIOS/UEFI）或由操作系统加载以初始化硬件 由操作系统在运行时加载以管理硬件功能 用途 管理基本的低级硬件功能 使操作系统能够访问和控制硬件 与操作系统的交互 通常独立于操作系统；在硬件级别工作 需要操作系统才能运行；作为操作系统和硬件之间的中介 示例 GPU 固件、SSD 固件、UEFI 固件 图形驱动程序、网络驱动程序、声音驱动程序 固件和驱动程序如何协同工作 # 固件控制硬件的内部操作：例如，网卡上的固件管理卡在网络上物理发送和接收数据包的方式。它在非常低的级别处理数据包缓冲和错误检查等事情。\n驱动程序在硬件（由固件控制）和操作系统之间进行接口：操作系统中的网卡驱动程序从操作系统接收更高级别的请求（如“发送此数据包”或“连接到此网络”）并将其传递给网卡，网卡使用其固件执行这些指令。\n固件 + 驱动程序示例：\n显卡上的固件设置其低级操作并确保它可以渲染基本图像，但操作系统中的图形驱动程序通过在应用程序和硬件之间提供接口，允许用户充分利用 GPU 的游戏、3 D 建模或视频渲染功能。\n摘要 # 固件直接控制硬件的内部、低级操作，使其正常运行。 驱动程序通过抽象硬件复杂性并提供标准化接口，允许操作系统与硬件进行通信和控制。 固件和驱动程序共同确保硬件设备正常工作，并可由操作系统和用户级应用程序充分利用。\n","date":"October 10, 2024","externalUrl":null,"permalink":"/notes/system/%E5%9B%BA%E4%BB%B6-%E9%A9%B1%E5%8A%A8%E7%A8%8B%E5%BA%8F/","section":"笔记","summary":"固件 # 定义：固件是嵌入硬件或由系统加载以直接控制硬件内部操作的低级软件。它通常存储在硬件设备本身的非易失性存储器（如 ROM、EEPROM 或闪存）中。\n","title":"固件 驱动程序","type":"notes"},{"content":"在 Linux 中，固件 是指与特定硬件组件紧密相关的低级软件，通过提供指令和控制机制使它们能够正常运行。此固件在内核之下运行，但对于硬件与操作系统交互至关重要。\n让我们分解一下问题的每个部分：\n1. Linux 中的固件 # 在 Linux 系统中，固件通常是指在以下硬件设备上运行的微代码或二进制代码：\nCPU（例如，Intel 或 AMD 微代码） GPU（显卡固件） 网卡（Wi-Fi 或以太网适配器固件） 存储控制器（例如，SSD 固件） 外围设备（例如，USB 设备、声卡） ==一些固件内置于硬件本身，而其他固件则由操作系统在启动期间加载。==Linux 使用存储在 /lib/firmware 目录中的文件来处理固件加载。\n2. /sys 目录中的固件 # Linux 中的 /sys 目录是 sysfs 虚拟文件系统的一部分，它提供了内核和用户空间之间的接口，公开了有关硬件设备和内核子系统的详细信息。尽管 sysfs 主要处理内核和设备信息，但它也可以公开固件相关数据。\n/sys/firmware：此目录包含与系统固件相关的信息和接口，例如： /sys/firmware/efi：如果您的系统以 UEFI 模式启动，则此目录包含有关 UEFI 固件的详细信息，例如配置表、运行时变量和内存映射。 /sys/firmware/acpi：与 ACPI（高级配置和电源接口）固件相关的信息，可帮助进行电源管理和硬件配置。 /sys/firmware/dmi：DMI（桌面管理接口）表提供系统信息，例如主板型号、系统制造商和 BIOS/UEFI 版本。 3. 固件、内核、硬件和系统之间的关系 # 这些组件中的每一个都在计算机的整体运行中发挥着关键作用：\n固件 # 它是什么：固件是嵌入硬件或由系统在运行时加载的低级软件。它提供了硬件应如何运行和与操作系统通信的基本指令。 角色：固件直接控制硬件，使其能够运行。如果没有固件，网卡、CPU 和 GPU 等硬件组件可能无法正常工作或根本无法工作。 位置：固件要么预装在硬件中（非易失性存储器），要么从操作系统加载（来自 Linux 中的“/lib/firmware”）。 内核 # 它是什么：Linux 内核是操作系统的核心。它管理系统资源，包括内存、进程和硬件。 角色：内核负责通过驱动程序与硬件通信，驱动程序充当内核和硬件设备之间的中介。在需要时，内核还可以加载固件来初始化和控制特定硬件。 与固件的关系：内核与固件交互以初始化设备。它在需要时将固件加载到设备中，并处理固件与更高级别系统进程之间的通信。 硬件 # 它是什么：计算机的物理组件，例如 CPU、GPU、网卡、硬盘和外围设备。 角色：硬件执行系统所需的物理任务（例如计算、存储、输入/输出）。但是，硬件需要固件和内核驱动程序才能正常运行并与系统的其余部分交互。 系统 # 它是什么：整个计算机，包括硬件、固件、内核和用户级软件（应用程序、实用程序等）。 角色：系统作为一个整体，提供用户可以与应用程序和服务交互的环境，而应用程序和服务又依赖内核来访问硬件资源。如果没有固件，系统可能无法正确初始化硬件组件。 交互示例： # 当 Linux 系统启动时：\n固件 (BIOS/UEFI) 初始化硬件，执行 POST（开机自检），然后将控制权传递给引导加载程序（如 GRUB）。 引导加载程序将 内核 加载到内存中。 加载内核后，它开始初始化设备（如网卡或 GPU）。如果硬件需要额外的固件，内核将从 /lib/firmware 加载它。 内核使用 驱动程序 来管理硬件和操作系统之间的通信。 硬件现在已正确初始化并由其固件和内核驱动程序控制，可以执行其任务（例如，网络传输、渲染图形等）。 关系摘要： # 固件是直接控制硬件组件的低级软件。==可以与硬件直接通信，不依赖于操作系统。== 内核是操作系统的核心，它使用驱动程序管理系统资源和硬件，并在必要时加载固件。 硬件依靠固件和内核驱动程序与操作系统通信。 系统包含所有这些组件，共同为用户提供功能环境。==系统要控制硬件，必须要通过驱动程序。== ","date":"October 10, 2024","externalUrl":null,"permalink":"/notes/system/%E5%9B%BA%E4%BB%B6-%E7%A1%AC%E4%BB%B6-%E7%B3%BB%E7%BB%9F/","section":"笔记","summary":"在 Linux 中，固件 是指与特定硬件组件紧密相关的低级软件，通过提供指令和控制机制使它们能够正常运行。此固件在内核之下运行，但对于硬件与操作系统交互至关重要。\n","title":"固件 硬件 系统","type":"notes"},{"content":" boot 方式和分区方案的最佳配合 # uefi+gpt bios+mbr BIOS+MBR # 笔记 MBR (主引导记录)的整体详细结构简单介绍就是：主引导代码+分区表+post-mbr gap\n单纯靠 主引导代码 无法完整引导系统，所以要有 post-mbr gap （MBR 与第一个磁盘分区之间的间隙）来辅助引导系统，可以说是一个过渡用的。\n第一阶段就是主引导代码，主要作用就是定位和加载下一阶段的引导 第 1.5 阶段就是（post-mbr gap 做的）主要作用就是理解存储阶段 2 引导加载程序的文件系统（例如 ext 4、NTFS 等）所需的基本驱动程序或代码, 就是可能也有一部分加载程序在文件系统(grub additional code)中，这样就可以读取文件 (一些加载程序文件从而辅助 boot) 第 2 阶段就是引导加载程序了（bootloader boot manager） 到此，基本就可以引导完系统了，然后操作系统就加载在内存中了，控制权就交给系统内核了\nUEFI+GPT # 这个基本就没有什么，就主要在分区时，有个 ESP 分区 就可以了，这个引导程序（bootloader boot manager）就存储在 ESP 分区\n一些 linux 中的命令：\n下载配置文件 grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=ARCH 生成配置文件并放置于/boot 分区（这个 ESP 分区） grub-mkconfig -o /boot/grub/grub.cfg 有多系统的记得下载 os-prober, 并 vim /etc/default/grub 添加 GRUB_DISABLE_OS_PROBER=false 这一行 :::tips **注意：**这里只要是将提前装好系统并使用上述命令生成了grub的，你将这个硬盘换到另一个机子上来运行OS的话。这是你要 重新用live-usb这些启动重新再新机子上重新使用上述命令来生成grub，否则无法启动系统 :::\nBIOS+GPT # 首先声明，这种情况，你要添加一个 1 M-2 M 的 BIOS 分区才行\n理由 # gpt 磁盘本身第一个扇区（磁盘的前 512 个字节）就是一个 protective mbr 分区，这是为了向只能 BIOS 引导的旧机子兼容而产生的。\n讲述 protective mbr 的文章。大致重要内容如下：\n大概内容说白了就是为了兼容和保护 gpt 磁盘\n==省流: 就是这个 BIOS boot 分区相当于 mbr 的 post-mbr gap 用来辅助 bootloader 的加载的==\n第 1 阶段引导加载程序加载 BIOS 引导分区： # 由于系统使用的是 GPT 分区方案，因此不存在 后 MBR 间隙（MBR 和第一个分区之间的空间），MBR 系统通常会使用该间隙来存储其他引导加载程序代码（如 GRUB Stage 1.5）。 相反，在磁盘上创建了一个专用的 BIOS 引导分区（通常大小为 1-2 MB）。此分区不包含文件系统，也不用于存储常规文件 - 它仅用于保存 GRUB Stage 1.5。 来自保护性 MBR 的 第 1 阶段代码定位 BIOS 引导分区并从中加载 第 1.5 阶段引导加载程序代码。 GRUB Stage 1.5 处理文件系统访问： # Stage 1.5 是至关重要的一步，因为它包含 GRUB 理解磁盘文件系统（例如 ext 4、Btrfs 等）所需的驱动程序和代码。 加载 BIOS 启动分区后，Stage 1.5 现在可以读取磁盘中的文件，例如 GRUB 配置和Stage 2 引导加载程序代码，这些代码存储在 /boot/grub/ 中。 Stage 2 引导加载程序从 /boot/grub 加载： # Stage 1.5 加载后，它会找到Stage 2 引导加载程序，这些代码存储在其中一个分区上的 /boot/grub/ 目录中。 Stage 2 是功能齐全的 GRUB 引导加载程序。它： 如果您安装了多个操作系统（双启动设置），则显示 GRUB 启动菜单。 允许您选择要启动的操作系统或内核。 将所选操作系统的 内核 加载到内存中。 ","date":"October 10, 2024","externalUrl":null,"permalink":"/notes/system/%E7%AE%80%E5%8D%95%E6%80%BB%E7%BB%93mbr%E5%92%8Cgpt%E5%88%86%E5%8C%BA/","section":"笔记","summary":"boot 方式和分区方案的最佳配合 # uefi+gpt bios+mbr BIOS+MBR # 笔记 MBR (主引导记录)的整体详细结构简单介绍就是：主引导代码+分区表+post-mbr gap\n","title":"简单总结MBR和GPT分区","type":"notes"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/tags/%E5%BC%80%E6%9C%BA%E6%B5%81%E7%A8%8B/","section":"Tags","summary":"","title":"开机流程","type":"tags"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/tags/%E5%90%AF%E5%8A%A8/","section":"Tags","summary":"","title":"启动","type":"tags"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/tags/%E9%A9%B1%E5%8A%A8%E7%A8%8B%E5%BA%8F/","section":"Tags","summary":"","title":"驱动程序","type":"tags"},{"content":"当然！启动计算机的过程，从开机到加载操作系统并进入桌面，涉及多个阶段和组件。下面详细介绍了整个启动过程，包括 BIOS/UEFI、引导加载程序、启动管理器的作用以及操作系统的加载。\n1. 开机自检 (POST) # 开机： 当您打开计算机电源时，电源会向主板和所有连接的组件供电。 POST： 固件（BIOS 或 UEFI）运行一系列称为开机自检 (POST) 的诊断测试。这会检查必要的硬件组件（CPU、RAM、存储设备等）以确保它们正常运行。如果发生任何错误，系统可能会显示错误消息或蜂鸣代码。 2. 固件初始化 # BIOS/UEFI 初始化： 完成 POST 后，固件初始化硬件并为启动过程设置环境。 BIOS（基本输入/输出系统）是旧系统中使用的传统固件，而 UEFI（统一可扩展固件接口）是现代替代品，可提供增强的功能，包括支持更大的磁盘和更快的启动时间。 UEFI 固件会初始化storage（SSD, HDD 等）, cpu, memory（RAM） 基本外围设备。一开始，主机控制权在 UEFI 固件中，然后如果有 bootloader 或者 boot manager，控制权可能就转交给他们。系统加载完成后，控制权就交给系统了。 3. 启动设备选择 # 启动顺序： 固件查看其设置中配置的启动顺序以确定从哪个设备启动（例如硬盘、SSD、USB、网络）。 UEFI 通常具有图形界面来配置启动选项，而 BIOS 可能具有基于文本的界面。 4. 加载引导加载程序（bootloader） # 引导设备访问： 固件访问所选的引导设备并查找引导加载程序。 如果 GPT 磁盘带有 UEFI，它将扫描 EFI 系统分区 (ESP) 以查找引导条目。 加载引导加载程序： UEFI 根据引导顺序找到适当的 .efi 文件（例如，GRUB 的 grubx64.efi）并将其加载到内存中。 5. 引导管理器（如果适用） # 引导管理器角色： 如果引导加载程序（如 GRUB）包含引导管理器功能，则在检测到多个操作系统时，它将向用户显示菜单。例如，GRUB 可以显示 Linux 和 Windows 的选项。 如果没有显示菜单，它将自动加载默认操作系统。 6. 加载操作系统 # 操作系统内核加载： 一旦用户选择操作系统或选择默认操作系统，引导加载程序就会执行必要的命令，将操作系统的内核加载到内存中。 引导加载程序将控制权交给内核，初始化内核的子系统并加载运行操作系统所需的基本驱动程序。 用户空间初始化： 内核初始化系统进程和服务，挂载根文件系统，并为用户空间应用程序准备环境。 这包括加载必要的驱动程序和系统服务，使计算机能够与键盘、鼠标和显示器等硬件进行通信。 启动过程摘要： # 固件 (BIOS/UEFI) 执行开机自检 (POST)。 固件初始化硬件组件并检查启动顺序。 找到并从EFI 系统分区或 MBR 加载引导加载程序（如 GRUB）。 如果适用，启动管理器会显示一个菜单供操作系统选择。 引导加载程序将操作系统内核加载到内存中。 操作系统初始化系统进程并准备用户环境。 从开机到进入桌面的整个过程通常只需几秒到一分钟，具体取决于硬件性能以及操作系统及其配置的复杂性。\n","date":"October 10, 2024","externalUrl":null,"permalink":"/notes/system/%E4%B8%80%E4%B8%AA%E6%95%B4%E4%BD%93%E7%9A%84%E5%BC%80%E6%9C%BA%E6%B5%81%E7%A8%8B/","section":"笔记","summary":"当然！启动计算机的过程，从开机到加载操作系统并进入桌面，涉及多个阶段和组件。下面详细介绍了整个启动过程，包括 BIOS/UEFI、引导加载程序、启动管理器的作用以及操作系统的加载。\n","title":"一个整体的开机流程","type":"notes"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/tags/%E5%BC%95%E5%AF%BC%E7%A8%8B%E5%BA%8F/","section":"Tags","summary":"","title":"引导程序","type":"tags"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/tags/%E5%BC%95%E5%AF%BC%E5%8A%A0%E8%BD%BD/","section":"Tags","summary":"","title":"引导加载","type":"tags"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/tags/%E5%BC%95%E5%AF%BC%E5%8A%A0%E8%BD%BD%E7%A8%8B%E5%BA%8F/","section":"Tags","summary":"","title":"引导加载程序","type":"tags"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/tags/%E7%A1%AC%E4%BB%B6%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"硬件管理","type":"tags"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/tags/%E7%A1%AC%E4%BB%B6%E6%8E%A7%E5%88%B6/","section":"Tags","summary":"","title":"硬件控制","type":"tags"},{"content":"","date":"October 10, 2024","externalUrl":null,"permalink":"/categories/%E7%A1%AC%E7%9B%98/","section":"Categories","summary":"","title":"硬盘","type":"categories"},{"content":"","date":"September 27, 2024","externalUrl":null,"permalink":"/tags/lvm/","section":"Tags","summary":"","title":"LVM","type":"tags"},{"content":"","date":"September 27, 2024","externalUrl":null,"permalink":"/tags/%E9%80%BB%E8%BE%91%E5%8D%B7/","section":"Tags","summary":"","title":"逻辑卷","type":"tags"},{"content":" 大致流程 # 流程如下：\n将硬盘分区，并将其初始化为 物理卷（PV）。 将物理卷加入到一个 卷组（VG） 中。 在卷组中创建 逻辑卷（LV）。 在逻辑卷上创建文件系统（如 Ext4、XFS 等）。 将逻辑卷 挂载 到系统目录中，以便访问和存储数据。 文件系统介绍 # 1. 文件系统 # 文件系统说白了就是操作系统管理硬盘中数据的一个方式。 详细点就是：问阿金系统可以管理 硬盘，SSD，u盘 等存储设备的数据布局，提供读写文件的接口。文件系统负责存储文件、文件的元数据（如文件名称、权限、时间戳等），以及组织文件所在的路径结构（如文件夹层级）。\n2. 文件系统格式 # 主流格式如下：\nNTFS（Windows默认文件系统） FAT32（一种兼容性较好的文件系统，用于U盘等设备） exFAT（常用于大容量的外部存储设备） XFS（高性能的文件系统，适合大数据存储） Btrfs（现代Linux文件系统，提供高级功能，如快照、子卷等） ZFS（跨平台、具有高数据完整性和快照功能） 笔记 不同的文件系统格式的区别就是：性能，容量，恢复 等的区别。\n硬盘操作和文件系统关系 # 回顾 # 上一节的硬盘相关-卷这个操作流程：\n将硬盘分区，并将其初始化为 物理卷（PV）。 将物理卷加入到一个 卷组（VG） 中。 在卷组中创建 逻辑卷（LV）。 其实还不能达到我们平时打开文件资源管理器中点击文件夹就可以实现跳转并显示下一层的文件内容。（说白了这个逻辑卷还只是一个没有关联的存储空间，无法读取和写入数据）我们还要在逻辑卷上操作然后才可以实现如上效果。 要进行的操作就是：\n在逻辑卷上创建文件系统（如 Ext 4、XFS 等）。 将逻辑卷 挂载 到系统目录中，以便访问和存储数据。 Tips 这个文件系统的格式是可以自己选择的，就是我们安装系统的时候，选择自定义安装或者高级安装方式，可以手动选择文件系统格式，手动划分硬盘，位每个分区或者逻辑卷选择文件系统格式。例如：\n你可以选择在 / 根目录分区上使用 Ext4。 在 /home 分区上使用 XFS。 在 /boot 分区上使用 Ext 3，等等。 创建好文件系统格式后，我们就要将其挂载到系统目录下了。原因如下：\n原因 # ==我们创建好文件系统格式后，是可以让硬盘知道怎么管理数据了，但是这个数据是怎么来的，就是从操作系统上来的。既然是从操作系统上来的，我们就要挂载到操作系统上才可以来让数据写入到硬盘中，从而文件系统可以按照格式来管理数据==\n让操作系统识别并访问数据： 挂载的作用是将存储设备（如硬盘分区或逻辑卷）关联到操作系统中的某个目录。\n例如，执行一下挂载命令：\nsudo mount /dev/myvg/mylv /mnt/mydata（/mnt一般是挂载目录） 文件系统的入口点： 文件系统是用于组织数据的，而挂载点（如 /mnt/mydata）是文件系统与操作系统交互的入口。 多卷管理：挂载可以实现在不同目录下挂载多个存储设备或逻辑卷。类比于：就是在电脑上可以同时挂载个 u 盘或者挂载个移动硬盘。 动态扩展和迁移：就是继续上面的例子，u 盘和移动硬盘可以随时卸载（umount），插在别的电脑上。 ","date":"September 27, 2024","externalUrl":null,"permalink":"/notes/system/%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E5%92%8C%E5%8D%B7%E7%9A%84%E5%85%B3%E7%B3%BB/","section":"笔记","summary":"大致流程 # 流程如下：\n将硬盘分区，并将其初始化为 物理卷（PV）。 将物理卷加入到一个 卷组（VG） 中。 在卷组中创建 逻辑卷（LV）。 在逻辑卷上创建文件系统（如 Ext4、XFS 等）。 将逻辑卷 挂载 到系统目录中，以便访问和存储数据。 文件系统介绍 # 1. 文件系统 # 文件系统说白了就是操作系统管理硬盘中数据的一个方式。 详细点就是：问阿金系统可以管理 硬盘，SSD，u盘 等存储设备的数据布局，提供读写文件的接口。文件系统负责存储文件、文件的元数据（如文件名称、权限、时间戳等），以及组织文件所在的路径结构（如文件夹层级）。\n","title":"文件系统和卷的关系","type":"notes"},{"content":"","date":"September 27, 2024","externalUrl":null,"permalink":"/tags/%E7%A3%81%E7%9B%98%E5%AD%98%E5%82%A8/","section":"Tags","summary":"","title":"磁盘存储","type":"tags"},{"content":" LVM（逻辑卷管理器）的概念 # LVM（Logical Volume Manager，逻辑卷管理器）是一种用于管理计算机存储设备上磁盘空间分配的方法。它提供了一种更灵活和动态的磁盘管理方式，与传统的分区方式相比，更加灵活和便于调整。\n1. 物理卷（Physical Volume，PV） # 物理卷是 LVM 的基本存储单元，通常对应于物理硬盘分区或整个硬盘。一个物理卷可以包含在一个或多个卷组中。\n物理卷的创建： 你可以使用 pvcreate 命令将一个物理分区或硬盘初始化为物理卷。例如：\nsudo pvcreate /dev/sda3 物理卷的信息： 通过 pvs 命令可以查看所有物理卷的信息。\nsudo pvs 2. 卷组（Volume Group，VG） # 卷组是 LVM 的第二个层次，包含了一个或多个物理卷。一个卷组可以看作是一个存储池，可以在其上创建逻辑卷。\n卷组的创建： 可以使用 vgcreate 命令将一个或多个物理卷组合成一个卷组。例如：\nsudo vgcreate ubuntu-vg /dev/sda3 卷组的信息： 通过 vgdisplay 命令可以查看卷组的详细信息。\nsudo vgdisplay 3. 逻辑卷（Logical Volume，LV） # 逻辑卷是在卷组之上创建的，可以看作是传统的分区。它提供了实际的存储空间，可以像普通分区一样使用（格式化、挂载等）。\n逻辑卷的创建： 可以使用 lvcreate 命令在卷组上创建逻辑卷。例如：\nsudo lvcreate -L 100G -n ubuntu-lv ubuntu-vg 逻辑卷的信息： 通过 lvdisplay 命令可以查看逻辑卷的详细信息。\nsudo lvdisplay 它们之间的关系 # 物理卷（PV）： 物理卷是 LVM 管理的最基本单位，它们是硬盘分区或整块硬盘。多个物理卷可以加入到一个卷组中。\n卷组（VG）： 卷组由一个或多个物理卷组成，它们将多个物理卷的空间整合在一起，形成一个大的存储池。卷组提供了逻辑卷创建和管理的基础。\n逻辑卷（LV）： 逻辑卷是在卷组之上创建的虚拟分区，它们可以动态调整大小，并且可以像普通的分区一样进行格式化和挂载。\n与硬盘的关系及使用 # 硬盘分区（或整块硬盘）： 首先将硬盘分区（如 /dev/sda1、/dev/sda2 等）初始化为物理卷。\n创建卷组： 将一个或多个物理卷添加到卷组中，形成一个统一的存储池。\n创建逻辑卷： 在卷组上创建一个或多个逻辑卷，逻辑卷将使用卷组中的空间。\n格式化和挂载： 创建好的逻辑卷可以进行格式化，如 ext 4 文件系统，并挂载到文件系统路径上。\n使用硬盘空间的方式 # 初始化物理卷：\n例如，使用 pvcreate /dev/sda3 将物理分区初始化为物理卷。 创建卷组：\n使用 vgcreate 命令将一个或多个物理卷组合成一个卷组，如 vgcreate ubuntu-vg /dev/sda3。 创建逻辑卷：\n使用 lvcreate 命令在卷组上创建逻辑卷，如 lvcreate -L 100G -n ubuntu-lv ubuntu-vg。 格式化逻辑卷：\n例如，使用 mkfs.ext4 /dev/ubuntu-vg/ubuntu-lv 格式化逻辑卷。 挂载逻辑卷：\n使用 mount 命令挂载逻辑卷，如 mount /dev/ubuntu-vg/ubuntu-lv /mnt。 通过这些步骤，你可以动态地管理和调整存储空间，而不必像传统分区那样需要在创建时确定固定的大小。这种灵活性对于服务器和大规模存储管理尤其重要。\n命令及其解释 # sudo vgdisplay： 显示卷组（Volume Group，VG）的详细信息。\nsudo vgdisplay 输出解释：\n--- Volume group --- VG Name ubuntu-vg # 卷组的名称 System ID Format lvm2 # 卷组使用的LVM版本 Metadata Areas 1 # 元数据区域数量 Metadata Sequence No 2 # 元数据序列号 VG Access read/write # 卷组的访问权限 VG Status resizable # 卷组的状态 MAX LV 0 # 最大逻辑卷数量（0表示没有限制） Cur LV 1 # 当前逻辑卷数量 Open LV 1 # 当前打开的逻辑卷数量 Max PV 0 # 最大物理卷数量（0表示没有限制） Cur PV 1 # 当前物理卷数量 Act PV 1 # 当前活动的物理卷数量 VG Size \u0026lt;462.71 GiB # 卷组的总大小 PE Size 4.00 MiB # 每个物理扩展区（PE）的大小 Total PE 118453 # 总的物理扩展区数量 Alloc PE / Size 25600 / 100.00 GiB # 已分配的物理扩展区数量及大小 Free PE / Size 92853 / \u0026lt;362.71 GiB # 未分配的物理扩展区数量及大小 VG UUID xDrQn5-82mb-GSRY-IqDV-VR4V-fNOk-JodsIE # 卷组的唯一标识符 sudo lvdisplay： 显示逻辑卷（Logical Volume，LV）的详细信息。\nsudo lvdisplay 输出解释：\n--- Logical volume --- LV Path /dev/ubuntu-vg/ubuntu-lv # 逻辑卷的路径 LV Name ubuntu-lv # 逻辑卷的名称 VG Name ubuntu-vg # 逻辑卷所属的卷组名称 LV UUID OeCqDd-O039-JMhk-TWaJ-og8T-BPgi-rL6hcd # 逻辑卷的唯一标识符 LV Write Access read/write # 逻辑卷的访问权限 LV Creation host, time ubuntu-server, 2024-07-13 10:44:07 +0000 # 逻辑卷创建的主机和时间 LV Status available # 逻辑卷的状态 # open 1 # 当前打开的逻辑卷数量 LV Size 100.00 GiB # 逻辑卷的大小 Current LE 25600 # 当前逻辑扩展区数量 Segments 1 # 逻辑卷的段数 Allocation inherit # 分配策略 Read ahead sectors auto # 预读扇区数 - currently set to 256 # 当前设置的预读扇区数 Block device 252:0 # 逻辑卷的块设备号 sudo pvs： 显示物理卷（Physical Volume，PV）的信息。\nsudo pvs 输出解释：\nPV VG Fmt Attr PSize PFree /dev/sda3 ubuntu-vg lvm2 a-- \u0026lt;462.71g \u0026lt;362.71g PV: 物理卷的路径。 VG: 所属的卷组名称。 Fmt: 物理卷的格式（LVM 版本）。 Attr: 属性（a-- 表示激活状态）。 PSize: 物理卷的总大小。 PFree: 物理卷的空闲大小。 sudo vgs： 显示卷组的摘要信息。\nsudo vgs 输出解释：\nVG #PV #LV #SN Attr VSize VFree ubuntu-vg 1 1 0 wz--n- \u0026lt;462.71g \u0026lt;362.71g VG: 卷组的名称。 #PV: 物理卷数量。 #LV: 逻辑卷数量。 #SN: 快照数量。 Attr: 属性（wz--n- 表示可写状态、可调整大小、未关闭的卷组）。 VSize: 卷组的总大小。 VFree: 卷组的空闲大小。 sudo lvs： 显示逻辑卷的摘要信息。\nsudo lvs 输出解释：\nLV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert ubuntu-lv ubuntu-vg -wi-ao---- 100.00g LV: 逻辑卷的名称。 VG: 所属的卷组名称。 Attr: 属性（-wi-ao---- 表示写入状态、初始化状态、可用状态）。 LSize: 逻辑卷的大小。 Pool: 池（如果是精简卷池）。 Origin: 源（如果是快照）。 Data%: 已使用数据的百分比（对于精简卷池）。 Meta%: 已使用元数据的百分比（对于精简卷池）。 Move Log: 移动日志。 Cpy%Sync: 同步的复制百分比。 Convert: 转换状态。 这些命令及其输出提供了详细的 LVM 布局和卷的状态信息，帮助你管理和监控你的存储配置。如果有其他问题或需要进一步的解释，请告诉我。\n命令 # 在 Linux 系统中，逻辑卷管理（LVM，Logical Volume Manager）是一种用于管理磁盘存储设备的工具。LVM 使得系统管理员可以更灵活地管理文件系统和磁盘分区。以下是一些常用的 LVM 命令以及它们的详细介绍：\n1. 物理卷 (Physical Volume, PV) # 物理卷是 LVM 的基本存储单元。物理卷可以是整个硬盘，硬盘分区，或者是 RAID 设备。\n创建物理卷:\npvcreate /dev/sda1 查看物理卷:\npvdisplay 查看物理卷信息:\npvscan 删除物理卷:\npvremove /dev/sda1 2. 卷组 (Volume Group, VG) # 卷组是由一个或多个物理卷组成的逻辑集合。逻辑卷是在卷组上创建的。\n创建卷组:\nvgcreate my_vg /dev/sda1 /dev/sdb1 扩展卷组:\nvgextend my_vg /dev/sdc1 查看卷组:\nvgdisplay 查看卷组信息:\nvgscan 减少卷组:\nvgreduce my_vg /dev/sdc1 删除卷组:\nvgremove my_vg 3. 逻辑卷 (Logical Volume, LV) # 逻辑卷是在卷组上创建的，它类似于传统的磁盘分区，但更灵活。\n创建逻辑卷:\nlvcreate -L 10G -n my_lv my_vg 扩展逻辑卷:\nlvextend -L +5G /dev/my_vg/my_lv 查看逻辑卷:\nlvdisplay 查看逻辑卷信息:\nlvscan 减少逻辑卷（在减小逻辑卷之前，请确保文件系统大小已经减小）:\nlvreduce -L -5G /dev/my_vg/my_lv 删除逻辑卷:\nlvremove /dev/my_vg/my_lv 4. 文件系统 # 在创建逻辑卷后，通常需要在其上创建文件系统。\n创建文件系统:\nmkfs.ext4 /dev/my_vg/my_lv 挂载文件系统:\nmount /dev/my_vg/my_lv /mnt/my_mount_point 查看挂载的文件系统:\ndf -h 5. 其他有用的 LVM 命令 # 显示所有 LVM 信息:\nlvs vgs pvs 显示详细的 LVM 信息:\nlvdisplay vgdisplay pvdisplay 通过这些命令，系统管理员可以更灵活地管理存储资源，动态调整存储容量，并且减少系统停机时间。\n补充 # 在 Ubuntu 中查看磁盘空间可以使用以下命令：\ndf -h：显示磁盘使用情况，以人类可读的格式（例如 GB、MB）。\ndf -h du -sh /path/to/directory：显示指定目录的大小，以人类可读的格式。\ndu -sh /home lsblk：列出所有块设备，包括它们的挂载点和大小。\nlsblk fdisk -l：列出所有分区和详细信息。\nsudo fdisk -l lsblk：列出所有块设备及其大小。\nlsblk fdisk -l：显示所有磁盘及其分区信息。\nsudo fdisk -l df -h：显示各个挂载点的使用情况，包括总空间、已用空间和可用空间。\ndf -h parted -l：显示所有磁盘及其分区信息。\nsudo parted -l 这些命令将帮助你查看硬盘的总大小和分区信息。\n","date":"September 27, 2024","externalUrl":null,"permalink":"/notes/system/%E7%A1%AC%E7%9B%98%E7%9B%B8%E5%85%B3-%E5%8D%B7/","section":"笔记","summary":"LVM（逻辑卷管理器）的概念 # LVM（Logical Volume Manager，逻辑卷管理器）是一种用于管理计算机存储设备上磁盘空间分配的方法。它提供了一种更灵活和动态的磁盘管理方式，与传统的分区方式相比，更加灵活和便于调整。\n","title":"硬盘相关-卷","type":"notes"},{"content":"","date":"September 21, 2024","externalUrl":null,"permalink":"/ja/tags/jar%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB/","section":"Tags","summary":"","title":"JARファイル","type":"tags"},{"content":"","date":"September 21, 2024","externalUrl":null,"permalink":"/tags/jar%E6%96%87%E4%BB%B6/","section":"Tags","summary":"","title":"JAR文件","type":"tags"},{"content":"","date":"September 21, 2024","externalUrl":null,"permalink":"/categories/java/","section":"Categories","summary":"","title":"Java","type":"categories"},{"content":"","date":"September 21, 2024","externalUrl":null,"permalink":"/notes/java/","section":"笔记","summary":"","title":"Java","type":"notes"},{"content":"下面是一个详细的教程，涵盖如何将多个 Java 文件组织成包、引入包以及如何打包成 JAR 文件的过程。\n1. 创建 Java 包 # 1.1 目录结构 # 假设我们要创建一个名为 com.example.myapp 的包，目录结构如下：\n笔记 一般包名就是目录结构名。因此，我们运行或者编译包时一般都是在目录根结构下运行命令，比如 com.example.myapp 这个包是根据 myapp/src/com/example/myapp 目录下命名的，我们就要在 src 根目录下运行命令\nmyapp/ │ ├── src/ │ └── com/ │ └── example/ │ └── myapp/ │ ├── HelloWorld.java │ └── Utils.java └── bin/ 1.2 编写 Java 文件 # HelloWorld. java:\npackage com.example.myapp; public class HelloWorld { public static void main(String[] args) { System.out.println(\u0026#34;Hello, World!\u0026#34;); Utils.printMessage(\u0026#34;This is a message from Utils.\u0026#34;); } } Utils. java:\npackage com.example.myapp; public class Utils { public static void printMessage(String message) { System.out.println(message); } } 2. 编译 Java 文件 # 打开终端，导航到 src 目录：\ncd path/to/myapp/src 使用 javac 编译 Java 文件：\njavac com/example/myapp/*.java -d ../../bin 这里 -d ../../bin 表示将编译生成的 .class 文件放到 bin 目录下。\n3. 运行 Java 程序 # 确保你在 bin 目录中：\ncd ../../bin 运行程序：\njava com.example.myapp.HelloWorld 4. 创建 JAR 文件 # 仍然在 bin 目录中，使用 jar 命令创建 JAR 文件：\njar cvf myapp.jar com/example/myapp/*.class 这里 c 表示创建，v 表示详细输出，f 表示指定 JAR 文件名。\n确认 JAR 文件已创建：\nls 你应该能看到 myapp.jar 文件。\n5. 运行 JAR 文件 # 运行 JAR 文件时需要指定主类。在 HelloWorld 类中，我们可以在 MANIFEST.MF 中指定主类，或者直接在命令行中运行： java -cp myapp.jar com.example.myapp.HelloWorld 这种方式可以运行 JAR 文件中任意个类，只要你使用 cp 命令指定就行\n6. 添加主类到 JAR 文件 # 为了使 JAR 文件可直接运行，可以添加主类到 MANIFEST.MF 文件中：\n创建一个名为 MANIFEST.MF 的文件，内容如下：\nManifest-Version: 1.0 Main-Class: com.example.myapp.HelloWorld 创建 JAR 文件时指定 MANIFEST.MF：\njar cvfm myapp.jar MANIFEST.MF com/example/myapp/*.class 7. 直接运行 JAR 文件 # 现在你可以直接运行 JAR 文件，而不需要再指定主类：\njava -jar myapp.jar 8. 用第三方包 # java 文件中要用到的包是别人打包好的，我们编译时要通过添加参数来实现使用别人的包，如下：\n编译：\njavac --module-path /path/to/javafx-sdk/lib --add-modules javafx.controls HappyFaceFX.java 运行：\njava --module-path /path/to/javafx-sdk/lib --add-modules javafx.controls HappyFaceFX 9. 用自己本地打好的包 # 首先，这是原始目录结构：\n```bash myapp ├── bin │ ├── com │ │ └── example │ │ ├── myapp │ │ ├── HelloWorld.class │ │ └── Utils.class │ │ │ │ │ └── myapp.jar └── src └── com └── example ├── app │ ├── HelloWorld.java │ └── Utils.java └── test └── MainApp.java 然后创建一个新的结构如下：\nmyapp ├── bin │ ├── com │ │ └── example │ │ ├── myapp │ │ │ ├── HelloWorld.class │ │ │ └── Utils.class │ │ └── test │ │ └── MainApp.class │ └── myapp.jar └── src └── com └── example ├── app │ ├── HelloWorld.java │ └── Utils.java └── test └── MainApp.java 这个 test 文件夹的 java 文件如下：\npackage com.example.test; import com.example.myapp.Utils; // 导入 Utils 类 public class MainApp { public static void main(String[] args) { Utils.printMessage(\u0026#34;Hello from MainApp!\u0026#34;); } } 导入了 com. example. myapp 这个包中的 Utils 类，并且将这个设置为com. example. test 包。\n编译这个文件方法 # 将 example 文件夹下的所有 java 文件一起编译，这样就不会出现依赖问题 编译时设置路径（-cp），在 src 文件夹下使用如下命令：javac -cp ../bin com/example/test/MainApp.java -d ../bin 结合（-cp）以及 jar 包也行，在 src 文件夹下使用如下命令：javac -cp ../bin/myapp.jar com/example/test/MainApp.java -d ../bin ==最后，就是因为我设置这个包时有个目录，使用 javac 编译文件输出到具体目录时也是会产生相应目录结构，就是会多出文件夹==\n运行这个文件方法 # 运行指令：\njava com.example.test.MainApp 看情况需不需要链接类文件，好像在同一个包的根目录 com/example 下就不需要使用 -cp。不在同一个根目录下就要使用 -cp。\n总之，如果不能运行就自己手动加上 -cp 就行了\n10. --module-path 和 -cp 区别 # --module-path 和 -cp （或 --class-path） 是 Java 中的两个不同选项，它们用于不同的场景，因为 Java 支持两种不同的代码组织方式：模块系统 和 类路径。\n区别： # 1. -cp（--class-path）： # -cp 是用于指定 类路径 的选项，适用于传统的 Java 项目结构，即所有的 .class 文件和 JAR 文件都是通过类路径来管理和加载的。\n使用场景：用于在传统的 Java 项目中查找 .class 文件和 JAR 包。 适合的项目：适合未使用 Java 9 引入的模块系统的项目，或者依旧通过类路径进行管理的库和依赖。 示例：\njavac -cp /path/to/library.jar YourProgram.java java -cp /path/to/library.jar YourProgram 2. --module-path（-p）： # --module-path 是用于 模块系统 的选项，这是 Java 9 引入的特性。在模块系统中，代码和依赖项（例如 JAR 文件）被打包为模块，每个模块都有一个明确的依赖关系和暴露的 API。\n使用场景：用于指定模块路径（即模块化的 JAR 文件），适合 Java 9 及以上的项目，特别是那些使用了 Java 的模块系统的项目。 适合的项目：适合将代码和依赖项组织为模块的项目。例如，JavaFX 是基于模块系统的库。 示例：\njavac --module-path /path/to/modules --add-modules javafx.controls YourProgram.java java --module-path /path/to/modules --add-modules javafx.controls YourProgram 为什么 JavaFX 需要 --module-path？ # JavaFX 自从 Java 9 起就成为了一个独立的模块（不再包含在 JDK 中）。因此，当你在编译和运行 JavaFX 应用时，你需要告诉 Java 编译器和运行时如何找到 JavaFX 模块，这就是为什么使用 --module-path 来指定 JavaFX 模块的路径。\n示例解释：\njavac --module-path /path/to/javafx-sdk/lib --add-modules javafx.controls HappyFaceFX.java --module-path：指定 JavaFX SDK 的路径，其中包含 JavaFX 的模块化 JAR 文件。 --add-modules javafx.controls：明确告诉编译器或运行时要加载 javafx.controls 模块，这是 JavaFX 提供的核心 UI 控件模块。 什么时候用 -cp 和 --module-path？ # 使用 -cp：当你使用的是非模块化的 Java 项目或依赖时，继续使用类路径（-cp）。 使用 --module-path：当你的项目或依赖是模块化的，特别是使用 Java 9 引入的模块系统时，比如 JavaFX。 小结 # -cp 适用于传统的类路径管理。 --module-path 适用于模块化的项目和库（如 Java 9 引入的模块系统）。 总结 # 你已经完成了以下步骤：\n创建 Java 包。 编写 Java 文件并编译。 运行 Java 程序。 创建 JAR 文件。 添加主类到 JAR 文件并直接运行。 这种方法可以帮助你组织代码、简化项目管理，并且便于分发。\n","date":"September 21, 2024","externalUrl":null,"permalink":"/notes/java/java%E4%B8%AD%E6%9C%89%E5%85%B3%E5%8C%85%E7%9A%84%E7%9B%B8%E5%85%B3%E9%85%8D%E7%BD%AE%E4%BB%A5%E5%8F%8A%E4%BB%8B%E7%BB%8D/","section":"笔记","summary":"下面是一个详细的教程，涵盖如何将多个 Java 文件组织成包、引入包以及如何打包成 JAR 文件的过程。\n1. 创建 Java 包 # 1.1 目录结构 # 假设我们要创建一个名为 com.example.myapp 的包，目录结构如下：\n","title":"Java中有关包的相关配置以及介绍","type":"notes"},{"content":"","date":"September 21, 2024","externalUrl":null,"permalink":"/tags/package/","section":"Tags","summary":"","title":"Package","type":"tags"},{"content":"","date":"September 21, 2024","externalUrl":null,"permalink":"/ja/tags/%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"パッケージ管理","type":"tags"},{"content":"","date":"September 21, 2024","externalUrl":null,"permalink":"/ja/tags/%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%A7%8B%E9%80%A0/","section":"Tags","summary":"","title":"プロジェクト構造","type":"tags"},{"content":"","date":"September 21, 2024","externalUrl":null,"permalink":"/tags/%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84/","section":"Tags","summary":"","title":"项目结构","type":"tags"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/categories/git/","section":"Categories","summary":"","title":"Git","type":"categories"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/notes/git/","section":"笔记","summary":"","title":"Git","type":"notes"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/ja/tags/svn%E6%AF%94%E8%BC%83/","section":"Tags","summary":"","title":"SVN比較","type":"tags"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/tags/svn%E5%AF%B9%E6%AF%94/","section":"Tags","summary":"","title":"SVN对比","type":"tags"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/ja/tags/%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E9%9B%86/","section":"Tags","summary":"","title":"コマンド集","type":"tags"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/ja/tags/%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"バージョン管理","type":"tags"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/ja/tags/%E3%83%AF%E3%83%BC%E3%82%AF%E3%83%95%E3%83%AD%E3%83%BC/","section":"Tags","summary":"","title":"ワークフロー","type":"tags"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/tags/%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/","section":"Tags","summary":"","title":"版本控制","type":"tags"},{"content":" 常用 Git 命令 # Git简介 # Git是一个开源的分布式版本控制系统，用于敏捷高效地处理任何或小或大的项目。 Git是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。 Git与常用的版本控制工具 CVS, Subversion 等不同，它采用了分布式版本库的方式，不必服务器端软件支持。 Git与SVN的区别 # Git不仅仅是个版本控制系统，它也是个内容管理系统(CMS),工作管理系统等。 如果你是一个具有使用SVN背景的人，你需要做一定的思想转换，来适应Git提供的一些概念和特征。 Git 与 SVN 区别点：\nGit是分布式的，SVN不是：这是Git和其它非分布式的版本控制系统，例如SVN，CVS等，最核心的区别。 Git把内容按元数据方式存储，而SVN是按文件：所有的资源控制系统都是把文件的元信息隐藏在一个类似.svn,.cvs等的文件夹里。 Git分支和SVN的分支不同：分支在SVN中一点不特别，就是版本库中的另外的一个目录。 Git没有一个全局的版本号，而SVN有：目前为止这是跟SVN相比GIT缺少的最大的一个特征。 Git的内容完整性要优于SVN：Git的内容存储使用的是SHA-1哈希算法。这能确保代码内容的完整性，确保在遇到磁盘故障和网络问题时降低对版本库的破坏。 经常使用 Git ，但是很多命令还是记不住。一般来说，日常使用只要记住下图7个命令就可以了。但要熟练掌握，恐怕要记住40~60个命令，所以整理了一份常用Git命令清单。\nWorkspace：工作区 Index / Stage：暂存区 Repository：仓库区（或本地仓库） Remote：远程仓库 配置用户名和邮箱 # $ git --version # 查看git的版本信息 $ git config --global user.name # 获取当前登录的用户 $ git config --global user.email # 获取当前登录用户的邮箱 登录git\n# 如果刚没有获取到用户配置，则只能拉取代码，不能修改 要是使用git，你要告诉git是谁在使用 $ git config --global user.name \u0026#39;userName\u0026#39; # 设置git账户，userName为你的git账号， $ git config --global user.email \u0026#39;email\u0026#39; # 获取Git配置信息，执行以下命令： $ git config –list 配置https和ssh推送时保存用户名和密码 # # https提交保存用户名和密码 $ git config --global credential.helper store # 生成公钥私钥，将公钥配置到GitHub，ssh提交就可以免输入用户名密码 # 三次回车即可生成 ssh key $ ssh-keygen -t rsa # 查看已生成的公钥 $ cat ~/.ssh/id_rsa.pub 推送到远程仓库正确流程 # 1. git init # 初始化仓库 2. git add .(文件name) # 添加文件到本地仓库 3. git commit -m \u0026#34;first commit\u0026#34; # 添加文件描述信息 4. git remote add origin 远程仓库地址 # 链接远程仓库，创建主分支 5. git pull origin master --allow-unrelated-histories # 把本地仓库的变化连接到远程仓库主分支 6. git push -u origin master # 把本地仓库的文件推送到远程仓库 一、新建本地仓库 # # 创建一个文件夹 $ mkdir GitRepositories # 创建文件夹GitRepositories $ cd GitRepositories # 切换到GitRepositories目录下 # 在当前目录新建一个Git代码库 $ git init # 新建一个目录，将其初始化为Git代码库 $ git init [project-name] # 下载一个项目和它的整个代码历史 $ git clone [url] 二、配置(全局和项目) # # Git的设置文件为.gitconfig，它可以在用户主目录下（全局配置），也可以在项目目录下（项目配置）。 # 显示当前的Git配置 $ git config --list # 编辑Git配置文件 $ git config -e [--global] # 设置提交代码时的用户信息 $ git config [--global] user.name \u0026#34;[name]\u0026#34; $ git config [--global] user.email \u0026#34;[email address]\u0026#34; 三、增加/删除文件 # # 添加指定文件到暂存区 $ git add [file1][file2] ... # 添加指定目录到暂存区，包括子目录 $ git add [dir] # 添加当前目录的所有文件到暂存区 $ git add . # 添加每个变化前，都会要求确认 # 对于同一个文件的多处变化，可以实现分次提交 $ git add -p # 删除工作区文件，并且将这次删除放入暂存区 $ git rm [file1] [file2] ... # 停止追踪指定文件，但该文件会保留在工作区 $ git rm --cached [file] # 改名文件，并且将这个改名放入暂存区 $ git mv [file-original] [file-renamed] 四、代码提交 # # 提交暂存区到仓库区 $ git commit -m [message] # 提交暂存区的指定文件到仓库区 $ git commit [file1] [file2] ... -m [message] # 提交工作区自上次commit之后的变化，直接到仓库区 $ git commit -a # 提交时显示所有diff信息 $ git commit -v # 使用一次新的commit，替代上一次提交 # 如果代码没有任何新变化，则用来改写上一次commit的提交信息 $ git commit --amend -m [message] # 重做上一次commit，并包括指定文件的新变化 $ git commit --amend [file1] [file2] ... 五、分支 # # 列出所有本地分支 $ git branch # 列出所有远程分支 $ git branch -r # 列出所有本地分支和远程分支 $ git branch -a # 新建一个分支，但依然停留在当前分支 $ git branch [branch-name] # 新建一个分支，并切换到该分支 $ git checkout -b [branch] # 新建一个分支，指向指定commit $ git branch [branch] [commit] # 新建一个分支，与指定的远程分支建立追踪关系 $ git branch --track [branch] [remote-branch] # 切换到指定分支，并更新工作区 $ git checkout [branch-name] # 切换到上一个分支 $ git checkout - # 建立追踪关系，在现有分支与指定的远程分支之间 $ git branch --set-upstream [branch] [remote-branch] # 合并指定分支到当前分支 $ git merge [branch] # 选择一个commit，合并进当前分支 $ git cherry-pick [commit] # 删除分支 $ git branch -d [branch-name] # 删除远程分支 $ git push origin --delete [branch-name] $ git branch -dr [remote/branch] 六、标签 # # 列出所有tag $ git tag # 新建一个tag在当前commit $ git tag [tag] # 新建一个tag在指定commit $ git tag [tag] [commit] # 删除本地tag $ git tag -d [tag] # 删除远程tag $ git push origin :refs/tags/[tagName] # 查看tag信息 $ git show [tag] # 提交指定tag $ git push [remote] [tag] # 提交所有tag $ git push [remote] --tags # 新建一个分支，指向某个tag $ git checkout -b [branch] [tag] 七、查看信息 # # 查看目录 $ ls -al\t或者$ ll # 查看仓库状态，显示有变更的文件 $ git status # 显示当前分支的版本历史 $ git log # 显示commit历史，以及每次commit发生变更的文件 $ git log --stat # 搜索提交历史，根据关键词 $ git log -S [keyword] # 显示某个commit之后的所有变动，每个commit占据一行 $ git log [tag] HEAD --pretty=format:%s # 显示某个commit之后的所有变动，其\u0026#34;提交说明\u0026#34;必须符合搜索条件 $ git log [tag] HEAD --grep feature # 显示某个文件的版本历史，包括文件改名 $ git log --follow [file] $ git whatchanged [file] # 显示指定文件相关的每一次diff $ git log -p [file] # 显示过去5次提交 $ git log -5 --pretty --oneline # 显示所有提交过的用户，按提交次数排序 $ git shortlog -sn # 显示指定文件是什么人在什么时间修改过 $ git blame [file] # 显示暂存区和工作区的差异 $ git diff # 显示暂存区和上一个commit的差异 $ git diff --cached [file] # 显示工作区与当前分支最新commit之间的差异 $ git diff HEAD # 显示两次提交之间的差异 $ git diff [first-branch]...[second-branch] # 显示今天你写了多少行代码 $ git diff --shortstat \u0026#34;@{0 day ago}\u0026#34; # 显示某次提交的元数据和内容变化 $ git show [commit] # 显示某次提交发生变化的文件 $ git show --name-only [commit] # 显示某次提交时，某个文件的内容 $ git show [commit]:[filename] # 显示当前分支的最近几次提交 $ git reflog 八、远程同步 # # 下载远程仓库的所有变动 $ git fetch [remote] # 显示所有远程仓库 $ git remote -v # 显示某个远程仓库的信息 $ git remote show [remote] # 增加一个新的远程仓库，并命名 $ git remote add [shortname] [url] # 取回远程仓库的变化，并与本地分支合并 $ git pull [remote] [branch] # 上传本地指定分支到远程仓库 $ git push [remote] [branch] # 强行推送当前分支到远程仓库，即使有冲突 $ git push [remote] --force # 推送所有分支到远程仓库 $ git push [remote] --all 九、撤销 # # 恢复暂存区的指定文件到工作区 $ git checkout [file] # 恢复某个commit的指定文件到暂存区和工作区 $ git checkout [commit] [file] # 恢复暂存区的所有文件到工作区 $ git checkout . # 重置暂存区的指定文件，与上一次commit保持一致，但工作区不变 $ git reset [file] # 重置暂存区与工作区，与上一次commit保持一致 $ git reset --hard # 重置当前分支的指针为指定commit，同时重置暂存区，但工作区不变 $ git reset [commit] # 重置当前分支的HEAD为指定commit，同时重置暂存区和工作区，与指定commit一致 $ git reset --hard [commit] # 重置当前HEAD为指定commit，但保持暂存区和工作区不变 $ git reset --keep [commit] # 新建一个commit，用来撤销指定commit # 后者的所有变化都将被前者抵消，并且应用到当前分支 $ git revert [commit] # 暂时将未提交的变化移除，稍后再移入 $ git stash $ git stash pop 十、其他 # # 从当前目录的所有文件中查找文本内容： $ git grep \u0026#34;Hello\u0026#34; # 在某一版本中搜索文本： $ git grep \u0026#34;Hello\u0026#34; v2.5 # 生成一个可供发布的压缩包 $ git archive 附：Git常用命令速查表 # 附：Git指令速查表 # 附：资料链接 # Git 常用命令总结 Git常用命令，很全很详细讲解的也不错 Git详细使用教程 Git使用详细教程 Git 安装和使用教程 Git 教程\n","date":"September 20, 2024","externalUrl":null,"permalink":"/notes/git/%E5%B8%B8%E7%94%A8git%E5%91%BD%E4%BB%A4/","section":"笔记","summary":"常用 Git 命令 # Git简介 # Git是一个开源的分布式版本控制系统，用于敏捷高效地处理任何或小或大的项目。 Git是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。 Git与常用的版本控制工具 CVS, Subversion 等不同，它采用了分布式版本库的方式，不必服务器端软件支持。 Git与SVN的区别 # Git不仅仅是个版本控制系统，它也是个内容管理系统(CMS),工作管理系统等。 如果你是一个具有使用SVN背景的人，你需要做一定的思想转换，来适应Git提供的一些概念和特征。 Git 与 SVN 区别点：\n","title":"常用Git命令","type":"notes"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/tags/%E5%B7%A5%E4%BD%9C%E6%B5%81/","section":"Tags","summary":"","title":"工作流","type":"tags"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/tags/%E5%91%BD%E4%BB%A4%E9%9B%86/","section":"Tags","summary":"","title":"命令集","type":"tags"},{"content":" git命令使用感想 # git命令初始用 # git init建一个.git仓库(相当于拍一张照，将本地文件都拍下来)，使得创建本地仓库，能够推送代码到远程 git add .这个是一点还是两点看路径，也可以添加想添加的代码文件,这样就将其推到一个暂存区了 git status就是查看推的状况了，可以查看有哪些文件推上去了，哪些失败了额 git commit -m “描述” 这个就是提交上去了 如果还没建分支就用git branch -M 分支名（一般是master或者是main） git remote add origin url（一般最好用ssh密钥即建一个，后面可以不问密码） git push origin 分支名 ssh密钥创建 # 先cd ~/.ssh用ll看看有无id_rsa,id_rsa_pub，无就创建，用该命令： *** ssh-keygen -t rsa -b 4096 -C \u0026ldquo;nni461904@gmail.com\u0026rdquo; *** 创建好后就cat命令直接获取id_rsa_pub里的密钥或者vim复制也可 然后就进github网页设置那设置ssh密钥就可了 git push报错 # 这个学问就大了，要看报错，这是冲突性问题 git push报错git push冲突\n因为折腾过很久，所以在最后提一下这个想法，这个是之前搞不定后，实现的一个操作： 先在github网页复制clone下来 这样操作之后就可以实现本地和远程的同步了，这样我本地版本就大于远程的了 之后就可以按add那样提交就是了 因为文件夹里包含.git文件导致github网页创建了个子系统模块 # 先删除这个.git文件 rm -rf .git 然后用git rm \u0026ndash;cached 文件名实现在暂存区删除这个文件夹，实现停止跟踪这个文件或文件夹，从而可以实现修复 使用git add命令重新添加到暂存区 最后用git commit命令来提交 最后git push 修复 git命令学习 # git命令学习\n","date":"September 20, 2024","externalUrl":null,"permalink":"/notes/git/git-use/","section":"笔记","summary":" git命令使用感想 # git命令初始用 # git init建一个.git仓库(相当于拍一张照，将本地文件都拍下来)，使得创建本地仓库，能够推送代码到远程 git add .这个是一点还是两点看路径，也可以添加想添加的代码文件,这样就将其推到一个暂存区了 git status就是查看推的状况了，可以查看有哪些文件推上去了，哪些失败了额 git commit -m “描述” 这个就是提交上去了 如果还没建分支就用git branch -M 分支名（一般是master或者是main） git remote add origin url（一般最好用ssh密钥即建一个，后面可以不问密码） git push origin 分支名 ssh密钥创建 # 先cd ~/.ssh用ll看看有无id_rsa,id_rsa_pub，无就创建，用该命令： *** ssh-keygen -t rsa -b 4096 -C “nni461904@gmail.com” *** 创建好后就cat命令直接获取id_rsa_pub里的密钥或者vim复制也可 然后就进github网页设置那设置ssh密钥就可了 git push报错 # 这个学问就大了，要看报错，这是冲突性问题 git push报错git push冲突\n","title":"git_use","type":"notes"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/ja/tags/ssh%E9%8D%B5/","section":"Tags","summary":"","title":"SSH鍵","type":"tags"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/tags/ssh%E5%AF%86%E9%92%A5/","section":"Tags","summary":"","title":"SSH密钥","type":"tags"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/ja/tags/%E3%82%B3%E3%83%BC%E3%83%89%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"コード管理","type":"tags"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/tags/%E4%BB%A3%E7%A0%81%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"代码管理","type":"tags"},{"content":"在多人协作时，使用 Git 进行版本管理可以有效地避免和解决冲突问题，但仍然可能会遇到版本冲突。冲突的主要原因是不同开发者对同一个文件的同一部分进行修改，而 Git 无法自动合并这些更改。下面详细解释 Git 版本冲突出现的情形、如何解决冲突以及如何在多人合作中进行版本管理。\n一、Git 版本冲突的出现情形 # 同一文件的同一部分被多个开发者修改： 当多个开发者修改同一个文件的同一部分时，如果尝试合并这些修改，Git 无法自动决定哪一部分修改应该保留。例如：\n开发者 A 修改了文件 file.txt 的第 10 行，提交并推送了。 开发者 B 在开发者 A 推送之前也修改了 file.txt 的第 10 行，并在之后尝试推送。 不同分支的合并： 不同分支上的代码通常会有不同的开发进展，当一个开发者在两个不同的分支上修改了同一文件的同一部分内容时，合并分支时会产生冲突。\n手动解决冲突不当： 当冲突发生时，开发者在手动解决冲突时没有正确地保留需要的代码，导致推送到远程仓库时再次产生冲突。\n二、如何解决 Git 版本冲突 # 当发生冲突时，Git 会标记出冲突的地方，文件内容会出现如下格式：\n\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt; HEAD # 当前分支的代码 ======= # 其他分支的代码 \u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt; 在这种情况下，需要手动决定保留哪一部分代码，或者将两个版本的代码合并。具体步骤如下：\n1. 检查冲突文件 # 当你尝试合并代码时，冲突会阻止提交并显示冲突文件。你可以通过以下命令查看冲突文件：\ngit status 冲突文件会显示在 \u0026ldquo;Unmerged paths\u0026rdquo; 下方。\n2. 手动解决冲突 # 打开冲突文件，根据冲突标记 \u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;\u0026lt;, =======, \u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt; 来手动解决冲突。例如，你可以决定保留其中一部分代码，或将不同的部分合并在一起。解决后删除冲突标记并保存文件。\n3. 标记解决冲突 # 解决冲突后，告诉 Git 冲突已经被解决，可以通过 git add 命令将文件标记为已解决：\ngit add \u0026lt;conflict-file\u0026gt; 4. 提交合并结果 # 最后，提交冲突解决后的更改：\ngit commit -m \u0026#34;Resolve merge conflict\u0026#34; 5. 推送到远程仓库 # 解决冲突后，可以将合并后的代码推送到远程仓库：\ngit push origin \u0026lt;branch-name\u0026gt; 三、多人合作中的 Git 版本管理实践 # 为了避免和减少冲突，团队在协作时应该遵循一些最佳实践。以下是详细的版本管理流程和策略：\n1. 使用分支策略 # 主分支（main 或 master）：主分支用于存放稳定的、已发布的代码。 开发分支（develop）：用于开发的主分支，集成了所有开发人员的代码。 功能分支（feature/xxx）：每个功能或修复创建独立的功能分支，避免在开发过程中对其他功能产生影响。 开发者可以在功能分支上工作，完成功能开发后再合并到 develop 分支，最后再由 develop 合并到 main。\n2. 定期拉取代码（git pull） # 开发者在工作之前和提交代码之前，应定期从远程仓库拉取最新的代码，以确保自己的代码基于最新的状态。\ngit pull origin \u0026lt;branch-name\u0026gt; 3. 提交频繁且小规模的更改 # 大规模的提交更容易产生冲突，提交频繁且小的更改可以减少冲突的发生。可以使用以下命令来提交更改：\ngit add . git commit -m \u0026#34;Describe the changes made\u0026#34; git push origin \u0026lt;branch-name\u0026gt; 4. 代码审查（Code Review）和合并请求（Pull Request） # 在多人协作中，开发者应该通过创建 Pull Request 来合并代码。通过 Pull Request，其他团队成员可以审查代码，确保代码质量并减少冲突。 Pull Request 通常会在 feature/xxx 分支与 develop 分支之间进行，审查通过后再合并。 5. 解决冲突的技巧 # 频繁拉取代码：当你在开发功能时，定期将 develop 分支合并到你的功能分支，以确保你的分支总是与其他人的工作同步，尽早发现潜在的冲突。 保持清晰的提交记录：在提交代码时，保持清晰的提交信息，可以让其他开发者清楚地了解每次提交的目的。 6. 回滚操作 # 当合并发生错误时，可以通过以下命令回滚：\n撤销最后一次提交（还未推送）： git reset --soft HEAD^ 回滚到某个特定的提交： git reset --hard \u0026lt;commit-hash\u0026gt; 四、总结 # 在多人合作中，版本冲突不可避免，但通过合理的 Git 分支策略、定期拉取代码、频繁小规模提交、代码审查等方式，可以有效减少冲突并高效解决冲突。每个团队成员应尽可能早地处理冲突，并在冲突发生时耐心、细致地解决，以确保代码的稳定性和协作的顺畅。\n","date":"September 20, 2024","externalUrl":null,"permalink":"/notes/git/git%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86/","section":"笔记","summary":"在多人协作时，使用 Git 进行版本管理可以有效地避免和解决冲突问题，但仍然可能会遇到版本冲突。冲突的主要原因是不同开发者对同一个文件的同一部分进行修改，而 Git 无法自动合并这些更改。下面详细解释 Git 版本冲突出现的情形、如何解决冲突以及如何在多人合作中进行版本管理。\n","title":"git版本管理","type":"notes"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/ja/tags/%E3%82%B3%E3%83%B3%E3%83%95%E3%83%AA%E3%82%AF%E3%83%88%E8%A7%A3%E6%B1%BA/","section":"Tags","summary":"","title":"コンフリクト解決","type":"tags"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/ja/tags/%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"ブランチ管理","type":"tags"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/tags/%E5%86%B2%E7%AA%81%E8%A7%A3%E5%86%B3/","section":"Tags","summary":"","title":"冲突解决","type":"tags"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/tags/%E5%88%86%E6%94%AF%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"分支管理","type":"tags"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/tags/%E5%8D%8F%E4%BD%9C%E5%BC%80%E5%8F%91/","section":"Tags","summary":"","title":"协作开发","type":"tags"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/ja/tags/%E5%8D%94%E8%AA%BF%E9%96%8B%E7%99%BA/","section":"Tags","summary":"","title":"協調開発","type":"tags"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/ja/tags/api%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88/","section":"Tags","summary":"","title":"APIドキュメント","type":"tags"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/tags/api%E6%96%87%E6%A1%A3/","section":"Tags","summary":"","title":"API文档","type":"tags"},{"content":" 什么是 Javadoc？ # Javadoc 是 Java 编程语言中用于生成 API 文档的工具。它通过解析源代码中的特殊注释，生成带有结构化文档的 HTML 文件，从而为类、方法、字段等生成说明文档。Javadoc 是 Java 编程中极为重要的文档生成工具，有助于开发者理解和使用代码。\nJavadoc 的作用 # 生成 API 文档：Javadoc 可以通过 Java 源代码中的注释生成详细的 API 文档，包含类、方法、字段的使用说明。 增强代码可读性：Javadoc 提供结构化的注释格式，帮助开发者理解代码的功能和使用方法。 简化维护：良好的 Javadoc 文档不仅便于自己维护，还能够让其他开发者快速上手使用代码，尤其在开源项目或团队协作中尤为重要。 javadoc 生成注释文件情况 # Javadoc 工具主要会提取 Java 源代码中的 Javadoc 注释部分（使用 /** ... */ 格式的注释），并通过解析这些注释生成 API 文档。然而，Javadoc 生成的文档不仅仅依赖于注释部分，它还会从源代码中提取类、接口、方法、构造函数、字段等声明的结构和签名，以生成完整的 API 文档。\nJavadoc 生成的文档包含两部分：\n注释部分：通过 /** ... */ 注释编写的描述、标签内容（如 @param、@return、@throws 等）。这些注释部分会被提取并生成相应的文档，解释代码的用途、参数的意义等。\n代码结构部分：Javadoc 不仅提取注释，还会自动包含类、接口、方法、构造函数、字段等的声明。这些元素即使没有注释，也会显示在生成的 API 文档中。Javadoc 会展示这些元素的名称、类型、修饰符（如 public、private）等，使文档不仅描述了代码的功能，还详细地列出了其结构。\n示例：没有注释的 Java 类 # 即使没有添加 Javadoc 注释，Javadoc 工具也会生成基础的 API 文档，列出类、方法和字段的结构。\npublic class Example { private int value; public Example(int value) { this.value = value; } public int getValue() { return value; } } 生成的 Javadoc 文档将展示 Example 类的结构，包括类名、构造函数和 getValue 方法的签名。\n示例：有 Javadoc 注释的 Java 类 # 如果为类、方法等添加 Javadoc 注释，生成的文档会更详细、包含解释性文字：\n/** * Example 类用于展示 Javadoc 的使用。 */ public class Example { /** 存储数值的字段 */ private int value; /** * 构造函数，初始化数值。 * * @param value 初始化的数值 */ public Example(int value) { this.value = value; } /** * 获取存储的数值。 * * @return 返回存储的数值 */ public int getValue() { return value; } } 在这个例子中，Javadoc 工具不仅会生成类和方法的结构信息，还会提取注释并在生成的 HTML 文档中显示这些详细的解释、参数说明和返回值说明。\n结论 # 总结来说，Javadoc 会生成完整的 API 文档，不仅包含代码中的注释部分，还会提取类和方法的结构信息。即使某些部分没有注释，Javadoc 也会生成基础的结构文档。如果添加了注释，文档会更详细，包含开发者为代码所编写的说明和提示。\nJavadoc 注释格式 # Javadoc 使用特殊的注释格式。通常，Javadoc 注释是写在类、方法、字段等声明之前，使用 /** ... */ 来标记注释内容。与普通注释 /* ... */ 不同，Javadoc 通过解析特定的标签生成结构化文档。\n/** * 这是一个简单的类，演示如何使用 Javadoc。 * 它包含两个字段和一个计算加法的方法。 * * @author 作者姓名 * @version 1.0 */ public class Calculator { /** 第一个数字 */ private int num1; /** 第二个数字 */ private int num2; /** * 构造函数，初始化两个数字。 * * @param num1 第一个数字 * @param num2 第二个数字 */ public Calculator(int num1, int num2) { this.num1 = num1; this.num2 = num2; } /** * 计算两个数字的和。 * * @return 返回 num1 和 num2 的和 */ public int add() { return num1 + num2; } } Javadoc 常用标签 # Javadoc 中包含许多标签（tags）来帮助组织和说明代码的功能，以下是一些常用的标签：\n@author\n用于标明类的作者。\n@author John Doe @version\n用于指定类或方法的版本信息。\n@version 1.0 @param\n描述方法的参数，每个参数都应使用一个 @param 标签，并且需要说明其含义。\n@param parameterName 参数描述 @return\n说明方法返回值的类型和意义。仅对有返回值的方法使用。\n@return 返回类型和含义 @throws / @exception\n描述方法可能抛出的异常类型及其含义。\n@throws IOException 如果读取文件时发生错误 @see\n用于提供参考的类、方法或文档链接。\n@see java.lang.String @since\n指示从哪个版本开始添加该类、方法或字段。\n@since 1.5 @deprecated\n标记该类或方法已过时，并建议不要使用。通常会附带替代的类或方法。\n@deprecated Use newMethod() instead. 生成 Javadoc # 使用命令行生成 Javadoc # 在命令行中，使用 javadoc 命令生成文档。假设源代码文件为 Calculator.java，执行以下命令：\njavadoc Calculator.java 生成的 Javadoc 文档会以 HTML 格式保存，可以通过浏览器查看。\n常见的 javadoc 命令选项：\n-d：指定生成的文档存放目录。 -author：在文档中包含 @author 标签的内容。 -version：在文档中包含 @version 标签的内容。 例如：\njavadoc -d doc -author -version Calculator.java 这将把文档生成到 doc 文件夹中，并包含作者和版本信息。\nJavadoc 文档结构 # 生成的 Javadoc 文档通常包含以下部分：\n类概览：列出所有类、接口和枚举的名称，并提供简要描述。 类详细信息：每个类的详细信息，包括构造函数、字段、方法的说明。 方法详细信息：列出每个方法的详细信息，包括参数说明、返回值说明以及可能抛出的异常。 继承结构：显示类的继承层次结构，便于开发者理解类之间的关系。 Javadoc 的最佳实践 # 为每个类和方法编写 Javadoc：即使是私有方法，也建议编写 Javadoc，因为它有助于后续维护和团队合作。\n使用简洁的语言：Javadoc 应该使用简洁明了的语言，避免复杂术语。重点是帮助其他开发者理解代码的用途和使用方式。\n详细描述参数和返回值：尤其是复杂的方法，需要详细说明每个参数的含义和方法的返回值。对可能抛出的异常也应加以说明。\n保持与代码同步：Javadoc 文档应保持与代码一致，如果代码发生变更，及时更新 Javadoc 注释。\n总结 # Javadoc 是 Java 开发中非常有用的工具，它帮助开发者生成清晰、结构化的 API 文档，增强了代码的可读性和可维护性。在团队合作、开源项目中，Javadoc 能极大地提升代码质量和理解效率。通过掌握 Javadoc 的格式和标签，并遵循最佳实践，可以生成易于理解且完整的文档，从而提高开发效率。\n","date":"September 20, 2024","externalUrl":null,"permalink":"/notes/java/javadoc%E7%94%9F%E6%88%90%E6%B3%A8%E9%87%8A%E6%96%87%E4%BB%B6/","section":"笔记","summary":"什么是 Javadoc？ # Javadoc 是 Java 编程语言中用于生成 API 文档的工具。它通过解析源代码中的特殊注释，生成带有结构化文档的 HTML 文件，从而为类、方法、字段等生成说明文档。Javadoc 是 Java 编程中极为重要的文档生成工具，有助于开发者理解和使用代码。\n","title":"javadoc生成注释文件","type":"notes"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/ja/tags/%E3%82%B3%E3%83%BC%E3%83%89%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88/","section":"Tags","summary":"","title":"コードコメント","type":"tags"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/ja/tags/%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88%E7%94%9F%E6%88%90/","section":"Tags","summary":"","title":"ドキュメント生成","type":"tags"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/tags/%E4%BB%A3%E7%A0%81%E6%B3%A8%E9%87%8A/","section":"Tags","summary":"","title":"代码注释","type":"tags"},{"content":"","date":"September 20, 2024","externalUrl":null,"permalink":"/tags/%E6%96%87%E6%A1%A3%E7%94%9F%E6%88%90/","section":"Tags","summary":"","title":"文档生成","type":"tags"},{"content":" 安装最新版本 neovim # 要在 Ubuntu 上将 Neovim 更新到 0.8.0 及以上版本，可以通过以下步骤操作：\n1. 卸载当前版本的 Neovim # 首先，卸载你当前版本的 Neovim：\nsudo apt remove neovim 2. 添加 Neovim 官方 PPA # Neovim 提供了一个官方的 PPA，包含最新的 Neovim 版本。你可以通过添加该 PPA 来获取最新版本：\nsudo add-apt-repository ppa:neovim-ppa/stable 之后更新软件包列表：\nsudo apt update 3. 安装最新版本的 Neovim # 现在安装 Neovim：\nsudo apt install neovim 这会安装最新的稳定版本，通常是 0.8.0 或更新版本。\n4. 检查版本 # 安装完成后，你可以检查是否成功更新到所需版本：\nnvim --version 5. 配置 LazyVim # 更新 Neovim 后，确保你的 ~/.config/nvim 目录下有正确的 LazyVim 配置文件。一般你可以通过 GitHub 获取 LazyVim 配置。\n其他提示 # 如果你仍然遇到版本问题，可以尝试通过 Neovim 官方发布页面 下载预编译二进制文件，手动安装 Neovim。\nLazyVim 是一个很受欢迎的 Neovim 配置框架，它提供了一个开箱即用的 Neovim 配置，使得 Neovim 的使用更加高效。下面是一些常用命令和导航键的介绍：\n常用命令 # 启动 Neovim\nnvim 打开文件\n:e 文件名 保存文件\n:w 保存并退出\n:wq 退出不保存\n:q! 查找文件\n:find 文件名 替换文本\n:%s/旧文本/新文本/g 跳转到行号\n:行号 显示帮助文档\n:help 常用导航键 # 基本导航\nh：左移 j：下移 k：上移 l：右移 单词导航\nw：跳到下一个单词的开始 e：跳到当前或下一个单词的结尾 b：跳到当前或上一个单词的开始 行导航\n0：跳到行首 $：跳到行尾 G：跳到文件末尾 gg：跳到文件开头 页面导航\nCtrl + f：向下翻一页 Ctrl + b：向上翻一页 跳转到标签\nCtrl + o：跳回到上一个跳转位置 Ctrl + i：跳到下一个跳转位置 LazyVim 特性 # LazyVim 在启动时会自动加载一系列插件，并且配置了很多便捷的功能。以下是一些特定于 LazyVim 的操作：\n插件管理\n使用 :Lazy 命令可以管理插件，如安装、更新和删除插件。 :Lazy install：安装插件 :Lazy update：更新插件 :Lazy clean：删除不再使用的插件 配置文件\nLazyVim 的配置文件通常位于 ~/.config/nvim/lua/user/ 目录下，你可以在这里找到和修改 LazyVim 的配置文件。 快捷键\nLazyVim 可能会定义一些自定义的快捷键，你可以在 LazyVim 的文档或者配置文件中找到这些快捷键的定义。 快速导航方法 # 在 Neovim 中，有几种高效的方式可以快速导航到当前行中的某个单词附近，而不需要使用方向键。这些方法可以帮助你更快速地定位到你需要编辑的位置：\n1. 使用 / 查找 # 你可以使用 / 命令进行文本查找：\n按 /\u0026lt;字符\u0026gt; 进入查找模式。 输入你要查找的单词或文本，然后按 Enter。 使用 n 跳转到下一个匹配项，N 跳转到上一个匹配项。 例如，要查找当前行中的 foo，你可以按 /foo，然后按 Enter。\n2. 使用 f 和 t # f 和 t 命令可以帮助你快速跳转到当前行中的特定字符位置：\nf\u0026lt;char\u0026gt;：跳转到当前行中第一个 \u0026lt;char\u0026gt; 位置。 t\u0026lt;char\u0026gt;：跳转到当前行中第一个 \u0026lt;char\u0026gt; 之前的位置。 例如：\n按 f 然后是 a 将跳转到当前行第一个 a 位置。 按 t 然后是 a 将跳转到当前行第一个 a 之前的位置。 3. 使用 ; 和 , # ;：重复上一个 f、t、F 或 T 命令。 ,：重复上一个 f、t、F 或 T 命令的反向操作。 4. 使用 g 命令 # g/\u0026lt;字符\u0026gt;：在当前行中查找 \u0026lt;字符\u0026gt;。 5. 使用 * 和 # # *：查找光标下单词的下一个匹配项。 #：查找光标下单词的上一个匹配项。 在插入模式下，你可以使用一些技巧来快速移动到行中的特定位置，无需退出插入模式或使用方向键。以下是一些常用的方法：\n1. 使用 \u0026lt;C-o\u0026gt; # \u0026lt;C-o\u0026gt;（Control + o）：在插入模式下按下 \u0026lt;C-o\u0026gt; 进入一次普通模式命令，然后可以使用普通模式命令进行导航。例如： \u0026lt;C-o\u0026gt;f\u0026lt;char\u0026gt;：跳转到当前行中的第一个 \u0026lt;char\u0026gt; 位置。 \u0026lt;C-o\u0026gt;t\u0026lt;char\u0026gt;：跳转到当前行中的第一个 \u0026lt;char\u0026gt; 之前的位置。 \u0026lt;C-o\u0026gt;/pattern：在当前行中查找 \u0026lt;pattern\u0026gt;。 2. 使用 \u0026lt;C-w\u0026gt; # \u0026lt;C-w\u0026gt;（Control + w）：在插入模式下按下 \u0026lt;C-w\u0026gt; 可以帮助你快速移动到单词边界： \u0026lt;C-w\u0026gt; + h/j/k/l：在插入模式下，按 \u0026lt;C-w\u0026gt; 后接方向键可以移动到当前单词的边界。 3. 使用插入模式快捷键 # \u0026lt;C-a\u0026gt; 和 \u0026lt;C-e\u0026gt;：这些快捷键帮助你在插入模式下进行更精确的光标移动： \u0026lt;C-a\u0026gt;：将光标移动到当前单词的开头。 \u0026lt;C-e\u0026gt;：将光标移动到当前单词的结尾。 4. 使用 \u0026lt;C-f\u0026gt; 和 \u0026lt;C-b\u0026gt; # \u0026lt;C-f\u0026gt; 和 \u0026lt;C-b\u0026gt;：在插入模式下，你可以使用这些快捷键在当前行中进行前向和后向滚动： \u0026lt;C-f\u0026gt;：向前滚动。 \u0026lt;C-b\u0026gt;：向后滚动。 5. 使用 \u0026lt;C-u\u0026gt; 和 \u0026lt;C-d\u0026gt; # \u0026lt;C-u\u0026gt; 和 \u0026lt;C-d\u0026gt;：这些快捷键可以用于向上和向下滚动文本，在插入模式下也有效： \u0026lt;C-u\u0026gt;：向上滚动半屏。 \u0026lt;C-d\u0026gt;：向下滚动半屏。 ","date":"September 15, 2024","externalUrl":null,"permalink":"/notes/vim/nvim-command/","section":"笔记","summary":"安装最新版本 neovim # 要在 Ubuntu 上将 Neovim 更新到 0.8.0 及以上版本，可以通过以下步骤操作：\n1. 卸载当前版本的 Neovim # 首先，卸载你当前版本的 Neovim：\n","title":"nvim_command","type":"notes"},{"content":"","date":"September 15, 2024","externalUrl":null,"permalink":"/tags/ppa/","section":"Tags","summary":"","title":"PPA","type":"tags"},{"content":"","date":"September 15, 2024","externalUrl":null,"permalink":"/tags/ubuntu/","section":"Tags","summary":"","title":"Ubuntu","type":"tags"},{"content":"","date":"September 15, 2024","externalUrl":null,"permalink":"/tags/%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B/","section":"Tags","summary":"","title":"安装教程","type":"tags"},{"content":"在 Vim 中，常用的命令和导航键位对初学者和高级用户都非常有帮助。以下是一些常用命令、移动方向的键位以及如何快速移动到某个行内的位置。\n常用命令模式命令 # 进入命令模式：按 Esc 键进入命令模式。 退出 Vim： :q 退出 :q! 强制退出（不保存） :wq 保存并退出 :x 保存并退出（等同于 :wq） 保存文件： :w 保存文件 :w new_filename 另存为新文件 移动方向的键位 # 基本方向移动：\nh：向左移动一个字符 j：向下移动一行 k：向上移动一行 l：向右移动一个字符 快速移动：\n0：移动到行首 $：移动到行尾 gg：移动到文件开头 G：移动到文件末尾 :n：移动到第 n 行（例如 :5 会移动到第 5 行） 精确跳转到行内的某个位置 # 精确跳转到某个字符：\nf\u0026lt;char\u0026gt;：向右跳转到下一个指定的字符，例如 fa 会跳到当前行的下一个字母 a。 F\u0026lt;char\u0026gt;：向左跳转到上一个指定的字符。 t\u0026lt;char\u0026gt;：向右跳转到指定字符的前一个位置。 T\u0026lt;char\u0026gt;：向左跳转到指定字符的后一个位置。 快速跳转到行内的某个位置：\nnG：跳到第 n 行。 n|：跳到当前行的第 n 列。例如 5| 会跳到当前行的第 5 列。 插入模式 # 进入插入模式：\ni：在当前光标位置前插入 I：在当前行的行首插入 a：在当前光标位置后插入 A：在当前行的行尾插入 o：在当前行下方插入新行 O：在当前行上方插入新行 退出插入模式：\n按 Esc 退出插入模式，返回命令模式。 其他常用命令 # dd：删除整行 x：删除光标所在的字符 u：撤销上一步操作 Ctrl+r：重做上一步操作 yy：复制当前行 p：粘贴复制/剪切的内容 Vim 的操作是高度可定制的，掌握这些基本的命令和导航后，你可以更高效地编辑代码。\n","date":"September 14, 2024","externalUrl":null,"permalink":"/notes/vim/vim-command/","section":"笔记","summary":"在 Vim 中，常用的命令和导航键位对初学者和高级用户都非常有帮助。以下是一些常用命令、移动方向的键位以及如何快速移动到某个行内的位置。\n常用命令模式命令 # 进入命令模式：按 Esc 键进入命令模式。 退出 Vim： :q 退出 :q! 强制退出（不保存） :wq 保存并退出 :x 保存并退出（等同于 :wq） 保存文件： :w 保存文件 :w new_filename 另存为新文件 移动方向的键位 # 基本方向移动：\n","title":"vim_command","type":"notes"},{"content":"","date":"September 14, 2024","externalUrl":null,"permalink":"/tags/%E5%AF%BC%E8%88%AA%E9%94%AE%E4%BD%8D/","section":"Tags","summary":"","title":"导航键位","type":"tags"},{"content":"","date":"September 14, 2024","externalUrl":null,"permalink":"/tags/%E5%91%BD%E4%BB%A4%E6%A8%A1%E5%BC%8F/","section":"Tags","summary":"","title":"命令模式","type":"tags"},{"content":"","date":"September 14, 2024","externalUrl":null,"permalink":"/tags/%E5%BF%AB%E6%8D%B7%E6%96%B9%E5%BC%8F/","section":"Tags","summary":"","title":"快捷方式","type":"tags"},{"content":"","date":"September 14, 2024","externalUrl":null,"permalink":"/tags/%E8%BD%AF%E8%BF%9E%E6%8E%A5/","section":"Tags","summary":"","title":"软连接","type":"tags"},{"content":" 两者区别 # 创建桌面图标和创建软连接的方式有不同的用途和应用场景，以下是它们的主要区别：\n1. 软连接（符号链接） # 作用：软连接（ln -s 命令）本质上是文件系统中的快捷方式，它为文件或目录创建一个指向目标文件的链接。通过软连接，用户可以使用不同的路径来访问同一个文件或目录。 应用场景： 用于命令行中简化对某个文件或可执行程序的路径访问。例如，你可以通过软连接将 /opt/myapp/myapp_executable 映射到 /usr/local/bin/myapp，使得你可以通过命令 myapp 来运行程序，而不需要输入完整路径。 主要用于命令行和文件系统级别的操作，无法直接创建带图形界面或图标的可视化快捷方式。 特点： 无图形界面，纯粹是文件系统中的一个链接。 用于命令行或脚本的便捷操作。 只适用于终端或脚本中调用程序，而不涉及桌面图标、应用菜单等图形界面元素。 2. 桌面图标（.desktop 文件） # 作用：.desktop 文件是一种定义应用程序启动方式的文件，通常用于 Linux 桌面环境。它不仅可以指向一个可执行文件，还可以包含应用的图标、名称、描述等信息。通过它，用户可以在桌面或应用菜单中图形化地启动应用程序。 应用场景： 用于在桌面环境下为应用程序创建图标或快捷方式，例如将应用程序显示在 Ubuntu 的应用菜单、启动器、桌面等地方。 允许通过图形界面启动程序，而不是通过命令行。 .desktop 文件可以定义更多的行为，如是否在终端运行、传递特定参数、设置图标等。 特点： 主要用于图形化的桌面环境，允许用户通过鼠标点击图标来启动应用程序。 可以设置图标、名称、描述、类别等信息，用户体验更友好。 可以配置应用程序是否运行在终端、是否使用特定的图标等。 主要区别总结： # 特性 软连接 桌面图标（.desktop 文件） 作用 创建文件或目录的快捷方式，用于简化命令行操作 为应用程序创建桌面快捷方式，允许在图形界面中启动应用 应用场景 文件系统、命令行中的快捷方式 桌面环境、应用菜单中的图标，图形化启动应用程序 使用方式 通过终端命令执行 通过点击桌面图标或应用菜单启动 图标支持 无图标支持，只是文件路径的指向 支持图标显示，可以自定义图标、描述、应用类别等 是否需要图形界面 不需要，完全依赖于命令行或文件系统的路径操作 需要图形界面环境，用于桌面或应用菜单中的应用程序快捷方式 可执行文件的配置选项 只能指向文件或目录，没有其他配置选项 可以配置是否使用终端、参数传递、应用程序分类、图标等 何时使用软连接？ # 当你主要通过命令行使用应用程序时，软连接是一个方便的方式。它可以让你在终端中使用简短命令，而无需输入完整路径。 何时使用桌面图标（.desktop 文件）？ # 当你希望在桌面、应用菜单或启动器中图形化启动应用程序时，应该使用 .desktop 文件。它适用于日常用户需要通过点击图标的方式启动应用程序的场景。 两者可以结合使用，比如为命令行用户创建软链接，同时为图形化用户提供 .desktop 文件，这样既方便终端操作，也方便图形界面使用。\n示例 # 当然，以下是分别使用软连接和 .desktop 文件的示例，以帮助你更清楚地理解两者的区别和用途。\n1. 软连接的示例 # 场景：假设你安装了一个应用程序 myapp，它的可执行文件位于 /opt/myapp/myapp_executable。你希望在终端中直接使用 myapp 命令来启动这个程序，而不需要输入完整路径。\n步骤： # 查找可执行文件的路径（假设它在 /opt/myapp/myapp_executable）。 创建软连接，将其链接到 /usr/local/bin/，方便在终端中使用： sudo ln -s /opt/myapp/myapp_executable /usr/local/bin/myapp 结果： # 你现在可以直接在终端中通过输入 myapp 来启动这个应用程序，而不需要输入完整路径 /opt/myapp/myapp_executable。 软连接就是文件系统中的一个快捷方式，可以通过命令行快速访问应用程序。 验证： # 可以运行以下命令查看软链接是否创建成功：\nls -l /usr/local/bin/myapp 你会看到输出类似于：\nlrwxrwxrwx 1 root root 24 Sep 14 10:00 /usr/local/bin/myapp -\u0026gt; /opt/myapp/myapp_executable 这意味着 /usr/local/bin/myapp 现在指向 /opt/myapp/myapp_executable。\n2. 创建桌面图标的示例 # 场景：你希望为 myapp 创建一个图标，显示在桌面或应用菜单中，方便通过点击启动程序。同时，你希望这个图标有一个合适的名称和图标文件。\n步骤： # 首先，找到应用程序的可执行文件（例如 /opt/myapp/myapp_executable）和图标文件（例如 /opt/myapp/myapp_icon.png）。 创建 .desktop 文件。你可以使用文本编辑器创建一个新的 .desktop 文件： gedit ~/.local/share/applications/myapp.desktop 在 .desktop 文件中添加以下内容： [Desktop Entry] Version=1.0 Name=MyApp Comment=This is my custom application Exec=/opt/myapp/myapp_executable # 应用程序的实际路径 Icon=/opt/myapp/myapp_icon.png # 图标文件路径 Terminal=false # 不需要在终端中运行 Type=Application Categories=Utility; # 类别，可根据需要选择 保存并关闭文件。\n赋予 .desktop 文件执行权限：\nchmod +x ~/.local/share/applications/myapp.desktop 结果： # 你现在可以在 Ubuntu 的应用菜单中搜索 MyApp，并通过图标启动它。 如果你希望将图标显示在桌面上，还可以将 .desktop 文件复制到桌面： cp ~/.local/share/applications/myapp.desktop ~/Desktop/ 验证： # 在应用菜单中搜索 MyApp。 双击桌面上的图标，应用程序将启动。 对比总结： # 特性 软连接示例 桌面图标示例 目的 在命令行中方便运行应用程序 在桌面或应用菜单中方便启动应用程序 操作系统级别 文件系统（终端和路径操作） 桌面环境（图形用户界面） 创建命令 sudo ln -s /opt/myapp/myapp_executable /usr/local/bin/myapp 编辑并创建 .desktop 文件，赋予执行权限 启动方式 在终端中输入 myapp 点击桌面或应用菜单中的图标 是否支持图标 不支持图标，仅支持命令行使用 支持图标，可以在桌面、应用菜单中显示自定义图标 适合的用户 终端用户、开发者、需要频繁通过命令行启动程序的用户 图形界面用户、希望通过点击图标方便启动应用的普通用户 这两个方法可以结合使用，例如：\n为终端用户创建一个软链接，方便通过命令行快速启动程序。 为桌面用户创建一个 .desktop 文件，提供友好的图形化启动方式。 说明 # 在 Ubuntu 及其他类 Unix 系统中，通常将软链接（符号链接）放到 /usr/local/bin 目录有几个重要原因。这个做法涉及到文件系统的组织、环境变量和可执行文件的查找机制。\n1. /usr/local/bin 是用户本地安装软件的标准目录 # /usr/local/bin 是 Linux 系统中一个专门用来存放用户自己安装的软件或脚本的目录，而不是操作系统包管理器（如 apt）管理的软件。其主要目的包括：\n分离系统软件和用户自定义软件：系统自带的核心程序通常位于 /bin、/usr/bin 等目录，而 /usr/local/bin 是为本地用户安装的软件或脚本准备的，不会被系统包管理器覆盖或删除。 避免系统升级影响：如果用户将自己的程序放在 /usr/bin 或 /bin，在系统升级时可能会被系统文件覆盖。而 /usr/local/bin 被保留用于本地用户操作，系统升级不会动这个目录里的文件。 2. /usr/local/bin 在系统的 PATH 环境变量中 # PATH 是 Linux 系统中的一个环境变量，存放着系统在终端中查找可执行文件的目录。当你在终端中输入命令时，系统会从 PATH 列出的目录中按顺序查找该命令对应的可执行文件。如果你将软链接放到 PATH 中的某个目录（如 /usr/local/bin），用户可以直接在终端中运行该程序，而无需输入完整路径。\n可以通过以下命令查看 PATH 变量的内容：\necho $PATH 通常 PATH 中包含 /usr/local/bin，所以将应用程序或其软链接放到这个目录中后，系统能够自动找到它。\n例如，$PATH 通常可能包含类似以下路径：\n/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin 如果将软链接放在 /usr/local/bin，就无需输入绝对路径，可以直接通过命令名运行程序。\n3. 方便命令行调用 # 将软链接放在 /usr/local/bin 中后，用户可以直接在终端中输入程序名称，而不必输入程序的完整路径。例如，如果你的程序可执行文件位于 /opt/myapp/myapp_executable，而你创建了一个软链接到 /usr/local/bin/myapp，则只需输入 myapp 即可运行该程序，而不需要每次都输入 /opt/myapp/myapp_executable。\n4. 遵循文件系统层次标准（FHS） # Linux 系统通常遵循 文件系统层次标准（FHS, Filesystem Hierarchy Standard）。根据 FHS 规范：\n/bin 和 /usr/bin：用于存放操作系统自带的程序和包管理器安装的软件。系统的核心工具和程序都放在这些目录中。 /usr/local/bin：这是专门为用户安装的程序、脚本或工具准备的目录，通常用于非操作系统提供的、用户自定义安装的软件。它可以看作是 \u0026ldquo;本地\u0026rdquo;（local）空间，供管理员或用户自行管理。 因此，遵循这种标准，可以帮助管理员和开发者更好地组织系统中的应用程序，确保系统升级和维护的安全性与一致性。\n5. 权限管理 # 通常，系统目录（如 /bin、/usr/bin）是只读的，只有超级用户（root）可以对其进行写操作。相反，/usr/local/bin 通常为系统管理员和某些用户提供写权限，便于用户安装自定义程序和脚本，而不会影响系统的核心文件。\n总结： # 将软链接放在 /usr/local/bin 是一种惯例和最佳实践，因为该目录是专门为用户安装的本地程序保留的，它在 PATH 变量中，确保了你可以在终端中方便地访问和运行程序。 这样做可以保持系统的整洁和安全，避免本地安装的软件与操作系统自带的核心软件发生冲突。 通过这种方式，用户可以在不破坏系统核心文件的情况下，自由安装和使用自己的软件。\n","date":"September 14, 2024","externalUrl":null,"permalink":"/notes/linux/%E8%BD%AF%E8%BF%9E%E6%8E%A5%E5%92%8C-desktop%E5%9B%BE%E6%A0%87/","section":"笔记","summary":"两者区别 # 创建桌面图标和创建软连接的方式有不同的用途和应用场景，以下是它们的主要区别：\n","title":"软连接和/.desktop图标","type":"notes"},{"content":"在Java中，编译和运行程序涉及几个主要命令，包括 javac（编译器）、java（运行器）以及其他辅助命令。以下是最常用的Java编译和运行命令：\n1. 编写Java源代码 # 首先你需要一个Java源文件，例如，创建一个名为HelloWorld.java的文件，内容如下：\npublic class HelloWorld { public static void main(String[] args) { System.out.println(\u0026#34;Hello, World!\u0026#34;); } } 2. 编译Java程序 # 使用javac命令编译Java源文件。该命令会将.java文件编译为Java字节码，并生成相应的.class文件。\njavac HelloWorld.java 执行后会生成HelloWorld.class文件，这就是可以由Java虚拟机（JVM）执行的字节码文件。\n编译多个文件： # 如果你有多个Java文件，也可以一起编译，例如：\njavac File1.java File2.java 或者编译当前目录下的所有Java文件：\njavac *.java 3. 运行Java程序 # 使用java命令运行编译好的Java类文件：\njava HelloWorld 注意：运行Java程序时不需要加.class扩展名，只需提供类名。\n4. 编译并指定输出目录 # 你可以通过-d选项指定编译后的.class文件的输出目录：\njavac -d bin HelloWorld.java 这会将编译后的.class文件输出到bin目录下。\n5. 设置类路径（CLASSPATH） # 如果你的Java程序依赖其他库或者class文件，你需要通过-cp（或-classpath）选项指定类路径。例如：\njava -cp .:lib/somelibrary.jar HelloWorld 在这个例子中，当前目录（.）和lib/somelibrary.jar都被加入到类路径中（注意Linux使用冒号:分隔，Windows使用分号;分隔）。\n6. 打包成JAR文件 # 你可以将Java程序打包成一个JAR文件（Java Archive），然后可以通过JVM直接运行这个JAR包。步骤如下：\n创建JAR文件： # jar cvf HelloWorld.jar HelloWorld.class 创建可执行的JAR文件： # 如果你的Java程序有main方法，可以创建一个可执行的JAR文件：\n首先，创建一个manifest.txt文件，指定入口点： Main-Class: HelloWorld 然后执行以下命令打包JAR文件： jar cfm HelloWorld.jar manifest.txt HelloWorld.class 运行JAR文件： java -jar HelloWorld.jar 打包成 JAR 文件另法 # 利用 jar 命令来对生成的字节码文件进行打包。\njar -cvf hello.jar HelloWorld.class 其中 c 表示创建一个新 jar 包，v 表示创建过程中打印创建过程中的信息，f 则表示对新生成的 jar 命名。\n最后，利用以下命令来运行 jar 包。\njava -jar hello.jar 不过并不会顺利出现我们想要的结果，此时会报错 hello.jar 中没有主清单属性。这是因为我们还没有在 MENIFEST.MF 文件中添加 Main-Class 属性。 用压缩软件打开刚创建的 hello.jar，里边除了 HelloWorld.class 文件之外，还会多一个 META-INF 文件夹，里边还有一个 MENIFEST.MF 文件，此时我们只需要用编辑器打开该文件，然后在文件中加入以下代码。（记得添加之后要保证整个文件最后保留一行空行）\nMain-Class: HelloWorld 添加完成之后，再次运行 java -jar hello.jar ，就可以成功在控制台打印 Hello World! 了。\n7. 常见的JDK命令总结 # javac：Java编译器，将Java源文件（.java）编译为字节码文件（.class）。\njavac HelloWorld.java java：Java解释器，运行编译后的字节码（.class）文件。\njava HelloWorld jar：用于创建和管理JAR文件（Java归档文件），可以打包多个.class文件和其他资源。\njar cvf HelloWorld.jar HelloWorld.class javadoc：用于生成Java源代码的API文档。\njavadoc HelloWorld.java javap：反编译.class文件，查看字节码或方法签名。\njavap HelloWorld 8. 调试（jdb） # 如果需要调试Java程序，Java开发工具包还提供了jdb命令（Java Debugger），用于设置断点、检查变量等：\njdb HelloWorld 总结 # Java程序的编译和运行过程主要使用到两个核心命令：javac（编译）和java（运行）。通过这些命令，可以编写、编译、运行Java代码。此外，jar命令允许将Java程序打包成JAR文件，javap可以反编译字节码，jdb可以调试Java程序。这些命令组合起来使得Java开发非常灵活且强大。\n","date":"September 14, 2024","externalUrl":null,"permalink":"/notes/java/java-command/","section":"笔记","summary":"在Java中，编译和运行程序涉及几个主要命令，包括 javac（编译器）、java（运行器）以及其他辅助命令。以下是最常用的Java编译和运行命令：\n","title":"java_command","type":"notes"},{"content":"","date":"September 14, 2024","externalUrl":null,"permalink":"/tags/jdk/","section":"Tags","summary":"","title":"JDK","type":"tags"},{"content":"","date":"September 14, 2024","externalUrl":null,"permalink":"/ja/tags/%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB/","section":"Tags","summary":"","title":"コンパイル","type":"tags"},{"content":"","date":"September 14, 2024","externalUrl":null,"permalink":"/tags/%E7%BC%96%E8%AF%91/","section":"Tags","summary":"","title":"编译","type":"tags"},{"content":" 笔记 在 javac 编译和使用 java 运行时自动添加库，使用\u0026ndash;module-path 添加库路径，使用\u0026ndash;module-add 添加库\n在较新的 Ubuntu 版本（如 22.04）中，openjdk-8-jdk 包仅包含 OpenJDK 8 的核心功能，不再包括 JavaFX 库。JavaFX 自 Java 11 起被移除出了 JDK，所以在 OpenJDK 8 也没有自动包含 JavaFX。\n原因： # JavaFX 的分离：\n自 Java 11 起，JavaFX 被从标准 JDK 中移除，成为一个独立的模块。OpenJDK 8 并不包含 JavaFX，因为它的设计理念和未来版本的 JavaFX 模块化策略有所不同。 JavaFX 与 OpenJDK 8：\n在 OpenJDK 8 时，JavaFX 是作为 JDK 的一部分提供的，但在更高版本中（如 Java 11 和之后），它被移除到一个独立的库中。因此，OpenJDK 8 的包中不再包含 JavaFX。 解决方法： # 要在使用 OpenJDK 8 的系统上添加 JavaFX，你需要手动安装 JavaFX SDK。请按照以下步骤操作：\n下载 JavaFX SDK：\n访问 OpenJFX官网 下载与你的操作系统匹配的 JavaFX SDK。 解压并配置 JavaFX：\n解压下载的 SDK 文件。你会得到一个包含 JavaFX JAR 文件的 lib 文件夹。 编译和运行 JavaFX 应用程序：\n编译 JavaFX 程序时，使用 --module-path 参数来指定 JavaFX JAR 文件的位置。 运行 JavaFX 程序时，使用 --module-path 和 --add-modules 参数来确保 JavaFX 模块可用。 示例： # 假设你下载并解压的 JavaFX SDK 位于 /path/to/javafx-sdk，然后你可以使用以下命令编译和运行 JavaFX 程序：\n编译：\njavac --module-path /path/to/javafx-sdk/lib --add-modules javafx.controls HappyFaceFX.java 运行：\njava --module-path /path/to/javafx-sdk/lib --add-modules javafx.controls HappyFaceFX 这样可以确保 JavaFX 库正确地与 OpenJDK 8 一起使用。\n","date":"September 14, 2024","externalUrl":null,"permalink":"/notes/java/java-lib%E5%B8%B8%E8%A7%81error/","section":"笔记","summary":" 笔记 在 javac 编译和使用 java 运行时自动添加库，使用–module-path 添加库路径，使用–module-add 添加库\n","title":"java_lib常见error","type":"notes"},{"content":"","date":"September 14, 2024","externalUrl":null,"permalink":"/tags/openjdk/","section":"Tags","summary":"","title":"OpenJDK","type":"tags"},{"content":"","date":"September 14, 2024","externalUrl":null,"permalink":"/ja/tags/%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"ライブラリ管理","type":"tags"},{"content":"","date":"September 14, 2024","externalUrl":null,"permalink":"/tags/%E5%BA%93%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"库管理","type":"tags"},{"content":"","date":"September 14, 2024","externalUrl":null,"permalink":"/tags/classpath/","section":"Tags","summary":"","title":"CLASSPATH","type":"tags"},{"content":"在Windows上配置Java环境以便能够编译和运行Java源程序涉及三个主要步骤：安装JDK、配置环境变量（包括JAVA_HOME、PATH和CLASSPATH），以及设置CLASSPATH以便程序能够找到所需的库文件。以下是详细步骤：\n1. 安装JDK # 访问 Oracle官网 或 OpenJDK 下载适合你操作系统的Java Development Kit (JDK) 版本。 安装下载的JDK，按照安装向导进行操作，默认安装路径通常是C:\\Program Files\\Java\\jdk-version。 2. 配置环境变量 # 为了使系统能够识别和使用Java编译器和运行时环境，需要将JAVA_HOME和PATH环境变量配置到系统中。\n设置JAVA_HOME环境变量 # 右键点击“此电脑”或“我的电脑”，选择**“属性”**。 点击**“高级系统设置”，然后点击“环境变量”**。 在**“系统变量”部分，点击“新建”**，创建一个新的环境变量： 变量名：JAVA_HOME 变量值：JDK的安装路径，例如C:\\Program Files\\Java\\jdk-17（根据你安装的版本路径来设置）。 点击确定保存。 配置 PATH 环境变量 # 在同一个“环境变量”窗口的系统变量部分，找到名为Path的变量，选中它后点击编辑。\n点击**“新建”**，然后将以下路径添加到Path变量中：\n%JAVA_HOME%\\bin\n点击确定保存。\n3. 设置CLASSPATH环境变量（可选） # CLASSPATH用于告诉Java编译器和运行时环境在哪里查找用户定义的类和包。如果你使用的是第三方库或你自己的Java类存放在自定义目录下，可以通过CLASSPATH来配置路径。对于普通Java开发，通常可以忽略这个步骤，因为JDK会自动查找标准库。\n如果需要配置CLASSPATH： # 在**“系统变量”部分，点击“新建”**，创建一个新的环境变量：\n变量名：CLASSPATH 变量值：包含库文件路径，添加.表示当前目录： .;%JAVA_HOME%\\lib;%JAVA_HOME%\\jre\\lib\n点击确定保存。\nCLASSPATH的每个路径之间用分号（;）隔开，.表示当前目录，JVM会从这里开始查找类文件。\n4. 验证配置 # 打开命令提示符，输入以下命令以验证JAVA_HOME是否配置正确：\necho %JAVA_HOME%\n它应该输出你安装的JDK的路径。\n验证java和javac命令是否能正常使用：\njava -version javac -version\n你应该能看到Java版本信息。如果没有看到正确的版本，检查环境变量的配置。\n4. Linux 下修改 java 版本 # 修改 java 版本 # 从你的输出中可以看到，OpenJDK 8（openjdk-8-jdk）已经安装在你的系统中，版本为 8u422-b05-1~22.04。因此，你现在应该已经拥有OpenJDK 8。\n如果你运行java -version后仍然看到OpenJDK 11，可能是因为系统仍在使用OpenJDK 11作为默认Java版本。\n你可以通过以下命令切换默认的Java版本：\nsudo update-alternatives --config java 然后在提示的列表中选择OpenJDK 8的路径（通常带有java-8-openjdk）。\n执行这个命令后，再次运行 java -version，应该会显示你刚设置的Java版本。\n修改 javac 版本 # 要将 javac 的版本切换为 OpenJDK 8，可以使用 update-alternatives 工具来设置默认的 javac 版本，和你之前设置 java 版本的方法相同：\n查看可用的 javac 版本： sudo update-alternatives --config javac 在出现的列表中选择与 OpenJDK 8 相关的路径，通常路径中会包含 java-8-openjdk。\n选择完成后，再次检查 javac 的版本：\njavac -version 这样你就可以将 javac 的版本与 java 设置为一致的 OpenJDK 8。\n","date":"September 14, 2024","externalUrl":null,"permalink":"/notes/java/java-set/","section":"笔记","summary":"在Windows上配置Java环境以便能够编译和运行Java源程序涉及三个主要步骤：安装JDK、配置环境变量（包括JAVA_HOME、PATH和CLASSPATH），以及设置CLASSPATH以便程序能够找到所需的库文件。以下是详细步骤：\n","title":"java_set","type":"notes"},{"content":"","date":"September 14, 2024","externalUrl":null,"permalink":"/tags/windows%E9%85%8D%E7%BD%AE/","section":"Tags","summary":"","title":"Windows配置","type":"tags"},{"content":"","date":"September 14, 2024","externalUrl":null,"permalink":"/ja/tags/windows%E8%A8%AD%E5%AE%9A/","section":"Tags","summary":"","title":"Windows設定","type":"tags"},{"content":"","date":"September 14, 2024","externalUrl":null,"permalink":"/tags/%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F/","section":"Tags","summary":"","title":"环境变量","type":"tags"},{"content":"","date":"September 14, 2024","externalUrl":null,"permalink":"/ja/tags/%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0/","section":"Tags","summary":"","title":"環境変数","type":"tags"},{"content":"","date":"September 7, 2024","externalUrl":null,"permalink":"/tags/github-pages/","section":"Tags","summary":"","title":"GitHub Pages","type":"tags"},{"content":" 笔记 三条常用命令：\nhexo clean hexo generate hexo deploy Tips 上面的这些命令以及配置文件（. yml）这些全部都是基于 source 目录下去搜索执行的。因此，如果是配置路径是就要注意这个是要基于 source 目录的，配置路径直接/img/test. jpg 这种是以 source 目录作为根目录的。因此要你就写完整相对目录，不然就在 source 文夹下创建文件夹。\nGithub + Hexo 实现个人博客及主题配置（超详细） # 前言 # 现在市面上的博客很多，如 CSDN，博客园，简书等平台，可以直接在上面发表，用户交互做得好，写的文章百度也能搜索到。缺点是比较不自由，会受到平台的各种限制和恶心的广告。\n而自己购买域名和服务器，搭建博客的成本实在是太高了，不光是说这些购买成本，单单是花力气去自己搭这么一个网站，还要定期的维护它，对于我们大多数人来说，实在是没有这样的精力和时间。\n那么就有第三种选择，直接在 GitHub Pages 平台上托管我们的博客。这样就可以安心的来写作，又不需要定期维护，而且 Hexo 作为一个快速简洁的博客框架，用它来搭建博客真的非常容易。\nHexo 简介 # Hexo 是一个快速、简洁且高效的博客框架。Hexo 使用 Markdown（或其他渲染引擎）解析文章，在几秒内，即可利用靓丽的主题生成静态网页。\n大家可以进入 Hexo 官网进行详细查看，因为 Hexo 的创建者是台湾人，对中文的支持很友好，可以选择中文进行查看。\nHexo 是基于 Node.js 的静态博客框架，依赖少易于安装使用，可以方便地生成静态网页托管在 GitHub 和 Coding 上，是搭建博客的首选框架。\nHexo 搭建教程 # 本次教程主要分为 8 个部分，看完便可搭建自己喜欢的个人博客。\n安装 Git 安装 Node.js 安装 Hexo GitHub 创建个人仓库 将 Hexo 部署到 GitHub 设置个人域名 更改主题（以 Fluid 为例） 发布文章 1. 安装 Git # Windows: 到 Git 官网 下载并安装即可，下载后会有一个 Git Bash 的命令行工具，以后就用这个工具来使用 Git。 Mac: 可通过 MacPorts 或安装程序安装。 Linux (Ubuntu, Debian): sudo apt-get install git-core Linux (Fedora, Red Hat, CentOS): sudo yum install git-core 2. 安装 Node.js # Hexo 是基于 Node.js 编写的，所以需要安装 Node.js 和 npm 工具。\nWindows: 官方安装，选择 LTS 版本即可。 Linux: sudo apt-get install nodejs sudo apt-get install npm 安装完成后，打开命令行，输入以下两行检查安装是否成功： node -v npm -v 3. 安装 Hexo # 安装好 Git 之后，选择你想要安装 Hexo 的地方（例如新建文件夹）。进入文件夹后，我们可以右击选择 \u0026ldquo;Git Bash Here\u0026rdquo; 来打开命令行窗口，输入：\n$ npm install -g hexo-cli 输入 hexo -v 查看一下版本，至此环境准备完成，开始使用 Hexo 搭建个人博客。\n4. 初始化 Hexo # 在你选择的文件夹中，输入以下指令：\n$ hexo init 文件夹名 $ cd 文件夹名 $ npm install 这会生成许多文件夹，不用担心，了解配置即可。\nnode_modules: 依赖包 public: 存放生成的页面 scaffolds: 生成文章的一些模板 source: 存放你的文章 themes: 主题 _config.yml: 博客的配置文件 接着，输入以下指令进行本地启动查看：\n$ hexo clean $ hexo g $ hexo s 在浏览器中输入 localhost:4000 就可以看到生成的博客页面了。使用 ctrl + c 可以关闭服务。\n5. 创建 GitHub 仓库 # 首先，你需要有一个 GitHub 账户。如果还没有，请去注册一个。\n注册完后，登录 GitHub，在 GitHub. com 中点击 \u0026ldquo;New repository\u0026rdquo; 创建一个新的仓库。创建一个与用户名相同的仓库，后面加 .github.io。例如，如果你的 GitHub 用户名是 zjc2782171149，则仓库名称应为 zjc2782171149.github.io。\n6. 生成 SSH 密钥并添加到 GitHub 仓库 # 如果之前已绑定 GitHub 仓库的 SSH 密钥，可跳过此步骤。在 Git Bash 中输入：\ngit config --global user.name \u0026#34;yourname\u0026#34; git config --global user.email \u0026#34;youremail\u0026#34; 替换 yourname 和 youremail 为你的 GitHub 用户名和邮箱。检查配置：\ngit config user.name git config user.email 生成 SSH 密钥：\nssh-keygen -t rsa -C \u0026#34;youremail\u0026#34; 在 GitHub 的设置中找到 SSH keys 选项，点击 \u0026ldquo;New SSH key\u0026rdquo; 并将 id_rsa.pub 的内容复制进去。\n7. 将 Hexo 部署到 GitHub # 打开 Hexo 的站点配置文件 _config.yml，修改以下内容：\ndeploy: type: git repo: git@github.com:zjc2782171149/zjc2782171149.github.io.git branch: master 安装 hexo-deployer-git 插件：\nnpm install hexo-deployer-git --save 进行部署：\nhexo clean hexo generate hexo deploy 访问 http://你的用户名.github.io 查看部署结果。\n8. 个人域名配置 # 购买并注册一个域名，例如通过阿里云。配置域名解析，将域名解析到 你的用户名.github.io 上。在 GitHub 仓库的 \u0026ldquo;Settings\u0026rdquo; 中，设置 Custom domain，输入你的域名。\n在 source 文件夹中创建一个名为 CNAME 的文件，文件内容为你的域名。例如：\nblog.zhangjiancong.top 更改默认分支为 master，并重新部署：\nhexo clean hexo g hexo d 更改主题 # 以 Fluid 主题为例：\n下载 Fluid 主题的压缩包，将其解压到 themes 文件夹下，并更名为 fluid。 修改 _config.yml 文件，将 theme 属性更改为 fluid，并将 language 属性修改为 zh-CN。 输入以下指令重新部署： hexo clean hexo g hexo d 发布文章 # 发布新文章：\nhexo new newpapername 编辑 source/_posts 中的 Markdown 文件，完成后进行重新部署：\nhexo clean hexo g hexo d 每次更新文章或配置文件时都需要重新部署。\n如有小伙伴觉得每次重新部署都麻烦，可以研究 GitHub Pages 的自动部署功能。\nhexo 网站的完善 # 在 Hexo 网站上，标签页面和分类页面是两种不同的内容组织方式，但它们都可以帮助你对文章进行分类和组织。让我们先看看如何分别实现 标签页面 和 分类页面，以及如何使用 tags 和 categories 来实现文章的标签和分类。\n1. 标签页面（Tags Page） # 标签页面展示所有文章的标签及其对应的文章。它通常和文章的 tags 属性相结合，让读者可以通过点击某个标签来浏览该标签下的所有文章。\n如何实现标签页面 # 创建标签页面： 在 source 目录下创建一个 tags 文件夹，并在其中创建一个 index.md 文件。\nmkdir -p source/tags touch source/tags/index.md 编辑 index.md： 在 index.md 文件中，添加以下内容来指定它是一个标签页面：\n--- title: Tags date: 2024-09-06 00:00:00 type: \u0026#34;tags\u0026#34; --- 这里 type: \u0026quot;tags\u0026quot; 告诉 Hexo 这是一个标签页面，Hexo 会自动生成所有标签的列表。\n使用文章中的 Tags： 在你的文章（.md 文件）中使用 tags 字段来为文章指定标签。例如：\n--- title: 我的文章 date: 2024-09-06 tags: - 技术 - Hexo --- 在这个例子中，文章被打上了 技术 和 Hexo 两个标签。生成网站后，在标签页面 (/tags/) 中，点击这些标签会列出相关的文章。\n2. 分类页面（Categories Page） # 分类页面展示所有文章的分类及其对应的文章。与 tags 不同，分类是更高层次的组织方式，通常用于将文章分组为不同的主题或类型。\n如何实现分类页面 # 创建分类页面： 和创建标签页面类似，在 source 目录下创建一个 categories 文件夹，并在其中创建一个 index.md 文件。\nmkdir -p source/categories touch source/categories/index.md 编辑 index.md： 在 index.md 文件中，添加以下内容来指定它是一个分类页面：\n--- title: Categories date: 2024-09-06 00:00:00 type: \u0026#34;categories\u0026#34; --- 这里 type: \u0026quot;categories\u0026quot; 告诉 Hexo 这是一个分类页面，Hexo 会自动生成所有分类的列表。\n使用文章中的 Categories： 在你的文章（.md 文件）中使用 categories 字段来为文章指定分类。例如：\n--- title: 我的文章 date: 2024-09-06 categories: - 编程 - 前端开发 --- 在这个例子中，文章被归类为 编程 和 前端开发。生成网站后，在分类页面 (/categories/) 中，点击这些分类会列出相关的文章。\n3. 标签（Tags）和分类（Categories）的区别与组合使用 # 标签 (Tags)：标签通常用于给文章添加多个关键字，便于读者快速找到具有相似标签的文章。文章可以有多个标签，没有层级结构，通常适合更细粒度的内容分类。\n分类 (Categories)：分类则是将文章组织成更大的类别，具有层级结构。例如，一个分类可以有子分类。每篇文章通常只有一个主分类，适合更广泛的内容分组。\n示例文章使用标签和分类 # --- title: 我的博客文章 date: 2024-09-06 tags: - Hexo - 博客搭建 categories: - 技术 - Web开发 --- Tags (标签)：文章有两个标签 Hexo 和 博客搭建，点击这些标签可以找到所有有这些标签的文章。 Categories (分类)：文章被归类为 技术 -\u0026gt; Web开发，读者可以通过分类页面找到属于该分类的所有文章。 4. 关于我（about）页面 # 在 Hexo 中，你可以通过创建“关于页面”（About Page）来展示个人信息，比如 GitHub 链接、博客、QQ 等。可以通过创建一个单独的 Markdown 文件，并在其中定义这些信息。接下来，我会详细介绍如何设置“关于页面”以及如何在其中展示和跳转到你的个人信息。\n1. 创建“关于页面” # 首先，你需要在 Hexo 的 source 目录下创建一个新的文件夹，并在其中添加一个 index.md 文件：\nmkdir -p source/about touch source/about/index.md 2. 编辑 index.md 文件 # 在 index.md 文件中，你可以使用 Markdown 来书写内容并添加个人信息。Hexo 会将这个文件渲染成 HTML 页面。你可以使用 HTML 或 Markdown 链接来跳转到 GitHub、博客等。\n以下是一个“关于页面”的示例：\n--- title: 关于我 date: 2024-09-06 type: \u0026#34;about\u0026#34; --- # 关于我 你好，我是 [你的名字]，这是一段关于我的介绍。 ## 联系方式 - **GitHub**: [我的 GitHub](https://github.com/yourusername) - **博客**: [我的博客](https://yourblog.com) - **QQ**: 123456789 欢迎通过以上方式联系我！ 3. 显示个人信息并添加链接 # 你可以用 Markdown 格式轻松展示个人信息并添加超链接，比如：\nGitHub 链接可以写成我的 GitHub。 博客链接可以写成我的博客。 QQ 号可以直接展示为文本或通过一些 QQ API 实现联系跳转。 4. 在导航栏中添加“关于”页面 # 为了让用户可以轻松访问“关于页面”，你可以在 Hexo 的主题配置文件（通常是 themes/your-theme/_config.yml）中，将“关于”页面的链接添加到导航栏中。\n找到 menu 配置项，并添加 about 页面：\nmenu: Home: / Archives: /archives About: /about 这样，导航栏中就会出现“关于”链接，点击后会跳转到 /about 页面。\n5. 自定义样式 # 如果你想要让“关于页面”更加个性化，可以在主题的 CSS 文件中添加自定义样式。例如，可以为页面的个人信息部分定义一些自定义的样式，以美化显示效果。\n在 themes/your-theme/source/css/ 中的样式文件（例如 style.css 或 custom.css）中，添加以下内容：\n.about-info { font-size: 18px; color: #333; } .about-info a { color: #007bff; text-decoration: none; } .about-info a:hover { text-decoration: underline; } 然后，在 index.md 文件中将内容包裹在一个 div 里：\n\u0026lt;div class=\u0026#34;about-info\u0026#34;\u0026gt; ## 联系方式 - **GitHub**: [我的 GitHub](https://github.com/yourusername) - **博客**: [我的博客](https://yourblog.com) - **QQ**: 123456789 \u0026lt;/div\u0026gt; 这样，你就可以通过 CSS 自定义“关于页面”的样式。\n","date":"September 7, 2024","externalUrl":null,"permalink":"/misc/hexo%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%E5%88%9B%E5%BB%BA/","section":"杂项","summary":" 笔记 三条常用命令：\n","title":"hexo个人博客创建","type":"misc"},{"content":"","date":"September 7, 2024","externalUrl":null,"permalink":"/ja/tags/%E3%83%86%E3%83%BC%E3%83%9E%E8%A8%AD%E5%AE%9A/","section":"Tags","summary":"","title":"テーマ設定","type":"tags"},{"content":"","date":"September 7, 2024","externalUrl":null,"permalink":"/ja/tags/%E3%83%96%E3%83%AD%E3%82%B0%E6%A7%8B%E7%AF%89/","section":"Tags","summary":"","title":"ブログ構築","type":"tags"},{"content":"","date":"September 7, 2024","externalUrl":null,"permalink":"/tags/%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA/","section":"Tags","summary":"","title":"博客搭建","type":"tags"},{"content":"","date":"September 7, 2024","externalUrl":null,"permalink":"/tags/%E4%B8%BB%E9%A2%98%E9%85%8D%E7%BD%AE/","section":"Tags","summary":"","title":"主题配置","type":"tags"},{"content":"","date":"September 7, 2024","externalUrl":null,"permalink":"/tags/%E4%BB%A3%E7%A0%81%E6%AE%B5/","section":"Tags","summary":"","title":"代码段","type":"tags"},{"content":"","date":"September 7, 2024","externalUrl":null,"permalink":"/tags/%E5%A0%86%E5%92%8C%E6%A0%88/","section":"Tags","summary":"","title":"堆和栈","type":"tags"},{"content":"","date":"September 7, 2024","externalUrl":null,"permalink":"/tags/%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"内存管理","type":"tags"},{"content":"","date":"September 7, 2024","externalUrl":null,"permalink":"/categories/%E5%86%85%E5%AD%98%E5%8C%BA%E5%9F%9F/","section":"Categories","summary":"","title":"内存区域","type":"categories"},{"content":" 笔记 The program code, global variables, and constant global variables are all stored in static segments, as these all have static lifetimes and known sizes at compile time.\nIf we look at the addresses printed by mexplore, we see that the global variables stored in are at relatively low memory addresses (around 0x60'0000 hexadecimal and 0x40'0000 hexadecimal), while the local variable is stored at a high address, close to 0x7fff'ffff'ffff (about 247), and the dynamically allocated character is stored in between (albeit closer to the static segments).\nThere is a reason for this placement: it allows both types of segments to grow without risk of getting in each other\u0026rsquo;s way. In particular, the automatic-lifetime segment grows downwards as more local variables come into scope, while the dynamic-lifetime segment grows upwards. If they ever meet, your computer runs out of memory.\n内存区域划分 # 在计算机中，程序运行时的内存布局通常分为多个区域，每个区域负责存储特定类型的数据。这种内存布局通常包括以下主要部分：\n代码段（Text Segment）:\n存储内容: 存放程序的可执行代码，也就是编译后的机器指令。该区域通常是只读的，以防止程序的代码被意外或恶意修改。 特性: 常见操作系统将其标记为只读并可执行。 数据段（Data Segment）:\n存储内容: 存放已初始化的全局变量和静态变量。这个段可以进一步分为两部分： 已初始化数据段（Initialized Data Segment）: 存储在编译时已初始化的全局和静态变量。 未初始化数据段（BSS Segment，Block Started by Symbol）: 存放未初始化的全局变量和静态变量。在程序开始执行时，BSS 段的内容会被初始化为零。 特性: 数据段是可读写的。 堆（Heap dynamic-lifetime segment is called the heap）:\n存储内容: 用于动态分配的内存块。malloc、calloc、realloc 等函数在运行时从堆中分配内存。堆的大小可以在运行时增长和缩小。 特性: 堆区域通常从低地址向高地址扩展。 栈（Stack automatic-lifetime segment is called the stack.）:\n存储内容: 用于存放函数的局部变量、函数参数和返回地址。当函数被调用时，会在栈上分配一个新的栈帧，用于存储该函数的局部数据。函数调用结束后，栈帧会被释放。 特性: 栈区域通常从高地址向低地址扩展。栈的大小是有限的，过度使用栈空间（如过深的递归调用或大量局部变量）可能导致栈溢出。 常量段（Rodata Segment）:\n存储内容: 存放只读数据，比如字符串常量和其他不变数据。这些数据通常在编译时确定并在运行时不会改变。 特性: 常量段通常是只读的。 执行堆栈（Call Stack）:\n存储内容: 特指用于存储每个线程的调用信息，包括函数调用的返回地址、局部变量和函数参数。执行堆栈与栈区域有些重叠的概念。 特性: 递归调用或大量局部变量可能导致栈溢出。 内存布局示意图 # 典型的内存布局==从高地址到低地址==可以如下表示：\n+--------------------+ | 栈 (Stack) | +--------------------+ | 堆 (Heap) | | (动态分配区) | +--------------------+ | 未初始化数据段 (BSS) | +--------------------+ | 已初始化数据段 (Data) | +--------------------+ | 只读数据段 (Rodata) | +--------------------+ | 代码段 (Text) | +--------------------+ | 其他系统区域 (Other) | +--------------------+ 在 C 语言中，变量根据其类型和作用域分布在程序的不同内存区域。以下是不同类型的变量及其存储位置的详细说明：\n全局变量（Global Variables）:\n定义: 在函数外部定义的变量，具有全局作用域，整个程序运行期间都可以访问。 存储位置: 已初始化的全局变量存储在**数据段（Data Segment）**的已初始化数据区域。 未初始化的全局变量存储在BSS 段（Block Started by Symbol Segment），程序开始时 BSS 段的内容被初始化为零。 静态变量（Static Variables）:\n定义: 使用 static 关键字修饰的变量。可以是全局或局部的，但在程序的生命周期内都只分配一次，并保留其值。 存储位置: 静态全局变量：与全局变量类似，已初始化的存储在数据段，未初始化的存储在 BSS 段。 静态局部变量：尽管其作用域仅限于函数内，但其生命周期与程序相同，也存储在数据段或 BSS 段中。 常量变量（Constant Variables）:\n定义: 使用 const 关键字修饰的变量，声明后不能更改其值。 存储位置: 常量全局变量：通常存储在只读数据段（Rodata Segment）。但如果常量是局部的，它可能存储在栈或寄存器中，具体取决于编译器优化。 常量局部变量：通常在栈上分配，或者在某些情况下编译器可能将它们优化到寄存器中。 局部变量（Local Variables）:\n定义: 在函数或块中定义的变量，只在其定义的作用域内有效。 存储位置: 通常存储在**栈（Stack）**中。函数调用时，局部变量在栈上分配空间，函数返回时这些空间被释放。 动态分配的内存:\n定义: 使用 malloc、calloc、realloc 等函数动态分配的内存。大小和数量在运行时决定。 存储位置: 存储在**堆（Heap）**中。堆内存的生命周期由程序员控制，需要手动释放（使用 free 函数），否则会导致内存泄漏。 总结 # 代码段（Text Segment）: 存放程序的可执行代码。 数据段（Data Segment）: 存放已初始化的全局变量和静态变量。 BSS 段（Block Started by Symbol Segment）: 存放未初始化的全局变量和静态变量，程序开始时初始化为零。 只读数据段（Rodata Segment）: 存放常量数据和字符串常量。 栈（Stack）: 存放局部变量、函数参数和返回地址。 堆（Heap）: 存放动态分配的内存。 理解这些内存区域及其作用对于高效和安全的编程至关重要，特别是在涉及到动态内存管理、全局状态和并发问题时。\n","date":"September 7, 2024","externalUrl":null,"permalink":"/notes/system/%E5%86%85%E5%AD%98%E5%8C%BA%E5%9F%9F%E4%BB%8B%E7%BB%8D/","section":"笔记","summary":" 笔记 The program code, global variables, and constant global variables are all stored in static segments, as these all have static lifetimes and known sizes at compile time.\n","title":"内存区域介绍","type":"notes"},{"content":"","date":"September 7, 2024","externalUrl":null,"permalink":"/tags/%E6%95%B0%E6%8D%AE%E6%AE%B5/","section":"Tags","summary":"","title":"数据段","type":"tags"},{"content":"","date":"September 7, 2024","externalUrl":null,"permalink":"/tags/futamura%E6%8A%95%E5%BD%B1/","section":"Tags","summary":"","title":"Futamura投影","type":"tags"},{"content":"","date":"September 7, 2024","externalUrl":null,"permalink":"/categories/%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E5%92%8C%E8%AF%AD%E8%A8%80/","section":"Categories","summary":"","title":"编程思想和语言","type":"categories"},{"content":"","date":"September 7, 2024","externalUrl":null,"permalink":"/tags/%E7%BC%96%E8%AF%91%E6%8A%80%E6%9C%AF/","section":"Tags","summary":"","title":"编译技术","type":"tags"},{"content":"","date":"September 7, 2024","externalUrl":null,"permalink":"/tags/%E9%83%A8%E5%88%86%E6%B1%82%E5%80%BC/","section":"Tags","summary":"","title":"部分求值","type":"tags"},{"content":"Futamura 投影（Futamura Projection）是编程语言和编译技术中的一个重要概念，由日本计算机科学家 Futamura Yoshihiko 在 1971 年提出。Futamura 投影解释了如何通过对解释器进行部分求值（Partial Evaluation），将解释器转换为编译器，以及进一步的可能转变。\n背景与基本概念 # 在理解 Futamura 投影之前，需要了解以下两个概念：\n解释器（Interpreter）：一种程序，它读取并执行另一个程序的代码。解释器按代码的一行或一段来处理，逐行或逐段地将源代码翻译成目标代码并执行。\n部分求值（Partial Evaluation）：一种编译技术，旨在通过提前计算程序中能够在编译时确定的部分，从而生成更高效的代码。部分求值会保留在运行时才知道的部分，从而使得程序既高效又灵活。\n三次 Futamura 投影 # Futamura 投影可以分为三个阶段，称为“第一次投影”、“第二次投影”和“第三次投影”：\n第一次 Futamura 投影：\n将解释器和一个特定的源程序作为部分求值器的输入，生成一个针对该特定源程序的专用编译器。这个编译器可以直接将源程序翻译成目标代码。 形式化描述：如果有解释器 I 和源程序 P，通过部分求值（PE），我们可以生成一个等价的目标程序 P'。 表示为：PE(I, P) = P'，其中 P' 是源程序 P 对应的目标代码。 第二次 Futamura 投影：\n将解释器本身作为部分求值器的输入，生成一个针对任何源程序的编译器。这意味着将解释器的行为部分求值，得到一个编译器。 形式化描述：通过部分求值器 PE 对解释器 I 进行部分求值，可以生成一个编译器 C，这个编译器可以将任何源程序 P 翻译成目标程序 P'。 表示为：PE(PE, I) = C，其中 C 是一个可以将源程序 P 编译成目标代码 P' 的编译器。 第三次 Futamura 投影：\n将部分求值器本身作为输入，对其进行部分求值，生成一个可以生成编译器的编译器生成器（也称为编译器生成器、编译器编译器）。 形式化描述：通过部分求值器 PE 对 PE 进行部分求值，可以生成一个编译器生成器 G，该生成器能够生成编译器。 表示为：PE(PE, PE) = G，其中 G 是一个可以生成编译器的编译器生成器。 意义与应用 # Futamura 投影展示了如何通过部分求值将解释器转化为编译器，并进一步展示了编译器生成器的潜力。这一思想在编程语言的实现和优化领域具有重要意义，特别是在自动化生成编译器和提高编译效率方面。\n第一次投影提供了从解释器生成专用编译器的方法，使得特定源程序可以直接被编译为目标代码。 第二次投影展示了如何生成通用编译器，从而无需解释器就可以直接编译任何源程序。 第三次投影则展示了生成编译器生成器的可能性，即自动化工具可以生成编译器，这对编程语言设计与实现具有深远影响。 Futamura 投影是一种理论工具，展示了编译和解释之间的深层联系，并引导了编译器自动生成技术的发展。\n摘自论坛 # 相比上一节强调物理或逻辑上客观存在的 “界限”，本节主要强调一种心智活动。这样的心智活动具有一个明显的特征，那就是它具有一种 “递归” 的结构。我们首先从一个概念中抽象出它的形成过程，再将这样的思维过程应用于这个概念自身。如此，我们得到的新概念常常具有这样的结构： A 的 A。\n个人认为，本节讨论的思想或许是本文的所有思想中最为重要的那一个。这是因为高阶这一思想具有很好的方法论层面上的指导意义，并且在实践中操作起来又有趣又直截了当——同时，个人的看法是通过这一思想生成的很多概念对启迪编程初学者的思维具有非常重要的意义（事实上，如果要从整个技术界中选一个本科教育一般不大重视、但又极度有价值的术语，我会选闭包和高阶函数）。\n通过这样一种 “递归式” 的抽象过程得到的 A 的 A 这一形式的概念，我们通常将其称作 “高阶 A”。比如，将函数代表的过程 (A -\u0026gt; B) 抽象出来，再应用于函数自身，我们就得到了 函数的函数，即 高阶函数（higher-order function）。个人认为高阶函数和闭包是计算机领域最为重要的抽象之一。\n例如，将类型的形成过程（类型描述一个语言中最小单位的类别）抽象出来，应用于类型自身，我们就得到了 类型的类型，即 型别（kind）。\n二村映射 (Futamura Projections) # 最后，作为压轴，讲一个我昨晚在一篇屯了半年一年的文章中看到的一个概念。我想这个概念应该是超出大部分读者的经验的，而我觉得它还蛮有意思，所以放到最后来大概说说。我们需要从故事的最开始讲起——可能要过很久才能最终看到这个词。\n编译技术的分类 # 我们知道大体上编译技术可以分为两种，分别是 编译器 (compiler) 和 解释器 (interpreter)。编译器执行的是一个两阶段的过程：首先，自源代码编译出目标程序 (target program)，再向目标程序提供输入，然后得到程序的输出；解释器执行的则是一个一阶段的过程：解释器接收源代码和输入，并直接得到程序的输出。在本节中，我们主要关注解释器。\n绝大部分的编译原理教科书和课程都只涵盖这两者。然而，事实上还存在着第三种编译技术：某种程度上，它介于解释器和编译器之间，但它既不是解释器又不是编译器——请特别注意这一点。为了方便理解，我先岔开点讲。\n函数式编程中的概念 # 稍有函数式经验的读者应该都听过 柯里化 (Currying)。如果有一个接收 n 个输入的函数，我们可以把它转化成一叠嵌套的高阶函数，这叠高阶函数中的第 i 个接收原函数中的第 i 个输入（仅一个），然后返回接收剩下 n-i 个输入的函数（故为 “高阶函数”）：比如，一个接收 3 个输入的函数 ((A, B, C) -\u0026gt; Output) 经柯里化后会成为一叠高阶函数，这个高阶函数中的第一个接收一个输入 A，然后返回一个接收输入 B 的高阶函数；再向其提供输入 C，才能得到最终结果，即柯里化后的结果是 (A -\u0026gt; (B -\u0026gt; (C -\u0026gt; Output)))。\n尽管在支持闭包和高阶函数的语言中柯里化和 反柯里化 (Uncurrying) 都能以非常浅显 (trivial) 的方式实现，但仅有少数的编程语言才一等地支持了柯里化，比如 ML 系语言（Haskell, SML, OCaml, F#）和 Scala。\n对于一个经柯里化后的函数，如果我们只向它提供前 i (i\u0026lt;n) 个输入，那么我们必然得不到最终的输出，而只能得到一个高阶函数，它会再处理余下的输入：这称作 部分应用 (Partial Application)。\n讲了这么一大堆，其实主要就是为了部分应用这个概念。你可能会想知道为什么我在上文不用参数这个惯用说法而是用输入，你可能也已经猜到了，这是因为我希望在此强调将解释器理解成一个函数的思想：它的输入是源代码和这段代码的输入，它的输出是这段代码的输出（事实上，很多语言都提供一些十分类似解释器的机制，它们确实是函数（比如 eval））。\n部分求值 (Partial Evaluation) # 是的，之所以要将解释器理解成一个函数，是因为我正是要借用部分应用的思想：既然解释器的输入是源代码和输入，那么我能不能部分应用这个 “函数”（即解释器），首先给定源代码，得到一个 “处理剩下参数的高阶函数”（即一个中间程序），然后对于这个中间程序，再提供它的输入，然后得到结果？\n答案是，这是可行的。事实上，这一过程称作 部分求值 (Partial Evaluation)，而执行这一过程的程序我们称作 部分求值器 (Partial Evaluator)。部分求值器作用于一个程序和它的一些参数，输出一个该程序对于这组参数 “特化” 后的新的程序。这里的 “一个程序” 我们称作 源程序 (Source Program)，“新的程序” 我们称作 残差程序 (Residual Program)。\n回到上边有关解释器举例上来。我们将一个解释器和一个源程序传入部分求值器中，得到的残差程序就是一个接收 输入 返回 输出 的中间程序。\n是不是觉得非常的熟悉……？第一阶段我们提供源代码，得到中间程序；第二阶段我们提供输入，随后得到输出。这是在做什么…？\n是的，这就是编译。神奇之处在于，我们并没有手工编写一个编译器，我们只是将一个解释器和需要被求值的源代码扔给了部分求值器而已——某种意义上来说，我们是 “免费” 得到了一个编译器。\n部分求值器与语言虚拟机 # 你可能在思考这有什么用？你或许已经将这一思路和语言虚拟机联系了起来：众所周知，实现编译器通常比实现解释器难得多，那我们能不能弄一个本质其实是一个部分求值器的语言虚拟机，在这个虚拟机上实现一门新的语言只需要编写一个解释器就行了，而语言虚拟机会调用它的部分求值器自动帮我生成对应的、更加高效的 “编译器” 呢？\n显然，这是完全可行的——事实上这是有产业实践的。PyPy 是一个 Python 语言的解释器实现，它正是基于以上语言虚拟机的思路实现的：在 PyPy VM 上工作的语言有 Python, Ruby 和逻辑编程语言 Prolog 等；除此以外，泛语言高级语言虚拟机（HLLVM）GraalVM 的一个重要组件同样如此，工作在 GraalVM 上的语言包括 JavaScript, Ruby, Python, Java, R 等。\n这其实就是 第一类二村映射 (First Futamura Projection)。\n二村映射的递归性 # 既然是放在 “高阶” 这一主题下的，我们当然要用到这一思想：正如前几段的粗体所提示的，部分求值器其实也是一个程序，它自身也能够被部分求值。\n回想一开始，解释器 的输入是 源代码 和 输入，我们将 解释器（主体） 和 源代码（主体的其中一个输入） 单独拎出来，扔进了部分求值器中，这是第一类二村映射；即是说，在第一类二村映射中，部分求值器 的输入是 解释器 和 源代码。\n我们将这一过程——即 “将 主体 和 主体的其中一个输入 单独拎出来扔进部分求值器中”—— “高阶化”，作用于自身，简单的代换就能得到，我们需要将 部分求值器（主体） 和 解释器 （主体的其中一个输入） 扔进一个部分求值器中。现在我们需要弄清楚，这个 “高阶过程” 得到的残差程序是什么。\n注意到，在第一类二村映射中输入只有 解释器 和 源代码。既然我们已经部分求值了前者，那么下一步自然只剩下后者了：向上一段提到的 残差程序 提供 源代码，我们就能得到 目标程序。\n向一个程序提供源代码得到另一个程序…… 听起来有点像是某个我们已经知道的东西会做的事情。是的，这就是编译。我们的这个 “高阶过程” 得到的残差程序其实是一个编译器，而我们的这个 “高阶过程” 所做的，正是在生成编译器，即，这一过程其实是编译器的编译器——如此，我们终于见到了本节概述中提到的 A 的 A 这样的结构，如果你愿意的话，或许你还可以将这称作高阶编译器。\n这其实就是 第二类二村映射 second Futamura projection。\n那么，这个 “高阶化” 的过程还能继续下去吗？显然如此。再继续下去，我们就能得到_编译器的编译器的编译器_，也即第三类二村映射 third Futamura projection。再往后呢？这点就留给读者自行阅读和思考啦。\n","date":"September 7, 2024","externalUrl":null,"permalink":"/notes/system/%E4%BA%8C%E6%9D%91%E6%98%A0%E5%B0%84/","section":"笔记","summary":"Futamura 投影（Futamura Projection）是编程语言和编译技术中的一个重要概念，由日本计算机科学家 Futamura Yoshihiko 在 1971 年提出。Futamura 投影解释了如何通过对解释器进行部分求值（Partial Evaluation），将解释器转换为编译器，以及进一步的可能转变。\n","title":"二村映射","type":"notes"},{"content":"","date":"September 7, 2024","externalUrl":null,"permalink":"/tags/%E8%A7%A3%E9%87%8A%E5%99%A8/","section":"Tags","summary":"","title":"解释器","type":"tags"},{"content":"","date":"September 7, 2024","externalUrl":null,"permalink":"/tags/c%E8%AF%AD%E8%A8%80/","section":"Tags","summary":"","title":"C语言","type":"tags"},{"content":"","date":"September 7, 2024","externalUrl":null,"permalink":"/tags/gcc/","section":"Tags","summary":"","title":"GCC","type":"tags"},{"content":" 编译系统（过程） # 编译过程是将源代码转换成可执行文件的几个步骤的集合。每一步生成不同类型的文件，这些文件在最终的可执行程序中扮演不同的角色。以下是详细的步骤以及每个阶段产生的文件的作用：\n1. 预处理（Preprocessing） # 命令: gcc -E test.c -o test.i\n文件: test.i\n作用:\n预处理器处理 #include、#define 和其他预处理指令。\n结果是一个展开的源文件，其中所有的宏已被展开，包含文件已被插入。\n2. 编译（Compilation） # 命令: gcc -S test.i -o test.s\n文件: test.s\n作用:\n编译器将预处理过的源文件（通常是 .i 文件，但可以直接使用 .c 文件）转换成汇编语言代码。 生成的汇编文件包含目标平台的汇编指令，但尚未转换为机器代码。 3. 汇编（Assembling） # 命令: gcc -c test.s -o test.o\n文件: test.o\n作用:\n汇编器将汇编语言代码（.s 文件）转换为机器代码。 生成的目标文件（.o 文件）包含机器代码以及与程序相关的其他信息，如符号表和重定位信息。 目标文件尚未完全链接为可执行文件。它是可以被其他目标文件链接的中间文件。 4. 链接（Linking） # 命令: gcc test.o -o test\n文件: test\n作用:\n链接器将一个或多个目标文件（.o 文件）和可能的库文件链接成一个最终的可执行文件。 处理符号解析和重定位。符号解析涉及到解决目标文件中引用的外部符号（例如库函数）的地址。 生成的可执行文件可以直接在操作系统上运行。 中间文件总结 # .i 文件（预处理输出）:\n包含展开的源代码，所有宏和包含文件都被处理。 用于检查预处理后的代码是否正确。 .s 文件（汇编代码）:\n包含汇编语言的源代码，描述了机器指令。 用于查看编译器生成的汇编代码。 .o 文件（目标文件）:\n包含机器代码、符号表、重定位信息等。 用于最终的链接步骤，是编译的中间产物。 可执行文件（通常没有文件扩展名）:\n包含最终的机器代码，可以直接运行。 经过链接器处理，所有符号都被解析，重定位信息已处理。 例子总结 # 假设 test.c 内容如下：\nvoid f() { // 空实现 } int main() { return 0; } 预处理: gcc -E test.c -o test.i 编译: gcc -S test.c -o test.s 汇编: gcc -c test.c -o test.o 链接: gcc test.c -o test 解释:\ntest.i 是预处理后的源代码。 test.s 是编译生成的汇编代码。 test.o 是编译生成的目标文件。 test 是最终生成的可执行文件，可以运行。 gcc 相关命令用法 # GCC（GNU Compiler Collection）是一个强大的编译器，支持多种编程语言。以下是一些常用的 GCC 命令和选项，特别是用于编译 C 程序以及生成汇编文件的相关选项：\n编译 C 程序 # 编译并链接\ngcc -o output_executable source.c -o output_executable：指定输出可执行文件的名称。 source.c：源文件。 仅编译，不链接\ngcc -c source.c -c：只编译源代码，==不链接==生成可执行文件。这会生成一个目标文件（source.o）。 生成汇编文件 # 生成汇编代码 gcc -S source.c -S：将源代码编译为汇编代码，而不是目标文件。生成的汇编文件扩展名通常为 .s。 预处理 # 仅进行预处理 gcc -E source.c -o source.i -E：仅执行预处理，并将结果输出到指定文件。 显示编译信息 # 显示编译过程中调用的命令\ngcc -v source.c -v：显示编译过程中的详细信息。 显示预定义宏\ngcc -dM -E - \u0026lt; /dev/null -dM：显示所有预定义的宏。 -E：执行预处理。 优化选项 # 优化代码 gcc -O1 source.c -o output_executable gcc -O2 source.c -o output_executable gcc -O3 source.c -o output_executable -O1, -O2, -O3：分别表示不同级别的优化。 调试信息 # 生成调试信息 gcc -g source.c -o output_executable -g：生成调试信息，用于调试器（如 gdb）。 其他常用选项 # 定义宏\ngcc -DNAME=value source.c -o output_executable -DNAME=value：定义一个预处理宏。 包含目录\ngcc -I/path/to/include source.c -o output_executable -I/path/to/include：指定额外的头文件搜索路径。 链接库\ngcc -L/path/to/lib -lname source.c -o output_executable -L/path/to/lib：指定库文件搜索路径。 -lname：链接名为 libname.so 或 libname.a 的库。 实例 # 假设有一个简单的 C 程序文件 main.c，我们可以使用以下命令：\n编译并链接生成可执行文件\ngcc -o my_program main.c 生成汇编文件\ngcc -S main.c 仅编译为目标文件\ngcc -c main.c 进行优化编译\ngcc -O2 -o my_program main.c 生成带有调试信息的可执行文件\ngcc -g -o my_program main.c 这些命令和选项可以帮助你在不同的场景下灵活使用 GCC 编译器。\nobjdump 和 hexdump 是两个在软件开发和分析中非常有用的命令行工具。它们用于处理和查看二进制文件的内容，但用途和输出方式不同。下面是这两个命令的详细介绍及示例说明。\n相关常用的代码分析工具 # objdump # objdump 是一个用于显示二进制文件的内容的工具，尤其是可执行文件、目标文件和库文件。它可以显示反汇编代码、符号表、段信息等。objdump 常用于调试和逆向工程。\n常用选项 # -d：反汇编可执行文件或目标文件的代码。 -S：在反汇编代码中插入源代码（如果可用）。 -t：显示符号表。 -h：显示段头信息。 -x：显示所有头信息。 示例 # 假设有一个简单的 C 文件 hello.c：\n#include \u0026lt;stdio.h\u0026gt; void hello() { printf(\u0026#34;Hello, World!\\n\u0026#34;); } int main() { hello(); return 0; } 编译成可执行文件 hello：\ngcc -o hello hello.c 使用 objdump 查看反汇编代码：\nobjdump -d hello 输出可能如下所示（部分）：\nhello: file format elf64-x86-64 Disassembly of section .text: 0000000000001139 \u0026lt;hello\u0026gt;: 1139: 55 push %rbp 113a: 48 89 e5 mov %rsp,%rbp 113d: bf 00 00 00 00 mov $0x0,%edi 1142: e8 00 00 00 00 callq 0 \u0026lt;puts@plt\u0026gt; 1147: 90 nop 1148: 5d pop %rbp 1149: c3 retq 000000000000114a \u0026lt;main\u0026gt;: 114a: 55 push %rbp 114b: 48 89 e5 mov %rsp,%rbp 114e: e8 f0 ff ff ff callq 1139 \u0026lt;hello\u0026gt; 1153: b8 00 00 00 00 mov $0x0,%eax 1158: 5d pop %rbp 1159: c3 retq hexdump # hexdump 是一个用于查看文件的十六进制表示的工具。它可以显示文件内容的十六进制值及其对应的 ASCII 字符。hexdump 常用于查看和分析二进制文件或数据文件的内容。\n常用选项 # -C：以十六进制和 ASCII 字符显示文件内容。 -n：指定读取的字节数。 -v：显示所有行（默认情况下，连续相同的行会被压缩）。 示例 # 假设有一个文本文件 example.txt，内容如下：\nHello, World! 使用 hexdump 查看其十六进制表示：\nhexdump -C example.txt 输出可能如下所示：\n00000000 48 65 6c 6c 6f 2c 20 57 6f 72 6c 64 21 0a |Hello, World!.| 0000000e 其中，左侧是文件的偏移地址，中间是文件内容的十六进制表示，右侧是对应的 ASCII 字符。\n总结 # objdump：用于分析和反汇编二进制文件，适合调试和逆向工程。 hexdump：用于查看文件的十六进制表示，适合查看和分析文件的低级内容。 这两个工具在不同的情况下都有很大的用处，可以帮助开发者和分析人员更好地理解和处理二进制文件。\nc 程序中内联汇编代码 # 在 C 程序中插入汇编代码有两种主要的方法：内联汇编（Inline Assembly） 和 汇编语言文件（Assembly Language Files）。这两种方法允许在 C 程序中嵌入汇编代码，但它们的用法和应用场景有所不同。下面是对这两种方法的详细说明：\n1. 内联汇编（Inline Assembly） # 内联汇编允许在 C 代码中直接嵌入汇编指令。这种方法适用于在 C 程序中需要插入少量汇编代码的情况。它使得在一个 C 函数中直接嵌入汇编代码成为可能。\n语法 # 在 GCC 中，内联汇编使用 __asm__ 或 asm 关键字。可以使用以下语法：\nasm(\u0026#34;assembly code\u0026#34; : output operands : input operands : clobbered registers); \u0026quot;assembly code\u0026quot;: 要插入的汇编代码。 output operands: 说明汇编代码将写入哪些 C 变量（如果有的话）。 input operands: 说明汇编代码将读取哪些 C 变量（如果有的话）。 clobbered registers: 说明汇编代码将修改哪些寄存器（如果有的话）。 示例 # 下面是一个在 C 代码中使用内联汇编的示例：\n#include \u0026lt;stdio.h\u0026gt; int main() { int a = 10; int b; // 内联汇编代码：将 a 的值加到 b 中 asm(\u0026#34;addl %1, %0\u0026#34; : \u0026#34;=r\u0026#34; (b) : \u0026#34;r\u0026#34; (a), \u0026#34;0\u0026#34; (b)); printf(\u0026#34;The result is %d\\n\u0026#34;, b); return 0; } 解释:\naddl %1, %0 是要执行的汇编指令，将 a 的值加到 b 中。 \u0026quot;=r\u0026quot; (b) 表示 b 是一个输出操作数，使用一个通用寄存器。 \u0026quot;r\u0026quot; (a) 表示 a 是一个输入操作数，也使用一个通用寄存器。 \u0026quot;0\u0026quot; (b) 表示 b 在汇编代码中也是一个输入操作数，但也用作输出操作数，0 表示它是输出操作数的寄存器的相同位置。 2. 汇编语言文件（Assembly Language Files） # 汇编语言文件是使用汇编语言编写的独立文件，通常具有 .s 或 .asm 扩展名。这种方法适用于需要大量汇编代码或需要将汇编代码与 C 代码分开的情况。\n使用方法 # 创建汇编语言文件:\n创建一个名为 example.s 的汇编文件，内容如下：\n.global my_function .text my_function: movl $42, %eax ret 解释:\n.global my_function 将 my_function 声明为全局符号，使其可以被其他文件引用。 movl $42, %eax 将值 42 加载到寄存器 %eax。 ret 返回到调用函数的地方。 在 C 程序中声明和调用汇编函数:\n#include \u0026lt;stdio.h\u0026gt; extern int my_function(); int main() { int result = my_function(); printf(\u0026#34;The result is %d\\n\u0026#34;, result); return 0; } 编译和链接:\n编译和链接汇编语言文件和 C 文件：\ngcc -c example.s -o example.o gcc -o main main.c example.o 这将生成可执行文件 main，其中包含汇编和 C 代码的混合。\n总结 # 内联汇编：直接在 C 代码中插入汇编指令，适用于少量汇编代码和需要直接与 C 变量交互的场景。 汇编语言文件：将汇编代码放在独立文件中，适用于较复杂的汇编代码或需要与多个 C 文件分开的情况。 这两种方法各有其优点和适用场景，可以根据具体需求选择使用。\n","date":"September 7, 2024","externalUrl":null,"permalink":"/notes/tools/gcc%E4%BB%8B%E7%BB%8D/","section":"笔记","summary":"编译系统（过程） # 编译过程是将源代码转换成可执行文件的几个步骤的集合。每一步生成不同类型的文件，这些文件在最终的可执行程序中扮演不同的角色。以下是详细的步骤以及每个阶段产生的文件的作用：\n","title":"gcc介绍","type":"notes"},{"content":"","date":"September 7, 2024","externalUrl":null,"permalink":"/tags/%E7%BC%96%E8%AF%91%E8%BF%87%E7%A8%8B/","section":"Tags","summary":"","title":"编译过程","type":"tags"},{"content":"","date":"September 7, 2024","externalUrl":null,"permalink":"/categories/%E7%BC%96%E8%AF%91%E5%99%A8/","section":"Categories","summary":"","title":"编译器","type":"categories"},{"content":"","date":"September 7, 2024","externalUrl":null,"permalink":"/tags/%E9%A2%84%E5%A4%84%E7%90%86/","section":"Tags","summary":"","title":"预处理","type":"tags"},{"content":"","date":"June 6, 2024","externalUrl":null,"permalink":"/tags/hamming-number/","section":"Tags","summary":"","title":"Hamming Number","type":"tags"},{"content":" hamming number 题解思路 # 题目：codewars网站 # solutions # ***个人第一题目思路：***就一个数的质因数分解，我只要检查这个数质因数是不是只有2 3 5就行了。 # def is_hamming_number(num): \u0026#34;\u0026#34;\u0026#34;判断一个数是否是 Hamming 数\u0026#34;\u0026#34;\u0026#34; while num % 2 == 0: num //= 2 while num % 3 == 0: num //= 3 while num % 5 == 0: num //= 5 return num == 1 def generate_hamming_numbers(n): \u0026#34;\u0026#34;\u0026#34;生成前 n 个 Hamming 数\u0026#34;\u0026#34;\u0026#34; hamming_numbers = [] num = 1 while len(hamming_numbers) \u0026lt; n: if is_hamming_number(num): hamming_numbers.append(num) num += 1 return hamming_numbers def nth_hamming_number(n): \u0026#34;\u0026#34;\u0026#34;查询第 n 个 Hamming 数\u0026#34;\u0026#34;\u0026#34; hamming_numbers = generate_hamming_numbers(n) return hamming_numbers[-1] if n \u0026lt;= len(hamming_numbers) else None 简单明了的思路，就是不断遍历，直到找到质因数只有2，3，5的数，然后放进列表内。\n然后，对比上面的solutions和我的思路可以发现很明显的优化，就是solutions不做多余的操作，直接找到下一个hamming number。他能做到这样，个人认为是简单分析了数字的规律，用到了一点数学分析和数论知识。以下是我的简单分析证明：\n一个自己关于算法正确性的证明 这个最后我认为不可能的，是因为之前没想到，只要这个数是2 3 5的倍数就行了的。\n","date":"June 6, 2024","externalUrl":null,"permalink":"/notes/math/hamming-number-solutions/","section":"笔记","summary":"hamming number 题解思路 # 题目：codewars网站 # solutions # ***个人第一题目思路：***就一个数的质因数分解，我只要检查这个数质因数是不是只有2 3 5就行了。 # def is_hamming_number(num): \"\"\"判断一个数是否是 Hamming 数\"\"\" while num % 2 == 0: num //= 2 while num % 3 == 0: num //= 3 while num % 5 == 0: num //= 5 return num == 1 def generate_hamming_numbers(n): \"\"\"生成前 n 个 Hamming 数\"\"\" hamming_numbers = [] num = 1 while len(hamming_numbers) \u003c n: if is_hamming_number(num): hamming_numbers.append(num) num += 1 return hamming_numbers def nth_hamming_number(n): \"\"\"查询第 n 个 Hamming 数\"\"\" hamming_numbers = generate_hamming_numbers(n) return hamming_numbers[-1] if n \u003c= len(hamming_numbers) else None 简单明了的思路，就是不断遍历，直到找到质因数只有2，3，5的数，然后放进列表内。\n","title":"hamming_number_solutions","type":"notes"},{"content":"","date":"June 6, 2024","externalUrl":null,"permalink":"/categories/math/","section":"Categories","summary":"","title":"Math","type":"categories"},{"content":"","date":"June 6, 2024","externalUrl":null,"permalink":"/ja/tags/%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0/","section":"Tags","summary":"","title":"アルゴリズム","type":"tags"},{"content":"","date":"June 6, 2024","externalUrl":null,"permalink":"/ja/tags/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E7%B7%B4%E7%BF%92/","section":"Tags","summary":"","title":"プログラミング練習","type":"tags"},{"content":"","date":"June 6, 2024","externalUrl":null,"permalink":"/tags/%E7%BC%96%E7%A8%8B%E7%BB%83%E4%B9%A0/","section":"Tags","summary":"","title":"编程练习","type":"tags"},{"content":"","date":"June 6, 2024","externalUrl":null,"permalink":"/ja/tags/%E8%A7%A3%E6%B3%95/","section":"Tags","summary":"","title":"解法","type":"tags"},{"content":"","date":"June 6, 2024","externalUrl":null,"permalink":"/notes/math/","section":"笔记","summary":"","title":"数学","type":"notes"},{"content":"","date":"June 6, 2024","externalUrl":null,"permalink":"/tags/%E6%95%B0%E5%AD%A6/","section":"Tags","summary":"","title":"数学","type":"tags"},{"content":"","date":"June 6, 2024","externalUrl":null,"permalink":"/tags/%E7%AE%97%E6%B3%95/","section":"Tags","summary":"","title":"算法","type":"tags"},{"content":"","date":"June 6, 2024","externalUrl":null,"permalink":"/tags/%E9%A2%98%E8%A7%A3/","section":"Tags","summary":"","title":"题解","type":"tags"},{"content":"","date":"May 15, 2024","externalUrl":null,"permalink":"/ja/tags/%E3%83%99%E3%82%BA%E3%83%BC%E3%81%AE%E7%AD%89%E5%BC%8F/","section":"Tags","summary":"","title":"ベズーの等式","type":"tags"},{"content":"","date":"May 15, 2024","externalUrl":null,"permalink":"/tags/%E8%B4%9D%E7%A5%96%E5%AE%9A%E7%90%86/","section":"Tags","summary":"","title":"贝祖定理","type":"tags"},{"content":"","date":"May 15, 2024","externalUrl":null,"permalink":"/tags/%E6%89%A9%E5%B1%95%E6%AC%A7%E5%87%A0%E9%87%8C%E5%BE%97/","section":"Tags","summary":"","title":"扩展欧几里得","type":"tags"},{"content":"","date":"May 15, 2024","externalUrl":null,"permalink":"/ja/tags/%E6%8B%A1%E5%BC%B5%E3%83%A6%E3%83%BC%E3%82%AF%E3%83%AA%E3%83%83%E3%83%89/","section":"Tags","summary":"","title":"拡張ユークリッド","type":"tags"},{"content":"","date":"May 15, 2024","externalUrl":null,"permalink":"/tags/%E6%95%B0%E8%AE%BA/","section":"Tags","summary":"","title":"数论","type":"tags"},{"content":" #数论知识 # :raised_hands:一些个人理解和思考（素数的一些建议看书，书本讲得不错，虽然有些点不加以细证），尤其Extened Chinese Remider Theroy那里，是个人思考总结的，仅供参考。:raised_hands:\n数论pdf #数论知识 #贝祖定理（Bézout\u0026rsquo;s identity） #扩展欧几里得思路解析： #扩展欧几里得c++代码 #中国剩余定理（Chinese Remainder Theorem） #扩展中国剩余定理（Extened Chinese Remider Theroy） #扩展中国剩余定理代码实现： #贝祖定理（Bézout\u0026rsquo;s identity） # 用贝祖定理求出一个方程：ax + by = c 的解\nx = x0 + b/gcd(a, b) * t y = y0 - a/gcd(a, b) * t 贝祖定理的右边c改为1，就是求解模逆元的公式。所以，我才说，这个扩展欧几里得，本质上就是用贝祖定理。\n贝祖定理的证明\n#扩展欧几里得思路解析： # 其实这个扩展欧几里得，我觉得本质上就是用贝祖定理\n扩展点就是：能够实现自验证；最有用的点就是在运算过程中，找到贝租系数，从而就求解模逆元\n看一下wiki就可以了\nextgcd wiki 手稿\n#扩展欧几里得c++代码 # #include \u0026lt;iostream\u0026gt; using namespace std; int extgcd(int a, int b, int \u0026amp;x, int \u0026amp;y) { if (b == 0) { x = 1; y = 0; return a; } int d = extgcd(b, a % b, x, y); int t = x; x = y; y = t - a / b * y; return d; } /* 另一种写法，更接近数学表达 struct EuclidResult { int d, x, y; }; int extgcd(int a, int b) { int x_0 = 1, y_0 = 0, x_1 = 0, y_1 = 1; while (b) { int temp; int q = a / b; temp = a- q * b; a = b; b = temp; temp = x_0 - q * x_1; x_0 = x_1; x_1 = temp; temp = y_0 - q * y_1; y_0 = y_1; y_1 = temp; } return EuclidResult{a, x_0, y_0}; } */ 这个x和y就是贝祖系数，一组解。\n#中国剩余定理（Chinese Remainder Theorem） # 大致阐述：一个数x对于模数n1,n2,n3...nr的余数分别为r1,r2,r3...rn，且n1,n2,n3...nr两两互质，则x对于模数n1*n2*n3...*nr的余数为r1,r2,r3...rn的解。\n这个思路还是要单独写一下，因为这个思路是很重要的，在后面的扩展中国剩余定理中会用到。其次要关注一下贝祖定理的思路分析\n$$ S : \\left\\{ \\begin{array}{ll} x \\equiv r_1 \\pmod{a_1} \\\\ x \\equiv r_2 \\pmod{a_2} \\\\ % \\cdots \\\\ \\vdots \\\\ x \\equiv r_n \\pmod{a_n} \\\\ \\end{array} \\right. $$模逆元求解使用到了扩展欧几里得算法，看下方tips\n$$ N = n_1 \\cdot n_2 \\cdot n_3 \\cdots n_i \\cdots n_n \\\\ 记N_i = \\frac{N}{n_i} = n_1 \\cdot n_2 \\cdots n_{i-1} \\cdot n_{i+1} \\cdots n_n \\\\ 可得 N_i 和 n_i 互质 即 gcd(N_i, n_i) = 1 \\\\ 所以 模逆元 N_i \\cdot t_i \\equiv 1 \\pmod{n_i} \\\\ 即 a_i \\cdot N_i \\cdot t_i \\equiv a_i \\pmod{n_i} \\\\ 可得：当j \\neq i时，a_j \\cdot N_j \\cdot t_j \\equiv 0 \\pmod{n_i} \\text{因为}N_j包含了n_i \\\\ $$ $$ \\begin{align} x\u0026=a_1 \\cdot N_1 \\cdot t_1 + a_2 \\cdot N_2 \\cdot t_2 + \\cdots + a_n \\cdot N_n \\cdot t_n \\\\ \u0026= a_i \\cdot N_i \\cdot t_i + \\sum_{j \\neq i} a_j \\cdot N_j \\cdot t_j \\\\ \u0026\\equiv a_i \\pmod{n_i} \\end{align} $$ $$ 所以可以得到：x的一个解为：a_1 \\cdot t_1 \\cdot N_1 + a_2 \\cdot t_2 \\cdot N_2 + \\cdots + a_n \\cdot t_n \\cdot N_n \\\\ 记x_1 and x_2 为两个解，可得：\\\\ x_1 \\equiv a_i \\pmod{n_i} \\\\ x_2 \\equiv a_i \\pmod{n_i} \\\\ x_1 - x_2 \\equiv 0 \\pmod{n_i} \\\\ 所以可得：n_i | x_1 - x_2 \\\\ 因为任何一个i都成立，所以可得：n_1 \\cdot n_2 \\cdots n_i \\cdots n_n | x_1 - x_2 \\\\ 即：N | x_1 - x_2 \\\\ =\u003e x_2 = x_1 + kN \\\\ 则x_2 和 x_1 差kN 所以最终的解为：x=a_1 \\cdot t_1 \\cdot N_1 + a_2 \\cdot t_2 \\cdot N_2 + \\cdots + a_n \\cdot t_n \\cdot N_n + kN $$中国剩余定理\n#扩展中国剩余定理（Extened Chinese Remider Theroy） # 一些分析：我们可以看到，中国剩余定理的前提是模数两两互质，那么如果模数不互质，有一些思路提供参考：一：我们直接将这个同余方程组硬求解，利用到贝祖定理和中国剩余定理的思路；二：我们将其转化为中国剩余定理，即将模数变为两两互质，从而转换解的求解\n扩展中国剩余定理的思路思考\n扩展中国剩余定理转为中国剩余定理的思路\n#扩展中国剩余定理代码实现： # int exCRT(int n, int *a, int *m) { int M = 1; for (int i = 0; i \u0026lt; n; i++) M *= m[i]; int ans = 0; for (int i = 0; i \u0026lt; n; i++) { int Mi = M / m[i]; ans = (ans + a[i] * Mi % M * mod_inv(Mi, m[i])) % M; } return (M + ans % M) % M; } ","date":"May 15, 2024","externalUrl":null,"permalink":"/notes/math/%E6%95%B0%E8%AE%BA%E7%9F%A5%E8%AF%86/","section":"笔记","summary":"#数论知识 # :raised_hands:一些个人理解和思考（素数的一些建议看书，书本讲得不错，虽然有些点不加以细证），尤其Extened Chinese Remider Theroy那里，是个人思考总结的，仅供参考。:raised_hands:\n","title":"数论知识","type":"notes"},{"content":"","date":"May 15, 2024","externalUrl":null,"permalink":"/ja/tags/%E6%95%B0%E8%AB%96/","section":"Tags","summary":"","title":"数論","type":"tags"},{"content":"","date":"May 15, 2024","externalUrl":null,"permalink":"/ja/tags/%E4%B8%AD%E5%9B%BD%E5%89%B0%E4%BD%99%E5%AE%9A%E7%90%86/","section":"Tags","summary":"","title":"中国剰余定理","type":"tags"},{"content":"","date":"May 15, 2024","externalUrl":null,"permalink":"/tags/%E4%B8%AD%E5%9B%BD%E5%89%A9%E4%BD%99%E5%AE%9A%E7%90%86/","section":"Tags","summary":"","title":"中国剩余定理","type":"tags"},{"content":" Divide-and-Conquer Method Share # \\section{Substitution Method}\n1. 阐释 # 代换法的关键在于利用数学归纳法。首先，要猜测一个大致的上界，并结合数学归纳法进行证明。然后，验证基本条件，确保递归满足这个上界。若无法准确猜测界限，可能需要一个一个试着证明，直到达到比较准确的界限为止。\n2. 例子 # $$ T(n) = 2T\\left(\\frac{n}{2}\\right) + \\Theta(n) $$ 3. 解释 # 这个递归表达式明显属于归并排序，时间复杂度为 $\\mathcal{O}(n \\log n)$。可以尝试用 $c n \\log n$ 进行证明。首先，假设小于 n 的情况符合这个式子，然后证明当 n 时也符合。\n$$ \\begin{align} T(n) \u0026= 2T\\left(\\frac{n}{2}\\right) + \\Theta(n) \\\\ \u0026= 2 \\cdot c \\cdot \\left(\\frac{n}{2}\\right) \\cdot \\log\\left(\\frac{n}{2}\\right) + \\Theta(n) \u0026\u0026 \\text{代入猜想} \\\\ \u0026= c n \\cdot (\\log n - \\log 2) + \\Theta(n) \u0026\u0026 \\text{展开} \\\\ \u0026= c n \\cdot \\log n - c n \\cdot \\log 2 + \\Theta(n) \\\\ \u0026\\leq c n \\cdot \\log n \u0026\u0026 \\text{得到不等式} \\end{align} $$一般 n 和常数 c 都要很大，通常都趋近于无穷大。这个分析表明 n 必须大于 1，因为有对数运算。因此，我们需要验证 n=2 或 n=3 之类的基本情况。具体分析过程可以参考《算法导论》。\n4. 注意 # 在代换法中，连续使用渐进符号 $\\mathcal{O}$ 会导致忽略常数的影响。在数学递归论证中，应避免使用渐进符号，因为它们可能会导致论证不严谨。此外，代换法中必须有余项，尤其是负的余项。这是因为渐进符号可能会忽略常数的影响。\n示例：若 n 为 $\\Theta(1)$，首先 1=$\\Theta(1)$。假设小于 n 的情况都符合这个式子，则 n-1=$\\Theta(1)$。因此，n = (n-1) + 1 = $\\Theta(1)$。这种情况是不行的，因为它忽略了常数的影响。\n\\section{Recursion-Tree Method}\n递归树方法是分析其他递归问题的有力工具。您可以参考此递归树的图片了解更多。\n\\section{Master Method}\n根据 PDF 所讲内容： Case 1: 如果存在常数 $\\epsilon \u003e 0$，使得 $f(n)=O(n^{log_ba-\\epsilon})$，则 $T(n)=\\Theta(n^{log_ba})$。 Case 2: 如果存在常数 k $\\geq 0$，使得 $f(n)=\\Theta(n^{log_ba} \\cdot lg^k n)$，则 $T(n)=\\Theta(n^{log_ba} \\cdot lg^{k+1}n)$。 Case 3: 如果存在常数 $\\epsilon \u003e 0$，使得 $f(n)=\\Omega(n^{log_ba+\\epsilon})$，并且 f(n) 满足正则性条件 $ af(n/b) \\leq cf(n) $ 对于某个常数 $c \u003c 1$ 且足够大的 n，则 $T(n) = \\Theta(f(n))$。 主要思路：比较 f(n) 和 $n^{log_b(a)}$ 即驱动函数和分水岭函数，即 $T(n)=aT(n/b)+f(n)$ 表达式，就是要比较 f(n) 和 $n^{log_b(a)}$。 # 为什么会有这个 $n^{log_b(a)}$? # 因为前面的 $T(n)$ 由递归树分析得到，因此直接比较最高项。\n1. case1: 对应数学符号是 \u0026gt;，即分水岭函数 $n^{log_b(a)}$ \u0026gt; 驱动函数 f(n)。 # $f(n)=O(n^{log_b(a)-\\epsilon})$，存在常数 $\\epsilon \u003e 0$。 那么 $T(n)=\\Theta(n^{log_b(a)})$。 2. case2: 对应数学符号是 =，即分水岭函数 $n^{log_b(a)}$ = 驱动函数 f(n)。 # $f(n)=\\Theta(n^{log_b(a)}*lg^k(n))$，k 是非负数，即 k $\\geq$ 0。 $T(n)=\\Theta(n^{log_b(a)}*lg^{k+1}(n))$。 3. case3: 对应数学符号是 \u0026lt;，即分水岭函数 $n^{log_b(a)}$ \u0026lt; 驱动函数 f(n)。 # $f(n)=\\Omega(n^{log_b(a)+\\epsilon})$，存在常数 $\\epsilon \u003e 0$。 若 $af(n/b)\\leq(1-\\epsilon')*f(n)$，其中 $\\epsilon' \u003e 0$，可判断 f(n) 是递减的，并且下一层是上一层的常数倍。因此，顶层是最大的，可以简化得出 $\\Theta(f(n))$。 $T(n)=\\Theta(f(n))$。 \\section{Master Method Prove}\nLemma Definition # 令 a \u0026gt; 0 和 b \u0026gt; 1 为常数，且 f(n/b) 为在实数范围内定义的函数 $n\\geq 1$。那么递归：\n\\[ T(n) = \\begin{cases} \\Theta(1), \u0026 \\text{if } 0\\leq n\u003c1 \\\\ aT(n/b)+f(n), \u0026 \\text{if } n \\geq 1 \\end{cases} \\]其解为 $T(n) =\\Theta(n^{\\log_b a}) + \\sum\\limits_{j=0}^{\\lfloor{log_bn}\\rfloor}a^j f(n/b^j)$。\n1. case 1 prove # f(n)=O($n^{log_b(a)}$)\n$$ \\begin{align} g(n) \u0026= \\sum_{j=0}^{\\lfloor{log_b{n}}\\rfloor}a^j*f\\left(\\frac{n}{b^j}\\right) \\\\ \u0026= \\sum_{j=0}^{\\lfloor{log_b{n}}\\rfloor}a^j*O\\left(\\left(\\frac{n}{b^j}\\right)^{log_b(a)-\\epsilon}\\right) \\\\ \u0026= O\\left(n^{log_b(a)-\\epsilon}\\sum_{j=0}^{\\lfloor{log_b{n}}\\rfloor}\\frac{a^j}{(b^j)^{log_b(a)-\\epsilon}}\\right) \\\\ \u0026= O\\left(n^{log_b(a)-\\epsilon}\\sum_{j=0}^{\\lfloor{log_b{n}}\\rfloor}(b^\\epsilon)^j\\right) \\\\ \u0026 \\leq O\\left(n^{log_b(a)-\\epsilon}*\\frac{n^\\epsilon*b^\\epsilon-1}{b^\\epsilon-1}\\right) \\\\ \u0026= O\\left(n^{log_b(a)}\\right) \\end{align} $$ 2. case 2 prove # f(n)=$\\Theta(n^{log_b(a)}*lg^k(n))$\n$$ \\begin{align} \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\lg^k n - {j \\cdot \\log_b b \\cdot \\lg^k n}\\right) \\tag{eq:1} \\\\ \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\lg^k n\\right) \\quad (\\text{因为 } b \u003e 1) \\end{align} $$将上式代入公式(\\ref{eq:1})并反复应用习题3-5(c)，得\n$$ \\begin{align} g(n)\u0026= \\Theta\\left(\\sum_{j=0}^{\\lfloor \\log_b n\\rfloor} a^j \\left(\\frac{n}{b^j}\\right)^{\\log_b a} \\cdot \\lg^k \\left(\\frac{n}{b^j}\\right)\\right) \\\\ \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\sum_{j=0}^{\\lfloor \\log_b n\\rfloor} \\frac{a^j}{(b^j)^{\\log_b a}} \\cdot \\lg^k \\left(\\frac{n}{b^j}\\right)\\right) \\\\ \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\sum_{j=0}^{\\lfloor \\log_b n\\rfloor} \\left(\\frac{a}{b^{\\log_b a}}\\right)^j \\cdot \\lg^k \\left(\\frac{n}{b^j}\\right)\\right) \\\\ \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\sum_{j=0}^{\\lfloor \\log_b n\\rfloor} \\left(\\frac{a}{b^{\\log_b a}}\\right)^j \\cdot \\lg^k n\\right) \\\\ \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\sum_{j=0}^{\\lfloor \\log_b n\\rfloor} \\left(\\frac{a}{b^{\\log_b a}}\\right)^j\\right) \\cdot \\lg^k n \\\\ \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\frac{\\left(\\frac{a}{b^{\\log_b a}}\\right)^{\\lfloor \\log_b n\\rfloor + 1} - 1}{\\frac{a}{b^{\\log_b a}} - 1}\\right) \\cdot \\lg^k n \\\\ \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\frac{\\left(\\frac{a}{b^{\\log_b a}}\\right)^{\\log_b n} \\cdot \\frac{a}{b^{\\log_b a}} - 1}{\\frac{a}{b^{\\log_b a}} - 1}\\right) \\cdot \\lg^k n \\\\ \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\frac{a^{\\log_b n} - b^{\\log_b a}}{a - b}\\right) \\cdot \\lg^k n \\\\ \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\frac{a^{\\log_b n}}{a - b}\\right) \\cdot \\lg^k n \\\\ \u0026= \\Theta\\left(n^{\\log_b a} \\cdot \\frac{n^{\\log_b a}}{a - b}\\right) \\cdot \\lg^k n \\\\ \u0026= \\Theta\\left(\\frac{n^{\\log_b a + 1}}{a - b}\\right) \\cdot \\lg^k n \\\\ \u0026= \\Theta\\left(\\frac{n^{\\log_b a} \\cdot n}{a - b}\\right) \\cdot \\lg^k n \\\\ \u0026= \\Theta\\left(\\frac{n^{\\log_b a} \\cdot n}{a - b} \\cdot \\lg^k n\\right) \\\\ \u0026= \\Theta\\left(\\frac{n^{\\log_b a} \\cdot n \\cdot \\lg^k n}{a - b}\\right) \\end{align} $$","date":"April 30, 2024","externalUrl":null,"permalink":"/notes/tools/latex/","section":"笔记","summary":"Divide-and-Conquer Method Share # \\section{Substitution Method}\n1. 阐释 # 代换法的关键在于利用数学归纳法。首先，要猜测一个大致的上界，并结合数学归纳法进行证明。然后，验证基本条件，确保递归满足这个上界。若无法准确猜测界限，可能需要一个一个试着证明，直到达到比较准确的界限为止。\n","title":"latex","type":"notes"},{"content":"","date":"April 30, 2024","externalUrl":null,"permalink":"/tags/latex/","section":"Tags","summary":"","title":"LaTeX","type":"tags"},{"content":"","date":"April 30, 2024","externalUrl":null,"permalink":"/categories/technology/","section":"Categories","summary":"","title":"Technology","type":"categories"},{"content":"","date":"April 30, 2024","externalUrl":null,"permalink":"/tags/%E4%BB%A3%E6%8D%A2%E6%B3%95/","section":"Tags","summary":"","title":"代换法","type":"tags"},{"content":"","date":"April 30, 2024","externalUrl":null,"permalink":"/tags/%E6%95%B0%E5%AD%A6%E5%BD%92%E7%BA%B3%E6%B3%95/","section":"Tags","summary":"","title":"数学归纳法","type":"tags"},{"content":"","date":"April 28, 2024","externalUrl":null,"permalink":"/tags/devops/","section":"Tags","summary":"","title":"DevOps","type":"tags"},{"content":"","date":"April 28, 2024","externalUrl":null,"permalink":"/tags/docker/","section":"Tags","summary":"","title":"Docker","type":"tags"},{"content":" 注意这些全都要根据自己所用系统是什么，ubuntu和debian这些可能都有些许出入，架构问题等 # 更新，安装依赖项 # sudo apt update sudo apt install apt-transport-https ca-certificates curl software-properties-common\n添加docker官方gpg密钥 # curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -\n根据自己来设定，因为这个可能debian已经不用apt-key了，已经改用gpg这些了 # 添加docker仓库 # sudo add-apt-repository \u0026ldquo;deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable\u0026rdquo;\n安装docker-ce # sudo apt update sudo apt install docker-ce\n验证是否安装 # sudo systemctl status docker\n创建gitlab目录来存放数据这些 # sudo mkdir -p /srv/gitlab/config /srv/gitlab/data /srv/gitlab/logs\n运行容器 # sudo docker run \u0026ndash;detach \u0026ndash;hostname ubuntu \u0026ndash;publish 443:443 \u0026ndash;publish 80:80 \u0026ndash;publish 22:22 \u0026ndash;name gitlab \u0026ndash;restart always \u0026ndash;volume /srv/gitlab/config:/etc/gitlab \u0026ndash;volume /srv/gitlab/logs:/var/log/gitlab \u0026ndash;volume /srv/gitlab/data:/var/opt/gitlab \u0026ndash;env GITLAB_ROOT_PASSWORD=LJB626901 yrzr/gitlab-ce-arm64v8:latest\n最后一行就是要根据自己架构来获取镜像了，arm架构和amd都不一样的，Your hostname要改 # 有时要注意这些端口是否被占用 # netstat -tuln | grep 80\n查询所有的镜像 # docker ps -a\n此时会看到一个 gitlab-ce 的容器是没有启动的状态 # 删除 gitlab-ce 容器 # docker rm gitlab-ce\n再次运行时，删除开放的 22 端口 # docker run \u0026ndash;detach \u0026ndash;restart always \u0026ndash;name gitlab \u0026ndash;privileged \u0026ndash;memory 4096M \u0026ndash;publish 80:80 \u0026ndash;publish 443:443 \u0026ndash;hostname ubuntu \u0026ndash;env GITLAB_OMNIBUS_CONFIG=\u0026quot; nginx[\u0026lsquo;redirect_http_to_https\u0026rsquo;] = true; \u0026ldquo;\n\u0026ndash;volume /srv/gitlab/config:/etc/gitlab \u0026ndash;volume /srv/gitlab/logs:/var/log/gitlab \u0026ndash;volume /srv/gitlab/data:/var/opt/gitlab \u0026ndash;env GITLAB_ROOT_PASSWORD=LJB626901 yrzr/gitlab-ce-arm64v8:latest\n","date":"April 28, 2024","externalUrl":null,"permalink":"/notes/tools/docker-gitlab-md/","section":"笔记","summary":"注意这些全都要根据自己所用系统是什么，ubuntu和debian这些可能都有些许出入，架构问题等 # 更新，安装依赖项 # sudo apt update sudo apt install apt-transport-https ca-certificates curl software-properties-common\n","title":"docker_gitlab.md","type":"notes"},{"content":"","date":"April 28, 2024","externalUrl":null,"permalink":"/tags/gitlab/","section":"Tags","summary":"","title":"GitLab","type":"tags"},{"content":"","date":"April 28, 2024","externalUrl":null,"permalink":"/tags/%E9%83%A8%E7%BD%B2/","section":"Tags","summary":"","title":"部署","type":"tags"},{"content":"","date":"April 28, 2024","externalUrl":null,"permalink":"/tags/%E5%AE%B9%E5%99%A8/","section":"Tags","summary":"","title":"容器","type":"tags"},{"content":" docker learn # table of contents # docker learn table of contents 虚拟机和docker docker和容器 docker讲解 docker_desktop介绍 虚拟机和docker # 虚拟机和容器都是一种虚拟化技术，都是能实现一个操作系统上运行一些别的系统，运行一些安装包什么的\n虚拟机和docker的区别：\n虚拟机是在主机操作系统上借助vwmare这些实现利用本地的硬件设施完成将本地服务器拆分为逻辑服务器，以此来实现可以运行多个系统。但是，这个就是会占用到很多本地硬件设施，会导致一些资源利用问题，比如：本来，我就只是想用一些软件包就行的，但非要安装os系统才行等。而且，虚拟机得启动缓慢，性能不是很好。\ndocker就是一个容器化平台，可以实现通过“鲸鱼”运载“集装箱”，以此我只是需要安装镜像就可以利用到我需要的容器即软件包等东西。占用的资源少，启动快，并且相比虚拟机可以安装很多个，只要看你想安装什么镜像，用到什么容器即可。\n图片连接解释\ndocker和容器 # 容器也是一种虚拟化技术，从上面的描述和比较就可以知道。而docker就是一种使用到容器虚拟化技术的一个平台，所以docker就是容器化平台。\n这个容器化平台就是有了很多功能和设置，使得我们更好利用到这个虚拟化技术，而且其实这个就是图形化界面，你直接用命令行安装docker也行的。\ndocker讲解 # docker包含镜像，容器等概念。\n镜像就是从dockerhub上下载的，你也可以自己编译吧，应该。这个dockerhub就是类似github的东西，这里有一些大佬弄出来的镜像。他相当于c++的类，这个镜像是自定义了一些东西。\n容器就是我们使用docker命令弄了出来的，然后是通过镜像这个类弄出来的一个实例，他是一个独立的，轻量级的可执行软件包，包含：代码，环境，系统工具等\n图片链接解释\ndocker_desktop介绍 # docker desktop里的container是指容器，就是镜像的一个实例就是你自己的一个东西了\nimage就是镜像，就是你自己从docker hub上下载下来，用于你自己搞东西的一个模板\nbuilds就是通过使用Docker Desktop中的Build功能，你可以方便地在本地开发环境中构建和测试Docker镜像，然后可以将这些镜像推送到Docker Hub或者其他Docker仓库中，以便在其他地方部署和使用。\nvolumes就是你本来用docker搞东西，这些文件什么的，就是一个类似虚拟化的东西，保存时间不久的，所以当你想保存的时候，你要将其放到本地主机的硬盘上，所以你就要用到volume了，将其弄成卷放到本地上。\n很好的视频讲解\n","date":"April 28, 2024","externalUrl":null,"permalink":"/notes/tools/docker/","section":"笔记","summary":"docker learn # table of contents # docker learn table of contents 虚拟机和docker docker和容器 docker讲解 docker_desktop介绍 虚拟机和docker # 虚拟机和容器都是一种虚拟化技术，都是能实现一个操作系统上运行一些别的系统，运行一些安装包什么的\n","title":"docker","type":"notes"},{"content":"","date":"April 28, 2024","externalUrl":null,"permalink":"/tags/%E8%99%9A%E6%8B%9F%E5%8C%96/","section":"Tags","summary":"","title":"虚拟化","type":"tags"}]