<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>EAimTY&#x27;s Blog</title>
      <link>https://www.eaimty.com</link>
      <description></description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://www.eaimty.com/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Sat, 22 Feb 2025 00:00:00 +0000</lastBuildDate>
      <item>
          <title>重启 TUIC 的开发，但我将不再是“作者”</title>
          <pubDate>Sat, 22 Feb 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2025/restart-developing-tuic-but-not-as-the-author/</link>
          <guid>https://www.eaimty.com/2025/restart-developing-tuic-but-not-as-the-author/</guid>
          <description>&lt;p&gt;一个开源项目如何才能长久生存？我一直在思考这个问题。&lt;&#x2F;p&gt;
&lt;p&gt;不久前，&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Itsusinn&quot;&gt;@iHsin&lt;&#x2F;a&gt; 找到了我。他一直在维护一个 TUIC 的 fork。看到时至今日社区中的一些人还在持续做贡献，我感到很高兴，但也意识到我曾经做出的停止开发的决定可能并不是最好的选择。&lt;&#x2F;p&gt;
&lt;p&gt;为什么当初 TUIC 没有发展出一个活跃且气氛良好的社区？我认为我自己负有很大的责任。我曾希望更多的开发者能够加入 TUIC 的开发，但我没有认真考虑过，贡献者能从中得到什么。作为 TUIC 的“作者”，我主导了所有的设计和开发工作。哪些建议被采纳，哪些 PR 被拒绝，往往都是根据我的个人喜好来决定的。更糟糕的是，我经常很久才回复它们。&lt;&#x2F;p&gt;
&lt;p&gt;在这种情况下，新贡献者能够获得什么呢？他们几乎无法得到物质上的回报，因为他们难以做出大量的代码贡献，也就无法把“我是 TUIC 的维护者之一”写进简历，从而获得更好的工作机会。情感上的回报也很有限：即便经过艰难的沟通，贡献被接受，他们的小改动也很难引起他人的关注和认可。&lt;&#x2F;p&gt;
&lt;p&gt;人类的理性行为，归根结底是对物质价值和情感价值的交换。物质价值容易量化，而如果我们能够量化情感价值，就能更好地理解所有理性行为的根本动机：追求最大化的物质和情感回报。开源项目的维护当然也不例外。&lt;&#x2F;p&gt;
&lt;p&gt;我的第一份工作也是在很大程度上靠着 TUIC 的星星数拿到的。尽管我没有从中获得直接的捐赠或盈利，TUIC 也为我带来了很多机会。我希望更多的开发者能够获得类似的机会。许多优秀的开发者缺少的，仅仅是一个能展示自己的平台，我希望 TUIC 能成为这样的平台。&lt;&#x2F;p&gt;
&lt;p&gt;另外，TUIC 作为代理协议的潜力依然巨大。作为最早实现 0-RTT 的代理协议之一，同时保持极简设计，我相信在此基础上，未来能够更容易地进行修改，使其适应更加复杂的网络环境。&lt;&#x2F;p&gt;
&lt;p&gt;那么，如何才能实现这样的目标呢？我认为最关键的一点是让 TUIC 的维护不再是一个人的“独裁”。更多人的声音应该被听到，更多人的贡献应该被接受。我不再是 TUIC 的“唯一作者”，而是众多贡献者中的一员。&lt;&#x2F;p&gt;
&lt;p&gt;具体来说，我计划采取下面的措施：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;将项目仓库转移到组织 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tuic-protocol&quot;&gt;TUIC Protocol&lt;&#x2F;a&gt; 下&lt;&#x2F;strong&gt;，使其不再是个人项目。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;标准化协议，规范化协议细节&lt;&#x2F;strong&gt;，确保同一协议版本内不需要对已有实现做任何修改。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;删除仓库内的协议实现，只保留协议规范和文档&lt;&#x2F;strong&gt;，集中精力在协议本身的改进上。既然已经有更加专业的人实现了协议，我们就没有必要重复这个工作。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;将用户引流到各个实现&lt;&#x2F;strong&gt;，让更多人的作品被看到和认可。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;规范化贡献流程&lt;&#x2F;strong&gt;，所有修改需先经过公开讨论，充分收集社区的意见，再共同做决策。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;我记得有人担心 TUIC 会“硬分叉”。我认为这样的担忧是多余的。作为一个开源项目，如果它能够激发更多人做出更好的作品，那它的价值就已经实现了。竞争者的出现不一定是坏事，它能让我们看到更多的可能性，启发彼此，相互贡献，让整个领域更加繁荣。&lt;&#x2F;p&gt;
&lt;p&gt;TUIC 不再有所谓“官方”的实现，这个想法可能让人觉得奇怪，但我相信这样做的利远大于弊。之前的经验告诉我们，作为一个定义协议的项目，如果大家的注意力集中在某个具体实现上，很多精力就会被浪费。我们应该将重心放在协议本身的维护和优化上。&lt;&#x2F;p&gt;
&lt;p&gt;我不知道这些改变是否会成功，但我更不希望继续维持现状，让更多人失去机会。我希望 TUIC 能成为一个更加开放、更加活跃的社区，让更多人从中获得价值。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>基于荷尔蒙开发的开源项目</title>
          <pubDate>Fri, 03 Nov 2023 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2023/opensource-project-based-on-hormone/</link>
          <guid>https://www.eaimty.com/2023/opensource-project-based-on-hormone/</guid>
          <description>&lt;p&gt;TUIC 终究又是一个失败的开源项目，至少现在看来如此。一方面是社区上的失败，另一方面是我个人的失败。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;kai-yuan-xiang-mu-dao-di-shi-shi-yao&quot;&gt;开源项目到底是什么&lt;&#x2F;h2&gt;
&lt;p&gt;究其根本，一个没有商业化的开源项目，本质上就是开发者的技术热情。换句话讲，这样的开源项目就是其开发者荷尔蒙在成果上的体现。&lt;&#x2F;p&gt;
&lt;p&gt;人类的本性永远是自私的。为什么我们会将自己辛苦思考并创作的结果，不求任何回报地分享给他人？&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;对技术的热爱&lt;&#x2F;li&gt;
&lt;li&gt;分享欲&lt;&#x2F;li&gt;
&lt;li&gt;成就感&lt;&#x2F;li&gt;
&lt;li&gt;“我要让世界变得更好”&lt;&#x2F;li&gt;
&lt;li&gt;捐赠收入&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;可惜，这些都不足以支撑一个开源项目长期发展。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wo-wei-shi-yao-yao-kai-fa-qie-kai-yuan-tuic&quot;&gt;我为什么要开发且开源 TUIC&lt;&#x2F;h2&gt;
&lt;p&gt;在我脑中出现这个想法，以及开发的早期，我觉得我的动力主要来源于技术热情。基于这方面的热情，我完成了 TUIC 最初的几个版本，主要是自己使用。后来，由于我的分享欲，这个项目被发布了出去，并且稍微做了一些宣传。开源文化类似一种宗教，我长期被开源文化影响，因此从最开始就没有想过以开源以外的方式发布。没想到这个项目的热度很高，大量 star 与用户反馈让我意识到这个项目可能真的能解决一些人的问题，此时成就感与一些所谓的“责任感”开始出现了，这些是我在发布后一段时间继续维护这个项目的主要动力。&lt;&#x2F;p&gt;
&lt;p&gt;但是，激素的作用永远是短暂的。当荷尔蒙褪去，留给我们的最终会是什么呢？&lt;&#x2F;p&gt;
&lt;h2 id=&quot;kai-yuan-ruan-jian-de-zuo-zhe-ye-shi-pu-tong-ren&quot;&gt;开源软件的作者也是普通人&lt;&#x2F;h2&gt;
&lt;p&gt;我们开源了自己的项目，但并不意味着我们有继续维护它的义务。开源软件并非是一个个独立的实体，其背后的开发者们也是普通人，我们也有自己的工作与生活。我们不依靠这些软件获取经济利益，维护这些软件还给我们增加了很多负担。拿 TUIC 来说，维护这样一个软件甚至会给我带来了人身安全的威胁。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ling-ren-ju-sang-de-she-qu&quot;&gt;令人沮丧的社区&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;she-qu-cun-zai-de-yi-yi&quot;&gt;社区存在的意义&lt;&#x2F;h3&gt;
&lt;p&gt;我们将源码贡献给社区，并非是想要让社区“监督”我们继续开发，而是希望有更多人参与进来，共同改进项目。&lt;&#x2F;p&gt;
&lt;p&gt;这一点其实 TUIC 的社区之前已经在一定程度上做到了这点。不少开发者参与到了这个项目的开发中，提出了一些很好的建议，贡献了一些很有用的修改，虽然其中一些由于我对这个项目的定位看法，以及由于后期不再有维护的动力，而没有被接纳。这里特别要感谢 Clash.Meta 与 sing-box 的维护者们，他们重新实现了 TUIC 的协议，使得社区有了更多的选择与竞争，这些不论对用户还是对项目本身都有好处，也符合我一直以来的定位：我深知自己能力不足，因此我只做最基本的协议设计与最小化的实现，希望将剩下的留给更加有经验并且有完善基础设施的人。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;she-qu-qi-fen&quot;&gt;社区气氛&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;FzDUFCnWYAIUEmJ.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;你不看文档不看 issue，留下一句“怎么不能用，什么垃圾软件”&lt;&#x2F;li&gt;
&lt;li&gt;你扫了一眼不知从哪里找来的所谓项目介绍，留下一句“就这？这不就是 XXX 吗？”&lt;&#x2F;li&gt;
&lt;li&gt;你提了一大堆功能需求，过了一段时间没看到你提的需求被实现，留下一句“怎么还没实现？这个软件太垃圾了”&lt;&#x2F;li&gt;
&lt;li&gt;你不知怎么遇到的 bug，不附带 log、config、复现步骤就提了一个 issue，过了一段时间没有被解决，留下一句“这作者脾气大还没有责任心，根本不管项目”&lt;&#x2F;li&gt;
&lt;li&gt;你提了一个 issue，过了一段时间没看到回复，留下一句“这个软件太垃圾了，根本没人维护”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;在你们眼里，留下一句话只需要动动手指敲敲键盘，不论发些什么，对你们都不会有什么损失。但是，开源项目的开发者们凭什么要帮你们解决这些问题？任何软件都会存在问题，更何况一个不靠利益运作、纯粹靠兴趣驱动的项目？&lt;&#x2F;p&gt;
&lt;p&gt;我们靠着热情开始并维护着这些项目，而热情是有限的，你们却在无时无刻消耗着这些热情。&lt;&#x2F;p&gt;
&lt;p&gt;现在的互联网，特别是简体中文网络上，人们连最基本的心平气和、温文尔雅的交流都做不到，更不要说换位思考的能力了。我不建立所谓的“用户群租”就是因为我深知，由庞大的、层次参差不齐的用户群组成的社区，一定是硝烟弥漫的。我所接触到的所谓的“代理娱乐圈”，就连开发者之间也无法做到相互尊重，心平气和地交流，更何况是用户呢？不明原理就把某些东西捧上天或骂到一文不值，这种行为太常见了。&lt;&#x2F;p&gt;
&lt;p&gt;我并不是说其它“圈子”就没有这种情况，也不是只有简体中文用户会做这种事，只是目前简体中文互联网的风气如此，并且很遗憾这个项目的主要受众就是简体中文用户，所以我才会有这样的感受。&lt;&#x2F;p&gt;
&lt;p&gt;这点我在很久之前就考虑到过。我希望以“提高门槛”的方式来减少这些问题的发生。项目文档用英文书写，配置尽可能暴露底层技术细节，这些都是为了避免让那些层次不足的人接触到这个项目。我最早的想法是这个项目的使用仅限于懂技术的人之间，所以除了发布时发过一篇文章，我再没有自己宣传过这个项目。然而没过多久各种一键脚本就开始满天飞，不只是 issue 中，连我的个人社交平台私信了都充斥着各种莫名其妙的问题：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;x8oepbNDMTanZcEj86.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;也有些人开始用 TUIC 来盈利，直接方式与间接方式都有。当然，我完全不反感我的开源项目被用于盈利，毕竟是开源。但问题是，某些人在根本不懂技术原理的情况下就开始误导他人，这是我不能接受的。&lt;&#x2F;p&gt;
&lt;p&gt;或许这也是我自己的问题吧，我对大众的期望太高了。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wo-yuan-ben-de-qi-dai-yu-ji-hua&quot;&gt;我原本的期待与计划&lt;&#x2F;h2&gt;
&lt;p&gt;在这两年的时间里，我看着这个项目慢慢变大，社区慢慢成熟。关于这个项目未来如何发展我想过很多。这个项目过多地被关联在中国网民的特殊需求上。我最初就意识到这个项目的开发是有很大风险的，因此我希望慢慢将重心从“实现”转移到“理论”上来，让协议更加泛化，而不是只被中国或伊朗网民拿去用。&lt;&#x2F;p&gt;
&lt;p&gt;我一直都认为精简就是美。我从项目初期就提到过很多次，我知道我的能力不足，就算我实现一个大而全、包含所有功能的程序，大概率也会烂到无法使用，况且我的时间与精力也难以支撑我开发这样庞大的工程。市面上有那么多实现良好的替代，为什么一定要我来做，而不是让更有经验的人来做呢？然而自始至终，不论我在 README 中写得多清楚，在 issue 中提过多少次，永远有源源不断的人来要求增加与协议核心无关的内容。&lt;&#x2F;p&gt;
&lt;p&gt;我本打算按照原本的计划继续改进这个项目，可惜我现在既没有动力，也没有能力了。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-wo-zi-shen-de-wen-ti-dao-zhi-liao-xiang-mu-de-shi-bai&quot;&gt;是我自身的问题导致了项目的失败&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;我对大众的期望过高，没有做好心理准备迎接各种各样的奇怪理论&lt;&#x2F;li&gt;
&lt;li&gt;我的能力不足，无法做到既有良好的设计，又有良好的实现&lt;&#x2F;li&gt;
&lt;li&gt;我“精简就是美”的理念与大众不符&lt;&#x2F;li&gt;
&lt;li&gt;我的时间与精力不足，没法做到某些大佬不吃不喝做开发的水平&lt;&#x2F;li&gt;
&lt;li&gt;我人在国内，无法承担继续开发的风险&lt;&#x2F;li&gt;
&lt;li&gt;我不需要你们的捐赠，也没有胆量去商业化这个项目，也就没有更多动力维护&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;可能很多人认为开源项目和项目作者可以划等号？&lt;&#x2F;p&gt;
&lt;p&gt;我也有自己的生活与工作，我也在各种破事中挣扎，我为什么要为了一个个素不相识的人牺牲自己？&lt;&#x2F;p&gt;
&lt;p&gt;从这个项目的最开始，我就没有建立过任何用户群组，没有同意任何人与我的合作邀请，也没有接受过任何捐赠。我不需要你们的钱，我早就能将自己养活得很好了。这个项目完全是我兴趣与热情的体现，换句话讲就是荷尔蒙的作用。一直以来我都尽量保持最大程度的低调，就是为了不让这个项目影响我的生活。&lt;&#x2F;p&gt;
&lt;p&gt;因此，这个项目的失败大概是必然的吧。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ke-xiao-de-wang-min-ke-bei-de-kai-fa-zhe&quot;&gt;可笑的网民，可悲的开发者&lt;&#x2F;h2&gt;
&lt;p&gt;最近几天，clash for windows 停止了更新，clash core 的仓库也被作者删除了。这种事之前也发生过，但这次的后续真得既可笑又可悲。&lt;&#x2F;p&gt;
&lt;p&gt;Clash 倒下后，不知网民们出于何种想法，将这件事的热度扩散到了微博上，甚至达到了微博热搜。你们真的有考虑过开发者吗？这件事的影响已远远超过了可控范围，有多少双眼睛在盯着微博热搜？其中又有多少不怀好意呢？&lt;&#x2F;p&gt;
&lt;p&gt;大多数网名大概都是以吃瓜的角度在看这件事。没有人真的在为开发者着想，这真的非常可悲。你们如何证明 clash 作者以外的开源代理开发者们没有因为此次事件被特殊关注，人身安全受到威胁呢？&lt;&#x2F;p&gt;
&lt;p&gt;我还是将大众想得太善良了。中国人的本性就是喜欢看别人的悲剧，除非火烧到自己，否则根本不会有觉悟。我觉得我不值得将自己的任何精力贡献给这样的群体，更何况我的人身安全也受到威胁。&lt;&#x2F;p&gt;
&lt;p&gt;我特别反感人们将开源开发者停止开发叫做“跑路”。我觉得这是一个偏贬义的词，本来有责任的人突然消失了才是“跑路”，而不是不求回报的开源开发者们。我不知道现在简体中文互联网用户的中文水平为何如此可笑。&lt;&#x2F;p&gt;
&lt;p&gt;就在刚刚，我又看到了一张非常可笑的图片：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;p9Ba47pdkjgnQ8ZXrh.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这里是知乎吗？与世界分享你刚编的故事？&lt;&#x2F;p&gt;
&lt;p&gt;这也又一次印证了我的观点：就算你抱着再善良的心为大家不求回报地贡献，现在的环境只会让你失望透顶。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;you-yuan-zai-jian&quot;&gt;有缘再见&lt;&#x2F;h2&gt;
&lt;p&gt;我的想法大致就是这样了。TUIC 停止更新是因为，这个项目在无时无刻为我带来压力、内耗与安全隐患，并且我现在觉得这个项目的受众群体不值得我为之付出。&lt;&#x2F;p&gt;
&lt;p&gt;希望某天我能在什么地方无忧无虑地写任何我想写的代码，而不是为了应付各种各样的问题而烦恼。或许当我再次燃起热情时还会回来。但是现在，我们有缘再见吧。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>近况说明与新的博客</title>
          <pubDate>Sat, 24 Jun 2023 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2023/recent-updates-and-new-blog/</link>
          <guid>https://www.eaimty.com/2023/recent-updates-and-new-blog/</guid>
          <description>&lt;p&gt;已经一年多没有再更新了。其间发生了很多事情，也有很多想法，但是我没有记录下来。这是一个很大的遗憾。&lt;&#x2F;p&gt;
&lt;p&gt;首先，关于本博客。&lt;&#x2F;p&gt;
&lt;p&gt;这些年来，我对一些事物的理解发生了很大的变化。对于这个记录我多年来技术与想法的记事本，我觉得其最有价值的是其中的内容，而非是它的形式，或是它本身。所以，我决定将它迁移到一个新的平台上。&lt;&#x2F;p&gt;
&lt;p&gt;从 2015 年到最近，我一直在用 &lt;a href=&quot;https:&#x2F;&#x2F;typecho.org&#x2F;&quot;&gt;Typecho&lt;&#x2F;a&gt; 作为博客引擎。Typecho 是一个非常棒的项目。对我而言，它还是很特殊的意义，甚至可以说，是它引导我开始在技术方面探索。在这些年间，为了装扮我的博客，我还写过几个主题，这也是我开始编程的契机。之前，我虽对技术有很大的兴趣，但做的大多数是所谓的“折腾”：尝试各种的电子设备、观看各种技术类文字与视频、搭建各种各样的应用与服务，而不是“写程序”。直到开始维护博客，我才开始真正写一些代码。&lt;&#x2F;p&gt;
&lt;p&gt;然而，由于 Typecho 已经是一个具有十多年历史的项目，虽然一直在更新，但其所依赖的技术，如 PHP、关系型数据库，在我眼里可能已经不是最好的选择了。我并不是在说 Typecho 不是一个好项目，而只是说它不再适合我。作为一个开源项目，它能坚持这么多年的更新，且建立了一个庞大的社区，这本身就很了不起。它对我也产生了很强的影响。我现在作为一个开源软件的维护者，更能理解这其中的困难。因此，我对 Typecho 的开发者们非常感谢与敬佩。&lt;&#x2F;p&gt;
&lt;p&gt;假如我某天对技术失去了热情，或放弃了现在身份与认同，或是因为某些原因倒下，我希望我的博客能够继续存在，而不是因为缺少维护而彻底消失。为了做到这一点，我选择将博客重新设计为全静态，仅保留最基本的功能，迁移至 GitHub Pages。同时，我也能够更加专注于博客的内容，而不是维护博客本身。&lt;&#x2F;p&gt;
&lt;p&gt;新的博客页面非常丑，可能是你见过最丑的博客。我不打算再花时间去装扮它了，因为这个博客最有价值的是它的内容。如果你因为它的丑陋而不愿意阅读，走好不送，因为我觉得你大概也不会从我的内容中获得什么价值。如果你想要更加统一的阅读体验，可以使用 &lt;a href=&quot;https:&#x2F;&#x2F;www.eaimty.com&#x2F;rss.xml&quot;&gt;RSS&lt;&#x2F;a&gt; 订阅。&lt;&#x2F;p&gt;
&lt;p&gt;描述一下新博客的技术细节。我使用静态网站生成器 &lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;zola&lt;&#x2F;a&gt; 构造网站的主体。为何不用 Hexo、Hugo 之类更加成熟的方案？主要原因是我不需要复杂的功能，只需要最基础的解析 markdown 与应用模板。zola 完全符合我的要求，我也就没必要去为此学习更复杂的生成器了。整个网站没有任何外部依赖的 CSS，除了评论部分，没有一行 Javascript。评论部分是完全由 JS 实现的，就算浏览器不加载 JS，对内容的显示也不会产生任何影响，而评论部分完全不会显示。由于所有 CSS 与 JS 都被内嵌到了网页中，理论上讲，可以将任意一个页面另存为至本地，在本地打开（由于浏览器重定向的限制，URL schemes 需要是 http，例如用 Python 简单开启一个 http server），也能够发表评论，且其他人可以看到。由于是静态网页，无法在后端做评论的存储。我的博客到现在已经有几百条评论，直接全部放弃掉非常可惜。因此我设计了一个使用 GitHub Issues 作为后端的评论系统。由于 CORS 的限制，我无法直接在前端拿到用户的 GitHub Access Token，因此简单在 Cloudflare Worker 上写了一个用于 proxy GitHub App auth 和 issue comment 的 REST API，方便在在前端调用，还能降低前端复杂度。在前端存储了之前已有的评论，与从 GitHub 上获取到的评论一并处理并显示，从而在不舍弃原有评论的情况下使用新的评论系统。&lt;&#x2F;p&gt;
&lt;p&gt;之前这个博客的大多数内容都是关于技术的，但我并不想将它定位为一个技术博客。之后我会更多地记录一些我的想法。另外，由于我非常讨厌目前的简体中文网络环境，以后我不会再写中文的技术类文章了。&lt;&#x2F;p&gt;
&lt;p&gt;最后来说说我自己。&lt;&#x2F;p&gt;
&lt;p&gt;过去的一年中，我从一个纯粹的学生，变成了一半是学生、一半是社畜，最近又变成了一个社畜完全体。截至目前，我不太成功也不太快乐的学生生涯已经结束了。在这一年里，我经历了很多奇特的事情。由于人际关系、兴趣、热情、就业、工作各种等各样的原因，这一年中我的精神状态都比较差。一直以来，我都认为我是一个理性主导、忙于自我优化、较少社交、情感波动很少的人。在性格、消极完美主义倾向、大量摄入负面信息、唯结果论的影响下，我觉得过去的这些年我错过了很多东西，而且有些东西根本无法补救。回想过去，我觉得后悔的事情变得越来越多。我本可以更普通一些，像多数人一样，想他们想的事，做他们做的事。一直以来我对（身边的）主流观念、文化嗤之以鼻，但现在回过头来发现，这可能只会给我带来更多的烦恼。如果我能够像多数人一样，不去想那么多东西，或许还会活得更快乐。&lt;&#x2F;p&gt;
&lt;p&gt;就在写这篇文章的时间点，我正式成为了一个数字游民。有些人会向往数字游民的生活。但是我觉得，这种生活并非是我的选择，而是我别无选择。究其根本，我所缺少的是归属感与安全感。虽然不至于无法融入社会，但我觉得我由于长期以来的思想、生活与习惯，我与大多数人的距离越来越远。就算回家，我出生的地方，由于家庭环境，我对“家”的概念也没有太多的感情。我不知道我能够在哪里找到归属感与安全感，也不知道我能够在哪里找到我想要的生活。与其说是数字游民，不如说是流浪。这么多年来，我第一次觉得我失去了目标。不知道以后我回在何时何处做何事。&lt;&#x2F;p&gt;
&lt;p&gt;希望我可以在未来找到我想要的生活。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>分布式状态机共识协议 Copilot——论文阅读：Tolerating Slowdowns in Replicated State Machines using Copilots</title>
          <pubDate>Sat, 07 May 2022 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2022/tolerating-slowdowns-in-replicated-state-machines-using-copilots/</link>
          <guid>https://www.eaimty.com/2022/tolerating-slowdowns-in-replicated-state-machines-using-copilots/</guid>
          <description>&lt;p&gt;论文原文：&lt;a href=&quot;https:&#x2F;&#x2F;www.usenix.org&#x2F;conference&#x2F;osdi20&#x2F;presentation&#x2F;ngo&quot;&gt;Tolerating Slowdowns in Replicated State Machines using Copilots&lt;&#x2F;a&gt;
以下内容是对这篇论文的阅读总结，以及部分重要章节（§3 Design、§5 Optimizations）的翻译。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;h1 id=&quot;qian-yan&quot;&gt;前言&lt;&#x2F;h1&gt;
&lt;p&gt;现有的分布式状态机共识协议，不论其是何种流派，何种实现，都在模型中将节点状态抽象为仅有“在线”与“下线”两种情况。然而在实践上，节点状态并非只有这两种互斥的状态——节点可能因配置错误，节点侧网络问题，部分硬件出现问题，垃圾回收事件，以及其它很多原因而导致响应速度变慢（slowdown）。由于现有的共识算法没有很好地处理这一状态，系统内一个节点 slowdown 可能导致整个系统的响应速度变慢。Copilot 协议旨在解决这一问题：它可以在任何情况下可以容忍系统中 1 个节点发生 slowdown（&lt;code&gt;1-slowdown-tolerant&lt;&#x2F;code&gt;）。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ding-yi-slowdown&quot;&gt;定义 slowdown&lt;&#x2F;h2&gt;
&lt;p&gt;要想解决这一问题，首先要对 slowdown 进行明确的定义。&lt;&#x2F;p&gt;
&lt;p&gt;首先要定义一个节点的处理速度。一个节点从收到一条消息开始，到其处理这条消息完毕，将回复送出为止，这段时间的长短就是一个节点的处理速度。这段时间不包含消息在网络链路上的传输时间，仅包含消息在节点本机处理所需的时间。假设一个节点处理一条消息的典型时间为 1ms，而设置超时阈值 &lt;code&gt;t&lt;&#x2F;code&gt; = 10ms，那么如果该节点处理一条消息花费了大于 10ms，就说明该节点出现了 slowdown。出错（failed）的节点一定是 slowdown 的，但 slowdown 的节点不一定 failed——它只是响应速度变慢，而不是停止响应。&lt;&#x2F;p&gt;
&lt;p&gt;下面定义 &lt;code&gt;s-slowdown-tolerance&lt;&#x2F;code&gt;。当一个系统中有 s 个节点发生 slowdown 时，系统仍能正常地提供服务，那么这个系统就是 &lt;code&gt;s-slowdown-tolerance&lt;&#x2F;code&gt;。假设一个分布式状态机系统有节点集 &lt;code&gt;{r1, ..., rs, ..., rn}&lt;&#x2F;code&gt;，且根据目前的 “slowdown” 定义排序，其中 &lt;code&gt;{r1, ..., rs}&lt;&#x2F;code&gt; 是最慢的节点。定义当前整个系统的处理速度为 &lt;code&gt;T&lt;&#x2F;code&gt;，并定义 &lt;code&gt;T&#x27;&lt;&#x2F;code&gt; 为将系统中节点 &lt;code&gt;{r1, ..., rs}&lt;&#x2F;code&gt; 全部替换为 &lt;code&gt;rs + 1&lt;&#x2F;code&gt; 时整个系统的处理速度。如果 &lt;code&gt;T&lt;&#x2F;code&gt; 与 &lt;code&gt;T&#x27;&lt;&#x2F;code&gt; 的差值接近 0，那么该系统就是 &lt;code&gt;s-slowdown-tolerance&lt;&#x2F;code&gt;。换句话讲，就是系统中出现 s 个节点 slowdown 不会影响整个系统的处理速度。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wei-shi-yao-xian-you-de-gong-shi-xie-yi-wu-fa-rong-ren-slowdown&quot;&gt;为什么现有的共识协议无法容忍 slowdown&lt;&#x2F;h2&gt;
&lt;p&gt;一个分布式状态机系统在处理一条客户端指令时，如果在处理过程中的任意时间点，只有一条路径可走，那么该系统就存在“单点故障”的可能性——在这点处负责处理的节点发生 slowdown 会影响整个节点的处理速度。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;pOpCbVg.png&quot; alt=&quot;Multi-Paxos&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在 Multi-Paxos 系统中，所有客户端指令会通过 leader 处理，而 leader 可能会 slowdown 甚至 failed，此时系统的整体处理速度就会降低。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;nQE4SmF.png&quot; alt=&quot;EPaxos&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在 EPaxos 系统中，客户端指令会通过与之通信的节点处理，如果此节点 slowdown，系统的整体处理速度就会降低。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;copilot-ru-he-chu-li-slowdown&quot;&gt;Copilot 如何处理 slowdown&lt;&#x2F;h2&gt;
&lt;p&gt;根据上一节，只要保证&lt;strong&gt;在处理一条客户端指令过程中的任意时间点，都有大于等于 s + 1 条路径处理该条客户端指令&lt;&#x2F;strong&gt;，那么该系统就是 &lt;code&gt;s-slowdown-tolerant&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;Copilot 容忍一个节点 slowdown，因此需要在处理客户端指令时有两条路径。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;cdbKOei.png&quot; alt=&quot;Copilot&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;she-ji&quot;&gt;设计&lt;&#x2F;h1&gt;
&lt;p&gt;本节内容是对论文 §3 Design 的翻译。&lt;&#x2F;p&gt;
&lt;p&gt;Copilot 的设计核心是同时使用两个 leader 互为冗余地处理每一条 client 指令。两个 leader 为 pilot (P) 与 copilot (P&#x27;)。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mo-xing&quot;&gt;模型&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;crash failure model - 出错的节点会停止发送与相应信息，无拜占庭错误&lt;&#x2F;li&gt;
&lt;li&gt;异步 - 执行指令与传递消息所需的时间没有限制，消息可能会延迟、乱序甚至丢失&lt;&#x2F;li&gt;
&lt;li&gt;容忍 2f + 1 个节点中出现 f 个错误并保证指令执行的线性顺序&lt;&#x2F;li&gt;
&lt;li&gt;可容忍 1 个节点发生 slowdown&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;pai-xu&quot;&gt;排序&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;s9Aa2ut.png&quot; alt=&quot;排序&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在 Copilot 排序中，pilot 与 copilot 都会收到 client 的指令并各自放入其 log 记录中，两者协调后得到最终顺序。pilot log 与 copilot log 通过&lt;strong&gt;依赖&lt;&#x2F;strong&gt;进行排序。依赖是指：应在执行某条指令前完成执行的指令。在提议阶段，pilot 与 copilot 会提出被排序指令的初始依赖。节点要么同意收到提议中的依赖顺序，要么回复其认为正确的依赖顺序。最终，每一条指令都会有其最终的依赖，以用于被执行。最终依赖可能会成环，这将在被执行时处理。执行时，pilot 与 copilot 的 log 记录通过上述排序过程被整合，在成环的依赖中 pilot 的指令会被优先执行以打破环。Copilot 排序会将指令与最终依赖持久化至其它节点以用于错误恢复。与 EPaxos 类似，每次排序操作都会经过 FastAccept 阶段，又是会经过 Accept 阶段。以下是排序的具体过程（暂时省略 fast takeover 与 view-change）。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;client-tong-shi-fa-song-zhi-ling-zhi-pilot-yu-copilot&quot;&gt;Client 同时发送指令至 pilot 与 copilot&lt;&#x2F;h3&gt;
&lt;p&gt;每个 client 都拥有 ID：cliid。client 会为每一个指令分配一个递增且不重复的 ID：cid。发送指令时，client 也会附加 cliid 与 cid 以标识指令，以便在执行阶段去重。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pilot-ti-yi-zhi-ling-yu-qi-chu-shi-yi-lai&quot;&gt;Pilot 提议指令与其初始依赖&lt;&#x2F;h3&gt;
&lt;p&gt;当一 pilot 收到 client 发来的一条指令时会将其追加至自身 log 记录中，同时将最后收到的来自另一 pilot 的指令作为该指令的初始依赖。该 pilot 向其它节点（包括自己与另一 pilot）发送 FastAccept 消息提议这条指令与其初始依赖。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;jie-dian-hui-fu-fastaccept&quot;&gt;节点回复 FastAccept&lt;&#x2F;h3&gt;
&lt;p&gt;当一节点收到 FastAccept 消息时，它会检查收到的指令初始依赖是否与其之前已 Accept 的其它 entry 冲突。若不冲突，节点会回复此 FastAcceptOk。若冲突，节点会回复其认为正确的依赖顺序。&lt;&#x2F;p&gt;
&lt;p&gt;compability check：&lt;&#x2F;p&gt;
&lt;p&gt;对于两个依赖，如果其中至少有一个的 entry 位于另一个的 entry 之后，则两依赖不冲突。在上图a 中表示了冲突与未冲突的依赖：依赖于 P.1 的 P&#x27;.1 与依赖于 P&#x27;.1 的 P.2 没有冲突，因为 P.2 位于 P&#x27;.1 之后. 依赖于 P.2 的 P&#x27;.3 与依赖于 P&#x27;.2 的 P.3 冲突，因为它们都不位于对方 entry 之后。由于冲突的依赖可能导致指令执行顺序不一致，所以必须避免，如有节点会先执行 P.3 再执行 P&#x27;.3，有节点会反之。&lt;&#x2F;p&gt;
&lt;p&gt;节点会使用 compatibility check 检查 pilot 提供的初始依赖是否冲突。依赖于 P&#x27;.j 的 entry P.i 与 P.i 与 P&#x27;.j 之前 accept 的 entry 不冲突，所以 compatibility check 只需要检查该依赖是否与另一 pilot 之后的 log 是否冲突。若节点已 accept 另一 pilot 的 entry P&#x27;.k (k &amp;gt; j)，且 P&#x27;.k 依赖位于 P.i 之前的 entry，则依赖冲突。&lt;&#x2F;p&gt;
&lt;p&gt;如果节点：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;还未收到之后的关于之后 entry 的依赖&lt;&#x2F;li&gt;
&lt;li&gt;已收到之后的 entry P&#x27;.k，但其依赖为 P.i 或在其后，如 P&#x27;.j, P.i, ... , P&#x27;.k&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;它会认为依赖不冲突，以 FastAcceptOk 回复，而相同的规则会阻止另一 pilot 之后的依赖冲突。否则节点会回复 FastAcceptReply 并附加其收到的最后一条来自另一 pilot 的 entry。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pilot-chang-shi-tong-guo-fast-path-lai-commit-gai-zhi-ling&quot;&gt;Pilot 尝试通过 fast path 来 commit 该指令&lt;&#x2F;h3&gt;
&lt;p&gt;与 EPaxos 类似，若 pilot 收到至少 f + (f + 1) &#x2F; 2 （fast quorum，包括该 pilot 本身）个节点的 FastAcceptOk 回复，就足以从之后可能发生的节点错误中恢复。此时 pilot 会通过 fast path 直接 commit 该指令与其依赖，向其它节点发送 commit 消息（无需等待回复）。&lt;&#x2F;p&gt;
&lt;p&gt;可能有两种原因导致 pilot 无法收到 fast quorum 的 FastAcceptOk 回复：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;有其它节点出错，导致剩余节点不足 fast quorum&lt;&#x2F;li&gt;
&lt;li&gt;有其它节点检测到依赖冲突，回复 FastAcceptReply 与其认为正确的依赖顺序&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;不论那种原因，pilot 都需要等待有至少 f + 1 个节点回复，才能进入下一步。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pilot-zai-accept-jie-duan-zui-zhong-que-ding-yi-lai&quot;&gt;Pilot 在 Accept 阶段最终确定依赖&lt;&#x2F;h3&gt;
&lt;p&gt;pilot 将上一步中收到的推荐依赖（包括自身，且 FastAcceptOk 即初始依赖）以升序排序，并选择第 f + 1 个推荐依赖作为最终依赖。选择第 f + 1 个是为了保证该依赖与任何已经 commit 的命令有交集以确保线性，不选择第 f + 1 个之后的依赖是为了防止之后的依赖与该依赖有依赖关系。&lt;&#x2F;p&gt;
&lt;p&gt;之后 pilot 使用 Accept 消息发送最终依赖到其它节点。最终依赖可能会导致成环，但这可以接受，因为执行时所有节点都会按照相同的方法打破环。其它节点收到该最终依赖后回复 AcceptOk。当 pilot 收到 f + 1 个 AcceptOk 回复后（包括其本身），即可 commit 该命令与最终依赖。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zhi-xing&quot;&gt;执行&lt;&#x2F;h2&gt;
&lt;p&gt;节点使用两 pilot 的 log 合并后的顺序执行。合并后的 log 中，每个 client 指令都会出现两次。节点会在发现已执行过的指令时跳过该指令。执行指令后，pilot 与 copilot 都会将结果返回给 client。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;copilot-zui-zhong-he-bing-hou-de-zhi-ling-shun-xu&quot;&gt;Copilot 最终合并后的指令顺序&lt;&#x2F;h3&gt;
&lt;p&gt;最终合并后的指令顺序由以下因素决定：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;各 pilot 内各自的 log 顺序&lt;&#x2F;li&gt;
&lt;li&gt;两 pilot 指令间的依赖关系&lt;&#x2F;li&gt;
&lt;li&gt;pilot 优先级，用于处理成环依赖&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;ol&gt;
&lt;li&gt;最终顺序包含各 pilot 内各自的 log 顺序。上图，P.0 &amp;lt; P.1 &amp;lt; P.2。该顺序可能成环&lt;&#x2F;li&gt;
&lt;li&gt;当依赖不成环时，依赖顺序即为最终顺序。上图，P.1 &amp;lt; P&#x27;.0 &amp;lt; P&#x27;.1 &amp;lt; P.2&lt;&#x2F;li&gt;
&lt;li&gt;当依赖成环时，最终顺序由 pilot 优先级决定，pilot 的指令会在 copilot 的指令前执行。上图，P.4 &amp;lt; P.5 &amp;lt; P&#x27;.5&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;yi-xu-zhi-xing&quot;&gt;依序执行&lt;&#x2F;h3&gt;
&lt;p&gt;各节点在得到每条指令的最终依赖后就会按照相同的顺序执行。当一条指令 commit，且对应 entry 前的所有指令已都被执行后，节点会执行这条指令。&lt;&#x2F;p&gt;
&lt;p&gt;以下规则决定何时可以执行一条 entry 为 P.i、依赖为 P&#x27;.j 的指令：&lt;&#x2F;p&gt;
&lt;ol start=&quot;0&quot;&gt;
&lt;li&gt;P.i 已 commit&lt;&#x2F;li&gt;
&lt;li&gt;P.(i - 1) 已被执行&lt;&#x2F;li&gt;
&lt;li&gt;P&#x27;.j 已被执行，或 P.i 与 P&#x27;.j 成环且 P 为 pilot 指令&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;节点可以乱序接受指令的 commit 消息，如节点可能先得知一指令 commit，之后再得知该指令的依赖 commit。为了达到所有指令的执行顺序一致，节点必须在执行一条指令前执行过该指令的依赖以及该指令 entry 前的所有指令。例如在执行一条 entry 为 P.i、依赖为 P&#x27;.j 的指令时，需要：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;P.i 之前 entry 的所有指令都已被执行&lt;&#x2F;li&gt;
&lt;li&gt;P&#x27;.j 已被执行&lt;&#x2F;li&gt;
&lt;li&gt;P&#x27;.j 之前 entry 的所有指令都已被执行&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;才能执行 P.i。Copilot 的 Fast Takeover 机制可以防止正常运行的 pilot 等待 slowdown 的 pilot。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;qu-zhong-zhi-xing-bing-hui-fu&quot;&gt;去重执行并回复&lt;&#x2F;h3&gt;
&lt;p&gt;若排序过程按正常情况执行，每一条指令在 log 合并后都会出现两次。节点只会在一条命令出现第一次时执行该命令。cliid 与 cid 组成的元组在这里可以用来标识指令。若该节点是 pilot 或 copilot，执行完成一条命令后会将结果返回给客户端。客户端收到一个返回的结果后，会忽略之后由另一 pilot 返回的结果。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fast-takeover&quot;&gt;Fast Takeover&lt;&#x2F;h2&gt;
&lt;p&gt;为了达到执行顺序一致，有时一 pilot 需要等待另一 pilot 的 commit 消息。正常工作的 pilot 等待另一 slowdown 的 pilot 会消耗大量时间，因此需要 Fast Takeover 机制。&lt;&#x2F;p&gt;
&lt;p&gt;每个节点 log 中的每一个 entry 都会附有一 ballot number。与 Paxos 的 proposal number 类似，Copilot 协议过程中的每条消息都会带有 ballot number。通过这些序列号可以让 pilot 安全地进行与 Paxos 类似的所有权竞争的 Fast Takeover。&lt;&#x2F;p&gt;
&lt;p&gt;当一个节点被选举为 pilot 或 copilot，所有节点中该 pilot log 的 entry ballot number 都会被设置为 b。节点只会接受附加 ballot number 大于等于对应 entry 的 ballot number 的消息。在 pilot 正常工作时，其发出的所有消息的 ballot number 值都为 b。&lt;&#x2F;p&gt;
&lt;p&gt;当一 pilot 发现另一 pilot slowdown ，希望代替其完成某 entry 的 commit 时，正常工作的 pilot 会向所有其它节点发送关于该 entry 的 Prepare 消息并附加大于 b 的 ballot number 值 b&#x27;。如果其它节点确认 b&#x27; 大于该 entry 原本的 ballot number 值 b 时会回复 PrepareOk 消息，并将该 entry 的 ballot number 值设置为 b&#x27;。PrepareOk 消息中会包含该 entry 目前的状态（not-accepted、fast-accepted、accepted 或 committed），同时包含该 entry 之前最大的 ballot number、指令与依赖。&lt;&#x2F;p&gt;
&lt;p&gt;在正常 pilot 发送 Prepare 消息后，它需要等待接收到至少 f + 1（包含它本身）个 PrepareOk 消息。如果通过某 PrepareOk 消息得知该指令已被 commit，该 pilot 会立即停止等待并 commit 该指令与其依赖，结束 Fast Takeover 过程。否则，该 pilot 会通过如下过程选择该指令与其依赖，并通过 Accept 消息发送，等待 f + 1 个 AcceptOk 回复，然后继续执行。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;entry-nei-rong-de-hui-fu-guo-cheng&quot;&gt;entry 内容的恢复过程&lt;&#x2F;h3&gt;
&lt;p&gt;Fast Takeover 与 view-change 都使用了本过程，以正确恢复任意可被 commit 并执行的 entry。本过程十分复杂，细节可参阅 &lt;a href=&quot;https:&#x2F;&#x2F;www.cs.princeton.edu&#x2F;research&#x2F;techreps&#x2F;TR-004-20&quot;&gt;Technical Report TR-004-20&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;在附加正确 ballot number 的 PrepareOk 回复集合中：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;若有至少一个回复 entry 状态为 accepted，则采用该回复的指令与依赖&lt;&#x2F;li&gt;
&lt;li&gt;当有小于 (f + 1) &#x2F; 2 个回复状态为 fast-accepted 时，设置该 entry 为 no-op 且依赖为空&lt;&#x2F;li&gt;
&lt;li&gt;当有大于等于 f 个回复状态为 fast-accepted 时，采用该回复的指令与依赖&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;在第一种情况中，该 entry 可能已经被 commit 并附加了更小的 ballot number，所以必须采用原值与依赖。在第二种情况中，该 entry 不可能已被 commit，所以采用 no-op 是安全的。在第三种情况下，该 entry 可能已经被 commit 并附加了更小的 ballot number，所以可以采用原值与依赖，因为 f 个 fast-accepted 回复与该 pilot 本身已构成了一个 majority quorum。&lt;&#x2F;p&gt;
&lt;p&gt;除上述情况外，还有一种可能，有大于等于 (f + 1) &#x2F; 2 且小于 f 个回复状态为 fast-accepted。这种情况下，该 entry 可能已被 commit，也可能有另一 pilot 已提案另一与之冲突的指令，且有其它节点已收到了该 fast-accept。为了确定情况，发起这次恢复过程的节点需要检查另一 pilot log 中是否存在冲突的 entry。若发现冲突的 entry 但还未 commit，则对此 entry 重复整个上述过程，最终安全地完成恢复。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hong-fa-fast-takeover&quot;&gt;触发 Fast Takeover&lt;&#x2F;h3&gt;
&lt;p&gt;当 pilot 得知一条 entry 为 P.i、依赖为 P&#x27;.j 的指令被 commit 时，如果：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;P.i 之前 entry 有指令未被 commit&lt;&#x2F;li&gt;
&lt;li&gt;P&#x27;.j 未被 commit&lt;&#x2F;li&gt;
&lt;li&gt;P&#x27;.j 之前 entry 有指令未被 commit&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;pilot 就会启动一个 timer。若 timer 达到了 takeover-timeout 且仍有执行 P.i 所需的指令没有被 commit，该 pilot 就会停止等待，主动接过 commit 所需指令与排序的任务，fast takeover 另一 pilot log 中所有可能为该指令执行前置条件的 entry。在该 Copilot 实现中，所有 entry 的 fast takeover 过程是被批量合并且是并行的。&lt;&#x2F;p&gt;
&lt;p&gt;如果 takeover-timeout 设置的时间太短，就会产生没有必要的错误 fast takeover，从而导致 proposal 竞争。proposal 竞争问题可以使用指数退避随机化 timeout 来避免。为了避免必要的错误 fast takeover，可以将 takeover-timeout 设置为一个中等大小的值（如 10ms）。中等大小的 takeover-timeout 可能导致处理速度变慢，但是使用 Null Dependency Elimination 可以避免这个问题。&lt;&#x2F;p&gt;
&lt;p&gt;Fast takeover 与 leader 选举看起来十分类似，它们都是通过等待某节点相应时超时触发的。Leader 选举是由于某节点无法在设定 timeout 内收到另一节点的某种消息（如心跳或新指令提议）触发的。但是有时虽然 leader 仍可正常发送心跳，但其实际处理指令的速度会因为一些因素而变慢。Fast takeover 是由指令处理路径上某点的 slowdown 触发的，配合指令同时有两条互不依赖的处理路径，可以在发生 slowdown 时总会由较快的 pilot 处理完成指令。当一个 pilot slowdown 时，另一个 pilot 会通过 fast overtake 处理所有指令，无需等待较慢的 pilot。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;e-wai-she-ji&quot;&gt;额外设计&lt;&#x2F;h2&gt;
&lt;p&gt;本节未提到的 Copilot 设计都与其它分布式状态机的设计一致。
保证指令仅被执行一次是通过检查 cliid 与 cid 实现的。记录一条指令已被执行一次的 cliid 与 cid 容器会在第二次遇到相同指令时清空。
对于结果非确定的指令（如涉及随机数生成，每次执行结果都不同），可以由两 pilot 完成非确定部分（随机数生成），此时该指令的执行结果变为可确定的值，再进行排序与执行等操作。两 pilot 在完成非确定部分时可能不同，所以最终的指令可能的执行结果会有两个版本，但执行的去重机制可以保证只有一个结果有效。&lt;&#x2F;p&gt;
&lt;p&gt;与 Multi-Paxos 的 leader 选举类似，pilot 与 copilot 选举使用 view-change。view-change 选举得到的新的 pilot 与 copilot 使用之前提到的 entry 对应内容的恢复过程。由于两 pilot 有各自独立的 log，在重新选举一个 leader 时另一 pilot 可以不间断地为 client 提供服务。在这个过程中，正常的 pilot 不会将另一 pilot 的 entry 作为依赖，所以可以一直在 fast path 上 commit，直到另一 pilot 选举完毕。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;copilot-neng-da-dao-1-slowdown-tolerant-de-yuan-yin&quot;&gt;Copilot 能达到 &lt;code&gt;1-slowdown-tolerant&lt;&#x2F;code&gt; 的原因&lt;&#x2F;h2&gt;
&lt;p&gt;Copilot 通过保证任何客户端指令在任意时间节点都在两条不同的处理路径上。两条路径的处理速度不同，处理速度快的路径得到的结果必定先返回客户端。&lt;&#x2F;p&gt;
&lt;p&gt;当两 pilot 都为正常状态时，就算有最多 f 个其它节点 slowdown 时，&lt;code&gt;1-slowdown-tolerant&lt;&#x2F;code&gt; 仍成立，因为在 regular path 上 commit 只需要 f + 1 个节点回复。
当一 pilot 发生 slowdown 或出错时，另一个正常的 pilot 仍可以正常 commit 其 entry，如果有对 slowdown pilot 指令的依赖，正常 pilot 会执行 fast takeover 解决这个问题。在 slowdown 发生后，正常 pilot 会通过 fast takeover 迅速的解决对 slowdown pilot 指令的依赖，之后即可按正常流程继续处理。此时该分布式状态机的总体执行速度取决于此正常的 pilot，所以 &lt;code&gt;1-slowdown-tolerant&lt;&#x2F;code&gt; 成立。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;you-hua&quot;&gt;优化&lt;&#x2F;h1&gt;
&lt;p&gt;本节内容是对论文 §5 Optimizations 的翻译。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ping-pong-batching&quot;&gt;Ping-Pong Batching&lt;&#x2F;h2&gt;
&lt;p&gt;当两个 pilot 都在正常运行时，如果几条指令同时以乱序到达两个 pilot，就可能因顺序不一致导致无法通过 compability check。Ping-Pong Batching 通过协调两 pilot 的提案顺序从而避免上述情况，使提案总会通过 fast path 完成。&lt;&#x2F;p&gt;
&lt;p&gt;在 Ping-Pong Batching 过程中，各 pilot 会打包（batching）在一定时间段内到达的指令。在此期间该 pilot 每收到一条指令都会将其放入自己的下一个 entry 中，因此打包后的指令会是一组连续的 entry。当该 pilot 收到另一 pilot 的 FastAccept 消息，或到达设定的打包最长时间段（ping-pong-
wait timeout），就会结束当前的打包操作。&lt;&#x2F;p&gt;
&lt;p&gt;当两 pilot 都未出现 slowdown 的正常情况下，打包操作会因收到另一 pilot 的 FastAccept 消息结束。如此，两 pilot 的 FastAccept 会如 ping-pong 一般进行。Pilot 结束其对第一批指令的打包并发送 FastAccept 消息，copilot 收到该消息后也会结束打包并发送这批指令的 FastAccept 消息，pilot 收到该消息后又会结束其对第二批指令的打包并发送 FastAccept 消息，如此往复。&lt;&#x2F;p&gt;
&lt;p&gt;上述流程可以保证每个 pilot 会在发出 FastAccept 前都会收到另一 pilot 的 entry 顺序，因此可以避免发出有依赖冲突的消息，因此其它节点在收到 FastAccept 消息后进行的 compability check 都会成功，使得每一次提案都可以在 fast path 上完成。&lt;&#x2F;p&gt;
&lt;p&gt;当有一 pilot 出现 slowdown 时，另一 pilot 会在达到 ping-pong-
wait timeout 后结束打包操作。这避免了因一个 pilot 出现 slowdown 导致整个系统 slowdown。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;null-dependency-elimination&quot;&gt;Null Dependency Elimination&lt;&#x2F;h2&gt;
&lt;p&gt;当一 pilot 发现自己将要执行的指令有依赖位于另一 pilot log 中，且还未 commit，这时它会检查自身是否已执行过该依赖所对应的指令，若发现已执行过，则无需等待另一 pilot 的 commit 消息，可直接跳过。满足以上条件的依赖称为 null dependency，该机制名为 Null Dependency Elimination。在 null dependency 出现时，pilot 无需等待该未 commit 的依赖，因为该依赖不会对最终的执行结果产生影响，pilot 只需将该依赖标记为已执行然后继续。&lt;&#x2F;p&gt;
&lt;p&gt;当有一 pilot 出现持续的 slowdown 时，Null Dependency Elimination 可以让正常的 pilot 省去 fast takeover 的步骤。因为 slowdown 的 pilot 的提案比正常的 pilot 慢，所以其 entry 都是 null dependency，正常 pilot 可以直接跳过等待。若一 pilot 在提案后才发生 slowdown，fast takeover 仍是必要的。在正常 pilot fast takeover 过 slowdown pilot 的提案后，就可以通过 Null Dependency Elimination 跳过等待发生 slowdown 的 pilot 了。因此 &lt;code&gt;1-slowdown-tolerant&lt;&#x2F;code&gt; 仍是成立的。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;zong-jie&quot;&gt;总结&lt;&#x2F;h1&gt;
&lt;p&gt;Copilot 协议借鉴了之前各协议的优点，其排序部分与 EPaxos 类似，fast takeover 过程与 Paxos 类似，pilot 选举与 Multi-Paxos 一致。同时，Copilot 排序不依赖客户端指令本身是否 interfere，而将指令到达时间作为排序的唯一依据，这也减小协议的实现难度。通过文中提到的优化，Copilot 在无节点 slowdown 时虽然无法达到 Multi-Paxos 或 EPaxos 性能水平，但仍有一定竞争力。在有节点 slowdown 时，Copilot 是唯一能避免整个系统速度收到影响的协议。综上，在选择分布式状态机系统的共识协议时，Copilot 协议不失为一个好的选择。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>基于 QUIC 的代理软件：TUIC</title>
          <pubDate>Thu, 10 Mar 2022 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2022/tuic/</link>
          <guid>https://www.eaimty.com/2022/tuic/</guid>
          <description>&lt;p&gt;QUIC 协议汲取了大量人们给 TCP 糊墙的经验教训，把连接结构优化到（目前来看）极致。但是现在市面上的代理工具还没有能完全利用 QUIC 特性的存在，所以我自己动手写了一个基于 QUIC 协议的新代理工具：TUIC&lt;&#x2F;p&gt;
&lt;p&gt;https:&#x2F;&#x2F;github.com&#x2F;EAimTY&#x2F;tuic&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;1-RTT TCP 中继&lt;&#x2F;li&gt;
&lt;li&gt;0-RTT UDP 中继，且 NAT 类型为 FullCone&lt;&#x2F;li&gt;
&lt;li&gt;在用户空间的拥塞控制，也就是说可以在任何系统平台实现双向的 BBR&lt;&#x2F;li&gt;
&lt;li&gt;两种 UDP 中继模式: native （原生 UDP 特性，数据仍被 TLS 加密）和 quic (100% 送达率，每个包单独单独作为一个 QUIC “流”，一个包的确认重传不会阻塞其它包)&lt;&#x2F;li&gt;
&lt;li&gt;完全多路复用，服务器和客户端之间始终只需要一条 QUIC 连接，所有任务作为这个连接中的 “流” 进行传输（一个流的暂时阻塞不会影响其它流），所以除连接第一个中继任务外的其它任务都不需要经过 QUIC 握手和 TUIC 的鉴权&lt;&#x2F;li&gt;
&lt;li&gt;网络切换时的会话平滑转移，例如在从 Wi-Fi 切换到移动数据时连接不会像 TCP 一样直接断开&lt;&#x2F;li&gt;
&lt;li&gt;0-RTT 、与中继任务并行的鉴权&lt;&#x2F;li&gt;
&lt;li&gt;支持 QUIC 的 0-RTT 握手（开启之后能达到 真・1-RTT TCP 和 0-RTT UDP ，但是就算不开启，多路复用的特性也能保证在绝大多数情况下 1-RTT 和 0-RTT ）&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;TUIC 的设计介绍在仓库中 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;EAimTY&#x2F;tuic#design&quot;&gt;Design&lt;&#x2F;a&gt; 一节有说明。TUIC 协议的详细内容在&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;EAimTY&#x2F;tuic&#x2F;tree&#x2F;master&#x2F;protocol&quot;&gt;这里&lt;&#x2F;a&gt;。简单来说，TUIC 的设计核心就是减少握手造成的网络往返时延（ rtt ），毕竟对于网络程序这是最大的瓶颈。&lt;&#x2F;p&gt;
&lt;p&gt;对比其它使用 TCP 的代理工具（ ss 、v2ray 、trojan ），TCP 握手慢，且不支持自定义拥塞控制，各工具对 UDP 的支持也各有问题。对比 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;HyNetwork&#x2F;hysteria&quot;&gt;Hysteria&lt;&#x2F;a&gt; ，Hysteria 的 UDP 中继需要 1 rtt 的握手，且只支持一种 UDP 模式。&lt;&#x2F;p&gt;
&lt;p&gt;最后说说安全性和协议特征。TUIC 现在基于原生 QUIC ，不支持 obfs ，但 QUIC 连接本身就是 TLS 加密的，每个 QUIC 连接从外面看都是一样的。国内的各大厂也慢慢开始使用 QUIC 了，所以我觉得 QUIC 特征应该不是什么大问题。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>HashMap，以及没有 hash 的 HashMap</title>
          <pubDate>Fri, 04 Mar 2022 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2021/how-hashmap-work-without-hash/</link>
          <guid>https://www.eaimty.com/2021/how-hashmap-work-without-hash/</guid>
          <description>&lt;p&gt;我之前对 HashMap 有很多错误的理解，特别是在 hash 函数对性能的影响上。最近我对 HashMap 的了解稍稍深入了一些，所以写了这篇笔记，对之前的错误理解修正总结。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;h1 id=&quot;tou-ming-de-hash-han-shu&quot;&gt;“透明”的 hash 函数&lt;&#x2F;h1&gt;
&lt;p&gt;首先从 hash 函数说起。
在我之前的理解中，hash 的用处就是：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;能根据一个任何（支持被 hash）类型的值计算出一个无符号整数&lt;&#x2F;li&gt;
&lt;li&gt;对于两个相同的值，hash 出的值必须相等&lt;&#x2F;li&gt;
&lt;li&gt;对于两个不同的值，hash 出的值必须有极大概率（接近 100%）不同，也就是碰撞概率要低&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;确实，上面这三点是任何一个 hash 函数都需要具备的。按照这种理解，可以发现一个有趣的事情：&lt;&#x2F;p&gt;
&lt;p&gt;如果一个 HashMap 的键刚好是一个 &lt;strong&gt;64 位无符号整数&lt;&#x2F;strong&gt;，或者是 &lt;strong&gt;能 cast 为 64 位无符号整数的类型&lt;&#x2F;strong&gt;，那它岂不是不需要进行 hash 操作就可以满足上面的三点？&lt;&#x2F;p&gt;
&lt;p&gt;大量的数据都符合这一条件，例如 UID（从 0 起不断增大的无符号整数）和时间戳。&lt;&#x2F;p&gt;
&lt;p&gt;大概很多人都有过这种想法，举个例子：&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;paritytech&#x2F;nohash-hasher&quot;&gt;NoHashHasher&lt;&#x2F;a&gt;。我之前在逛 &lt;a href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;rust&#x2F;&quot;&gt;r&#x2F;rust&lt;&#x2F;a&gt; 的时候发现了这个库。简单来说，就是针对整数构造一个 hash 函数，将输入的值直接输出。也就是说，我们可以零成本地利用整数作为 HashMap 的键，在能得到 HashMap 平均 &lt;code&gt;O(1)&lt;&#x2F;code&gt; 的查找效率的同时不需要将性能浪费在 hash 函数上！&lt;&#x2F;p&gt;
&lt;p&gt;然而事实并非如此，用整数省略 hash 过程可能会让 HashMap 的性能低很多，这与 HashMap 的内部实现有关。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;hashmap-hashset-de-shi-xian&quot;&gt;HashMap（HashSet） 的实现&lt;&#x2F;h1&gt;
&lt;p&gt;HashMap 和 HashSet 之间唯一的区别就是，HashSet 只存键，HashMap 存键和值（每个键和对应的值是放在一起的，完全可以看作一个 tuple），所以下面都用 HashSet 描述。HashSet 的实现有很多，这里简单描述三个。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;c-stl-zhong-de-unordered-set&quot;&gt;C++ STL 中的 &lt;a href=&quot;https:&#x2F;&#x2F;gcc.gnu.org&#x2F;onlinedocs&#x2F;gcc-11.2.0&#x2F;libstdc++&#x2F;api&#x2F;a00365_source.html&quot;&gt;unordered_set&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;STL 没有实现标准，但是对 HashTable 的实现都大同小异，比如 GCC 的实现。&lt;&#x2F;p&gt;
&lt;p&gt;简单来说，就是一块长度为 n 个指针的内存作为 HashSet 的索引，每个指针都指向一个链表，链表里之后要存放的是元素的 hash 值和元素本身。操作时，计算要操作元素的 hash 值，用得到的值模除 n，得到这个元素应该放在这个索引中的哪个位置指向的链表中，然后去链表中执行相应的操作。扩容时，只需要为新节点分配内存，不需要动旧节点的位置。&lt;&#x2F;p&gt;
&lt;p&gt;上面的描述简化了 rehash、链表前导元素等很多细节，不过这些不重要，&lt;strong&gt;hash 值模除索引的长度&lt;&#x2F;strong&gt; 和 &lt;strong&gt;扩容时不需要移动旧节点&lt;&#x2F;strong&gt; 是关键。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;robin-hood-unordered-set&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;martinus&#x2F;robin-hood-hashing&quot;&gt;robin_hood::unordered_set&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;这是一个很常用的 HashTable 实现，因为性能好，很多人用它代替 STL 的实现。Rust 在 1.36 版本之前的标准库 HashMap 实现也是这个。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;robin_hood::unordered_set&lt;&#x2F;code&gt; 有 &lt;code&gt;node&lt;&#x2F;code&gt; 和 &lt;code&gt;flat&lt;&#x2F;code&gt; 两种布局，&lt;code&gt;node&lt;&#x2F;code&gt; 是类似于 STL 版本的链表实现，&lt;code&gt;flat&lt;&#x2F;code&gt; 顾名思义是 probing table 的布局，把元素直接放进一个连续的，类似 vector 的结构。在不考虑容量时 vector 几乎每一项性能都肯定比链表强，因此这个表的实现当然比 STL 版本性能好。&lt;&#x2F;p&gt;
&lt;p&gt;用 STL 版本类比，&lt;code&gt;robin_hood&lt;&#x2F;code&gt; 版就相当于把链表放进了 vector，因此“链表”的最大长度是有限的，只要有任意一个“hash 值模除分段（也就是上面的“链表”）个数“对应的段满了就需要扩容，扩容时需要把每一段增大并依次向内存后部平移，因此开销很大。&lt;&#x2F;p&gt;
&lt;p&gt;上面的描述同样简化了很多细节，不过这些也不重要，&lt;strong&gt;hash 值模除分段个数&lt;&#x2F;strong&gt; 和 &lt;strong&gt;扩容时需要移动旧节点，开销大&lt;&#x2F;strong&gt; 是关键。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;swisstable&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;abseil.io&#x2F;blog&#x2F;20180927-swisstables&quot;&gt;SwissTable&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;这是一个新的高性能 HashTable 实现，Rust 在 1.36 版本之后的标准库实现就是这个。&lt;&#x2F;p&gt;
&lt;p&gt;与 &lt;code&gt;robin_hood::unordered_map&lt;&#x2F;code&gt; 的 &lt;code&gt;flat&lt;&#x2F;code&gt; 布局类似，SwissTable 也把值存在连续的内存中，但在查找时并不直接到存放元素的内存部分查，而是建立一个“索引”标识每个内存位置的状态，先到“索引”中查找。&lt;&#x2F;p&gt;
&lt;p&gt;通过这个实现可以理解 hash 值对表性能的影响为什么很大，所以要详细描述一下。当然还是忽略如 Group 之类的无关细节。&lt;&#x2F;p&gt;
&lt;p&gt;建立 HashSet 时，SwissTable 创建了两个类似 vector 的结构，一个是索引表，一个是数据表。两个表的长度一致，对应位置数据之间是相关联的。数据表中存放的是插入 HashSet 的数据本身，索引表中存放的是 1 bit 标识这个位置是否是空位，以及数据 hash 值的后 7 bit，加起来正好是 1 字节。表被分成 n 段，每段中存放的是 hash 值的前 57 bit 模除 n 后结果相同的值。&lt;&#x2F;p&gt;
&lt;p&gt;在操作元素时：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;计算需要操作的元素的 hash 值&lt;&#x2F;li&gt;
&lt;li&gt;取 hash 值的前 57 bit 模除 n 得到应该在表的第几分段&lt;&#x2F;li&gt;
&lt;li&gt;前往索引表的这个分段，查找所存储的值与 hash 值的后 7 bit 一致，以及另外那 1 bit 标识不是空位的位置&lt;&#x2F;li&gt;
&lt;li&gt;这时，hash 值的前 57 bit 取模后的值，以及后 7 bit 都已经与需要操作的元素对应了，直接到数据表的对应位置操作数据即可&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;SwissTable 的设计实在是太精妙了，虽然在这篇文章的主题以外，但还是推荐去看一下 SwissTable 的实现，它能将操作所需的几乎所有数据都放进 L1 缓存，并且通过分组操作实现 SIMD 加速。&lt;&#x2F;p&gt;
&lt;p&gt;跑题了，总之 SwissTable 的重点是 &lt;strong&gt;用 hash 的前 57 bit 确定分组位置，后 7 bit 确定元素位置&lt;&#x2F;strong&gt;，以及与 robin_hood flat 实现一样的 &lt;strong&gt;扩容时需要移动旧节点，开销大&lt;&#x2F;strong&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;准备工作完成，终于可以来解答为什么“透明”的 hash 函数不一定好了。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;bu-shi-suo-you-zheng-shu-du-shi-ping-deng-de&quot;&gt;不是所有整数都是平等的&lt;&#x2F;h1&gt;
&lt;p&gt;利用之前提到“透明”的 hash 函数，假设有这些数据要插入一个分段数刚好是 100 的 HashTable 中：&lt;&#x2F;p&gt;
&lt;p&gt;100、300、1400、2500、40600、79137500&lt;&#x2F;p&gt;
&lt;p&gt;这些值看似都符合最早提到的对 hash 值的要求，然而模除后所有的余数都是 0，全部都碰撞了。当然这只是极端情况，一般来说 HashTable 的分段数都是 2 的幂。
举这个例子是为了说明：只有当作为键的数模除表分段数 n 的结果在 0 到 n 间均匀散布时， HashTable 的效率才是最高的。不经过 hash 处理的数据很难达到这个要求。&lt;&#x2F;p&gt;
&lt;p&gt;那假如要插入的数据如下（都是 8 位无符号整数）：&lt;&#x2F;p&gt;
&lt;p&gt;0、1、2、3、4、5、6、7、...、255&lt;&#x2F;p&gt;
&lt;p&gt;这组数据确实符合唯一且分布均匀的要求，只要把把这些 8 位无符号整数 cast 成 64 位无符号整数就可以直接作为 hash 值使用？&lt;&#x2F;p&gt;
&lt;p&gt;对于 STL 中的 &lt;code&gt;std::unordered_set&lt;&#x2F;code&gt; 和 &lt;code&gt;robin_hood::unordered_set&lt;&#x2F;code&gt;，确实可以直接作为 hash 值使用。但是对于 SwissTable 就不可以了。把上面这些数加上前导 0 扩充到 64 位，取前 57 位拿来模除，后 7 位放进索引表，会出现一个问题：这些数的前 57 位要么是 0，要么是 1，只有这两种情况，再次出现了非常严重的碰撞。&lt;&#x2F;p&gt;
&lt;p&gt;有什么解决方法吗？当然有：&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;n = (n &amp;gt;&amp;gt; 1) | (n &amp;lt;&amp;lt; 7)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;但这也不完美，因为这样 &lt;code&gt;(n &amp;gt;&amp;gt; 1)&lt;&#x2F;code&gt; 就成了索引表中的数据，0 和 1，2 和 3，4 和 5，...，254 和 255 在索引表中的数据变成了一样的，产生了碰撞。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;zong-jie&quot;&gt;总结&lt;&#x2F;h1&gt;
&lt;p&gt;综上，同样的 hash 值，在不同的 HashMap 实现中的性能可能相差巨大。就算是根据统一思路实现的 HashMap，在实现细节上有细微差距，也会导致性能相差悬殊，举个例子：Rust 1.36 后标准库中的 HashTable 采用了 SwissTable，但两者在实现上有一点非常小的区别：原版 SwissTable 将低 7 位放入索引表，而 Rust 实现是将高 7 位放入索引表。这一点微小的不同，就可能会让你本来调教好的“透明” hash 出现严重的碰撞。大量的碰撞意味着大量的扩容，在时间和空间上都会造成浪费。&lt;&#x2F;p&gt;
&lt;p&gt;当然，如果你的数据是：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;已经 hash 过的数&lt;&#x2F;li&gt;
&lt;li&gt;或是保证在 0 到 18446744073709551615(uint64::MAX) 间均匀分布&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;用“透明” hash 完全没有问题。&lt;&#x2F;p&gt;
&lt;p&gt;所以，除非完全搞懂了要用的 HashMap 实现，否则不要用“透明” hash。对于现代的处理器，对数字进行 hash 几乎可以说是 free 的。&lt;&#x2F;p&gt;
&lt;p&gt;hash 的作用到底是什么？&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;能根据一个任何（支持被 hash）类型的值计算出一个无符号整数&lt;&#x2F;li&gt;
&lt;li&gt;对于两个相同的值，hash 出的值必须相等&lt;&#x2F;li&gt;
&lt;li&gt;对于两个不同的值，hash 出的值必须有极大概率（接近 100%）不同，也就是碰撞概率要低&lt;&#x2F;li&gt;
&lt;li&gt;hash 出的值要分布均匀，从而避免在数据结构上的碰撞&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;最后推荐一个视频：&lt;a href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;ncHmEUmJZf4&quot;&gt;CppCon 2017: Matt Kulukundis “Designing a Fast, Efficient, Cache-friendly Hash Table, Step by Step”&lt;&#x2F;a&gt;
这是 SwissTable 作者之一在 CppCon 2017 分享如何从 &lt;code&gt;std::unordered_set&lt;&#x2F;code&gt; 一步步优化最后得到 SwissTable，知识密度很高，非常棒的分享。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>将 git 仓库作为数据库的动态 CMS：hummingbird</title>
          <pubDate>Mon, 20 Dec 2021 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2021/hummingbird/</link>
          <guid>https://www.eaimty.com/2021/hummingbird/</guid>
          <description>&lt;p&gt;我从今年六月开始学 Rust，到现在差不多有半年了。写 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;EAimTY&#x2F;hummingbird&quot;&gt;hummingbird&lt;&#x2F;a&gt; 这个项目的想法我在八月就已经有了：写一个用 git 仓库作为数据库的内容管理系统，给 git repo 中 markdown 格式的文章套 HTML 模板，然后 serve。
这样能结合传统 CMS 和 GitHub Pages 的优点——能用对于 CMS 本身只读的 git 仓库保证数据的安全性，也能像传统的 CMS 一样提供动态内容，比如搜索文章内容，甚至支持在一次搜索中使用多个 filter 来缩小范围，还不必像（免费版） GitHub Pages 一样只能建在公开仓库上。GitHub 的仓库文件编辑器还可以直接视为管理后台。
当然这种实现也会导致一些限制，例如不能原生支持评论，能实现的功能比较少，数据库更新不能太频繁，比起 GitHub Pages 需要一台服务器来跑服务端...
hummingbird 比 WordPress 大概会快十倍吧，大概（&lt;&#x2F;p&gt;
&lt;p&gt;总之我觉得这个项目应该有适合的使用场景，所以就开始动手了。经过断断续续 4 个月的开发，终于把雏形写出来了。&lt;&#x2F;p&gt;
&lt;p&gt;写 hummingbird 的过程基本上就是我入门 Rust 的过程。每过一段时间，回头看之前写的代码就会觉得写得稀烂，想重构。这个项目的数据库实现我重写过不下五次，然而现在还是觉得写得很差。这也算是学习的过程吧。&lt;&#x2F;p&gt;
&lt;p&gt;不论从代码上看，还是从软件工程角度看 hummingbird，都有些问题——某些功能实现地很幼稚，抽象也到处漏，但我总算是写完了一个比较复杂的项目，比起之前总是纸上谈兵还是有进步的。&lt;&#x2F;p&gt;
&lt;p&gt;说说大体的实现思路吧：&lt;&#x2F;p&gt;
&lt;p&gt;整个项目主要分成 3 个部分：配置、数据库和服务器 &#x2F; 路由。&lt;&#x2F;p&gt;
&lt;p&gt;配置部分很简单，就是读一个 TOML 格式的配置文件然后解析数据，没什么好说的。&lt;&#x2F;p&gt;
&lt;p&gt;数据库部分大概又能分 3 个部分吧：内存里的数据库存储部分，集成的 git 客户端，还有模板系统。
存储的实现比较原始，就是在解析过 git 仓库之后把其中的所有内容存进二叉堆排序，然后转成 Vec。另外还有些关于内容作者之类的映射。
git 部分是用 &lt;a href=&quot;https:&#x2F;&#x2F;libgit2.org&#x2F;&quot;&gt;libgit2&lt;&#x2F;a&gt; 实现的，用了 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;git2-rs&quot;&gt;git2-rs&lt;&#x2F;a&gt; 这个 Rust 的 bindings。这里算是写项目前期坑最多的地方，libgit2 暴露的 API 层级比较低，不像平时直接用 git 命令一样方便，而且当时还不太熟悉 Rust，实现反向遍历 commits 拿到文章作者、创建时间和更改时间花了很大力气。另外，git2-rs 的仓库抽象是 &lt;code&gt;!Send&lt;&#x2F;code&gt; 和 &lt;code&gt;!Sync&lt;&#x2F;code&gt; 的，所以刚开始写数据库时我只能做一个 &lt;code&gt;RepoGuard&lt;&#x2F;code&gt; 包住 git 仓库把它留在主线程用 Channel 通信，把剩下的部分 spawn 成 tasks 出去，从 git 仓库取数据的过程又臭又长。后面我发现 libgit2 的文档中只提到不能 parallel 地使用仓库，所以才会 &lt;code&gt;!Send&lt;&#x2F;code&gt; 和 &lt;code&gt;!Sync&lt;&#x2F;code&gt;。hummingbird 只有在被手动触发数据更新的时候才会操作 git 仓库，而且操作会上排他锁，所以直接把仓库标成 &lt;code&gt;Sync&lt;&#x2F;code&gt; 作为数据库成员走状态共享肯定没问题。
模板系统中的 markdown 解析用了 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;raphlinus&#x2F;pulldown-cmark&quot;&gt;pulldown-cmark&lt;&#x2F;a&gt;，HTML 模板应用是自己手写的，因为 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&quot;&gt;tera&lt;&#x2F;a&gt; 这类的模板实现实在是太重了。说实话我不是很满意现在的实现，有一大堆 clone。之后我想写一个完全 Evaluate-on-Write 的、带 Cacher 的 StringBuilder。&lt;&#x2F;p&gt;
&lt;p&gt;服务器 &#x2F; 路由部分我也改过很多次。最早是用 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tokio-rs&#x2F;axum&quot;&gt;axum&lt;&#x2F;a&gt; 写的，但后来发现 axum 的很多功能我完全用不到，比如 middleware 之类的，而且 axum 有大约四百个依赖，太重了。所以我换到了 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hyperium&#x2F;hyper&quot;&gt;hyper&lt;&#x2F;a&gt;，不过就要自己解决路由的问题了。开始时我想用一张大的字典当路由表，在更新数据库的时候就把所有所有数据解析好，但是发现存储效率不是很高，改成存过程也比较难实现。最后我的解决方案是用一张字典存文章、页面和的其它的静态路径，其它有参数的路径用字典树匹配和捕获。我写了一个泛型的字典树实现，但是 bench 后发现效率比 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ibraheemdev&#x2F;matchit&quot;&gt;matchit&lt;&#x2F;a&gt; 低至少一倍，&lt;del&gt;我还是太年轻&lt;&#x2F;del&gt;，所以就用了 matchit。&lt;&#x2F;p&gt;
&lt;p&gt;hummingbird 有什么适用的具体使用场景吗？我觉得可能用来 serve 长篇的文档、说明比较好（类似于 &lt;a href=&quot;https:&#x2F;&#x2F;llvm.org&#x2F;docs&#x2F;LangRef.html&quot;&gt;LLVM IR&lt;&#x2F;a&gt; 这种），用来做一个简单的博客也不错，只是需要外挂评论系统。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>使用 teledustry，通过 Telegram bot 远程管理你的 Mindustry 游戏服务器</title>
          <pubDate>Thu, 30 Sep 2021 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2021/manage-your-mindustry-game-server-through-telegram-bot/</link>
          <guid>https://www.eaimty.com/2021/manage-your-mindustry-game-server-through-telegram-bot/</guid>
          <description>&lt;p&gt;最近架了个 Mindustry 游戏服务器和朋友一起玩 PvP（&lt;del&gt;然而没玩几天就弃坑跑去 MC 了&lt;&#x2F;del&gt;），感觉不错，只是每次输命令和上传地图的时候都要 ssh&#x2F;sftp 到服务器上有点不方便，所以就写了个 Telegram 机器人用来输命令和上传地图：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;EAimTY&#x2F;teledustry&quot;&gt;teledustry - Manage your Mindustry server through Telegram bot&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我不会写 Java，所以没把这个 bot 写成 mod 的形式，而是直接把游戏服务器进程创建为子进程，然后读写子进程的 stdio。
这个 bot 用起来很简单，只要用&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ teledustry -t API_TOKEN -u YOUR_TELEGRAM_USERNAME SERVER_FILE.jar&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;就可以启动 Mindustry 服务器和 bot，然后去找 bot 聊天就能执行命令和上传地图了（记得用 &lt;code&gt;&#x2F;output&lt;&#x2F;code&gt; 命令让 bot 把输出发到当前聊天里）。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Xuanwu - 在全角字符与半角字符间添加空格的命令行工具</title>
          <pubDate>Wed, 15 Sep 2021 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2021/xuanwu/</link>
          <guid>https://www.eaimty.com/2021/xuanwu/</guid>
          <description>&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;vinta&#x2F;pangu.js&quot;&gt;Pangu&lt;&#x2F;a&gt; 是一个有名的用来在全角字符与半角字符间添加空格的库，支持多种语言，其中 Node.js 版 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;vinta&#x2F;pangu.js&quot;&gt;pangu.js&lt;&#x2F;a&gt; 可以作为命令行前端来使用。
但是由于是 Node.js，想要用 pangu 就需要装很多依赖，类 Unix 系统有包管理还好说，在 Windows 平台下装 node 只为了用 Pangu 实在是有点麻烦。&lt;&#x2F;p&gt;
&lt;p&gt;为了解决这个问题，我写了一个 Pangu 的命令行前端 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;EAimTY&#x2F;xuanwu&quot;&gt;Xuanwu&lt;&#x2F;a&gt;，基于 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;airt&#x2F;pangu-rs&quot;&gt;pangu-rs&lt;&#x2F;a&gt;，非常简单，只有六十行代码😂，还没有任何依赖。&lt;&#x2F;p&gt;
&lt;p&gt;需要用 Pangu 的时候，可以直接到 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;EAimTY&#x2F;xuanwu&#x2F;releases&quot;&gt;Release&lt;&#x2F;a&gt; 里下载编译好的可执行文件（暂时没有 macos 的，darwin 交叉编译有点麻烦），比装 node + pangu.js 方便得多。&lt;&#x2F;p&gt;
&lt;p&gt;至于为什么叫 Xuanwu 这个名字，传说玄武大帝是盘古的儿子，这个工具基于 Pangu，那自然就要叫 Xuanwu 了😂&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>用 Rust 写一个用于 OCR 的 Telegram Bot</title>
          <pubDate>Mon, 09 Aug 2021 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2021/create-a-telegram-bot-for-ocr-in-rust/</link>
          <guid>https://www.eaimty.com/2021/create-a-telegram-bot-for-ocr-in-rust/</guid>
          <description>&lt;p&gt;最近一段时间在学 Rust，想写一些简单的小工具来巩固一下。之前用其它语言写过 Telegram bot，所以我就用 Rust 写 Telegram bot 吧。&lt;&#x2F;p&gt;
&lt;p&gt;Rust 相比于其它流行的语言网络上的资源比较少，中文内容更是寥寥无几。虽然我的 Rust 连入门都谈不上，代码里可能会有不少不合理的地方，但是还是想把过程记录一下，供他人参考，希望可以为 Rust 社区做一些微不足道的贡献，也算是抛砖引玉吧。&lt;&#x2F;p&gt;
&lt;p&gt;成品 bot 在 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;EAimTY&#x2F;eaimty_bot&quot;&gt;EAimTY&#x2F;eaimty_bot&lt;&#x2F;a&gt;。这是个用来练手的小项目，不止有 OCR 功能，还有一些其它的杂七杂八的功能。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;h1 id=&quot;she-ji-luo-ji&quot;&gt;设计逻辑&lt;&#x2F;h1&gt;
&lt;p&gt;先来设计一下这个 OCR bot 的逻辑：&lt;&#x2F;p&gt;
&lt;p&gt;用户通过 &lt;code&gt;&#x2F;ocr&lt;&#x2F;code&gt; 命令触发流程开始，bot 以一条带按钮的消息要求选择 OCR 的目标语言回复触发消息，在用户选择目标语言后显示选择的语言并提供“重新选择”选项，bot 接收用户回复的消息，如果收到的是图片就用 Tesseract 进行处理，最后发送 OCR 结果，整个流程结束。
在整个流程中需要确保只有触发流程开始的用户才可以通过点击按钮选择 OCR 目标语言，并且只接受触发流程开始的用户发送的图片。&lt;&#x2F;p&gt;
&lt;p&gt;整理一下，整个流程分为 3 个部分：&lt;code&gt;&#x2F;ocr&lt;&#x2F;code&gt; 命令触发，处理用户点击按钮选择语言，处理用户发送的图片。
由于每个步骤之间都是独立的，因此需要引入 session 保存状态。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;xuan-ze-kuang-jia-yu-ku&quot;&gt;选择框架与库&lt;&#x2F;h1&gt;
&lt;p&gt;用 Rust 实现的 Bot API 框架列表可以在 Telegram 官方的 &lt;a href=&quot;https:&#x2F;&#x2F;core.telegram.org&#x2F;bots&#x2F;samples#rust&quot;&gt;Bot Code Examples#Rust&lt;&#x2F;a&gt; 中找到。&lt;&#x2F;p&gt;
&lt;p&gt;其中的 &lt;a href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;carapax&quot;&gt;carapax&lt;&#x2F;a&gt; 基于另一个框架 &lt;a href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;tgbot&quot;&gt;tgbot&lt;&#x2F;a&gt; ，是同一个作者的作品，加了一些杂七杂八的功能方便开箱即用。写这个简单的 OCR bot 用不到那么多组件，所以我直接用了 tgbot。&lt;&#x2F;p&gt;
&lt;p&gt;对于 OCR，最好的开源实现肯定是 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tesseract-ocr&#x2F;tesseract&quot;&gt;Tesseract&lt;&#x2F;a&gt;。Tesseract 在 Rust 下的 binding 有 &lt;a href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;tesseract&quot;&gt;tesseract&lt;&#x2F;a&gt; 和 &lt;a href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;leptess&quot;&gt;leptess&lt;&#x2F;a&gt;，我这里用的是 leptess。&lt;&#x2F;p&gt;
&lt;p&gt;编译 Bot 程序的机器上必须装 Tesseract、Leptonica 和 clang，否则会编译失败。运行 Bot 程序的机器上必须装 Tesseract（&lt;strong&gt;与编译的机器上必须是同一大版本&lt;&#x2F;strong&gt;） 和需要 OCR 的 Tesseract 数据包，比如 eng、jpn、chi_sim 和 chi_tra。&lt;&#x2F;p&gt;
&lt;p&gt;有了 Telegram Bot API 框架和 OCR 库，就可以开始动手了。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;shi-xian-session-cun-chu&quot;&gt;实现 session 存储&lt;&#x2F;h1&gt;
&lt;p&gt;在写 bot handler 前，先要把 session 部分搓出来，后面才能用 session 保存 OCR 流程的状态。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ding-yi-zhi-chi-de-ocr-yu-yan&quot;&gt;定义支持的 OCR 语言&lt;&#x2F;h2&gt;
&lt;p&gt;假设这个 OCR bot 支持 4 种语言：English、日本語、简体中文、繁體中文。对应的 Tesseract 的语言包名是 &amp;quot;eng&amp;quot;、&amp;quot;jpn&amp;quot;、&amp;quot;chi_sim&amp;quot;、&amp;quot;chi_tra&amp;quot;。为了把两者联系起来并且方便存储，定义一个枚举：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;#[derive(Clone, Copy)]
enum Language {
    English,
    Japanese,
    SimplifiedChinese,
    TraditionalChinese,
}

impl Language {
    &amp;#x2F;&amp;#x2F; 语言的数据包名称
    const ENG: &amp;amp;&amp;#x27;static str = &amp;quot;eng&amp;quot;;
    const JPN: &amp;amp;&amp;#x27;static str = &amp;quot;jpn&amp;quot;;
    const CHI_SIM: &amp;amp;&amp;#x27;static str = &amp;quot;chi_sim&amp;quot;;
    const CHI_TRA: &amp;amp;&amp;#x27;static str = &amp;quot;chi_tra&amp;quot;;

    fn from_tesseract_data_str(s: &amp;amp;str) -&amp;gt; Option&amp;lt;Self&amp;gt; {
        match s {
            Self::ENG =&amp;gt; Some(Self::English),
            Self::JPN =&amp;gt; Some(Self::Japanese),
            Self::CHI_SIM =&amp;gt; Some(Self::SimplifiedChinese),
            Self::CHI_TRA =&amp;gt; Some(Self::TraditionalChinese),
            _ =&amp;gt; None,
        }
    }

    fn as_tesseract_data_str(&amp;amp;self) -&amp;gt; &amp;amp;&amp;#x27;static str {
        match self {
            Self::English =&amp;gt; Self::ENG,
            Self::Japanese =&amp;gt; Self::JPN,
            Self::SimplifiedChinese =&amp;gt; Self::CHI_SIM,
            Self::TraditionalChinese =&amp;gt; Self::CHI_TRA,
        }
    }

    &amp;#x2F;&amp;#x2F; 用来遍历所有支持语言的迭代器，之后生成语言选择键盘的时候会用到
    fn iter() -&amp;gt; impl Iterator&amp;lt;Item = Self&amp;gt; {
        [
            Self::English,
            Self::Japanese,
            Self::SimplifiedChinese,
            Self::TraditionalChinese,
        ]
        .into_iter()
    }
}

&amp;#x2F;&amp;#x2F; 语言的显示名称
impl Display for Language {
    fn fmt(&amp;amp;self, f: &amp;amp;mut Formatter&amp;lt;&amp;#x27;_&amp;gt;) -&amp;gt; fmt::Result {
        let lang_name = match self {
            Self::English =&amp;gt; &amp;quot;English&amp;quot;,
            Self::Japanese =&amp;gt; &amp;quot;日本語&amp;quot;,
            Self::SimplifiedChinese =&amp;gt; &amp;quot;简体中文&amp;quot;,
            Self::TraditionalChinese =&amp;gt; &amp;quot;繁體中文&amp;quot;,
        };

        write!(f, &amp;quot;{lang_name}&amp;quot;)
    }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;she-ji-session&quot;&gt;设计 session&lt;&#x2F;h2&gt;
&lt;p&gt;怎么区分每个 OCR 流程的会话？最简单的就是用触发 “&#x2F;ocr” 命令的 tg 消息的 id 了。用 &lt;code&gt;[chat_id, message_id]&lt;&#x2F;code&gt; 就可以保证会话标识的唯一性。&lt;&#x2F;p&gt;
&lt;p&gt;但是这里有个问题：用户点击语言选择按钮时，可以通过按钮所依附消息的 &lt;code&gt;reply_to&lt;&#x2F;code&gt; 属性得到最初触发 OCR 流程的命令信息的 id，也就是上面的 &lt;code&gt;message_id&lt;&#x2F;code&gt;。但是在后面用户回复要识别的图片时，bot 收到的消息的 &lt;code&gt;reply_to&lt;&#x2F;code&gt; 属性不是 &lt;code&gt;message_id&lt;&#x2F;code&gt;，而是要求用户选择语言的消息的 id，这里叫它 &lt;code&gt;relay_id&lt;&#x2F;code&gt;。想要通过 &lt;code&gt;relay_id&lt;&#x2F;code&gt; 得到 &lt;code&gt;message_id&lt;&#x2F;code&gt;，就需要某种映射：建立一个 &lt;code&gt;HashMap&amp;lt;[i64; 2], i64&amp;gt;&lt;&#x2F;code&gt;，其中 key 是 &lt;code&gt;[chat_id, relay_id]&lt;&#x2F;code&gt;，value 是 &lt;code&gt;message_id&lt;&#x2F;code&gt;，这样就能解决这个问题了。&lt;&#x2F;p&gt;
&lt;p&gt;session 里需要存什么？首先是触发 “&#x2F;ocr” 命令的 tg 用户 id，用来验证点击选择语言按钮和发送图片的的用户。然后是用户选择的目标语言，用户可能还没有选择语言，所以要用 &lt;code&gt;Option&lt;&#x2F;code&gt; 包起来抽象。&lt;&#x2F;p&gt;
&lt;p&gt;综上，session 的存储结构就出来了：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;struct SessionPool {
    sessions: HashMap&amp;lt;[i64; 2], Session&amp;gt;,
    relay: HashMap&amp;lt;[i64; 2], i64&amp;gt;,
}

struct Session {
    user: i64,
    lang: Option&amp;lt;Language&amp;gt;,
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;la-ji-hui-shou&quot;&gt;垃圾回收&lt;&#x2F;h2&gt;
&lt;p&gt;试想你的 bot 受到了洪泛，有一大堆触发但没有完成的 OCR 流程 session。虽然每条 session 需要的存储空间很小，但迟早还是会爆内存，所以肯定需要定时清理长时间没有完成的 session。
给 &lt;code&gt;Session&lt;&#x2F;code&gt; 加两个成员，顺便加上用来创建的关联函数：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;struct Session {
    user: i64,
    lang: Option&amp;lt;Language&amp;gt;,

    &amp;#x2F;&amp;#x2F; session 的创建时间，垃圾回收时，如果检查到这个 session 已经存在超过了设定的时间，就把它清理掉
    c_time: Instant, 

    &amp;#x2F;&amp;#x2F; 用来存储在 `SessionPool` 的 `relay` 表中对应的记录的键名。垃圾回收时也同时要清除掉。在用户点击语言选择按钮前， bot 不知道自己发出的语言选择消息的 id，这时的值是 `None`。点击选择语言按钮后，值变成 `Some([chat_id, relay_id])`
    relay: Option&amp;lt;[i64; 2]&amp;gt;, 
}

impl Session {
    fn new(user_id: i64) -&amp;gt; Self {
        Self {
            user: user_id,
            lang: None,
            relay: None,
            c_time: Instant::now(),
        }
    }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这样就能写出垃圾回收方法：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;impl SessionPool {
    fn collect_garbage(&amp;amp;mut self, lifetime: Duration) {
        self.sessions.retain(|_, Session { c_time, relay, .. }| {
            &amp;#x2F;&amp;#x2F; 创建时间与现在的时间差超过 lifetime 的删掉，`relay` 表里的也别忘记删
            if c_time.elapsed() &amp;lt; lifetime {
                true
            } else {
                relay.map(|relay| self.relay.remove(&amp;amp;relay));
                false
            }
        });
    }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;需要每隔一段时间就运行一次上面的垃圾回收方法。
bot handler 处理每个 update，以及垃圾回收都是并行的，所以要包成 &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;SessionPool&amp;gt;&amp;gt;&lt;&#x2F;code&gt; 防止数据竞争。
写出&lt;code&gt;SessionPool&lt;&#x2F;code&gt; 的创建函数：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;impl SessionPool {
    fn new() -&amp;gt; Arc&amp;lt;Mutex&amp;lt;Self&amp;gt;&amp;gt; {
        let pool = Arc::new(Mutex::new(Self {
            sessions: HashMap::new(),
            relay: HashMap::new(),
        }));

        let lifetime = Duration::from_secs(3600); &amp;#x2F;&amp;#x2F; session 存在的最长时间
        let gc_period = Duration::from_secs(3); &amp;#x2F;&amp;#x2F; session 垃圾回收的间隔

        let mut interval = time::interval(gc_period);

        let pool_for_gc = pool.clone();

        &amp;#x2F;&amp;#x2F; spawn 出去一个 task，每隔 `gc_period` 时间运行一次垃圾回收
        tokio::spawn(async move {
            loop {
                interval.tick().await;

                let mut pool = pool_for_gc.lock().await;
                pool.collect_garbage(lifetime);
            }
        });

        pool
    }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;需要注意，这里的 &lt;code&gt;Mutex&lt;&#x2F;code&gt; 不是必须要用 tokio 的异步版本，因为只有涉及到 I&#x2F;O 操作的需要长时间等待才推荐用异步锁，对于其它情况，异步锁的 await 过程反而会增加开销。推荐用 &lt;a href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;parking_lot&quot;&gt;parking_lot&lt;&#x2F;a&gt; 库里的锁。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;bot-yu-handler&quot;&gt;bot 与 handler&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;da-hao-kuang-jia&quot;&gt;搭好框架&lt;&#x2F;h2&gt;
&lt;p&gt;tgbot 这个框架用起来特别简单。
为了方便，错误处理用了 &lt;code&gt;anyhow::Result&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;#[tokio::main]
async fn main() {
    let token = &amp;quot;TG_BOT_API_TOKEN&amp;quot;; &amp;#x2F;&amp;#x2F; 这里是 bot 的 api token
    let api = Api::new(token).unwrap();
    LongPoll::new(api.clone(), Handler::new(api)).run().await;
}

#[derive(Clone)]
struct Handler {
    api: Arc&amp;lt;Api&amp;gt;,
    pool: Arc&amp;lt;Mutex&amp;lt;SessionPool&amp;gt;&amp;gt;,
}

impl Handler {
    fn new(api: Api) -&amp;gt; Self {
        Self {
            api: Arc::new(api),
            pool: SessionPool::new(),
        }
    }
}

impl UpdateHandler for Handler {
    type Future = BoxFuture&amp;lt;&amp;#x27;static, ()&amp;gt;;

    fn handle(&amp;amp;self, update: Update) -&amp;gt; Self::Future {
        let cx = self.clone();

        Box::pin(async move {
            let res = match update.kind {
                UpdateKind::CallbackQuery(cb_query) =&amp;gt; {
                    handle_ocr_callback_query(&amp;amp;cx, &amp;amp;cb_query).await
                }
                &amp;#x2F;&amp;#x2F; bot 的命令也算一条 message，所以处理触发命令和用户回复的图片都在这个分支。从 `Message` 尝试转换成 `Command` 过程会消耗 `Message` 本身，所以在 `handle_ocr_message()` 处理完并且没有匹配到操作时再尝试转换成 `Command` 交给 `handle_ocr_command()`
                &amp;#x2F;&amp;#x2F; 这里用了不稳定特性 `try block`，把两个 handler 的结果合并成一个 `Result&amp;lt;()&amp;gt;`。自己手写 match 也能达到一样的效果，只是写出来有点丑而已
                UpdateKind::Message(msg) =&amp;gt; try {
                    if !handle_ocr_message(&amp;amp;cx, &amp;amp;msg).await? {
                        if let Ok(cmd) = Command::try_from(msg) {
                            handle_ocr_command(&amp;amp;cx, &amp;amp;cmd).await?
                        }
                    }
                }
                _ =&amp;gt; Ok(()),
            };

            &amp;#x2F;&amp;#x2F; 遇到错误时打印到 stderr
            if let Err(err) = res {
                eprintln!(&amp;quot;{err}&amp;quot;);
            }
        })
    }
}

&amp;#x2F;&amp;#x2F; 处理收到的命令的 handler
async fn handle_ocr_command(cx: &amp;amp;Handler, cmd: &amp;amp;Command) -&amp;gt; Result&amp;lt;()&amp;gt; {
    todo!()
}

&amp;#x2F;&amp;#x2F; 处理收到的消息附加键盘点击时的 callback query 的 handler
async fn handle_ocr_callback_query(cx: &amp;amp;Handler, cb_query: &amp;amp;CallbackQuery) -&amp;gt; Result&amp;lt;()&amp;gt; {
    todo!()
}

&amp;#x2F;&amp;#x2F; 处理收到的所有消息的 handler，只有消息发送方用户对应了 session，并且消息是图片并回复了要求用户选择语言的消息，此时返回 `Ok(true)`，没有匹配成功的话返回 `Ok(false)`
async fn handle_ocr_message(cx: &amp;amp;Handler, msg: &amp;amp;Message) -&amp;gt; Result&amp;lt;bool&amp;gt; {
    todo!()
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;chu-li-shou-dao-de-ming-ling&quot;&gt;处理收到的命令&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;async fn handle_ocr_command(cx: &amp;amp;Handler, cmd: &amp;amp;Command) -&amp;gt; Result&amp;lt;()&amp;gt; {
    if cmd.get_name() == &amp;quot;&amp;#x2F;ocr&amp;quot; {
        let msg = cmd.get_message();

        if let Some(user_id) = msg.get_user_id() {
            let chat_id = msg.get_chat_id();
            let msg_id = msg.id;

            let mut pool = cx.pool.lock().await;
            let session = Session::new(user_id);
            pool.sessions.insert([chat_id, msg_id], session);

            let send_message = SendMessage::new(chat_id, &amp;quot;请选择 OCR 目标语言&amp;quot;)
                .reply_markup(get_lang_select_keyboard())
                .reply_to_message_id(msg_id);

            drop(pool);

            cx.api.execute(send_message).await?;
        }
    }

    Ok(())
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这里注意中间的 &lt;code&gt;drop(pool)&lt;&#x2F;code&gt;。在执行涉及耗时长 I&#x2F;O 的异步操作前先把锁释放掉，否则在 bot 与 telegram 服务器通讯的整个过程里锁都是被独占并堵住的。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;chu-li-shou-dao-de-xiao-xi-fu-jia-jian-pan-dian-ji-shi-de-callback-query&quot;&gt;处理收到的消息附加键盘点击时的 callback query&lt;&#x2F;h2&gt;
&lt;p&gt;下面可以看出 Rust 的解构能力和抽象能力非常强，而且是零成本的。&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;async fn handle_ocr_callback_query(cx: &amp;amp;Handler, cb_query: &amp;amp;CallbackQuery) -&amp;gt; Result&amp;lt;()&amp;gt; {
    if let CallbackQuery {
        id,
        from: user,
        message: Some(msg),
        data: Some(cb_data),
        ..
    } = cb_query
    {
        if let (Some(data), Some(cmd_msg)) = (parse_callback_data(cb_data), &amp;amp;msg.reply_to) {
            let cmd_msg_id = cmd_msg.id;
            let msg_id = msg.id;
            let chat_id = msg.get_chat_id();
            let user_id = user.id;

            let mut pool = cx.pool.lock().await;

            if let Some(session) = pool.sessions.get_mut(&amp;amp;[chat_id, cmd_msg_id]) {
                if session.user == user_id {
                    let edit_message = if let CallbackData::Select(lang) = data {
                        session.lang = Some(lang);
                        session.relay = Some([chat_id, msg_id]);
                        pool.relay.insert([chat_id, msg_id], cmd_msg_id);

                        EditMessageText::new(
                            chat_id,
                            msg_id,
                            format!(&amp;quot;目标语言：{lang}，请以需要识别的图片回复此条消息（以图片方式发送）&amp;quot;),
                        )
                        .reply_markup(get_lang_unselect_keyboard())
                    } else {
                        session.lang = None;

                        EditMessageText::new(chat_id, msg_id, &amp;quot;请选择 OCR 目标语言&amp;quot;)
                            .reply_markup(get_lang_select_keyboard())
                    };

                    let answer_callback_query = AnswerCallbackQuery::new(id);

                    drop(pool);

                    tokio::try_join!(
                        cx.api.execute(edit_message),
                        cx.api.execute(answer_callback_query)
                    )?;
                } else {
                    drop(pool);

                    let answer_callback_query = AnswerCallbackQuery::new(id)
                        .text(&amp;quot;不是命令触发者&amp;quot;)
                        .show_alert(true);

                    cx.api.execute(answer_callback_query).await?;
                }
            } else {
                drop(pool);

                let answer_callback_query = AnswerCallbackQuery::new(id)
                    .text(&amp;quot;找不到会话&amp;quot;)
                    .show_alert(true);

                cx.api.execute(answer_callback_query).await?;
            }
        }
    }

    Ok(())
}

&amp;#x2F;&amp;#x2F; 把收到操作抽象为一个枚举：选择语言和取消选择
enum CallbackData {
    Select(Language),
    Unselect,
}

&amp;#x2F;&amp;#x2F; 解析 callback query 携带的字符串
fn parse_callback_data(data: &amp;amp;str) -&amp;gt; Option&amp;lt;CallbackData&amp;gt; {
    let mut data = data.split(&amp;#x27;-&amp;#x27;);

    if let (Some(&amp;quot;ocr&amp;quot;), Some(target), None) = (data.next(), data.next(), data.next()) {
        if target == &amp;quot;unselect&amp;quot; {
            return Some(CallbackData::Unselect);
        } else if let Some(lang) = Language::from_tesseract_data_str(target) {
            return Some(CallbackData::Select(lang));
        }
    }

    None
}

&amp;#x2F;&amp;#x2F; 生成选择语言列表按钮
fn get_lang_select_keyboard() -&amp;gt; InlineKeyboardMarkup {
    let vec = Language::iter()
        .map(|lang| {
            vec![InlineKeyboardButton::new(
                lang.to_string(),
                InlineKeyboardButtonKind::CallbackData(format!(
                    &amp;quot;ocr-{}&amp;quot;,
                    lang.as_tesseract_data_str()
                )),
            )]
        })
        .collect();

    InlineKeyboardMarkup::from_vec(vec)
}

&amp;#x2F;&amp;#x2F; 生成重新选择语言按钮
fn get_lang_unselect_keyboard() -&amp;gt; InlineKeyboardMarkup {
    let vec = vec![vec![InlineKeyboardButton::new(
        &amp;quot;重新选择&amp;quot;,
        InlineKeyboardButtonKind::CallbackData(String::from(&amp;quot;ocr-unselect&amp;quot;)),
    )]];

    InlineKeyboardMarkup::from_vec(vec)
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;chu-li-shou-dao-de-xiao-xi&quot;&gt;处理收到的消息&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;async fn handle_ocr_message(cx: &amp;amp;Handler, msg: &amp;amp;Message) -&amp;gt; Result&amp;lt;bool&amp;gt; {
    if let (MessageData::Photo { data, .. }, Some(user_id), Some(relay_msg)) =
        (&amp;amp;msg.data, msg.get_user_id(), msg.reply_to.as_ref())
    {
        let msg_id = msg.id;
        let chat_id = msg.get_chat_id();
        let relay_msg_id = relay_msg.id;

        let mut pool = cx.pool.lock().await;

        if let Some(cmd_msg_id) = pool.relay.get(&amp;amp;[chat_id, relay_msg_id]).copied() {
            if let Some(Session {
                user,
                lang: Some(lang),
                ..
            }) = pool.sessions.get(&amp;amp;[chat_id, cmd_msg_id])
            {
                if user_id == *user {
                    let lang = *lang;

                    pool.sessions.remove(&amp;amp;[chat_id, cmd_msg_id]);
                    pool.relay.remove(&amp;amp;[chat_id, relay_msg_id]);

                    drop(pool); &amp;#x2F;&amp;#x2F; 及早释放掉锁，越早越好

                    let PhotoSize { file_id, .. } = unsafe {
                        data.iter()
                            .max_by(|a, b| (a.width, a.height).cmp(&amp;amp;(b.width, b.height)))
                            .unwrap_unchecked()
                    }; &amp;#x2F;&amp;#x2F; 根据 telegram bot 的文档，返回的图片文件列表不可能为空，所以这里 unwrap 是安全的，可以直接用 unchecked 的 unwrap 减少开销

                    let get_file = GetFile::new(file_id);

                    if let File {
                        file_path: Some(path),
                        ..
                    } = cx.api.execute(get_file).await?
                    {
                        let mut stream = cx.api.download_file(path).await?;

                        let mut pic = Vec::new();

                        while let Some(chunk) = stream.next().await {
                            pic.put_slice(&amp;amp;chunk?);
                        }

                        let mut leptess = LepTess::new(None, lang.as_tesseract_data_str())?;
                        leptess.set_image_from_mem(&amp;amp;pic)?;
                        let res = leptess.get_utf8_text()?;

                        let send_message =
                            SendMessage::new(chat_id, res).reply_to_message_id(msg_id);

                        cx.api.execute(send_message).await?;
                    } else {
                        let send_message =
                            SendMessage::new(chat_id, &amp;quot;图片获取失败&amp;quot;).reply_to_message_id(msg_id);

                        cx.api.execute(send_message).await?;
                    }

                    return Ok(true);
                }
            }
        }
    }

    Ok(false)
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;到此为止，我们用四百行代码就写出了一个安全、高性能的 OCR telegram bot。我把全部代码放到了 gist 一份：
&lt;a href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;EAimTY&#x2F;451f7d48ade325777303b7d062039eb9&quot;&gt;https:&#x2F;&#x2F;gist.github.com&#x2F;EAimTY&#x2F;451f7d48ade325777303b7d062039eb9&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>牛年伊始，自我总结</title>
          <pubDate>Sat, 13 Feb 2021 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2021/summary-of-2020/</link>
          <guid>https://www.eaimty.com/2021/summary-of-2020/</guid>
          <description>&lt;p&gt;又是整整一年过去，是时候该总结一下之前的一年了。&lt;&#x2F;p&gt;
&lt;p&gt;过去的一年我干了什么？无非只是吃饭、睡觉、摸鱼、应付考试而已。有时候心血来潮会学习或写一些新东西，可惜都是三天打鱼两天晒网，没有向哪个方向深入就停下了。深知自己没什么技术，可是却一直提不起干劲。&lt;&#x2F;p&gt;
&lt;p&gt;由于大流行，去年有近9个月的时间都是在家中度过的，经常熬夜，靠外卖度日，下半年回到学校后也是经常摸鱼。过了这样一年“懒散”的生活，再加上作息与饮食的不规律，我感觉自己变“老”了。这里的“老”指的并不是年龄的增长，而是指精神状态变差。我能实在地感觉到自己的思维貌似变慢了，敏锐不再，而且对思考新问题的兴趣也变低了。我之前经常嘲讽蔑视那些因循保守，不愿意接触新事物的“笼中人”。然而现在我觉得自己也在慢慢地成为他们其中的一员。&lt;&#x2F;p&gt;
&lt;p&gt;不知这种情况是由何导致的，但是看来今年必须要处理一下了。从高中后半段开始，我就感觉自己的精神状态一天比一天差，持续到现在，睡眠质量也不断下降，而且由于身体原因，我一直以来都很少运动。在高中前半段时我还很瘦，然而现在已经变成中等偏胖的体型了。这可能是甲减和睾酮低的症状？所以我准备从适量运动开始改善精神状态。当然，这些都是推测，但运动总归是利大于弊的。饮食和睡眠也要更规律一些，不能像之前一样经常熬夜到两三点或者一天只吃一顿饭。
还有就是尽量多做一些“正经事”。很多时候我就算觉得无聊，无事可做，也不愿意学或写点新东西。之后尽量督促自己多做更有意义的事吧。&lt;&#x2F;p&gt;
&lt;p&gt;本来准备1月初就写出这篇文章，但是由于期末考试与放寒假之后的习惯性懒散，现在才静下心来总结。发文励己，今年加油吧。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>从 Pixel 2 XL 换到 Pixel 4</title>
          <pubDate>Thu, 03 Dec 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2020/pixel-4/</link>
          <guid>https://www.eaimty.com/2020/pixel-4/</guid>
          <description>&lt;p&gt;在服役几乎整整两年后，我的 Pixel 2 XL 终于迎来了退休。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;h1 id=&quot;wei-shi-yao-yao-huan-pixel-4&quot;&gt;为什么要换 Pixel 4？&lt;&#x2F;h1&gt;
&lt;p&gt;Google 在 2020 年的发布会彻底搅乱了整个 Pixel 产品线。
不以“a”结尾的 Pixel 系列向来都是旗舰，代表 Android 阵营的最高水平。就算 Pixel 5 在除了 Soc 以外的其它方面做得还不错，但是终归算不上是旗舰机。最重要的是，即使换了更差的 Soc，Pixel 5 的价格还是那么高，综合来看，连 OnePlus Nord 都打不过？&lt;&#x2F;p&gt;
&lt;p&gt;所以我买了上一年的旗舰。Pixel 4 的价格已经比发布时低了很多，而且机器本身也绝对算不上落伍。
用了这些年大屏手机后我开始怀念小屏了，那种~~一只手就能抓过来~~单手操控的感觉才是最爽的，所以我选了 4 而不是 4 XL。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;s4uY617pqTHNl5W.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;我入手的是黑色版，因为后面要贴贴纸，其它颜色不太搭。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;yao-kua-de&quot;&gt;要夸的&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;ping-mu&quot;&gt;屏幕&lt;&#x2F;h2&gt;
&lt;p&gt;要说第一感受，那肯定是“流畅”。90Hz 相较于 60Hz 是巨大的提升，习惯了高刷屏后看所有 60Hz 都感觉不够流畅。
个人感觉，高刷新率手机屏幕比电脑屏幕对使用体验的提升大得多──毕竟在电脑屏幕上能感受到高刷新率优势的场景只有竞技性强节奏快的游戏，而且用鼠标操控的大屏幕桌面设备的操作逻辑重点在于“精确”，这一点从大部分桌面应用在滚动时都没有做近似于摩擦力缓冲的 smooth scrolling 效果就能看出。在手机上则不同，高刷屏让滚动时更清晰、更“跟手”，在日常使用中很容易感受到。&lt;&#x2F;p&gt;
&lt;p&gt;在如色彩之类的其它方面，Pixel 4 的屏幕也足够优秀，至少比 Pixel 2 XL 的垃圾屏幕好很多。而且对于防烧屏方面 Google 在近几个版本的 Android 里也做了很多，基本能保证屏幕“烧得均匀”。&lt;&#x2F;p&gt;
&lt;p&gt;但是非 XL 版本的 Pixel 4 分辨率只有 1080p，再加上 OLED 的像素排列方式，在近距离下颗粒感非常明显。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ying-xiang-xi-tong&quot;&gt;影像系统&lt;&#x2F;h2&gt;
&lt;p&gt;Pixel 系列的影像系统向来都是大卖点，特别是夜间拍照方面，算得上是数一数二的。
与拍照已经不错的二代相比，四代还是有不小的提升，下面是晚上随手拍的两张圣心大教堂：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;Tv2iYrEWXFtCfzI.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这是 Pixel 2 XL 拍出的效果。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;LjogziTC26QbKPY.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这是 Pixel 4 拍出的效果。&lt;&#x2F;p&gt;
&lt;p&gt;尽管两张图都是压缩过的，但仍能看出很多差别。Pixel 2 XL 拍出的画面整体偏绿，而 Pixel 4 拍出的色彩还原更准确，噪点更少，在原图上细节保留也更多。&lt;&#x2F;p&gt;
&lt;p&gt;用 Pixel 4 拍照是“所见即所得”的，取景框内就是最终成片的效果（夜景除外），这一点很好。&lt;&#x2F;p&gt;
&lt;p&gt;Pixel 4 比起前代多出了一颗长焦镜头，然而倍率比较低，拍远处景物时比起前几代有提升，但不是质变。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;cao-zuo-xi-tong&quot;&gt;操作系统&lt;&#x2F;h2&gt;
&lt;p&gt;买系统送手机，亲儿子系列一路用下来，换机也没什么迁移成本。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;yao-cai-de&quot;&gt;要踩的&lt;&#x2F;h1&gt;
&lt;p&gt;人脸识别，速度和精度都不错，毕竟靠的是雷达，无光情况下也能解锁，比起那些连景深感知都没有的前置摄像头做出的人脸识别高到不知道哪里去了。但是个人感觉和指纹识别比起来没有太大优势？大概 Google 也意识到了这点，所以 Pixel 5 才换回了指纹吧。&lt;&#x2F;p&gt;
&lt;p&gt;在最近瘟疫流行的情况下，戴着口罩解锁手机比登天还难。我认为解锁手机最好的方式应该是屏下指纹+虹膜这样的组合，只是现在还没有这样的手机。&lt;&#x2F;p&gt;
&lt;p&gt;然后是去年 Google 在发布会上大吹特吹的 Motion Sense 功能，由于专利问题只能在特定的几十个国家开启，其中当然不包括中国。在不支持的地区想要使用这功能需要 root，而且在某次系统更新后开启难度变大了，原本可用的 magisk 模块失效了，现在需要手动在终端执行几条命令才能开启，而且开启后还需要软重启一次。
刚拿到手机后我还为了这个功能解了 bootloader，但是尝试过后发现识别率堪忧，而且用处也不大，就算能 remap 体验也一般。所以之后我又把 bootloader 锁上了，还是 OTA 更重要。&lt;&#x2F;p&gt;
&lt;p&gt;然后是扬声器。Pixel 4 屏幕下方的扬声器是在中框上的，虽然最终播放出的效果与前置双扬声器差不多，但在横屏握持的时候手经常会挡住扬声器。外放音质倒是不错。&lt;&#x2F;p&gt;
&lt;p&gt;之后就是整机最大的问题：&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dian-chi-wen-ti&quot;&gt;电池问题&lt;&#x2F;h2&gt;
&lt;p&gt;一块臭肉坏了一锅好汤，这块 2800 mAh 的电池是整机最大的败笔。
关闭 Always on Display 的情况下，待机一晚上竟然能掉电百分之二十。
亮屏时间大概 4 个小时左右，基本上是垫底水平。
更要命的是，Pixel 4 的快充根本谈不上“快”，这么小的电池也要一个多小时才能充满。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;guan-yu-zhi-qian-de-pixel-2-xl&quot;&gt;关于之前的 Pixel 2 XL&lt;&#x2F;h1&gt;
&lt;p&gt;之前那台 Pixel 2 XL 应该是我使用过时间最长的手机了，之前我用过的手机中根本没有使用超过一年的。
尽管这台 Pixel 2 XL 已步入高龄，但实际使用体验仍然非常好，几乎感受不出它已是三年前的手机。不愧是人们口中的 the very best Pixel.
去年年底，我为了给这台 2 XL 续命，给它换了一块电池。然而偷鸡不成蚀把米，在拆机过程中搞坏了 NFC、Active Edge，还把GPS弄得半残，上扬声器也爆音严重。&lt;&#x2F;p&gt;
&lt;p&gt;由于这些问题，我收了一台成色完美的银色 Xperia XZ Premium 作为主力机。为什么要选 XZP 呢？因为它是最后一款保留索尼经典外观设计，并且有镜面银配色的手机。
很难想象一台与 Pixel 2 XL 同代的旗舰能差到如此地步。用着已经被时代抛弃的 LCD 屏，就算是 4K 屏在手机上也绝对算不上实用，况且还不是全时 4K，日常使用时都是合并 4 个像素到 1080p，就算这样续航还是尿崩。影像方面 XZP 也达不到旗舰的水准，真是白费了这么好的硬件。
索尼家的手机是只适合裱在框子里“看”的手机，除了外观以外其它方面做得都是稀烂。XZP 除了机身能当镜子以外，真的找不到什么优点。
一个月后，我把 XZP 丢进了垃圾堆，换回了那台 NFC 和扬声器报废、GPS 半残的 Pixel 2 XL。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;zong-jie&quot;&gt;总结&lt;&#x2F;h1&gt;
&lt;p&gt;总的来说让我比较失望。这次换机带给我体验上的提升远不如两年多前从 Nexus 6P 换到 Pixel 2 XL。
上次换机给我的感觉是，亲儿子系列在那两年时间里产生了质变，Pixel 2 XL 相比于 Nexus 6P 几乎在每个方面的提升都是巨大的。而这次换机给我的感觉截然不同，基本上就是牺牲了续航能力换到了高刷屏和好一点点的影像系统。
是 Google 停止进步了？还是整个手机产业停止进步了？&lt;&#x2F;p&gt;
&lt;h2 id=&quot;gou-mai-jian-yi&quot;&gt;购买建议&lt;&#x2F;h2&gt;
&lt;p&gt;如果你已经下定决心，雷打不动要买 Pixel 系列，我的建议是：
不要买”a“结尾的机型，也不要买 Pixel 5，由于电池原因更不要买 Pixel 4。除非你对小屏有特殊的执念，否则请选 Pixel 4 XL。这是 Pixel 系列目前在各方面最好的机器。&lt;&#x2F;p&gt;
&lt;p&gt;如果你的选择不限于 Pixel 系列，那就请不要再考虑 Pixel 了。时至今日，Pixel 剩下的优势已经不多了，甚至一台国产千元机都可能在硬件方面跟 Pixel 打得有来有回。
从性价比来讲，一加和小米都是不错的选择。但考虑到小米在软件方面的劣势以及它非常难解的 bootloader 锁，闭上眼睛买一加就对了。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>请不要让你的博客仅存在于互联网档案馆</title>
          <pubDate>Wed, 28 Oct 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2020/do-not-let-your-blog-exist-only-in-the-internet-archive/</link>
          <guid>https://www.eaimty.com/2020/do-not-let-your-blog-exist-only-in-the-internet-archive/</guid>
          <description>&lt;p&gt;晚上睡不着，于是翻了翻博客里之前的那些评论，发现之前的评论者们所留下的网址，已经有一大半无法访问了...&lt;&#x2F;p&gt;
&lt;p&gt;我觉得这是一件很可悲的事。人们认真创造的网页、写成的文字已不再存在于它们最初产生的地方了。&lt;&#x2F;p&gt;
&lt;p&gt;互联网档案馆是什么？它是一个非营利组织，运营着一个“时光机”项目，存档它的爬虫所能收集到的所有开放网页。
也就是说，当你的博客仅存在于互联网档案馆时，你的博客本身，就已经不存在了。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;p&gt;土豆写过一篇文章：&lt;a href=&quot;https:&#x2F;&#x2F;dmesg.app&#x2F;reason-of-this-blog.html&quot;&gt;《为什么我要写独立域名的博客》&lt;&#x2F;a&gt;，其中写到了他建立博客的原因。不论是不是“独立域名博客”，包括我在内的很多人应该都是抱着这种想法才开始写博客的。但是我更想讲的是“坚持下去”，不管内容多么简短、更新多么缓慢，也不要直接关掉网站，让博客无法访问。&lt;&#x2F;p&gt;
&lt;p&gt;在现在的环境下，能让大家看到微小的“你”的最佳方式就是通过高密度的信息载体──文字，也就是你的博客──不论是你自己通过web程序搭建的，还是建立在 wordpress.com 或是 GitHub Pages 之类这些已有平台上的。博客与社交平台不同，博客所记录并展现的更多地是你的想法。&lt;&#x2F;p&gt;
&lt;p&gt;博客是给大家看的，但同时也是留给你自己看的。就算访问量很少，甚至没有访客都没有关系，因为你自己也可以是访客。当你看到自己曾经写的文章时，也就看到了自己当初的想法。这种思维的碰撞会让人感到非常神奇，就像是真正的“时光机”一样，能让你感到你自己的变化。我觉得这才是博客的最大用途。&lt;&#x2F;p&gt;
&lt;p&gt;“EAimTY 的博客”的历史不是很长，从建立到现在只有短短不到5年时间。尽管如此，我每翻到曾经所写的文章时，都会感叹我自己想法的变化，而这些变化是在生活中难以察觉的。有时我会觉得自己曾写的文章幼稚，甚至会感到羞耻，这不就意味我有所提高吗？假如没有这种感觉，反而会说明自己没有长进。&lt;&#x2F;p&gt;
&lt;p&gt;我记得我博客文章的评论者们所留下的网址中有很多十分有趣的博客，各有各的特点，读起来也很开心，但是其中很多现在已经无法访问了。这很令人悲哀。&lt;&#x2F;p&gt;
&lt;p&gt;现在想要建立并运营一个博客的成本几乎为零，你可以找到免费的域名、免费的空间、免费的程序、免费的主题。唯一的成本可能就是时间──你需要花时间将自己的想法记录下来，但这绝对是很值的。&lt;&#x2F;p&gt;
&lt;p&gt;总之，就算是每年只更新一篇很短的文章，也请不要放弃你的博客，否则，你将会失去展示你有趣灵魂的机会，同时也会失去自己本应获得的成就感与快乐。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>对 Linux 下触控板按键、加速和手势的优化（libinput）</title>
          <pubDate>Fri, 11 Sep 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2020/optimize-touchpad-on-linux-with-libinput-driver/</link>
          <guid>https://www.eaimty.com/2020/optimize-touchpad-on-linux-with-libinput-driver/</guid>
          <description>&lt;p&gt;我们都知道 macOS 下的触控板体验非常好，而 Windows 下的触控板体验最近几年靠着厂商的跟进和 Windows Precision Drivers 也进步了不少。其实在 Linux 的 Xorg 下想要达到类似的体验也非常简单。&lt;&#x2F;p&gt;
&lt;p&gt;Xorg 下经常用到的触控板驱动有 &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;Touchpad_Synaptics&quot;&gt;Synaptics（已停止维护）&lt;&#x2F;a&gt;、&lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;Libinput&quot;&gt;libinput&lt;&#x2F;a&gt; 和 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;p2rkw&#x2F;xf86-input-mtrack&quot;&gt;mtrack&lt;&#x2F;a&gt;。这篇文章将会用 libinput 来优化改善触控板的按键、加速和手势设置。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;h1 id=&quot;suo-xu-ruan-jian-bao&quot;&gt;所需软件包&lt;&#x2F;h1&gt;
&lt;p&gt;首先需要的肯定是驱动本体，libinput 在各个发行版的官方仓库里都有打包，可以直接安装。&lt;&#x2F;p&gt;
&lt;p&gt;实现触控板手势需要 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bulletmark&#x2F;libinput-gestures&quot;&gt;libinput-gestures&lt;&#x2F;a&gt;，直接编译安装， Arch 系可以直接用 AUR 安装。&lt;&#x2F;p&gt;
&lt;p&gt;（可选）可以安装 libinput-gestures 的 GUI 前端 &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;cunidev&#x2F;gestures&quot;&gt;gestures&lt;&#x2F;a&gt; 用来设置 libinput-gestures，但我认为实在没有必要，因为配置文件改起来也不难。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;hong-kong-ban-jia-su-yu-an-jian-she-zhi&quot;&gt;触控板加速与按键设置&lt;&#x2F;h1&gt;
&lt;p&gt;首先查看 &lt;code&gt;&#x2F;etc&#x2F;X11&#x2F;xorg.conf.d&#x2F;&lt;&#x2F;code&gt; 下有没有已有的触控板配置文件，具体是看 Section 中是否有 &lt;code&gt;Identifier &amp;quot;touchpad&amp;quot;&lt;&#x2F;code&gt;。如果有就把这个 Section 删掉（最好先备份以防出问题）。
新建一个配置文件，优先级与文件名随意，如 &lt;code&gt;20-touchpad.conf&lt;&#x2F;code&gt;，内容如下：&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;Section &amp;quot;InputClass&amp;quot;
    # 限定此配置文件只适用于触控板
    Identifier &amp;quot;touchpad&amp;quot;
    MatchIsTouchpad &amp;quot;on&amp;quot;

    # 设定驱动为 libinput
    Driver &amp;quot;libinput&amp;quot;

    # 在后面插入具体配置
    # ...

EndSection
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;之后在文件中填入需要的配置。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ling-min-du-yu-shu-biao-jia-su&quot;&gt;灵敏度与鼠标加速&lt;&#x2F;h2&gt;
&lt;pre&gt;&lt;code&gt;    # 使用预设的触控板加速配置 2
    Option &amp;quot;AccelerationProfile&amp;quot; &amp;quot;2&amp;quot;

    # 设置灵敏度为 0.05
    Option &amp;quot;Sensitivity&amp;quot; &amp;quot;0.05&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;如果用起来不顺手，你可以尝试调整 &lt;code&gt;&amp;quot;Sensitivity&amp;quot;&lt;&#x2F;code&gt; 的值，毕竟每个触控板的大小和素质都不同。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;qing-hong-ji-dian-ji&quot;&gt;轻触即点击&lt;&#x2F;h2&gt;
&lt;p&gt;默认情况下 Xorg 不会将“轻点触控板”识别为点击，想要开启轻触即点击，加入：&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;    Option &amp;quot;Tapping&amp;quot; &amp;quot;on&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;xiu-gai-dan-zhi-shuang-zhi-san-zhi-dui-ying-de-zuo-zhong-you-jian&quot;&gt;修改单指、双指、三指对应的左、中、右键&lt;&#x2F;h2&gt;
&lt;p&gt;默认情况下 Xorg 会将单指视为左键、双指视为中键、三指视为右键，需要修改可以加入：&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;    Option &amp;quot;TappingButtonMap&amp;quot; &amp;quot;lrm&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;值中的 &lt;code&gt;l&lt;&#x2F;code&gt; 是 Left，&lt;code&gt;r&lt;&#x2F;code&gt; 是 Right，&lt;code&gt;m&lt;&#x2F;code&gt; 是 Mid，顺序为单、双、三指。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fan-zhuan-hong-kong-ban-gun-dong-de-fang-xiang&quot;&gt;反转触控板滚动的方向&lt;&#x2F;h2&gt;
&lt;p&gt;默认情况下 Xorg 的触控板滚动方向与 Windows 和 macOS 相反，觉得别扭可以开启“自然滚动”：&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;    Option &amp;quot;NaturalScrolling&amp;quot; &amp;quot;on&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;最终，我的配置文件 &lt;code&gt;20-touchpad.conf&lt;&#x2F;code&gt; 的内容是：&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;Section &amp;quot;InputClass&amp;quot;
    Identifier &amp;quot;touchpad&amp;quot;
    MatchIsTouchpad &amp;quot;on&amp;quot;
    Driver &amp;quot;libinput&amp;quot;
    Option &amp;quot;AccelerationProfile&amp;quot; &amp;quot;2&amp;quot;
    Option &amp;quot;Sensitivity&amp;quot; &amp;quot;0.05&amp;quot;
    Option &amp;quot;Tapping&amp;quot; &amp;quot;on&amp;quot;
    Option &amp;quot;TappingButtonMap&amp;quot; &amp;quot;lrm&amp;quot;
    Option &amp;quot;NaturalScrolling&amp;quot; &amp;quot;on&amp;quot;
EndSection
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h1 id=&quot;hong-kong-ban-shou-shi&quot;&gt;触控板手势&lt;&#x2F;h1&gt;
&lt;p&gt;触控板手势最好配合WM的快捷键使用，例如将四指左右扫动绑定为 Ctrl+Right&#x2F;Left，同时将 WM 的工作区切换也设置为同样的快捷键。
修改 libinput-gestures 的配置文件，&lt;code&gt;&#x2F;etc&#x2F;libinput-gestures.conf&lt;&#x2F;code&gt; 或是 &lt;code&gt;~&#x2F;.config&#x2F;libinput-gestures.conf&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;我的 &lt;code&gt;libinput-gestures.conf&lt;&#x2F;code&gt;：&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;# 识别阈值
swipe_threshold 0

# 切换工作区
gesture swipe left 4 xdotool key Ctrl+Right
gesture swipe right 4 xdotool key Ctrl+Left

# 切换窗口
gesture swipe left 3 xdotool key Alt+Right
gesture swipe right 3 xdotool key Alt+Left

# 显示&amp;#x2F;隐藏所有窗口
gesture swipe down 4 xdotool key Super+a

# 放大与缩小
gesture pinch in 2 xdotool key Ctrl+minus
gesture pinch out 2 xdotool key Ctrl+plus
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;最后启动 libinput-gestures，手势就会生效。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;kai-guan-hong-kong-ban&quot;&gt;开关触控板&lt;&#x2F;h1&gt;
&lt;p&gt;在不用触控板时可以将其关掉。利用 &lt;code&gt;xinput enable&#x2F;disable TOUCHPAD_ID&lt;&#x2F;code&gt; 可以开关触控板。&lt;&#x2F;p&gt;
&lt;p&gt;可以写一个脚本，执行就会开关触控板：&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;#!&amp;#x2F;bin&amp;#x2F;bash
TOUCHPAD_ID=`xinput list | grep &amp;quot;Touchpad&amp;quot; | sed -n -e &amp;#x27;s&amp;#x2F;^.*id=&amp;#x2F;&amp;#x2F;p&amp;#x27;|cut -f1`
if xinput list $TOUCHPAD_ID | grep -q &amp;#x27;This device is disabled&amp;#x27;; then
xinput enable $TOUCHPAD_ID
else
xinput disable $TOUCHPAD_ID
fi
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;再绑定一个键盘快捷键，就可以轻松开关触控板了。
需要注意，这个脚本不会停止 libinput-gestures 识别触控板手势，如果要把手势同时禁用掉，就在脚本里分别加入启动、停止 libinput-gestures 的命令即可。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>使用 Openbox 作为基底打造你自己的 Linux 桌面环境</title>
          <pubDate>Thu, 10 Sep 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2020/use-openbox-to-create-your-own-desktop-environment/</link>
          <guid>https://www.eaimty.com/2020/use-openbox-to-create-your-own-desktop-environment/</guid>
          <description>&lt;p&gt;这篇文章会教你如何使用 Openbox 作为基底打造你自己的 Linux 桌面环境。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;p&gt;很久没有写过这类教程了，如果发现文章中的问题或不足，可以在评论中告诉我😁&lt;&#x2F;p&gt;
&lt;h1 id=&quot;kai-shi-zhi-qian&quot;&gt;开始之前&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;openbox-shi-shi-yao&quot;&gt;Openbox 是什么？&lt;&#x2F;h2&gt;
&lt;p&gt;对于能够找到这篇文章的你，这应该不是你想问的问题吧...&lt;&#x2F;p&gt;
&lt;p&gt;Openbox 是一个 WM（Window Manager&#x2F;窗口管理器）用来显示并管理每个 GUI 程序的窗口。
经常被人们提到的 Gnome、KDE 之类的东西是 DE（Desktop Environment&#x2F;桌面环境）。一个 DE 通常会包括一个 DM（Display Manager&#x2F;显示管理器，通常用于用户登录并启动桌面环境）、一个 WM（用于显示窗口）、一个 compositor（或许可以翻译为“合成器”？用于渲染特效、透明效果等）和一大堆附加组件（如窗口列表、dock 栏、托盘）。
理论上讲，各个 DE 中的每个部分都可以被替换掉，例如用 LightDM 替换掉 Gnome 自带的 DM：GDM，或用 openbox 替换掉 XFCE 自带的 WM：xfwm。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wei-shi-yao-bu-yong-yi-you-de-zhuo-mian-huan-jing&quot;&gt;为什么不用已有的桌面环境？&lt;&#x2F;h2&gt;
&lt;p&gt;原因如下：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;打包好的桌面环境总会包含你不需要的组件。这就像你去饭店点了一份不要辣椒的菜，结果上的菜里还是有辣椒，老板说这辣椒是送你的，免费的──你觉得 compositor 影响性能，不想要它，但大多数桌面环境（就算是最小化安装的也一样）都会“免费”送你一个，虽说你可以禁用掉它，但是有你完全用不到的组件在你的系统里，还由于软件包依赖问题无法卸装，这还是挺恶心的。&lt;&#x2F;li&gt;
&lt;li&gt;重量级的桌面环境总是会有类似于 Windows 注册表的东西，例如 dconf。一个文本配置文件就能解决的问题，非要搞得这么复杂。&lt;&#x2F;li&gt;
&lt;li&gt;你很难搞清桌面环境中每一个组件、每一个软件包的用途，这导致出现 bug 时的问题排查与解决变得非常麻烦复杂。&lt;&#x2F;li&gt;
&lt;li&gt;你就是喜欢折腾，你就是喜欢与众不同🥴&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;自己动手拼凑桌面环境就不会有这些问题。你喜欢 LXDE 的 Panel、Xfce 的终端、Gnome 的截图工具？没有任何问题，你可以你自己喜欢的所有组件放在一起。这就是不使用已有桌面环境的最大优点。&lt;&#x2F;p&gt;
&lt;p&gt;你的桌面环境是什么样的并不重要，重要的是它是否能让你用得顺手，符合你的习惯，提升你的效率。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wei-shi-yao-xuan-yong-openbox&quot;&gt;为什么选用 Openbox？&lt;&#x2F;h2&gt;
&lt;p&gt;Openbox 可以说是一个较为“传统”的窗口管理器。它并不支持 wayland，只能运行在传统的 X11 上，但这也保证了它的稳定性，并且对 N 卡有更好的支持。
另外，Openbox 在交互上偏向于使用鼠标。虽然你可以按照你的喜好设置一大堆快捷键，但如果你对键盘操作特别钟情，或是讨厌操作鼠标，可能 &lt;a href=&quot;https:&#x2F;&#x2F;i3wm.org&quot;&gt;i3&lt;&#x2F;a&gt;（X11）、&lt;a href=&quot;https:&#x2F;&#x2F;swaywm.org&quot;&gt;sway&lt;&#x2F;a&gt;（wayland）类的桌面更适合你。&lt;&#x2F;p&gt;
&lt;p&gt;相较于重量级的 DE，如 Gnome、KDE，Openbox 极其轻量，并且基本不存在什么依赖。
对于其他轻量级 WM，openbox 可以说是配置起来最简单的，并且有几乎是最完备的生态和社区。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mu-biao-yu-zhun-ze&quot;&gt;目标与准则&lt;&#x2F;h2&gt;
&lt;p&gt;这篇文章会以我的桌面环境的配置为例，详细地介绍如何拼凑一个桌面环境。我会详细介绍我自己的方案，但也会给出其它方案，你可以按照自己的喜好来选择。就算WM相同，两个人的桌面也可以从外观和使用上完全不同。&lt;&#x2F;p&gt;
&lt;p&gt;我自己的准则：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;选择稳定、不频繁更改功能、但仍在开发中的组件&lt;&#x2F;li&gt;
&lt;li&gt;不使用已过时或已停止开发&#x2F;支持的组件，也不使用有已过时依赖（如python2）的组件&lt;&#x2F;li&gt;
&lt;li&gt;尽量使用图形库比较新的组件，也就是尽量不用使用了 GTK2&#x2F;QT4 的组件&lt;&#x2F;li&gt;
&lt;li&gt;效率优先，不在外观与特效上花太多心思，当然成品看起来也不能丑&lt;&#x2F;li&gt;
&lt;li&gt;在日常使用上，能用 GUI 解决的操作，绝不用 terminal&lt;&#x2F;li&gt;
&lt;li&gt;使用配置起来简单的组件，就是那些就算用直接改配置文件的方式更改设置也不会很难的组件&lt;&#x2F;li&gt;
&lt;li&gt;用到的所有组件都可以透过 Arch 官方源与 AUR 安装，当然手动编译也不难&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;再次强调，你不需要完全照搬我的配置，我也不推荐你这么做，毕竟只有尝试过更多方案才能选择出最适合你的。这正是 Linux 的精髓。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;pei-zhi-guo-cheng&quot;&gt;配置过程&lt;&#x2F;h1&gt;
&lt;p&gt;在这里，假设你的系统是 Arch（其他系统也大同小异，只不过可能需要手动编译一些组件），且系统中还没有安装桌面环境与 X Window System。&lt;&#x2F;p&gt;
&lt;p&gt;下面列出的程序全部都是没有过时或停止开发&#x2F;支持并且依赖也没有过时的。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ji-chu-ruan-jian-bao-x-window-system-display-manager-he-openbox&quot;&gt;基础软件包：X Window System、Display Manager 和 Openbox&lt;&#x2F;h2&gt;
&lt;p&gt;首先需要的是处在最底层的 X Window System。一般来说只需要 xorg-server 包和它的依赖。
另外，这里还需要安装你的显卡对应的 xf86-video 包，如 xf86-video-intel。如果你的机器同时有核显与独显，最好暂时只安装核显的包。&lt;&#x2F;p&gt;
&lt;p&gt;你还需要一个 DM 来启动 Openbox，我用的是 &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;LightDM&quot;&gt;LightDM&lt;&#x2F;a&gt;，当然还有其它选择，如 &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;LXDM&quot;&gt;LXDM&lt;&#x2F;a&gt;。顺便一提，如果你准备使用 LXDM 的话，要注意它有 GTK2 与 GTK3 两个版本，一般来说选择 GTK3 版本的比较好。
如果你和我一样选择了 LightDM，那么你还需要一个 Greeter 用来在 GUI 下登录。这里有很多选择，可以参考 &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;LightDM#Greeter&quot;&gt;Wiki 中的 Greeter 一节&lt;&#x2F;a&gt;。我使用的是默认的 lightdm-gtk-greeter，主要原因是它可以使用与桌面环境相同的 GTK 主题来保持界面风格的一致。&lt;&#x2F;p&gt;
&lt;p&gt;当然，不用 DM 也是完全可以的。利用 &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;Xinit&quot;&gt;Xinit&lt;&#x2F;a&gt; 你可以在终端中启动 X Window System，并且可以通过切 tty 来实现多用户。&lt;&#x2F;p&gt;
&lt;p&gt;最后，别忘了安装 Openbox。这里最好顺便装上 xterm，因为刚刚安装好的 Openbox 的应用菜单默认是硬编码的，常用的终端只有 xterm 在菜单中，后面将你喜欢的 GUI 终端添加到 Openbox 应用菜单后你可以直接将 xterm 卸装掉。不装 xterm 也没有任何问题，只不过稍微麻烦一点，最初几步中你需要切换到其它 tty 来执行命令。&lt;&#x2F;p&gt;
&lt;p&gt;将你的 DM 设置为自启后，重启电脑，不出意外的话重启后你会进入 DM 的登录界面。登录后，你便可以进入 Openbox。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-zhuo-mian-huan-jing-ke-yong&quot;&gt;使桌面环境“可用”&lt;&#x2F;h2&gt;
&lt;p&gt;进入一个未经配置的 Openbox，你只会看到黑色的背景，没有任何其它东西，鼠标右键会显示应用菜单。&lt;&#x2F;p&gt;
&lt;p&gt;这时你的桌面系统还远远达不到“可用”，至少还需要窗口列表和系统托盘。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;chuang-kou-lie-biao-yu-xi-tong-tuo-pan&quot;&gt;窗口列表与系统托盘&lt;&#x2F;h3&gt;
&lt;p&gt;这里有几种不同方案：&lt;&#x2F;p&gt;
&lt;h4 id=&quot;fang-an-yi-shi-yong-yi-ge-dock-lan-he-yi-ge-xi-tong-tuo-pan&quot;&gt;方案一：使用一个 Dock 栏和一个系统托盘&lt;&#x2F;h4&gt;
&lt;p&gt;这种情况下，你没有窗口列表来显示已打开的窗口，所以需要一个可以显示并重新打开最小化后的窗口的 Dock 栏，这里列出 2 个可选项：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;Cairo-Dock&quot;&gt;Cairo-Dock&lt;&#x2F;a&gt;──可定制程度比较高，但我个人不太喜欢，因为它不是很轻量，特效太多，而且一些功能需要 compositor 来实现。在我的笔记本（只开了核显）上打开它后散热风扇竟然都会开始转...&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;Plank&quot;&gt;Plank&lt;&#x2F;a&gt;──非常轻量化，可定制性也不错，只是缺少工作区切换的功能，但可以通过设置 Openbox 快捷键或使用其它小组件实现&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;窗口列表实现了，但你还需要一个系统托盘，可以选择 &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;Stalonetray&quot;&gt;Stalonetray&lt;&#x2F;a&gt;，或 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sargon&#x2F;trayer-srg&quot;&gt;trayer-srg&lt;&#x2F;a&gt;，或 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;taffybar&#x2F;taffybar&quot;&gt;taffybar&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;fang-an-er-shi-yong-yi-ge-panel&quot;&gt;方案二：使用一个 Panel&lt;&#x2F;h4&gt;
&lt;p&gt;这是我选择的方案。&lt;&#x2F;p&gt;
&lt;p&gt;Panel 一般会包含窗口列表、工作区切换器、系统托盘之类的东西。通常来说，有一个 Panel 已经足够让你的桌面环境“可用”了。
下面列出几个可选的 Panel：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;Tint2&quot;&gt;tint2&lt;&#x2F;a&gt;或许是最大名鼎鼎的独立 Panel，很轻量，也高度可定制化&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;wiki.lxde.org&#x2F;en&#x2F;LXPanel&quot;&gt;LXPanel&lt;&#x2F;a&gt;是LXDE桌面环境的默认 Panel，也没什么依赖，可以脱离 LXDE 而单独运行，轻量且定制性也不错，有 GTK2 和 GTK3 版本。在我这里 GTK2 版本可以完美运行，但 GTK3 版本系统托盘中的图标貌似有点 bug,不能实时更新。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;lxqt&#x2F;lxqt-panel&quot;&gt;lxqt-panel&lt;&#x2F;a&gt;和 LXPanel 差不多，是 LXQt 桌面环境的默认 Panel。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.xfce.org&#x2F;xfce&#x2F;xfce4-panel&#x2F;start&quot;&gt;Xfce Panel&lt;&#x2F;a&gt;是我最终的选择（后面会讲到为什么），它是 xfce 桌面环境的默认 Panel，也没什么依赖可以独立运行。它足够简单，可以定制的部分也不少，并且有很多使用的插件。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;fang-an-san-shi-yong-yi-ge-panel-he-yi-ge-dock-lan&quot;&gt;方案三：使用一个 Panel 和一个 Dock 栏&lt;&#x2F;h4&gt;
&lt;p&gt;通常 Panel 也可以作为 Dock 栏使用，但如果你很想再揉进去一个独立的 Dock 也没有问题。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jue-ding-shi-fou-jiang-zhuo-mian-zuo-wei-yi-ge-mu-lu&quot;&gt;决定是否将桌面作为一个目录&lt;&#x2F;h2&gt;
&lt;p&gt;要不要在桌面上堆放文件？&lt;&#x2F;p&gt;
&lt;h3 id=&quot;xu-yao-zai-zhuo-mian-shang-dui-fang-wen-jian&quot;&gt;需要在桌面上堆放文件&lt;&#x2F;h3&gt;
&lt;p&gt;如果你喜欢平时把一些文件和目录直接放在桌面上以便操作，就遵照这个部分进行。
下面列出几种选择让你能够在桌面放置文件和目录：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;PCManFM&quot;&gt;PCManFM&lt;&#x2F;a&gt; 是一个文件管理器，不过它有将桌面作为一个目录进行管理的功能。它有 GTK2、GTK3 和 QT 版。但要注意，PCManFM 与 LXPanel 共同依赖于 libfm 库，而 libfm 分为 GTK2 和 GTK3 版本且相互冲突。也就是说，如果你同时使用 LXPanel 与 PCManFM，那么两者必须是同一 GTK 版本的。PCManFM-GTK3 在我这里有拖动文件时不显示的 bug，而 GTK2 毕竟是 10 年前的老设计，个人感觉不太美观，所以最后弃用了这个方案。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.xfce.org&#x2F;xfce&#x2F;xfdesktop&#x2F;start&quot;&gt;xfdesktop&lt;&#x2F;a&gt; 是 XFCE 用来管理桌面的程序，简单易用，但它依赖 &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;Thunar&quot;&gt;Thunar&lt;&#x2F;a&gt; 文件管理器，如果你本身就准备使用 Thunar 的话倒是没什么，但如果你不喜欢 Thunar，xfdesktop 可能不是个好的选择。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;rox.sourceforge.net&#x2F;desktop&quot;&gt;ROX&lt;&#x2F;a&gt; 其实本身就可以认为是一个桌面环境了，不过它也可以用来管理桌面。我没有用过 ROX，所以就不做更多表述了。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;一般来说，用户的“桌面”文件夹在 &lt;code&gt;~&#x2F;Desktop&lt;&#x2F;code&gt;，但修改起来也不难。&lt;del&gt;我就是直接将我的 home 目录作为桌面目录的。&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;bu-xu-yao-zai-zhuo-mian-shang-dui-fang-wen-jian&quot;&gt;不需要在桌面上堆放文件&lt;&#x2F;h3&gt;
&lt;p&gt;如果你为了为了整洁的外观或是其它原因不准备在桌面上放置文件，在这一步你最好开始编辑 Openbox 的右键菜单与壁纸。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;bian-ji-openbox-de-you-jian-cai-dan&quot;&gt;编辑 Openbox 的右键菜单&lt;&#x2F;h4&gt;
&lt;p&gt;ArchWiki 中有很详细的介绍，可以直接参照&lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;openbox#Menus&quot;&gt;这里&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;she-zhi-bi-zhi&quot;&gt;设置壁纸&lt;&#x2F;h4&gt;
&lt;p&gt;在桌面模式下运行的 PCManFM、xfdesktop 和 ROX 都支持设置壁纸，但在不用它们的情况下，&lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;Nitrogen&quot;&gt;Nitrogen&lt;&#x2F;a&gt; 可以用来设置壁纸。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rang-cheng-xu-zi-qi-dong&quot;&gt;让程序自启动&lt;&#x2F;h2&gt;
&lt;p&gt;只需要在 &lt;code&gt;~&#x2F;.config&#x2F;openbox&#x2F;autostart&lt;&#x2F;code&gt; 或 &lt;code&gt;~&#x2F;.xprofile&lt;&#x2F;code&gt; 加入需要自动执行的命令。两个文件的区别在于，&lt;code&gt;~&#x2F;.xprofile&lt;&#x2F;code&gt; 中的命令是在 X Server 启动时执行的，而 &lt;code&gt;~&#x2F;.config&#x2F;openbox&#x2F;autostart&lt;&#x2F;code&gt; 中的内容是在 Openbox 启动后才执行的，一般来说两者区别不大。&lt;&#x2F;p&gt;
&lt;p&gt;一个例子：&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;xfdesktop &amp;amp;
xfce4-panel &amp;amp;
nm-applet &amp;amp;
optimus-manager-qt &amp;amp;
pasystray &amp;amp;
blueman-applet &amp;amp;
xfce4-power-manager &amp;amp;
fcitx5 &amp;amp;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;she-zhi-cheng-xu&quot;&gt;“设置”程序&lt;&#x2F;h2&gt;
&lt;p&gt;Openbox 有自己的设置程序 &lt;a href=&quot;http:&#x2F;&#x2F;openbox.org&#x2F;wiki&#x2F;ObConf:About&quot;&gt;ObConf&lt;&#x2F;a&gt;，需要单独安装。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;wiki.lxde.org&#x2F;en&#x2F;LXAppearance&quot;&gt;lxappearance&lt;&#x2F;a&gt; 可以用来设置 GTK 主题等一些选项，虽说从名字里看它是 LXDE 的组件，但它完全可以独立运行。推荐安装它的GTK3版本。&lt;&#x2F;p&gt;
&lt;p&gt;如果你的电脑同时有核显和独显，Arch 系可以用&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Askannz&#x2F;optimus-manager&quot;&gt;optimus-manager&lt;&#x2F;a&gt;配合 GUI 前端 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Shatur95&#x2F;optimus-manager-qt&quot;&gt;optimus-manager-qt&lt;&#x2F;a&gt; 切换显卡，体验极佳。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;yi-xie-zhong-yao-de-cheng-xu&quot;&gt;一些重要的程序&lt;&#x2F;h2&gt;
&lt;p&gt;到这里，你的 Openbox 已经达到“可用”状态了，但还需要一些如文件管理器、终端之类的程序。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;wen-jian-guan-li-qi&quot;&gt;文件管理器&lt;&#x2F;h3&gt;
&lt;p&gt;有很多选择，之前提到的 PCManFM、Thunar 都是很不错的轻量级文件管理器，而且在网上随便查查就能找到不少其它的选择。&lt;&#x2F;p&gt;
&lt;p&gt;注意，大多数文件管理器会通过 &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;File_manager_functionality&quot;&gt;gvfs&lt;&#x2F;a&gt; 实现回收站和自动挂载可移动设备的功能，并且可能需要通过一个 daemon 进程监听。&lt;&#x2F;p&gt;
&lt;p&gt;我选择的是 Thunar。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;zhong-duan&quot;&gt;终端&lt;&#x2F;h3&gt;
&lt;p&gt;之前我们一直临时在用 xterm 作为终端。有很多更好的终端，我自己在用的是 &lt;a href=&quot;https:&#x2F;&#x2F;launchpad.net&#x2F;sakura&quot;&gt;Sakura&lt;&#x2F;a&gt;，非常简洁轻量。&lt;a href=&quot;https:&#x2F;&#x2F;docs.xfce.org&#x2F;apps&#x2F;terminal&#x2F;start&quot;&gt;xfce4-terminal&lt;&#x2F;a&gt;、&lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;Termite&quot;&gt;Termite&lt;&#x2F;a&gt; 也是不错的选择。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;wen-ben-bian-ji-qi&quot;&gt;文本编辑器&lt;&#x2F;h3&gt;
&lt;p&gt;同样有很多选择。单纯作为文本编辑器，&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;codebrainz&#x2F;mousepad&quot;&gt;mousepad&lt;&#x2F;a&gt; 很不错。如果你喜欢 Windows 上 Notepad++ 的体验，&lt;a href=&quot;https:&#x2F;&#x2F;notepadqq.com&quot;&gt;notepadqq&lt;&#x2F;a&gt; 几乎一模一样，只是现版本的行首缩进貌似有些 bug。
我用的是 Gvim，这个就不用多解释了。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;yong-lai-suo-ding-tui-chu-guan-ji-he-zhong-qi-de-mian-ban&quot;&gt;用来锁定、退出、关机和重启的面板&lt;&#x2F;h3&gt;
&lt;p&gt;我用的是 &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;Oblogout&quot;&gt;Oblogout&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;xi-tong-tuo-pan-zhong-de-dong-xi&quot;&gt;系统托盘中的东西&lt;&#x2F;h2&gt;
&lt;p&gt;这里列出我使用的组件：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;NetworkManager#nm-applet&quot;&gt;nm-applet&lt;&#x2F;a&gt; 是 NetworkManager 的托盘程序，我还在用 NetworkManager 的原因就是其它网络管理程序没有什么好的 GUI──Wicd 因为使用 Python2 而被 pass 掉，netctl 也没有可以与 nm-applet 媲美的 GUI 前端。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;Blueman&quot;&gt;blueman-applet&lt;&#x2F;a&gt; 配合 blueman-manager 可以很方便地管理蓝牙。想要通过 PulseAudio 使用 APTX 或 LDAC 的话可以使用 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;EHfive&#x2F;pulseaudio-modules-bt&quot;&gt;pulseaudio-modules-bt&lt;&#x2F;a&gt;，Arch 系可以直接通过 AUR 安装，同时要安装 libldac。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;christophgysin&#x2F;pasystray&quot;&gt;pasystray&lt;&#x2F;a&gt; 是 PulseAudio 的托盘，功能非常强大，配合 &lt;a href=&quot;https:&#x2F;&#x2F;freedesktop.org&#x2F;software&#x2F;pulseaudio&#x2F;pavucontrol&quot;&gt;pavucontrol&lt;&#x2F;a&gt;，操作音频设备就非常容易了。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.xfce.org&#x2F;xfce&#x2F;xfce4-power-manager&#x2F;start&quot;&gt;xfce4-power-manager&lt;&#x2F;a&gt; 是 XFCE 管理电源和屏幕亮度的托盘程序，可以脱离 XFCE 独立安装运行。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Shatur95&#x2F;optimus-manager-qt&quot;&gt;optimus-manager-qt&lt;&#x2F;a&gt; 之前提到过，是 optimus-manager 的 GUI 前端，用来切换显卡。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;ding-yi-jian-pan-kuai-jie-jian&quot;&gt;定义键盘快捷键&lt;&#x2F;h2&gt;
&lt;p&gt;Openbox的键盘快捷键可以通过编辑 &lt;code&gt;~&#x2F;.config&#x2F;openbox&#x2F;rc.xml&lt;&#x2F;code&gt; 修改，当然也有 GUI 前端可以修改快捷键，但是我觉得没有必要，因为 &lt;code&gt;rc.xml&lt;&#x2F;code&gt; 修改起来非常简单。&lt;&#x2F;p&gt;
&lt;p&gt;可以参考 &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;openbox#Keybinds&quot;&gt;Arch Wiki 中 Openbox 词条的 Keybinds 章节&lt;&#x2F;a&gt;进行配置。&lt;&#x2F;p&gt;
&lt;p&gt;下面列出我的 &lt;code&gt;rc.xml&lt;&#x2F;code&gt; 中的 &lt;code&gt;&amp;lt;keyboard&amp;gt;&lt;&#x2F;code&gt; 段，其中包含一些有用的小组件，下文会提到：&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;keyboard&amp;gt;
    &amp;lt;!-- Oblogout 面板 --&amp;gt;
    &amp;lt;keybind key=&amp;quot;C-A-Delete&amp;quot;&amp;gt;
        &amp;lt;action name=&amp;quot;Execute&amp;quot;&amp;gt;
            &amp;lt;command&amp;gt;oblogout&amp;lt;&amp;#x2F;command&amp;gt;
        &amp;lt;&amp;#x2F;action&amp;gt;
    &amp;lt;&amp;#x2F;keybind&amp;gt;

    &amp;lt;!-- 窗口与工作区相关 --&amp;gt;
    &amp;lt;keybind key=&amp;quot;W-a&amp;quot;&amp;gt;
        &amp;lt;action name=&amp;quot;ToggleShowDesktop&amp;quot;&amp;#x2F;&amp;gt;
    &amp;lt;&amp;#x2F;keybind&amp;gt;
    &amp;lt;keybind key=&amp;quot;A-Left&amp;quot;&amp;gt;
        &amp;lt;action name=&amp;quot;PreviousWindow&amp;quot;&amp;#x2F;&amp;gt;
    &amp;lt;&amp;#x2F;keybind&amp;gt;
    &amp;lt;keybind key=&amp;quot;A-Right&amp;quot;&amp;gt;
        &amp;lt;action name=&amp;quot;NextWindow&amp;quot;&amp;#x2F;&amp;gt;
    &amp;lt;&amp;#x2F;keybind&amp;gt;
    &amp;lt;keybind key=&amp;quot;C-Left&amp;quot;&amp;gt;
        &amp;lt;action name=&amp;quot;DesktopLeft&amp;quot;&amp;gt;
            &amp;lt;dialog&amp;gt;no&amp;lt;&amp;#x2F;dialog&amp;gt;
            &amp;lt;wrap&amp;gt;no&amp;lt;&amp;#x2F;wrap&amp;gt;
        &amp;lt;&amp;#x2F;action&amp;gt;
    &amp;lt;&amp;#x2F;keybind&amp;gt;
    &amp;lt;keybind key=&amp;quot;C-Right&amp;quot;&amp;gt;
        &amp;lt;action name=&amp;quot;DesktopRight&amp;quot;&amp;gt;
            &amp;lt;dialog&amp;gt;no&amp;lt;&amp;#x2F;dialog&amp;gt;
            &amp;lt;wrap&amp;gt;no&amp;lt;&amp;#x2F;wrap&amp;gt;
        &amp;lt;&amp;#x2F;action&amp;gt;
    &amp;lt;&amp;#x2F;keybind&amp;gt;
    &amp;lt;keybind key=&amp;quot;C-A-Left&amp;quot;&amp;gt;
        &amp;lt;action name=&amp;quot;SendToDesktopLeft&amp;quot;&amp;gt;
            &amp;lt;dialog&amp;gt;no&amp;lt;&amp;#x2F;dialog&amp;gt;
            &amp;lt;wrap&amp;gt;no&amp;lt;&amp;#x2F;wrap&amp;gt;
        &amp;lt;&amp;#x2F;action&amp;gt;
    &amp;lt;&amp;#x2F;keybind&amp;gt;
    &amp;lt;keybind key=&amp;quot;C-A-Right&amp;quot;&amp;gt;
        &amp;lt;action name=&amp;quot;SendToDesktopRight&amp;quot;&amp;gt;
            &amp;lt;dialog&amp;gt;no&amp;lt;&amp;#x2F;dialog&amp;gt;
            &amp;lt;wrap&amp;gt;no&amp;lt;&amp;#x2F;wrap&amp;gt;
        &amp;lt;&amp;#x2F;action&amp;gt;
    &amp;lt;&amp;#x2F;keybind&amp;gt;

    &amp;lt;!-- 用 pactl 控制音量 --&amp;gt;
    &amp;lt;keybind key=&amp;quot;XF86AudioMute&amp;quot;&amp;gt;
        &amp;lt;action name=&amp;quot;Execute&amp;quot;&amp;gt;
            &amp;lt;command&amp;gt;pactl set-sink-mute 0 toggle&amp;lt;&amp;#x2F;command&amp;gt;
        &amp;lt;&amp;#x2F;action&amp;gt;
    &amp;lt;&amp;#x2F;keybind&amp;gt;
    &amp;lt;keybind key=&amp;quot;XF86AudioRaiseVolume&amp;quot;&amp;gt;
        &amp;lt;action name=&amp;quot;Execute&amp;quot;&amp;gt;
            &amp;lt;command&amp;gt;pactl set-sink-volume @DEFAULT_SINK@ +5%&amp;lt;&amp;#x2F;command&amp;gt;
        &amp;lt;&amp;#x2F;action&amp;gt;
    &amp;lt;&amp;#x2F;keybind&amp;gt;
    &amp;lt;keybind key=&amp;quot;XF86AudioLowerVolume&amp;quot;&amp;gt;
        &amp;lt;action name=&amp;quot;Execute&amp;quot;&amp;gt;
            &amp;lt;command&amp;gt;pactl set-sink-volume @DEFAULT_SINK@ -5%&amp;lt;&amp;#x2F;command&amp;gt;
        &amp;lt;&amp;#x2F;action&amp;gt;
    &amp;lt;&amp;#x2F;keybind&amp;gt;

    &amp;lt;!-- 用 playerctl 控制上一曲、下一曲、播放与暂停 --&amp;gt;
    &amp;lt;keybind key=&amp;quot;XF86AudioPrev&amp;quot;&amp;gt;
        &amp;lt;action name=&amp;quot;Execute&amp;quot;&amp;gt;
            &amp;lt;command&amp;gt;playerctl next&amp;lt;&amp;#x2F;command&amp;gt;
        &amp;lt;&amp;#x2F;action&amp;gt;
    &amp;lt;&amp;#x2F;keybind&amp;gt;
    &amp;lt;keybind key=&amp;quot;XF86AudioPlay&amp;quot;&amp;gt;
        &amp;lt;action name=&amp;quot;Execute&amp;quot;&amp;gt;
            &amp;lt;command&amp;gt;playerctl play-pause&amp;lt;&amp;#x2F;command&amp;gt;
        &amp;lt;&amp;#x2F;action&amp;gt;
    &amp;lt;&amp;#x2F;keybind&amp;gt;
    &amp;lt;keybind key=&amp;quot;XF86AudioNext&amp;quot;&amp;gt;
        &amp;lt;action name=&amp;quot;Execute&amp;quot;&amp;gt;
            &amp;lt;command&amp;gt;playerctl previous&amp;lt;&amp;#x2F;command&amp;gt;
        &amp;lt;&amp;#x2F;action&amp;gt;
    &amp;lt;&amp;#x2F;keybind&amp;gt;

    &amp;lt;!-- 用 escrotum 截图 --&amp;gt;
    &amp;lt;keybind key=&amp;quot;Print&amp;quot;&amp;gt;
        &amp;lt;action name=&amp;quot;Execute&amp;quot;&amp;gt;
            &amp;lt;command&amp;gt;escrotum -C&amp;lt;&amp;#x2F;command&amp;gt;
        &amp;lt;&amp;#x2F;action&amp;gt;
    &amp;lt;&amp;#x2F;keybind&amp;gt;
    &amp;lt;keybind key=&amp;quot;C-Print&amp;quot;&amp;gt;
        &amp;lt;action name=&amp;quot;Execute&amp;quot;&amp;gt;
            &amp;lt;command&amp;gt;escrotum -Cs&amp;lt;&amp;#x2F;command&amp;gt;
        &amp;lt;&amp;#x2F;action&amp;gt;
    &amp;lt;&amp;#x2F;keybind&amp;gt;
    &amp;lt;keybind key=&amp;quot;S-Print&amp;quot;&amp;gt;
        &amp;lt;action name=&amp;quot;Execute&amp;quot;&amp;gt;
            &amp;lt;command&amp;gt;escrotum&amp;lt;&amp;#x2F;command&amp;gt;
        &amp;lt;&amp;#x2F;action&amp;gt;
    &amp;lt;&amp;#x2F;keybind&amp;gt;
    &amp;lt;keybind key=&amp;quot;C-S-Print&amp;quot;&amp;gt;
        &amp;lt;action name=&amp;quot;Execute&amp;quot;&amp;gt;
            &amp;lt;command&amp;gt;escrotum -s&amp;lt;&amp;#x2F;command&amp;gt;
        &amp;lt;&amp;#x2F;action&amp;gt;
    &amp;lt;&amp;#x2F;keybind&amp;gt;
&amp;lt;&amp;#x2F;keyboard&amp;gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;yi-xie-hao-yong-de-cheng-xu-yu-xiao-gong-ju&quot;&gt;一些好用的程序与小工具&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Roger&#x2F;escrotum&quot;&gt;escrotum&lt;&#x2F;a&gt; 是一个非常轻量的截图工具。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;deadbeef.sourceforge.io&quot;&gt;DeaDBeeF&lt;&#x2F;a&gt; 神似 foobar2000，很轻量好用的音乐播放器，可惜用的不是 MPRIS D-Bus Interface Specification 标准，不过可以在应用内设置快捷键达到一样的效果。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;altdesktop&#x2F;playerctl&quot;&gt;playerctl&lt;&#x2F;a&gt; 可以控制使用了 &lt;a href=&quot;https:&#x2F;&#x2F;specifications.freedesktop.org&#x2F;mpris-spec&quot;&gt;MPRIS D-Bus Interface Specification&lt;&#x2F;a&gt; 标准的播放器进行上一曲、下一曲、播放与暂停操作。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;wiki.lxde.org&#x2F;en&#x2F;GPicView&quot;&gt;GPicView&lt;&#x2F;a&gt; 是 LXDE 默认的图片查看器，轻量好用。有 GTK2 和 GTK3 版本，一般用 GTK3 版本就可以了。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mate-desktop&#x2F;engrampa&quot;&gt;Engrampa&lt;&#x2F;a&gt; 是 MATE 默认的压缩文件管理器，个人认为 Linux 下最好的 GUI 压缩文件管理器。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;parcellite.sourceforge.net&quot;&gt;Parcellite&lt;&#x2F;a&gt; 是一个轻量的剪切板管理器，简单够用。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;guan-yu-compositor&quot;&gt;关于 compositor&lt;&#x2F;h2&gt;
&lt;p&gt;首先你需要问问自己，你是否真的需要 compositor？
诚然，compositor 可以装饰窗口、实现透明效果，Compiz 这样重量级的 compositor 甚至可以实现很多炫酷的特效。但这值得吗？这些效果无法带来实质上的效率提升。compositor 在后台会占用很多 GPU 资源从而导致帧率的下降，如果你是游戏玩家的话这尤为关键：在我的电脑上，开启 compositor 后 CS:GO 的帧率会下降接近四分之一。
另外，没有正确配置的 compositor 可能会导致令人很不舒服的画面撕裂等问题。&lt;&#x2F;p&gt;
&lt;p&gt;如果你一定要使用 compositor，我推荐以下两个：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;Picom&quot;&gt;picom&lt;&#x2F;a&gt; 是已经停止开发的 Compton 的后继开发产物，简单轻量，适合不需要太多装饰与特效的人。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;www.compiz.org&quot;&gt;Compiz&lt;&#x2F;a&gt; 是最有名的重量级 compositor，它甚至可以单独作为一个 WM 使用。能实现非常多效果，并且有完备的生态与社区。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;dui-hong-kong-ban-de-diao-xiao&quot;&gt;对触控板的调校&lt;&#x2F;h2&gt;
&lt;p&gt;可以参考我的另一篇文章&lt;a href=&quot;https:&#x2F;&#x2F;www.eaimty.com&#x2F;2020&#x2F;09&#x2F;optimize-touchpad-on-linux-with-libinput-driver.html&quot;&gt;《对 Linux 下触控板按键、加速和手势的优化（libinput）》&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;xie-zuo-chu-zhong&quot;&gt;写作初衷&lt;&#x2F;h1&gt;
&lt;p&gt;去年我买了一台游戏本作为主力 PC，当时觉得，既然是游戏本，那用 Windows 应该是理所当然的，就把我的整个工作环境迁移到了 Windows 上。用了大概半年，我彻底受够了 Windows：催命般的 Windows Update、相当于没有的包管理...最恶心的还是注册表，这东西简直就是人类科技的倒退。&lt;&#x2F;p&gt;
&lt;p&gt;今年一月，我切换到了最近大红大紫的 Manjaro，具体版本是 Manjaro XFCE Edition Minimal。
这一用就是半年，我最终还是决定换掉它，原因就是：它注重“傻瓜化”。但我不是傻瓜啊...Manjaro 为了达到“开箱即用”的状态集成了太多没用的组件，就算是 Minimal 版本也是如此。例如，yay 已经够好了，然而 Manjaro 还是要再带一个 pamac，虽然说 pamac 的重点在于它的 GUI,但市面上 pacman 和 AUR Helper 的 GUI 已经很多了，使用体验不错的也有不少。另外，不知为何，Manjaro 经常会在更新时搞坏一些东西，默认的 theme 就在某次大版本更新时坏掉过，导致很多 GTK 程序变得辣眼睛；Optimus Manager 也在某次更新过后出过问题，需要重启 2 次 DE 才能成功切换显卡。每次处理这类问题都十分麻烦耗时，这也是庞大臃肿系统桌面环境的通病。
我对 Manjaro 的最终印象是：适合小白与非常懒的用户（连手动配置一次环境都懒得做的那类人），且由于 Manjaro 基于 Arch，有 AUR 这样一个方便的软件包来源，几乎没有手动编译软件的需要。&lt;&#x2F;p&gt;
&lt;p&gt;最终我还是换回了 Arch，自己动手拼凑了桌面环境。
感觉在中文互联网上关于自己拼凑 Linux 桌面环境的文章少之又少，于是便有了写这篇文章的想法。在鸽了 N 个星期后，终于写完并发布了这篇文章。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>网页自适应系统暗色模式的 W3C 新规范：prefers-color-scheme</title>
          <pubDate>Sun, 02 Aug 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2020/prefers-color-scheme/</link>
          <guid>https://www.eaimty.com/2020/prefers-color-scheme/</guid>
          <description>&lt;p&gt;近几年，各个主流操作系统都逐渐开始重视暗色模式，从而改善用户在环境光亮低时的阅读体验。随着水果在 iOS 13 与 macOS Mojave 中添加了暗色模式，除了 Linux 以外所有的主流操作系统都已经实现了系统层级的暗色模式。Linux 由于 DE、WM 的种类繁杂暂时没有统一的标准，但目前可以通过暗色 GTK+ 主题、浏览器插件等方式实现“伪全局”暗色模式。既然有了系统层级的适配，浏览器就可以读取暗色模式开关，从而实现网页的自适应。这就是新标准 &lt;code&gt;prefers-color-scheme&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;h1 id=&quot;prefers-color-scheme-shi-shi-yao&quot;&gt;&lt;code&gt;prefers-color-scheme&lt;&#x2F;code&gt; 是什么&lt;&#x2F;h1&gt;
&lt;p&gt;W3C 在 2020 年 7 月 31 日发布的 &lt;a href=&quot;https:&#x2F;&#x2F;www.w3.org&#x2F;TR&#x2F;mediaqueries-5&#x2F;&quot;&gt;Media Queries Level 5 标准草案&lt;&#x2F;a&gt;中提到了新的属性 &lt;code&gt;prefers-color-scheme&lt;&#x2F;code&gt;，网页现在可以通过条件规则组来获取浏览器宿系统的暗色模式状态并应用了。也就是说，现在我们可以很简单地实现“暗色模式系统访问的页面是暗色的，亮色模式系统访问的页面是亮色的”。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;prefers-color-scheme-zen-yao-yong&quot;&gt;&lt;code&gt;prefers-color-scheme&lt;&#x2F;code&gt; 怎么用&lt;&#x2F;h1&gt;
&lt;p&gt;可以参考 MDN 关于 &lt;code&gt;prefers-color-scheme&lt;&#x2F;code&gt; 的&lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;CSS&#x2F;@media&#x2F;prefers-color-scheme&quot;&gt;描述&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;prefers-color-scheme&lt;&#x2F;code&gt; 有 2 种值：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;light&lt;&#x2F;code&gt;——浏览器宿系统使用亮色主题的界面，同时也是默认值，浏览器 &lt;code&gt;privacy.resistFingerprinting&lt;&#x2F;code&gt; 被设置为 &lt;code&gt;true&lt;&#x2F;code&gt; 时返回的也将是这个值&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;dark&lt;&#x2F;code&gt;——浏览器宿系统使用暗色主题的界面&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;还有一个已废弃的值：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;no-preference&lt;&#x2F;code&gt;——浏览器宿系统使用未知主题的界面，当较旧版本的浏览器在宿系统不支持系统层级的暗色模式时会返回这个值，较旧版本的浏览器 &lt;code&gt;privacy.resistFingerprinting&lt;&#x2F;code&gt; 被设置为 &lt;code&gt;true&lt;&#x2F;code&gt; 时返回的也将是这个值&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;通过条件规则组就可以作出判断。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;css&quot;&gt;CSS&lt;&#x2F;h2&gt;
&lt;pre&gt;&lt;code&gt;@media (prefers-color-scheme: dark) {
  &amp;#x2F;&amp;#x2F; 暗色模式样式
}
@media not (prefers-color-scheme: dark) {
  &amp;#x2F;&amp;#x2F; 非暗色模式样式
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;javascript&quot;&gt;JavaScript&lt;&#x2F;h2&gt;
&lt;p&gt;只使用CSS条件规则很难实现某些需求，我们可以对 &lt;code&gt;window&lt;&#x2F;code&gt; 使用 &lt;code&gt;matchMedia&lt;&#x2F;code&gt; 方法得到的 Media 使用 &lt;code&gt;matches&lt;&#x2F;code&gt; 方法来获取系统暗色模式状态：&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;if (window.matchMedia(&amp;#x27;prefers-color-scheme: dark&amp;#x27;).matches) {
  &amp;#x2F;&amp;#x2F; 是暗色模式做什么
} else {
  &amp;#x2F;&amp;#x2F; 非暗色模式做什么
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;另外还可以监听系统暗色模式的状态，在系统开关暗色模式时作出反应：&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;window.matchMedia(&amp;#x27;(prefers-color-scheme: dark)&amp;#x27;).addListener(e =&amp;gt; {
  if (e.matches) {
    &amp;#x2F;&amp;#x2F; 系统开启暗色模式后做什么
  } else {
    &amp;#x2F;&amp;#x2F; 系统关闭暗色模式后做什么
  }
});
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h1 id=&quot;jian-rong-xing&quot;&gt;兼容性&lt;&#x2F;h1&gt;
&lt;p&gt;其实 &lt;code&gt;prefers-color-scheme&lt;&#x2F;code&gt; 也不是什么新东西了，去年水果就已经在 macOS Mojave 上的 Safari 中添加了它，随后各浏览器不断跟进，现在的兼容性还算不错：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;CSS&#x2F;@media&#x2F;prefers-color-scheme#Browser_compatibility&quot;&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;NsaMSv5pkiThG7Z.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;che-dian-bu-xiang-guan-de&quot;&gt;扯点不相关的&lt;&#x2F;h1&gt;
&lt;blockquote&gt;
&lt;p&gt;“为什么没有夜间模式？你的夜晚太珍贵，我们不忍心占用，更不愿意成为你半夜醒来看手机的原因。愿你每夜好眠。”
——微信团队张某&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;“为了优化用户体验，微信与苹果达成了合作，共同探索微信在 iOS 系统的暗黑模式体验，目前该功能已完成开发，将有望在下一个新版本中上线，敬请期待。”
——微信团队张某&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;只有水果配教你做产品？&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>为灵刃更换电池</title>
          <pubDate>Thu, 14 May 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2020/replace-battery-for-blade/</link>
          <guid>https://www.eaimty.com/2020/replace-battery-for-blade/</guid>
          <description>&lt;p&gt;在之前的文章&lt;a href=&quot;https:&#x2F;&#x2F;www.eaimty.com&#x2F;2020&#x2F;03&#x2F;why-razer.html&quot;&gt;《我的灵刃可能要返修了──为什么我对雷蛇又爱又恨？》&lt;&#x2F;a&gt;中，我提到了我的这台机器出现的各种问题：A 面 Logo 不亮、屏幕漏光、性能衰弱过热降频...&lt;&#x2F;p&gt;
&lt;p&gt;又两个月过去了，这台笔记本又出现了一些奇奇怪怪的问题：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;触控板难以按下&lt;&#x2F;li&gt;
&lt;li&gt;关闭独显，只通电牙膏厂核显时，在某些程序（例如 Telegram Desktop）中会出现渲染错误问题&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;我实在无法再忍，于是准备处理一下这些问题。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;p&gt;一打开 D 面后盖，我傻眼了：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;ouc4aPdBqHWhQl8.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这电池怎么鼓得像个馒头似的...🙃&lt;&#x2F;p&gt;
&lt;p&gt;从侧面看：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;4y3A2kK5IjzEcD8.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;电池高出一大截...&lt;&#x2F;p&gt;
&lt;p&gt;这次算是搞清了病灶所在。这台机器出现的问题或多或少都与电池鼓包扯上了关系...
毕竟是“烧烤煎炸蛇”，机器内部温度达到五六十度并不少见。原本硬盘与机器的金属外壳之间有散热贴，但因为电池太高，外壳被撑起了不少，导致硬盘无法通过金属外壳散热，机器内部的高温加上硬盘读写时的发热，最终导致了 IO 问题。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;l5evUrwhDkA9uZf.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这可能是性能衰弱的主因。另外，鼓起电池给了主板很大的压力可能也是导致某些问题的原因。触控板难以按下是因为电池太高，从底部撑起了触控板...&lt;&#x2F;p&gt;
&lt;p&gt;最有趣的是，这台电脑的续航貌似没有被电池鼓包所影响...可能是因为 tlp 太厉害了😂&lt;&#x2F;p&gt;
&lt;p&gt;于是，我联系了雷蛇的客服，但由于是保外维修，我被“天价”维修费吓了回来：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;送修来回的邮费需要我承担&lt;&#x2F;li&gt;
&lt;li&gt;到达维修站并检测后即使不维修，我也要支付 120 元检测费&lt;&#x2F;li&gt;
&lt;li&gt;电池售价 410 元&lt;&#x2F;li&gt;
&lt;li&gt;人工费 350 元&lt;&#x2F;li&gt;
&lt;li&gt;如果想修 LOGO 与略微漏光的屏幕，需要将 A&#x2F;B 面整个换掉，而新的 A&#x2F;B 面售价 2076 元，这还未包含人工费&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;也就是说，就算只换一块电池，走官方维修也需要花将近千元，而如果还要修 LOGO 与屏幕的话，四千都不一定能解决。
我这个穷学生怎么可能支付得起这天价维修费？四千元在现在都能买一台性能不错的红队架构的笔记本了🤦‍♂️&lt;&#x2F;p&gt;
&lt;p&gt;说实话，屏幕些许的漏光并不影响日常使用，毕竟是通病。虽然这台电脑 90% 的价格都花在了这 A 面 LOGO 上，但就算它不亮也不太会影响到正常的使用，不是吗🙉
于是我决定只换电池，在万能的马云家选了一家看起来比较靠谱的店，花 380 元买了一块“原装”电池。几天之后，电池就到了。&lt;&#x2F;p&gt;
&lt;p&gt;更换灵刃的电池非常简单，电池只有一根排线与主板连接，拧下螺丝、断开排线，电池很容易就能拆下。然而装上新电池后，出现了一个大问题：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;有几个螺丝的孔位对不上！！！&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;🤦‍♂️🤦‍♂️🤦‍♂️&lt;&#x2F;p&gt;
&lt;p&gt;发消息给那家店的客服，结果客服半天都没理我...&lt;&#x2F;p&gt;
&lt;p&gt;算了，先只上 3 颗螺丝吧：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;qsBgt857UAyXf1P.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;由于旧的散热贴与外壳之间的那一面已经积灰严重，我从柜子里翻了半天，找出了这条散热贴：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;eiWtsxEP1HozrdN.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这是 3 年前我折腾的 X58 架构服务器（具体记录在&lt;a href=&quot;https:&#x2F;&#x2F;www.eaimty.com&#x2F;2017&#x2F;07&#x2F;budget-workstation-with-powerful-cpu.html&quot;&gt;《用 900 元组装一台 CPU 算力（较）强劲的工作站》&lt;&#x2F;a&gt;）留下的为数不多的“遗物”，想不到它终于熬到了出头之日，能发挥余热了🥴&lt;&#x2F;p&gt;
&lt;p&gt;在发热最严重的几个位置贴上了散热贴，使热量能更高效地通过金属外壳发散：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;hG9FEJZBqLHyRPI.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;装好后盖，开机，电池信息没什么问题。跑了一些性能测试，这台机器在数据上已经接近当初刚购买时的水平了。只开核显跑了一遍续航测试，跑出了轻度使用 6.5 个小时的成绩。&lt;&#x2F;p&gt;
&lt;p&gt;爷青回...🤣&lt;&#x2F;p&gt;
&lt;p&gt;淘宝店客服也回复了我，让我把螺丝孔位的金属片弯折一下，这样就能对齐了。虽然有点将就，但总之是正常装上了...😑&lt;&#x2F;p&gt;
&lt;p&gt;对比一下新旧电池（上 - 旧，下 - 新）：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;XUwLgaPrWvGpJQF.jpg&quot; alt=&quot;&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;pictures&#x2F;HkRZa2uP3cXKxiW.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;（嗯...是比亚迪的电池？😂）&lt;&#x2F;p&gt;
&lt;p&gt;插曲：我的所有拆机工具都放在宿舍，这次拆机只能去再买一套螺丝刀了。然而，一分价钱一分货，我花 10 块钱买的螺丝刀彻彻底底就是垃圾。拧 D 面螺丝时，螺丝没拧下来，螺丝刀的头反而花了...这破螺丝刀还拧花了好几颗螺丝，最后实在是没办法，只好去修电脑的店里让人帮忙把螺丝顶部整个磨掉...🤬现在 D 面也只上了 3 颗螺丝，有好几个孔位里都留着没有头的螺丝😷&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;2021 年 4 月 25 日 更新&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;电池又鼓了…还好之前换的这块电池还有几天的保修，直接拿去换新了🤦&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>对未部署 SSL 的网站能否保证数据传输安全的探究</title>
          <pubDate>Sun, 29 Mar 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2020/why-website-without-ssl-suck/</link>
          <guid>https://www.eaimty.com/2020/why-website-without-ssl-suck/</guid>
          <description>&lt;p&gt;由于瘟疫的影响，我校从开学到现在一直在进行网上授课。&lt;&#x2F;p&gt;
&lt;p&gt;网上授课本身没有什么问题，然而有些平台，特别是那些杂七杂八“XX 慕课”平台，在拥有大量用户的情况下不知廉耻地对保护用户数据没有任何做为，比如拒不使用 HTTPS——甚至在用户登录流程中也是。
不过，假如有某种方式可以保证在没有 SSL 保护时用户数据的安全，就不能排除这些平台使用了这种方式保障安全，从“没有 SSL”批评这些平台的这个切入点也就不存在了。所以我决定试着探究一下“未部署 SSL 的网站能否保证数据传输安全”这个问题。&lt;&#x2F;p&gt;
&lt;p&gt;最近正好在写一个对安全要求比较高的小工具，当中涉及到了相关问题，所以写了这篇一半是笔记、一半是吐槽的文章。&lt;&#x2F;p&gt;
&lt;p&gt;通常，介绍 HTTPS 的文章大多是从“为什么用了 SSL 会安全”这个角度写的。我准备换个角度，写一写“为什么不用 SSL 会不安全”。&lt;&#x2F;p&gt;
&lt;p&gt;下面以用户登录为例，来探究无 SSL 安全传输数据的可能性。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;h1 id=&quot;ming-wen-chuan-shu-shu-ju-de-feng-xian&quot;&gt;明文传输数据的风险&lt;&#x2F;h1&gt;
&lt;p&gt;当用户与服务器间的连接未被加密时，数据在传输途中经过的几乎所有设备都可以进行对其进行截取甚至修改。因此，在这种情况下最需要防范的就是重放攻击与中间人攻击。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zhong-fang-gong-ji&quot;&gt;重放攻击&lt;&#x2F;h2&gt;
&lt;p&gt;爱丽丝给鲍勃写了一封信：亲爱的，我遇到了些麻烦，现在需要一千块钱，请寄给我，谢谢！
邮递员马洛里私自打开了这封信，把信的内容原封不动复印了一份留下。信件照常送到了鲍勃手中，鲍勃发了一封内有一千块钱的信件给爱丽丝，爱丽丝正常地收到了。
马洛里到处鬼混把工资花光了，于是他将之前复印的那封信交给了鲍勃，鲍勃依旧发了一封内有一千块钱的信件，但是这次钱落进了马洛里手里。&lt;&#x2F;p&gt;
&lt;p&gt;这就是重放攻击。攻击者抓取到了用户向服务器传输的登录凭证的数据包（不需要是明文），攻击者可以向服务器发送相同的数据包从而通过用户验证流程，侵入系统内。&lt;&#x2F;p&gt;
&lt;p&gt;HTTPS 连接天生免疫重放攻击，在每次连接握手的过程中，都会产生一串随机的加密密钥，用以加密这个连接会话中传输的数据。就算攻击者抓到了用户发送的登录凭证数据包，也不能用它通过用户验证流程，因为每个连接的密钥都是不同的。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zhong-jian-ren-gong-ji&quot;&gt;中间人攻击&lt;&#x2F;h2&gt;
&lt;p&gt;爱丽丝给鲍勃写了一封信：亲爱的，我遇到了些麻烦，现在需要一千块钱，请寄给我，谢谢！
邮递员奥斯卡私自打开了这封信，将“一千块钱”改成了“两千块钱”。信件送到了鲍勃手中，鲍勃发了一封内有两千块钱的信件给爱丽丝，奥斯卡拿走了其中的一千块钱，把剩下钱的交给了爱丽丝。&lt;&#x2F;p&gt;
&lt;p&gt;这就是中间人攻击。中间人可以截获并修改客户端与服务器之间传输的数据，从而达到目的，侵入系统内。&lt;&#x2F;p&gt;
&lt;p&gt;HTTPS 连接可以在一定程度上防御中间人攻击，因为连接是非对称加密的。但是如果客户端设备上安装了“虚假的证书”，那么中间人攻击者就可以通过这个证书劫持并解密连接内容。
考虑到你国绝大多数人在计算机方面的知识水平，欺骗他们安装“虚假的证书”非常容易，毕竟他们用电脑从来不看提示信息，从来不看对话框的内容就点“下一步”。
连 HTTPS 的中间人攻击都这么容易实现，更何况是 HTTP。&lt;&#x2F;p&gt;
&lt;p&gt;下面来探究HTTP连接是否可以通过某些方式防御上面两种攻击。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;ke-hu-duan-xiang-fu-wu-qi-chuan-shu-shu-ju&quot;&gt;客户端向服务器传输数据&lt;&#x2F;h1&gt;
&lt;p&gt;如何使客户端加密的数据只能在对应的服务器上解密？&lt;&#x2F;p&gt;
&lt;p&gt;首先，对称加密不可能做到这一点，因为加密所用的密钥不论是客户端还是服务器生成的，都不可避免地需要明文传输。
因此我们很自然地会想到用诸如 RSA 之类的非对称加密算法来保护数据。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-fei-dui-cheng-jia-mi&quot;&gt;使用非对称加密&lt;&#x2F;h2&gt;
&lt;p&gt;我们可以设计出类似如下的流程：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;服务器收到用户端对登录页面的请求&lt;&#x2F;li&gt;
&lt;li&gt;服务器在返回登录页面的同时返回预先设置好的 RSA 公钥&lt;&#x2F;li&gt;
&lt;li&gt;用户输入登录信息后点击登录按钮，此时用 JS 将用户输入的内容用 RSA 公钥加密，之后 POST 给服务器&lt;&#x2F;li&gt;
&lt;li&gt;服务器收到 POST 后使用存储的 RSA 私钥解密数据，并与存储的信息进行比对&lt;&#x2F;li&gt;
&lt;li&gt;依据验证是否成功向用户端返回响应的页面并在 session 中记录&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;使用非对称加密是为了达到“只能用公钥加密”、“只能用私钥解密”的效果，对于用户端使用服务器发送的 RSA 公钥加密后的数据，除非持有对应的私钥，否则根本无法解密，因此中间人通过监听截获的只是一串无法解密的无意义乱码。&lt;&#x2F;p&gt;
&lt;p&gt;以上流程看起来是不是没有任何问题？很多登录系统采用的就是这种设计。&lt;&#x2F;p&gt;
&lt;p&gt;然而答案却是否定的。事实上，这种流程没有任何卵用。&lt;&#x2F;p&gt;
&lt;p&gt;对于重放攻击，这个 RSA 加密没有任何用处，它与明文传输唯一的区别仅在于攻击者不能得到明文密码，但攻击者仍然可以用抓到的数据包通过用户验证，从而侵入系统。&lt;&#x2F;p&gt;
&lt;p&gt;对于中间人攻击，这种流程也不会起到任何数据保护作用，可能会出现如下情况：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;服务器收到用户端对登录页面的请求&lt;&#x2F;li&gt;
&lt;li&gt;服务器在返回登录页面的同时返回预先设置好的 RSA 公钥&lt;&#x2F;li&gt;
&lt;li&gt;中间人劫持服务器返回的数据，并将服务器返回的公钥替换为“虚假的公钥”&lt;&#x2F;li&gt;
&lt;li&gt;用户输入登录信息后点击登录按钮，此时用 JS 将用户输入的内容用“虚假的公钥”加密，之后 POST 给服务器&lt;&#x2F;li&gt;
&lt;li&gt;中间人劫持用户端发送的数据，用“虚假的私钥”解密用户发送的数据&lt;&#x2F;li&gt;
&lt;li&gt;用户的密码落到了中间人手中&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;这只是让攻击的流程多了几步而已，攻击者还是可以得到用户密码。
我们发现，只要有密钥传输的过程，中间人总可以“套”出明文密码。另外，只要传输的数据没有随机的因子，重放攻击就可以实现。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-san-lie-suan-fa-bing-jia-ru-yi-ci-xing-yin-zi&quot;&gt;使用散列算法并加入一次性因子&lt;&#x2F;h2&gt;
&lt;p&gt;发现这些问题后，下面我们可以有针对性地改善这些缺点，两个比较重要的点：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;传输时使用散列而非加密&lt;&#x2F;li&gt;
&lt;li&gt;为散列使用一次性的盐&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;由此得到以下流程：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;服务器收到用户端对登录页面的请求&lt;&#x2F;li&gt;
&lt;li&gt;服务器随机生成一串字符，并将其存储至当前 session 中&lt;&#x2F;li&gt;
&lt;li&gt;服务器在返回登录页面的同时返回这串字符&lt;&#x2F;li&gt;
&lt;li&gt;用户输入登录信息后点击登录按钮，此时用 JS 将这串字符作为盐，散列计算已处理过的用户输入的内容，之后 POST 给服务器&lt;&#x2F;li&gt;
&lt;li&gt;服务器收到 POST 后使用在 session 中存储的这串字符作为盐，散列计算保存的用户信息，并与收到的 POST 数据进行比对&lt;&#x2F;li&gt;
&lt;li&gt;依据验证是否成功向用户端返回响应的页面并在 session 中记录&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;这样，就算中间人修改了字符串并监听得到了用户 POST 的数据，他也无法得到明文的用户密码，毕竟加过盐的散列结果很难破解，只要你用的不是 MD5、SHA-1 之类的弱算法，并且进行了多次散列。
同样的，由于作为盐的字符串是一次性的，含有登录凭证的数据包被抓到也无法二次使用，所以重放攻击无法实现。
这么搞，数据是不是就安全了呢？&lt;&#x2F;p&gt;
&lt;p&gt;非也！&lt;&#x2F;p&gt;
&lt;p&gt;拥有用户与服务器之间路由的人能做的可远远不止上面那些，攻击者可以这样：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;服务器收到用户端对登录页面的请求（同上）&lt;&#x2F;li&gt;
&lt;li&gt;服务器随机生成一串字符，并将其存储至当前 session 中（同上）&lt;&#x2F;li&gt;
&lt;li&gt;服务器在返回登录页面的同时返回这串字符（同上）&lt;&#x2F;li&gt;
&lt;li&gt;用户输入登录信息后点击登录按钮，此时用JS将这串字符作为盐，散列计算已处理过的用户输入的内容，之后POST给服务器（同上）&lt;&#x2F;li&gt;
&lt;li&gt;攻击者监听到了用户凭证数据包，保存后立即将其丢弃，服务器无法收到任何请求&lt;&#x2F;li&gt;
&lt;li&gt;攻击者伪造 session（非常容易，session 一般是通过 cookies、隐藏表单记录的，中间人可以轻松获取），然后重放这个包，由于服务器端仍在使用那串字符作为盐，所以攻击者可以通过用户验证从而进入系统&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;某堵墙可以丢掉你请求访问 Google 的数据包，攻击者凭什么不能丢掉你请求登录的数据包？&lt;&#x2F;p&gt;
&lt;p&gt;综上，可以看出，在没有 SSL 保护的情况下，保证客户端向服务器传输数据的安全非常非常难，这是否能说明没有 SSL 的话一切保护数据的努力都是徒劳？
先不急着得到结论，我们先讨论下一个部分。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;fu-wu-qi-xiang-ke-hu-duan-chuan-shu-shu-ju&quot;&gt;服务器向客户端传输数据&lt;&#x2F;h1&gt;
&lt;p&gt;通常情况下，登录各个网课系统后，页面上会显示出你的名字、学校、班级、学号之类的信息。
如果这些信息没有经过加密传输（很遗憾，很多网站的确没有对其加密传输），会是对用户隐私安全的毁灭性打击。因此我们需要向一些方法加密它们。
由于用户端是解密端，所以用非对称加密不合理。所以，需要设计一种能够保证密钥安全的对称加密流程。&lt;&#x2F;p&gt;
&lt;p&gt;我只能想到下面这一种实现方式：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;用户在输入密码后，取散列后的密码的一部分（比如散列后得到一串 512 位的字符串，取其 16~32 位），再次 hash，可以多次重复这个过程，然后将结果存储于 HTML5 Web Storage 中&lt;&#x2F;li&gt;
&lt;li&gt;服务器端需要安全地向用户端传输数据时，取保存的用户登录凭证用相同的算法处理，作为密钥&lt;&#x2F;li&gt;
&lt;li&gt;用户端收到信息后调用存储于 HTML5 Web Storage 中的字符串进行解密&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;这里使用 HTML5 Web Storage 而非 Cookies 存储密钥是因为 HTML5 Web Storage 相对安全一些。Cookies 会在请求 header 中被明文传输，使用 Cookies 存储密钥和一叶障目差不多。
这是权衡后的实现，既能在一定程度上保证用户密码、密钥的安全性，也能保证密钥无需传输。&lt;&#x2F;p&gt;
&lt;p&gt;然而，就算能保证服务器向客户端传输数据的安全，还是会涉及到那个问题：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;先有鸡，还是先有蛋?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;客户端不向服务器传输数据，服务器向客户端传输什么数据？&lt;&#x2F;p&gt;
&lt;p&gt;由此，我们可以得出结论：&lt;&#x2F;p&gt;
&lt;h1 id=&quot;bao-zheng-httplian-jie-de-shu-ju-chuan-shu-an-quan-xing-wu-jie&quot;&gt;保证HTTP连接的数据传输安全性？无解。&lt;&#x2F;h1&gt;
&lt;p&gt;从理论上讲，保证 HTTP 连接的数据传输安全是不可能的，以上一切的努力都是徒劳。
就算你找到了你认为更好的加密流程，只要不用 SSL，中间人永远都能获取他想要的信息。
你在 JS 里写了一大堆算法，中间人只需要插入一小段 JS，将用户输入的发送到攻击者的服务器，你的所有努力就都白费了。
你做的只是微微增加了破解难度，但根本无法阻止破解。&lt;&#x2F;p&gt;
&lt;p&gt;了解过 HTTPS 原理的朋友应该能看出，上面那些对数据传输安全的尝试几乎就是在自行实现一个简单 SSL 协议了。这可不是一个网站该干的事情，况且本身就是无法实现的。
HTTPS 的根基，是在客户端预装可信的根证书，这保证了用户与服务器之间的加密无需交换密钥。几段简简单单的 JS 怎么可能做到这点？&lt;&#x2F;p&gt;
&lt;p&gt;想自行实现 SSL？不是傻就是疯。都 2020 年了，为什么不乖乖上 SSL？更何况现在连免费的证书都出现了！&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;不用 SSL，就是对用户最大的不尊重。&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;zui-hou-de-tu-cao&quot;&gt;最后的吐槽&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;ling-ren-wu-nai-de-xian-zhuang&quot;&gt;令人无奈的现状&lt;&#x2F;h2&gt;
&lt;p&gt;当年应该有很多人玩过“贴吧云签到”，还记得这些程序是如何实现百度帐号登录的吗？
没错，是 &lt;code&gt;BDUSS&lt;&#x2F;code&gt; 这个 cookie。
只要你拿到某个百度帐号的 &lt;code&gt;BDUSS&lt;&#x2F;code&gt;，你就可以非常轻松地登入这个帐号，甚至无需用户名与密码。&lt;&#x2F;p&gt;
&lt;p&gt;连百度这种体量的公司都不舍得用一次性 token，可见中国的企业在用户安全上做得有多差。&lt;&#x2F;p&gt;
&lt;p&gt;当年 CSDN 被脱裤、爆出明文存储用户密码后，我觉得应该不会再有大企业敢这么干了。毕竟 CSDN 仅仅是一群脑残用户自嗨的地方，影响面不会很大。&lt;&#x2F;p&gt;
&lt;p&gt;然而就在前几天，微博被曝脱裤了，这次的影响面可不小。还是同样的愚蠢问题，竟然被这些大公司一次次地重演，只能感叹可悲了。&lt;&#x2F;p&gt;
&lt;p&gt;出现这类事故，责任在谁身上？写代码的程序员？我觉得他们的失误并非这些事件最大的原因。&lt;&#x2F;p&gt;
&lt;p&gt;真正应该付全责的应该是这个大环境。&lt;&#x2F;p&gt;
&lt;p&gt;那些服务的用户们，他们不就是那群“每天没事就刷抖音的人”吗？他们不会关注、也不想关注自己的信息卖给了谁，他们也不会试图去搞清自己的信息有没有被加密传输。&lt;&#x2F;p&gt;
&lt;p&gt;写这些代码的程序员，无非是每天 996、每天忙着在完成任务的前提下抽空摸鱼的普通人。人们都想着在高强度工作下自保，用户信息的安全？屁！拿这些信息换 KPI 才是正确的选择！&lt;&#x2F;p&gt;
&lt;p&gt;人是有极限的，这些程序员在高强度工作下能写出真正的好代码吗？明显不能。他们只会碌碌无为地 Copying and Pasting from Stack Overflow，甚至有些人连 Stack Overflow 是什么都不知道，Copying and Pasting from CSDN。等到了 35 岁转行去送外卖。&lt;&#x2F;p&gt;
&lt;p&gt;可悲的是，由于这个国家劳动力的富足，以及大量“每天没事就刷抖音的人”涌入计算机行业，企业不会缺少为他们 996 的人。&lt;&#x2F;p&gt;
&lt;p&gt;他们就是韭菜，割了一茬，又长一茬。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wo-men-neng-zuo-xie-shi-yao&quot;&gt;我们能做些什么？&lt;&#x2F;h2&gt;
&lt;p&gt;如果你认为“谁闲得无聊会去偷你的个人信息？”，不好意思，请赶快关掉这个浏览器页面，以节省您宝贵的时间去多刷刷抖音，顺便还能减轻我服务器的负担。
你是个丑八怪老太婆，没胸没屁股，没人会强奸你，那么你就可以不穿衣服出门吗？&lt;&#x2F;p&gt;
&lt;p&gt;如果你讨厌现在的网络安全环境，请你：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;不要写出对用户不负责的代码&lt;&#x2F;li&gt;
&lt;li&gt;在自己管理的网站上部署 HTTPS 与 HSTS&lt;&#x2F;li&gt;
&lt;li&gt;向更多人普及网络安全知识&lt;&#x2F;li&gt;
&lt;li&gt;尽量不用无法确保数据安全的网络服务&lt;&#x2F;li&gt;
&lt;li&gt;如果不得不用那些服务，设置足够长、足够随机的密码，且尽量不要填写太多的真实信息&lt;&#x2F;li&gt;
&lt;li&gt;不要当韭菜&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;由于我的技术水平限制，在这篇文章中，小到用词错误、大到概念错误，都可能会出现。如果你在文章中发现了问题或有更好的解决方案，请在下面评论，谢谢！😂&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>新的 materiality-typecho-theme</title>
          <pubDate>Tue, 24 Mar 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2020/new-materiality-typecho-theme/</link>
          <guid>https://www.eaimty.com/2020/new-materiality-typecho-theme/</guid>
          <description>&lt;p&gt;时间过的真快啊，从 2017 年 3 月 16 日我提交了这个主题的首个 commit，到现在已经整整 3 年过去了。
在之前的三年里作为一个完成度极低的主题能收获几十个 star 我已经很满意了，不过经过这两个月间的重构与完善，这个主题应该会更好用，所以特意写了这篇文章来宣传😂&lt;&#x2F;p&gt;
&lt;p&gt;当初这个博客刚刚建立时，由于我对 MD 的无脑粉，我使用了 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Hanson&#x2F;typecho_material_theme&quot;&gt;HanSon&#x2F;typecho_material_theme&lt;&#x2F;a&gt;。但是...这个主题用的是 material bootstrap，所以我总觉得缺那么一股味道...&lt;&#x2F;p&gt;
&lt;p&gt;于是，我顺藤摸瓜地找到了 Google 官方的 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;google&#x2F;material-design-lite&quot;&gt;Material Design Lite&lt;&#x2F;a&gt;。我照着 HanSon 的 typecho_material_theme，用 Material Design Lite 仿造出了一个简陋的主题：&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;EAimTY&#x2F;typecho_material_theme&quot;&gt;EAimTY&#x2F;typecho_material_theme&lt;&#x2F;a&gt;。我对前端的知识量几乎为零，所以那个主题几乎丑到不能看...&lt;&#x2F;p&gt;
&lt;p&gt;一次，我偶然发现了 &lt;a href=&quot;https:&#x2F;&#x2F;www.mdui.org&#x2F;&quot;&gt;MDUI&lt;&#x2F;a&gt; 这个库。相比与 Material Design Lite，MDUI 的完成度高得多，功能与组件丰富，用起来简单方便，而且还包含了一个 JS 工具库 &lt;code&gt;mdui.JQ&lt;&#x2F;code&gt;，体积比 jquery 小很多，但功能也够用。
我用 MDUI 重写了博客的主题，产物就是 &lt;a href=&quot;https:&#x2F;&#x2F;www.eaimty.com&#x2F;theme.html&quot;&gt;materiality-typecho-theme&lt;&#x2F;a&gt;。
由于我对很多前端知识只是一知半解，况且当时在上高中没有太多空余时间，一直到 2019 年末，这个主题的完成度仍旧不高。尽管如此，MDUI 的作者仍然将这个主题放在了 &lt;a href=&quot;https:&#x2F;&#x2F;www.mdui.org&#x2F;&quot;&gt;MDUI 项目的首页&lt;&#x2F;a&gt;进行展示，十分感谢！&lt;&#x2F;p&gt;
&lt;p&gt;转眼间三年就过去了，Google 推出了“更加扁平的 Material Design 2”来变相搞死 MD，我对其执着也逐渐变淡了。期间我对这个主题的更新“很不上心”。直到 2020 年寒假，我有了大把的空闲时间，才开始对这个主题的大规模修改。&lt;&#x2F;p&gt;
&lt;p&gt;为了填当年挖的坑，同时也为了让主题值得被继续“挂”在 MDUI 的官网上，我明确了这个主题要实现的功能与侧重点：简洁、文字显示，并在 2020 年初的两个月里提交了比前面三年间提交总数还多的 commits...&lt;&#x2F;p&gt;
&lt;p&gt;这是主题最近的 CHANGELOG：&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;EAimTY&#x2F;materiality-typecho-theme&#x2F;blob&#x2F;master&#x2F;CHANGELOG.md&quot;&gt;CHANGELOG.md&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;现在这个主题的完成度已经不是那么低了，所以为了保持简洁性，主题今后的更新将以修复漏洞为主，不再会像之前两个月一样非常频繁地加入新功能。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>我的灵刃可能要返修了──为什么我对雷蛇又爱又恨？</title>
          <pubDate>Tue, 03 Mar 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2020/why-razer/</link>
          <guid>https://www.eaimty.com/2020/why-razer/</guid>
          <description>&lt;p&gt;没错，才用了不到一年，我的灵刃就需要返修了。&lt;&#x2F;p&gt;
&lt;p&gt;发生了什么？&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A 面 Logo 不亮，屏幕有些的漏光，特别是左下角，而性能也不知为何“衰弱”了&lt;&#x2F;li&gt;
&lt;li&gt;我这台的 3DMark Time Spy 显卡跑分只有不到四千分，灵刃 15 2018 精英版的 1070 Max-Q 在正常情况下跑 4500~4600 应该没问题吧？&lt;&#x2F;li&gt;
&lt;li&gt;过热降频还极其严重，跑 CS:GO 死斗 Dust2 这张图平时帧率在 150 左右浮动，但有时候竟然能掉到 20~30 fps，还伴随强烈的 input lag...曾经玩 MC 能开着 SEUS 光影和 512 的材质包跑 60 fps 以上，现在把材质包分辨率降到 128 跑起来还是一卡一卡的&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;本来以为是太长时间没清灰才导致这个问题，结果用吸尘器和吹风机把机器内部仔仔细细清理了一遍之后问题依旧。
最麻烦的是，学校推迟开学了，我最早也要等到 4 月中旬才能回广州，而我现在所在的这个二线城市还没有雷蛇的维修点...&lt;&#x2F;p&gt;
&lt;p&gt;但其实我早就有了心理准备，预料到灵刃迟早会出问题。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;灵刃的“脆弱”早已众人皆知。
Linus &lt;a href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;r97u20HvtF4?t=13&quot;&gt;对灵刃 15 2018 的评测&lt;&#x2F;a&gt; 中就曾提到，他给员工们发的灵刃 14 在一段时间后都或多或少出现了问题：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;tYA8zvVOdmNrCna.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在网上也经常看到不少“我的灵刃突然就坏了 QAQ”之类的哀嚎。
之前有一个朋友被我安利买了灵刃，最近他的机器也出了问题：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;foOen89KiRgPZH7.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Emm...虽然我很难想象出“拖拉机一般的风扇”是什么样的，但这恐怕也是雷蛇的常规操作吧。&lt;&#x2F;p&gt;
&lt;p&gt;到这里，恐怕有些人会奇怪：为什么你明知道灵刃总会出大大小小的问题还偏要去踩坑？&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wo-wei-shi-yao-xuan-ze-lei-she-ling-ren&quot;&gt;我为什么选择雷蛇灵刃？&lt;&#x2F;h2&gt;
&lt;p&gt;讲讲我的故事。&lt;&#x2F;p&gt;
&lt;p&gt;大概是五、六年前吧，我还是个初中生。那时候我还没有像样的电脑，只有一台配有 Athlon 64 X2 3800+ 的古董机（准确来说是我爸换新机器而替换下的电脑）和一部很差的 Android 手机。&lt;&#x2F;p&gt;
&lt;p&gt;一次偶然，我在 Bilibili 看了一个视频。虽然是仅仅一个视频，我还是成功地被那个操着加拿大口音的矮个子吸引成为了他的粉丝，从此再没有落下他的任何一期视频。
“他”是谁？很容易猜到，他就是 &lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;user&#x2F;linustechtips&quot;&gt;Linus Sebastian&lt;&#x2F;a&gt;。而一直以来作为他的 daily driver laptop 的，就是 Razer Blade 系列。&lt;&#x2F;p&gt;
&lt;p&gt;我第一眼看到那台笔记本就被它深深地吸引了：低调而优雅的外观、强劲的性能...我当时梦想着有一天也能用上一台这么“完美”的笔记本。&lt;&#x2F;p&gt;
&lt;p&gt;不久后，我攒钱买到了第一台真正属于自己的电脑。
那是一台翻新的富士通笔记本，售价仅仅800rmb。我还很清楚地记得它的厚度，大约有半分米，也就是三倍于灵刃吧。i5-560M，2C4T，4G DDR3 RAM，没有固态硬盘。&lt;&#x2F;p&gt;
&lt;p&gt;就是这么一台低端笔记本，陪我走过了近2年的时光。我不知道用它赢弱的内核编译过多少次程序、写过多少行代码、推过多少游戏、看了多少期 Linus 的视频。&lt;&#x2F;p&gt;
&lt;p&gt;近2年以后，我卖掉了它，之后踩了不少各种各样的坑：&lt;a href=&quot;https:&#x2F;&#x2F;www.eaimty.com&#x2F;2017&#x2F;07&#x2F;fkin-workstation.html&quot;&gt;双路 X58&lt;&#x2F;a&gt;、X79、m-atx X58、&lt;a href=&quot;https:&#x2F;&#x2F;www.eaimty.com&#x2F;2018&#x2F;07&#x2F;chromebook.html&quot;&gt;Chromebook&lt;&#x2F;a&gt;...&lt;&#x2F;p&gt;
&lt;p&gt;虽然我有了 SSD，有了 6C12T，有了 16GB RAM，有了 GTX970，但灵刃仍旧在我的心中占据着不可替代的地位。&lt;&#x2F;p&gt;
&lt;p&gt;终于，在我有机会、有能力选择市面上大多数笔记本时，我没有一丝犹豫地选择了灵刃。
因为她一直以来都是我的梦想，是我心目中最好的电脑。&lt;&#x2F;p&gt;
&lt;p&gt;这个故事告一段落了，故事中的我已成了现在的我。&lt;&#x2F;p&gt;
&lt;p&gt;当我第一次真的把灵刃拿在手里时，我就知道我的选择是无比正确的。&lt;&#x2F;p&gt;
&lt;p&gt;她有着所有笔记本电脑中最漂亮、非常简洁的外观，却同时有着最华丽、最张扬的 Logo；有着与 Macbook 不相上下的做工，单手开合不在话下；有着非常强劲的配置，却有着堪比轻薄本的厚度与在游戏本中优秀的续航；有着 Windows 阵营中最好的触控板和超一流的屏幕...
把她的 Logo 遮住，她就是低调优雅而效能超群的办公用具；露出 Logo，她就是使用者对他人“狂放”的自我介绍。
雷蛇对细节、对完美的追求造就了这样一款笔记本电脑，用她来嘲讽粗俗、无趣的其它厂商。&lt;&#x2F;p&gt;
&lt;p&gt;纵使灵刃有不少缺点：噪音、价格（轻薄税、雷蛇税）、故障率...她依旧是市面上最好的电脑。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;上面的内容参杂了不少我的个人感情，但事实证明，不仅有我这种出于感情而选择灵刃系列的人，还有很多人出于各种原因选择灵刃系列：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;3i0XCn3HhNo&quot;&gt;My Laptop DIED - Time to Pick a New One!&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;7 个月前，Linus 的灵刃潜行版坏掉了，他面前摆了满桌子的高端轻薄本，可他最后还是选择了新款灵刃潜行版。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;qyuyKwMujCY&quot;&gt;I Broke My Razer Blade&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;1个月前，Dave Lee 的 2018 款灵刃被他摔坏了，他把市面上的其它笔记本“批判了一番”，然后把主力机换成了 2019 款灵刃。&lt;&#x2F;p&gt;
&lt;p&gt;类似的例子还有很多。
虽然灵刃系列有着各种各样的小毛病，但总是瑕不掩瑜。&lt;&#x2F;p&gt;
&lt;p&gt;那么，&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wo-de-xia-yi-tai-ji-qi-huan-hui-shi-lei-she-ling-ren-ma&quot;&gt;我的下一台机器还会是雷蛇灵刃吗？&lt;&#x2F;h2&gt;
&lt;p&gt;答案是否定的&lt;&#x2F;p&gt;
&lt;p&gt;&lt;del&gt;上面花了这么大的篇幅夸灵刃，最后还是要抛弃灵刃，你这人怎么回事？！&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这是我的灵刃出现问题后这段时间里考虑后得到的答案。&lt;&#x2F;p&gt;
&lt;p&gt;我觉得我之前的思路可能是错误的。&lt;&#x2F;p&gt;
&lt;p&gt;灵刃的主要卖点是“轻薄却性能强大”。
然而，世间万物没有“全能”的存在。每个方面都想顾及，到头来可能每个方面都不能做到出众。
&lt;strong&gt;便携性与续航不如轻薄本，性能不如台式机，这就是我看来灵刃最大的问题。&lt;&#x2F;strong&gt;
当然这不是灵刃本身的错，而是“游戏本”整个类型电脑的通病。
况且，若将所有工作托付给一台机器，出现“单点故障”是很危险的。&lt;&#x2F;p&gt;
&lt;p&gt;所以，如果我要换电脑，我会选择轻薄本+台式机这样的配置。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wo-hui-xuan-ze-na-kuan-qing-bo-ben-ni&quot;&gt;我会选择哪款轻薄本呢？&lt;&#x2F;h2&gt;
&lt;p&gt;我现在也无法完全确定，但基本上已经做好了选择。&lt;&#x2F;p&gt;
&lt;p&gt;除了灵刃之外，曾经还有一款在设计上惊艳到我的笔记本：&lt;a href=&quot;https:&#x2F;&#x2F;store.hp.com&#x2F;th-en&#x2F;default&#x2F;hp-spectre-13&quot;&gt;HP Spectre 13&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这台机器在最初发布的时候是世界上最薄的笔记本。我最喜欢的是它铰链的设计，感觉像工艺品一样十分精妙。
遗憾的是，这个系列已经很久没有更新过了。如果惠普之后能够推出保留原有的设计语言的新型号，我也会重新考虑它的。&lt;&#x2F;p&gt;
&lt;p&gt;于是，现在市面上符合我需求的产品只剩下了两个：MacBook系列和雷蛇灵刃潜行版&lt;&#x2F;p&gt;
&lt;p&gt;出于我对雷蛇的信赖与对 Linux 的需求，没有意外的话，
我的下一台笔记本会是 Razer Blade Stealth&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>ZFS──瑞士军刀般的文件系统</title>
          <pubDate>Sat, 15 Feb 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2020/zfs-file-system/</link>
          <guid>https://www.eaimty.com/2020/zfs-file-system/</guid>
          <description>&lt;p&gt;你是否曾遇到过如下情况：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;应该给这个分区分配多少空间？另一个呢？&lt;&#x2F;li&gt;
&lt;li&gt;我正在覆写一个文件，突然间设备断电了！我的源文件和更改全都损坏了！&lt;&#x2F;li&gt;
&lt;li&gt;硬 RAID 卡太贵了，mdadm 用起来又太麻烦...&lt;&#x2F;li&gt;
&lt;li&gt;我不想用 RAID，但是想把两块硬盘上的空间分配至一个分区中，有没有除了用 LVM 外的其它办法？&lt;&#x2F;li&gt;
&lt;li&gt;LUKS 用起来好麻烦，每加载一个分区都要输入一次密钥...&lt;&#x2F;li&gt;
&lt;li&gt;每次备份系统都要把整个系统打包，太浪费空间了！&lt;&#x2F;li&gt;
&lt;li&gt;我的硬盘太太太大了！&lt;&#x2F;li&gt;
&lt;li&gt;我的某个文件太太太大了！&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;那么为何不试试这个瑞士军刀般的文件系统──ZFS？&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;玩过 NAS 的朋友们应该对 ZFS 不陌生，FreeNAS 之类的很多存储服务器系统都原生支持 ZFS 文件系统，配置简单而优点众多。&lt;&#x2F;p&gt;
&lt;p&gt;ZFS 有非常多优点，然而为什么在 PC 上使用 ZFS 却不多见呢？我很早之前就听说过 ZFS 的各种优点，于是在写这篇文章前一个月，我把自己的主力 PC 的整个文件系统（包括根分区）迁移至 ZFS。经过一个月的使用体验，我发现：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;ZFS 真香！&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;注：&lt;&#x2F;strong&gt;
&lt;strong&gt;①这是一篇介绍与安利 ZFS 的文章，不会深入分析其架构与实现。同时本文也不是使用教程，如果你想尝试在 Linux 下 ZFS，请移步 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;zfsonlinux&#x2F;zfs&#x2F;wiki&quot;&gt;ZFS on Linux Wiki&lt;&#x2F;a&gt; 与各大发行版的 Wiki；&lt;&#x2F;strong&gt;
&lt;strong&gt;②我的所有体验都是在 Linux(Debian sid) 下完成的，也就是说下面内容基于 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;zfsonlinux&quot;&gt;ZFS on Linux 项目&lt;&#x2F;a&gt;，Solaris 系、BSD 系对 ZFS 的支持应该会更好。另外，我使用的 Kernel 版本是5.4，太低的内核版本可能会影响使用体验。&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h1 id=&quot;zfs-de-qian-sheng-jin-shi&quot;&gt;ZFS 的前生今世&lt;&#x2F;h1&gt;
&lt;p&gt;ZFS 的开发并非是一帆风顺的。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dan-sheng&quot;&gt;诞生&lt;&#x2F;h3&gt;
&lt;p&gt;ZFS 与 Solaris 是密不可分的。
让我们回到 20 年前。当时，Sun 公司如日中天，1992 年发布的 Solaris、1995 年发布 Java 都是其辉煌的证明。1993 年，Sun 进入了财富 500 强。&lt;&#x2F;p&gt;
&lt;p&gt;在公司业绩蒸蒸日上的环境下，ZFS 诞生了。&lt;&#x2F;p&gt;
&lt;p&gt;2001 年，Solaris 已诞生了整整 10 年，Sun 希望改进 Unix 文件系统（&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Unix_File_System&quot;&gt;Unix File System&lt;&#x2F;a&gt;）以解决一些问题。Sun 内部的 Jeff Bonwick 就是在此时有了对 ZFS 架构的基本构思。他说服了 Sun 高管，组建了 ZFS 的开发团队。&lt;&#x2F;p&gt;
&lt;p&gt;2004 年 9 月 14 日，Sun 正式&lt;a href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20041204083530&#x2F;http:&#x2F;&#x2F;wwws.sun.com&#x2F;software&#x2F;solaris&#x2F;10&#x2F;ds&#x2F;zfs.jsp&quot;&gt;宣布了 ZFS 文件系统&lt;&#x2F;a&gt;。2005 年 6 月 14 日，Sun 公司将正在开发中的 Solaris 11 的源代码以 CDDL 许可开放，这一开放版本就是 OpenSolaris。2005 年 10 月 31 日，ZFS 并入了 Solaris 开发的主干源代码，并在 2005 年 11 月 16 日作为 OpenSolaris build 27 的一部分发布。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;bo-zhe&quot;&gt;波折&lt;&#x2F;h3&gt;
&lt;p&gt;好景不长，2009 年 4 月 20 日，甲骨文公司宣布以 74 亿美金收购 Sun 公司。此时的 Sun 已如夕阳。由于甲骨文公司对 OpenSolaris 计划没有积极支援的意图。OpenSolaris 委员会于 2010 年 7 月 12 日对甲骨文给出“最后通牒”，要求在 8 月 16 日派出一位代理人商讨计划的走向，否则将在 8 月 23 日的委员会会议中做出回应。由于甲骨文未加回应，委员会于该日达成共识，解散 OpenSolaris 委员会，社区将不再提供新的源码，计划的控制权由开发员社区交还给甲骨文。&lt;&#x2F;p&gt;
&lt;p&gt;至此，Sun 的 ZFS 已成为 Oracle 的专有软件，ZFS 成为 Oracle 的注册商标，而 OpenSolaris，包括其中的 ZFS，由 &lt;a href=&quot;https:&#x2F;&#x2F;illumos.org&#x2F;&quot;&gt;illumos 项目&lt;&#x2F;a&gt; 继续着开发。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;zhuang-da&quot;&gt;壮大&lt;&#x2F;h3&gt;
&lt;p&gt;但 ZFS 并没有至此没落。社区在 2013 年成立了 &lt;a href=&quot;http:&#x2F;&#x2F;open-zfs.org&#x2F;wiki&#x2F;Main_Page&quot;&gt;OpenZFS&lt;&#x2F;a&gt; 以配合 ZFS 在 illumos 中的开源发展，由 Jeff Bonwick 当年组建的 ZFS 开发团队中的 &lt;a href=&quot;http:&#x2F;&#x2F;open-zfs.org&#x2F;wiki&#x2F;User:Mahrens&quot;&gt;Matt Ahrens&lt;&#x2F;a&gt; 作为 OpenZFS 项目的负责人。在开源社区的努力下，ZFS 不断增加新特性、提升稳定性。&lt;&#x2F;p&gt;
&lt;p&gt;回到 2007 年，苹果将 ZFS 移植到了 Mac OS X 上，但该项目在 2009 年被关闭，之后 MacZFS 项目继续维护着代码；2008 年，ZFS 随 FreeBSD 7.0 正式进入了 FreeBSD；同年，ZFS 开始被原生移植至 Linux，后来成为了 OpenZFS 下属的 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;zfsonlinux&quot;&gt;ZFS on Linux 项目&lt;&#x2F;a&gt;。如今，几乎所有主流操作系统都支持了 ZFS，甚至有 &lt;a href=&quot;https:&#x2F;&#x2F;openzfsonwindows.org&#x2F;&quot;&gt;OpenZFS on Windows 项目&lt;&#x2F;a&gt; 为 Windows 提供 ZFS 移植！&lt;&#x2F;p&gt;
&lt;p&gt;我们今天所说的 ZFS 在绝大多数情况下指的都是 OpenZFS。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h1 id=&quot;shi-yao-shi-zfs&quot;&gt;什么是 ZFS&lt;&#x2F;h1&gt;
&lt;p&gt;ZFS 这个名字本身没有含义，只是&amp;quot;Zettabyte File System&amp;quot;的首字母缩写。但 ZFS 本身并不具备任何的缩写意涵，只是想阐述做为一个具备高扩展容量文件系统且还有支持许多延伸功能的一个产品。
它是一个 128 位的文件系统，也就是说它能存储 1800 亿亿（18.4×10^18）倍于当前 64 位文件系统的数据。ZFS 的设计如此超前以至于这个极限就当前现实可能永远无法遇到。&lt;&#x2F;p&gt;
&lt;p&gt;Jeff Bonwick 曾说过：&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;要填满一个 128 位的文件系统，将耗尽地球上所有存储设备。除非你拥有煮沸整个海洋的能量，不然你不可能将其填满。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;并解释填满ZFS与煮沸海洋的关系：&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;尽管我们都希望摩尔定律永远延续，但是量子力学给定了任何物理设备上计算速率与信息量的理论极限。举例而言，一个质量为 1 公斤，体积为 1 升的物体，每秒至多在 10^31 位信息 上进行 10^51 次运算。一个完全的 128 位存储池将包含 2^128 个块 =2^137 字节 = 2^140 位；因此，保存这些数据位至少需要 (2^140 位) &#x2F; (10^31 位 &#x2F; 公斤) = 1360 亿公斤的物质。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;但单纯的“大”并不能成为 ZFS 的代替其它文件系统的原因，因为如今有很多文件系统（如 XFS）也可做到这点，虽然不及 ZFS，但数据存储量也暂时不会成为其瓶颈。&lt;&#x2F;p&gt;
&lt;p&gt;ZFS 的优点远不止“大”，下面来讲 ZFS 的众多优秀特性。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h1 id=&quot;zfs-de-te-xing&quot;&gt;ZFS 的特性&lt;&#x2F;h1&gt;
&lt;h3 id=&quot;xu-ni-she-bei-vdev-cun-chu-chi-zpool-shu-ju-ji-dataset-xu-ni-juan-zvol&quot;&gt;虚拟设备（VDEV）、存储池（zpool）、数据集（dataset）、虚拟卷（zvol）&lt;&#x2F;h3&gt;
&lt;p&gt;这些是 ZFS 的实现核心，是 ZFS 易用性的基础，ZFS 的绝大多数优点是在其上实现的。&lt;&#x2F;p&gt;
&lt;p&gt;我们在使用传统文件系统时，“分区”与“分区表”是相互独立的，基于不同的技术实现。ZFS 则不同，可以认为它同时承担了分区与分区表的角色。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;xu-ni-she-bei-vdev&quot;&gt;虚拟设备（VDEV）&lt;&#x2F;h4&gt;
&lt;p&gt;VDEV 类似于 Linux 中的 Device Mapper 层，可以认为是 ZFS 架构中的最底层，它用来定义使用设备的类型。
为何叫做虚拟设备？因为一个 VDEV 并不需要是一个特定的独立设备（例如一块硬盘），它可以是一块硬盘、一个 RAID 阵列、RAID-Z（ZFS 实现的 RAID 方式，下文会详细介绍）、甚至是一个文件。&lt;&#x2F;p&gt;
&lt;p&gt;ZFS 支持以下 VDEV 类型：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;文件（File）&lt;&#x2F;li&gt;
&lt;li&gt;一块磁盘或一个 RAID0 阵列（Stripe）&lt;&#x2F;li&gt;
&lt;li&gt;标准 RAID1 阵列（Mirror）&lt;&#x2F;li&gt;
&lt;li&gt;RAID-Z（包括 RAID-Z1、RAID-Z2、RAID-Z3）&lt;&#x2F;li&gt;
&lt;li&gt;热备盘（Spare）&lt;&#x2F;li&gt;
&lt;li&gt;L2ARC（Cache，用 ARC 算法实现的二级缓存，保存于高速存储设备上，通常使用 SSD）&lt;&#x2F;li&gt;
&lt;li&gt;ZIL（Log，记录两次 transaction group 之间发生的 fsync，保证突发断电时的一致性）&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;cun-chu-chi-zpool&quot;&gt;存储池（zpool）&lt;&#x2F;h4&gt;
&lt;p&gt;存储池建立于 VDEV 之上，可以认为与传统文件系统中的“分区表”对应。它拥有类似于 LVM 的功能，能管理一个或多个 VDEV。类似于内存，存储池大小会是所有设备的大小总和。这意味着在一个存储池中，可以同时包含一块硬盘、一个 RAID1 阵列和 RAID0 阵列等不同部分！
LVM 不能作为最终文件系统，建立的 LV 需要格式化为最终文件系统（如 ext4、fat32、NTFS、F2FS 等）后才能使用。不同于 LVM，建立好的 ZFS 存储池不需要任何操作即可直接使用。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;shu-ju-ji-dataset-xu-ni-kuai-zvol&quot;&gt;数据集（dataset）、虚拟块（zvol）&lt;&#x2F;h4&gt;
&lt;p&gt;将数据集与虚拟块放在一起介绍是因为它们是“平级”的，都建立于存储池之上，可以认为与传统文件系统中的“分区”对应。&lt;&#x2F;p&gt;
&lt;p&gt;虽然存储池可以直接使用，但是有时我们需要实现一些特殊的需求，例如：你将 ZFS 作为根系统挂载，在快照系统时不想将 &#x2F;tmp 下的文件和目录加入快照，怎么办？这时你可以定义一个“tmp”数据集，并设置其属性为 &lt;code&gt;com.sun:auto-snapshot=false&lt;&#x2F;code&gt; 
细心的朋友可能会发现，即使在没有创建“tmp”数据集时，&#x2F;tmp 仍然是存在的，因为在 ZFS 中，可以认为数据集是为设置权限，限额，快照，挂载点等高级特性才存在的。
在没有定义数据集大小的情况下，ZFS 会自动为其分配存储池中的空间。ZFS 也允许子数据集的存在，在没有特别设定的情况下子数据集将继承父数据集的属性。&lt;&#x2F;p&gt;
&lt;p&gt;虚拟块是 ZFS 提供的块设备方式，类似于数据集，虚拟块为 block 设备，可以被格式化，可以被 iSCSI 分享。举个例子，你想在一块被分入存储池的空间上划分出一个 F2FS 文件系统格式的“分区”，你需要使用的就是虚拟块。&lt;&#x2F;p&gt;
&lt;p&gt;由此可见，在ZFS下，操作一个“分区”就像是操作一个目录一样简单，并且“分区”是动态大小的！&lt;&#x2F;p&gt;
&lt;h3 id=&quot;xie-shi-fu-zhi-copy-on-write-cow&quot;&gt;写时复制（copy-on-write，CoW）&lt;&#x2F;h3&gt;
&lt;p&gt;在绝大多数文件系统下，当我们覆写一个文件时，已覆写的部分会完全丢失，在覆写的中途想要取消是不可能的。
ZFS 不同，当你执行覆盖文件操作时，新数据会先存储在不同的区域，在写入完成后再将文件系统元数据信息指向新写入的区域。
这能极大地提升系统的稳定性与安全性。突然断电时，正在操作的文件也不会损坏。所以如果使用了 ZFS，就可以完全抛弃 fsck 了。&lt;&#x2F;p&gt;
&lt;p&gt;当然，这么做有一个显著的缺点：文件会散落在磁盘的各个区域，也就是我们常说的“磁盘碎片”。
但是，随着 SSD 的普及，磁盘碎片已经变得无伤大雅，而传统的机械硬盘也更多地成为了“长期存储设备”，例如用于 NAS，其上数据不会非常频繁地进行覆写操作。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shu-ju-wan-zheng-xing-yan-zheng-zi-dong-xiu-fu&quot;&gt;数据完整性验证、自动修复&lt;&#x2F;h3&gt;
&lt;p&gt;写入的数据时，ZFS 会创建数据的校验和，校验和是数据的256位散列，校验和功能的范围可以从简单快速的 fletcher4（默认）到强加密散列（如 SHA256）。
读取数据时，ZFS 会检查读到数据的校验和，如果与写入时的检验和不符，那么就说明遇到了错误，ZFS 会尝试自动修正错误。这能在很大程度上保证资料完整性。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;raid-z&quot;&gt;RAID-Z&lt;&#x2F;h3&gt;
&lt;p&gt;上文在介绍 VDEV 时提到了 RAID-Z，这是 ZFS 实现的 RAID 方式，不需要任何其它硬件或软件。
现时 RAID-Z 有 3 种类型：RAID-Z1、RAID-Z2、RAID-Z3。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;RAID-Z1，类似于 RAID5，一重奇偶校验，至少需要三块硬盘；&lt;&#x2F;li&gt;
&lt;li&gt;RAID-Z2，类似于 RAID6，双重奇偶校验，至少需要四块硬盘；&lt;&#x2F;li&gt;
&lt;li&gt;RAID-Z3，三重奇偶校验，ZFS 独有，至少需要五块硬盘。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;kuai-zhao-snapshot&quot;&gt;快照（Snapshot）&lt;&#x2F;h3&gt;
&lt;p&gt;这时 ZFS 在文件系统层级实现的备份功能。
当 ZFS 写入新数据时，可以保留包含旧数据的块，因而能够维护文件系统的快照版本。而因为组合快照的所有数据都会被储存，且整个存储池通常每小时会进行几次快照，所以快照的创建速度非常快。任何未变动的数据会在文件系统及其快照之间进行共享，因此也具备空间高效性。快照本质上是只读的，确保在创建后快照不会被修改。快照可以被整个恢复，也可以恢复快照中的某些文件或目录。&lt;&#x2F;p&gt;
&lt;p&gt;ZFS 也可以创建可写快照：“克隆（Clone）”。“克隆”让两个独立的文件系统共享一组块。对克隆文件系统的修改都会创建新的数据块以反映这些更改。但是无论存在多少个克隆，未变动的块仍然会被共享。这是写入时复制原则的实施方式。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;qu-zhong-deduplication&quot;&gt;去重（Deduplication）&lt;&#x2F;h3&gt;
&lt;p&gt;ZFS 提供文件级别（file-level）、块级别（block-level）、字节级别（byte-level）的去重。
文件级别去重通过比对校验和来发现重复，是一般使用的级别，对性能影响最小。
其它两个级别的去重可以用于某些特殊场合，能节省更多的存储空间。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tou-ming-ya-suo-compression&quot;&gt;透明压缩（Compression）&lt;&#x2F;h3&gt;
&lt;p&gt;ZFS 原生提供透明压缩功能，也就是在读写文件时实时进行压缩与解压缩。ZFS 会尝试压缩文件的头部，如果压缩率不佳，会自动放弃压缩将原数据写入。
压缩可以在一定程度上“提高 IO”，但代价是占用CPU性能，要在考虑 IO 性能与 CPU 性能后再选择是否开启。
ZFS 支持以下压缩算法：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;lz4，默认且推荐的压缩算法，对系统性能影响小，存取数据的速度几乎与不采用压缩时一样；&lt;&#x2F;li&gt;
&lt;li&gt;gzip，这个不用解释，ZFS 默认的 gzip 级别是 6。对性能有一定的要求，但如果存储的数据是文档类型，可以考虑使用 gzip；&lt;&#x2F;li&gt;
&lt;li&gt;lzjb，与 lz4 类似，但更推荐 lz4；&lt;&#x2F;li&gt;
&lt;li&gt;zle，简单来说就是“去 0”。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;一般来说，不推荐同时开启ZFS的压缩与块级别以上的去重。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tou-ming-jia-mi-zfs-native-encryption&quot;&gt;透明加密（ZFS Native Encryption）&lt;&#x2F;h3&gt;
&lt;p&gt;这是 ZFS 支持的最新特性，能够加密存储池或数据集。
默认下，ZFS 使用 aes-128-ccm 作为加密算法，目前为止是绝对安全的，如果不放心，还可以选择更高级别的加密。
传统的 LUKS 需要为每一个加密分区提供密钥，而 ZFS 可以对存储池加密。
当然，开启加密必定会影响性能，要在考虑性能后再选择是否开启。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;综上，不难发现 ZFS 不只是一个单纯的文件系统，它还提供了一系列工具来提高使用体验。&lt;&#x2F;p&gt;
&lt;p&gt;但是，拥有如此多的优点，&lt;&#x2F;p&gt;
&lt;h1 id=&quot;wei-shi-yao-zfs-mei-you-zai-geng-da-cheng-du-shang-liu-xing&quot;&gt;为什么 ZFS 没有在更大程度上流行？&lt;&#x2F;h1&gt;
&lt;p&gt;回到 2005 年，最初的 ZFS 随着 OpenSolaris 的开源而进入了开源世界，但同时也因许可证问题与 Linux “天生不合”。
OpenSolaris 开源时采用&lt;a href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20090805063020&#x2F;http:&#x2F;&#x2F;www.sun.com&#x2F;cddl&#x2F;&quot;&gt;通用开发与散布许可证（CDDL）&lt;&#x2F;a&gt;，从而 ZFS 也成为了 CDDL 协议下的开源软件。
Oracle 收购 Sun，终止 OpenSolaris 开发，让我们口中的 ZFS 变成了 OpenZFS，OpenZFS 项目因无法联系到所有 ZFS 的初期开发者而无法变更许可证。CDDL 协议与 GPL 协议不兼容，所以很多 Linuxer 无法接触到 ZFS，从而导致了 ZFS 无法在更大程度上流行。&lt;&#x2F;p&gt;
&lt;p&gt;但最近 ZFS 也在慢慢融入 Linux 家庭中，上文中提到的 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;zfsonlinux&quot;&gt;ZFS on Linux 项目&lt;&#x2F;a&gt;对此作出了巨大的贡献。
2019 年，Canonical 在 Ubuntu 19.10 中默认加入了 ZFS 的支持。这是 ZFS 融入 Linux 的历史性的一步！虽然有着许可证问题，但是更多人能逐渐理解并包容这一历史遗留问题。Canonical 目前为止还没有因 ZFS 的引入而被缠入法律纠纷。有了第一个“吃西红柿的人”，相信之后会有更多的发行版拥抱 ZFS。&lt;&#x2F;p&gt;
&lt;p&gt;今天，ext 在某些方面的缺点已经成为瓶颈，BtrFS 因开发缓慢且不够稳定而暂时无法成为生产环境下的文件系统，ZFS 应是最佳文件系统的候选之一。&lt;&#x2F;p&gt;
&lt;p&gt;多年以后，当 BtrFS 成为主流时，也请不要忘记 ZFS，不要忘记这个优秀的开源社区，不要忘记这个伟大的文件系统探路者。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;strong&gt;本文参考了：&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;ZFS&quot;&gt;ZFS - Wikipedia&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;History_and_implementations_of_ZFS&quot;&gt;History and implementations of ZFS - Wikipedia&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;farseerfc.me&#x2F;zhs&#x2F;zfs-layered-architecture-design.html&quot;&gt;ZFS 分层架构设计 - Farseerfc的小窝&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;wiki2.xbits.net:4430&#x2F;storage:zfs:zfs%E6%89%8B%E5%86%8C&quot;&gt;ZFS 手册 - Knowledge Base&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;wiki.ubuntu.com&#x2F;Kernel&#x2F;Reference&#x2F;ZFS&quot;&gt;Kernel&#x2F;Reference&#x2F;ZFS - Ubuntu Wiki&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.oracle.com&#x2F;cd&#x2F;E24847_01&#x2F;html&#x2F;819-7065&#x2F;docinfo.html&quot;&gt;Oracle Solaris ZFS 管理指南&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.xiangzhiren.com&#x2F;archives&#x2F;283&quot;&gt;ZFS存储池介绍：Stripe、Mirror、RAIDZ和高速缓存 - 致远任重&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.xiangzhiren.com&#x2F;archives&#x2F;317&quot;&gt;为FreeNAS节约存储空间，是压缩还是重复数据删除? - 致远任重&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;superuser.com&#x2F;questions&#x2F;616239&#x2F;how-strong-is-zfs-encryption&quot;&gt;How strong is ZFS encryption? - Super User&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zhuanlan.zhihu.com&#x2F;p&#x2F;45137745&quot;&gt;初学者指南：ZFS 是什么，为什么要使用 ZFS？ - 知乎&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;如果你在文章中发现任何纰漏，请在评论中告诉我，谢谢！&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>进入二十年代后的第一篇文章</title>
          <pubDate>Tue, 28 Jan 2020 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2020/20s/</link>
          <guid>https://www.eaimty.com/2020/20s/</guid>
          <description>&lt;p&gt;&lt;del&gt;哎呦老兄去年一整年你只发了 3 篇文章这博客是不是快要倒闭了&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;
&lt;p&gt;没有，我还活得好好的！(╯｀□′)╯~ ╧╧&lt;&#x2F;p&gt;
&lt;p&gt;与“2019”那篇庆祝开博 3 年的文章仅隔了 2 篇文章，就又该庆祝 4 周年了，按这个势头下去这个博客怕是要变成“年份变更的提醒 BOT”了😂~~（欢迎大家订阅本站的 RSS，专为您提供新年提醒哦）~~&lt;&#x2F;p&gt;
&lt;p&gt;这一年，我会多发一些文章，免得网站里空荡荡得闹鬼😂
我重新思考了这个博客上到底该放些什么文章：其实归根结底博客就是一个别人有权限阅览的私人日记本（或是周记&#x2F;月记~~&#x2F;年记~~），之前我太拘泥于文章的“实用性”（好吧就我这点儿破伎俩好像根本不实用...），所以我准备换个思路来更新这个博客。
首先，这个博客一直会保持非营利，我绝对不会挂任何广告、接任何推广~~（好吧怕是这辈子也不会有人找我做推广了🥴）~~。
另外，我会更新更多非技术类型的文章，比如生活、想法、音乐、游戏什么的，让内容更丰富一些。
毕竟这网站本质就是个笔记本，有什么不能放上来？😂&lt;&#x2F;p&gt;
&lt;p&gt;下面说说博主的近况。
去年6月高考结束了，但成绩没达到我的目标，所以最终去了某个一线城市里的某所高校读软工。
大一上学期课业不忙，写了几个小工具自己用，有机会的话也会开源出来（虽然放出来也没人用...）
11月的时候因为气候不适应（大概吧）得了一次有点严重肺炎（废话，肯定不是 COVID-19...），作为宅本来我也不是很强壮，所以花了差不多一个月才痊愈。&lt;&#x2F;p&gt;
&lt;p&gt;我的 Pixel 2 XL 被我玩坏了（物理上），NFC Boom、SIM 卡读取 Boom，尾插板貌似也有问题，充电慢，无法连接电脑。无奈之下，只能暂时换个手机。
说实话我个人觉得最近两年的手机市场在越走越偏，先是集体去掉耳机接口，又集体搞全面屏。恕我直言，在技术达不到 99% 完美的情况下强推消费者都是耍流氓，市面上一票刘海屏、水滴屏、挖孔屏甚至是加装机械结构放相机的产品真的是一个比一个蠢，还使劲堆摄像头数量忽悠消费者，我所认为的真正的“好”手机屈指可数。这是整个手机行业的迷茫期，所以我决定搞一部与 Pixel 2 XL 同为 2 年前旗舰的机器作为缓冲，等一两年行业在技术与概念上有所突破后再考虑换新机。
最后，我选择了大法的 Xperia XZ Premium，港版单卡，镜面银，4K 屏，一大堆音频增强之类的功能正好配我的 1000XM3，防水，该有的功能都有，还附送 3.5mm 耳机孔！最重要的是，**炒！鸡！漂！亮！**从此我再不缺镜子了。
当然这机器还是有些小缺点的：电池太小，还好有快充；影像方面着实一般，真是白费了大法这么好的硬件，之后准备试试看能不能把 Google Camera 全功能移植过来；系统太旧，不出意料官方没有升 Q，补丁还在去年 7 月；还有不知为何蓝牙和 WiFi 争带宽，听 Hi-Res 的时候网络时不时会变得有些差...
总体而言，这手机还是符合我对它的预期，现在这机器解锁之后丢 TA 分区的问题也得到解决了，之后准备解锁后动手试试给它适配 LineageOS。&lt;&#x2F;p&gt;
&lt;p&gt;寒假放假回家后 2019-nCoV 开始流行，不过对我这种宅宅的影响不大。
在家每天肝舰（tg 频道 EAimTY&#x27;s🌊Kancolle Notebook：https:&#x2F;&#x2F;t.me&#x2F;kc_eaimty）。
~~试了试 Manjaro 发现真香，最终上了车把主力系统换成了 Manjaro。~~已经换回 Arch了，Manjaro 过于傻瓜化，感觉不太适合...&lt;&#x2F;p&gt;
&lt;p&gt;另外，我还花了很久重构了博客的主题，完善功能。同时，我也思考了这个主题的侧重点：文字。华丽的 Material Design Typecho 主题有很多，大家全都搞得一模一样也没什么意思。另外不可否认，文字在大多数情况下都是高效的信息主体，所以我决定做专注显示文字内容、延续现有风格、简洁同时也美观的主题。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;新年快乐！&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>将 Atom 改造为一个简单的 C&#x2F;C++ IDE</title>
          <pubDate>Sun, 06 Oct 2019 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2019/atom-c-ide/</link>
          <guid>https://www.eaimty.com/2019/atom-c-ide/</guid>
          <description>&lt;p&gt;如果你符合以下这些特点，这篇文章会很有用：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;单纯喜欢 Atom 的人&lt;&#x2F;li&gt;
&lt;li&gt;单纯觉得 VS 难用的人&lt;&#x2F;li&gt;
&lt;li&gt;有洁癖，不愿使用闭源软件的人&lt;&#x2F;li&gt;
&lt;li&gt;讨厌微软的人&lt;&#x2F;li&gt;
&lt;li&gt;喜欢折腾的人（其实折腾 Atom 也不是非常难）&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;当然，即使你不属于上面这几类人，也应该体验一下使用 Atom 的感觉，因为只有对比过后才能知道哪个更适合自己。&lt;&#x2F;p&gt;
&lt;p&gt;最近在学校修 cpp 课程，授课老师要求用 VS，然而我翻了一下教材，发现其实没有用 VS 的必要，所以准备把我一直在用的 Atom 改造为一个 cpp 的 IDE。&lt;&#x2F;p&gt;
&lt;p&gt;成品可以实现 C&#x2F;C++ 程序的实时检查代码中的错误，可以一键编译，还可以和 VS 一样用图形化界面打断点、逐行运行来 Debug。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;p&gt;Atom 有如下几个有点，让它比 VS 高到不知道哪里去：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;开源，且跨平台，这意味着如果你是一个使用 Atom 的 Linuxer，在某些情况下被迫要用 Windows，你可以很简单地打包一份相同配置的 Windows 版 Atom，即插即用&lt;&#x2F;li&gt;
&lt;li&gt;高度可定制化，每个人的 Atom 都可以完全不一样&lt;&#x2F;li&gt;
&lt;li&gt;原生高度集成图形化的 Git&lt;&#x2F;li&gt;
&lt;li&gt;基于 Electron，运用了大量 Web 技术，原生对前端编写的支持优秀&lt;&#x2F;li&gt;
&lt;li&gt;极度模块化，真正的 Atom 只是一个用来实现插件功能的核心，没有任何功能，全部功能都是由插件提供的&lt;&#x2F;li&gt;
&lt;li&gt;庞大的社区，数不胜数的插件，与上一条搭配意味着 Atom 的改造难度极低，这让 Atom 可以轻松充当多面手，不只前端，C 家族、Python 等众多语言的 IDE 也可胜任，同时它还是一个优秀的 Markdown 编辑器。&lt;&#x2F;li&gt;
&lt;li&gt;自从某次更新后，Atom 的运行速度有了很大的改善。到现在，Atom 已经是一个轻快的编辑器了。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;什么？你说 VS Code？那不就是微软给 Atom 披了个马甲吗？&lt;&#x2F;p&gt;
&lt;h1 id=&quot;zhun-bei-yuan-cai-liao&quot;&gt;准备原材料&lt;&#x2F;h1&gt;
&lt;p&gt;即使不考虑 Atom，想要编译、Debug C&#x2F;C++ 程序，首选也一定是 gcc、g++ 和 gdb，所以我们要做的就是把 Atom 和上面三个程序结合起来，结合的媒介就是 Atom 的插件。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;an-zhuang-atom&quot;&gt;安装 Atom&lt;&#x2F;h2&gt;
&lt;p&gt;对于 Unix-Like 系统，一般发行版的仓库都会有 atom，就算没有，也可以去&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;atom&#x2F;atom&#x2F;releases&quot;&gt;项目的 Releases 页&lt;&#x2F;a&gt;下载对应版本，甚至可以直接下载打包好的压缩包，解压后运行主程序文件。&lt;&#x2F;p&gt;
&lt;p&gt;对于 Windows，你可以选择去&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;atom&#x2F;atom&#x2F;releases&quot;&gt;项目的 Releases 页&lt;&#x2F;a&gt;下载对应的 exe 安装包，但安装过程中你无法选择安装路径，所以我一般会下载 zip 包直接解压使用。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;an-zhuang-gcc-g-he-gdb&quot;&gt;安装 gcc、g++ 和 gdb&lt;&#x2F;h2&gt;
&lt;p&gt;对于 Unix-Like，不用多说，大家都会。&lt;&#x2F;p&gt;
&lt;p&gt;对于 Windows，这里要用到 &lt;a href=&quot;http:&#x2F;&#x2F;www.mingw.org&quot;&gt;MinGW&lt;&#x2F;a&gt;，这是一个 Windows 下 GNU 家软件的安装器，使用起来非常方便。我们需要的是 mingw32-gcc-bin、mingw32-gcc-g++-bin 和 mingw32-gdb-bin 这三个软件包，其它依赖会自动下载。
MinGW 的食用方法网上一搜一大把，我就不在赘述了。
注意 MinGW 只提供 32 位版的软件包，如果你有什么特殊需求或者是强迫症必须要 64 位版本的软件，可以使用 &lt;a href=&quot;http:&#x2F;&#x2F;mingw-w64.org&quot;&gt;Mingw-w64&lt;&#x2F;a&gt;，道理都是一样的。&lt;&#x2F;p&gt;
&lt;p&gt;在安装过 gcc、g++、gdb 后，需要将 MinGW 安装目录下的 bin 文件夹（里面有一大堆可执行文件）添加到 Windows 的环境变量 &lt;code&gt;PATH&lt;&#x2F;code&gt; 中。环境变量的添加方法网上也有一大堆，不再重复。
添加到 &lt;code&gt;PATH&lt;&#x2F;code&gt; 后需要重启 Windows 以使做过的更改生效。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;an-zhuang-cha-jian&quot;&gt;安装插件&lt;&#x2F;h1&gt;
&lt;p&gt;下面的步骤都是所有平台通用的。&lt;&#x2F;p&gt;
&lt;p&gt;进入 Atom 的设置，进入插件查找安装选项卡，装上以下几个插件：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;linter&lt;&#x2F;li&gt;
&lt;li&gt;linter-gcc2（原版 linter-gcc 停止开发了，这是另一位开发者接手后的版本）&lt;&#x2F;li&gt;
&lt;li&gt;gcc-make-run&lt;&#x2F;li&gt;
&lt;li&gt;dbg&lt;&#x2F;li&gt;
&lt;li&gt;dbg-gdb&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;确保以上插件全部安装成功后重启 Atom，会弹出一些窗口让你安装这些插件的依赖，全部安装即可。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;she-zhi-cha-jian&quot;&gt;设置插件&lt;&#x2F;h1&gt;
&lt;p&gt;打开插件管理面板，这里能看到你安装过的所有插件。&lt;&#x2F;p&gt;
&lt;p&gt;首先是 linter-gcc2 插件
需要在它的设置中打开 Lint on-the-fly，这是实时查错功能。&lt;&#x2F;p&gt;
&lt;p&gt;之后是 gcc-make-run
需要在它的设置中的编译参数（Compiler Flags）里增加&lt;code&gt;-g&lt;&#x2F;code&gt;参数，以使 gdb 可以正常 debug 编译好的 binary 文件。&lt;&#x2F;p&gt;
&lt;p&gt;在这里，对于 Linux 用户还有一步额外的操作。在 Linux 下，gcc-make-run 默认调用的终端是 xterm。我相信很大一部分 Linuxer 们都没有装这个又老又丑的终端，即使装了也大概率是因为某些依赖问题，不会日常使用。所以我们需要修改默认调用的终端。
gcc-make-run 插件设置中的最后一项 &lt;code&gt;Terminal Start Command (only Linux platform)&lt;&#x2F;code&gt; 就是自定义终端调用命令，你可以查看你使用的终端的帮助或者问问“好男人” (man) 来自行设置调用命令。注意要用 bash 的 &lt;code&gt;-c&lt;&#x2F;code&gt; 参数来使程序执行完后终端保持打开状态。
举个例子，我用的是 xfce4-terminal：&lt;code&gt;xfce4-terminal -T $title -x bash -c&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;以上就是改造的步骤，下面说一说如何使用。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;kuai-jie-jian&quot;&gt;快捷键&lt;&#x2F;h1&gt;
&lt;p&gt;在这套配置中，默认下，F6 键是编译并运行，F5 键是打开 Debug 窗口，F9 键是对选中的行打断点。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;ru-he-debug&quot;&gt;如何 Debug？&lt;&#x2F;h1&gt;
&lt;p&gt;首先，你要保证代码没有被 linter 查出错误，这是最基本的；
然后对着你想打断点的行的行数左侧点一下，出现的红色按钮就是断点标识，用 F9 键也是同样的效果；
之后，按下 F6 键，也就是编译出代码的可执行文件；
紧接着在左侧 Tree View 中右键点击编译好的二进制文件，选择 Debug this File；
最后点击右下角的 Debug 按钮，就能开始 Debug 了。&lt;&#x2F;p&gt;
&lt;p&gt;同时，dbg 插件还可以保存断点之类的调试信息，按钮就在 Debug 按钮的下方。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;zui-hou-tui-jian-ji-ge-atom-cha-jian&quot;&gt;最后推荐几个 Atom 插件&lt;&#x2F;h1&gt;
&lt;p&gt;&lt;strong&gt;minimap&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;可以说是 Atom 的必装插件之一，提供整个文件的缩略图预览。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;autocomplete-clang&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;依赖于 autocomplete-plus（atom 自带），且系统中需要有 clang 软件包。
可以提供非常强大的自动补全功能。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;autocomplete-paths&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;顾名思义，是一个自动补全 path 的插件，也依赖于 autocomplete-plus。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;atom-beautify&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;只需按一下快捷键，自动规范格式化你的代码。
Beautify C 语言依赖于 Uncrustify，但安装配置起来很简单。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;activate-power-mode&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;越写越爽&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;atom-miku&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;（引述原作者的话：）&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;做工粗糙&lt;&#x2F;li&gt;
&lt;li&gt;仅供娱乐&lt;&#x2F;li&gt;
&lt;li&gt;没有卵用&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;（逃&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>关于 Android 10</title>
          <pubDate>Wed, 11 Sep 2019 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2019/android10/</link>
          <guid>https://www.eaimty.com/2019/android10/</guid>
          <description>&lt;p&gt;从 9 月 3 号 Android 10 正式发布到现在已经一个星期了。写篇文章说说我在这一个星期的使用后对新系统感觉，顺便聊聊我现在对 Android 本身的看法。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;p&gt;这篇文章不是介绍 Android 新 Feature 的文章，那类文章网上一搜一大把，况且我的水平也肯定不如专业人士，再写一遍就是费力不讨好了。&lt;&#x2F;p&gt;
&lt;p&gt;以下内容都是基于我个人的使用习惯与看法得出的，可能会和一些人的观点冲突。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;bu-cuo-de-gai-jin&quot;&gt;不错的改进&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;scoped-storage&quot;&gt;Scoped Storage&lt;&#x2F;h2&gt;
&lt;p&gt;Android Q Beta 3 去掉 sandbox 时人们哭声一片，本来看到了镇住流氓的希望，这一下子又回到了解放前。
但仔细想想，毕竟生态有历史遗留问题，Google 像水果一样强推某项功能很难。
取代 sandbox 的就是 Scoped Storage，虽然显得不那么积极，就像 Storage Access Framework 一样，但这也是无奈之举。
当前 Scoped Storage 秉承了 Google 一贯的“防君子不防小人”政策，但同时也承诺在明年的 Android R 中会强制对所有 app 开启，乐观一点的话，希望到明年应用在存储里到处拉屎的现象会少见一点吧。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;sui-ji-chuan-ru-de-mac-di-zhi-yu-ke-kong-zhi-de-read-privileged-phone-state-quan-xian&quot;&gt;随机传入的 MAC 地址与可控制的 READ_PRIVILEGED_PHONE_STATE 权限&lt;&#x2F;h2&gt;
&lt;p&gt;简单来说，就是应用无法再获取用户唯一标识符了，&lt;code&gt;getWifiMacAddress()&lt;&#x2F;code&gt; 这个方法是设备管理员专用，&lt;code&gt;&#x2F;proc&#x2F;net&lt;&#x2F;code&gt; 也被限制访问，另外只有拥有&lt;code&gt;READ_PRIVILEGED_PHONE_STATE&lt;&#x2F;code&gt; 权限的 app 才能读取 IMEI。
剩下的方式中，Advertising ID 可以由用户改变，建立文件存储 ID 这种方法对我这种经常清理存储中文件的用户来说也没什么用。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;qi-ta-dui-ying-yong-de-xian-zhi&quot;&gt;其它对应用的限制&lt;&#x2F;h2&gt;
&lt;p&gt;新系统中，剪切板被限制只能由输入法和焦点程序访问、应用开关 WiFi 也被限制。
总的来说，在隐私管控方面，Google 还是做了一些有意义的事。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;xin-gesture-navigation-xia-de-google-assistant-huan-chu-fang-shi&quot;&gt;新 Gesture navigation 下的 Google Assistant 唤出方式&lt;&#x2F;h2&gt;
&lt;p&gt;虽然我对新Gesture navigation感觉一般，但说实话，新的Assistant唤出方式我还是很喜欢的，虽然改变这么多年长按home键的习惯不那么容易，但是这新的手势会给人一种更加灵动的感觉。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zhuang-tai-lan-xia-la-cai-dan-zhong-ke-zhi-jie-kai-qi-an-se-mo-shi&quot;&gt;状态栏下拉菜单中可直接开启暗色模式&lt;&#x2F;h2&gt;
&lt;p&gt;这个功能本身很不错，但问题是Google自家的app都还没完全适配，更别说第三方app了。如果后续有更多应用适配，我想体验会很不错。&lt;&#x2F;p&gt;
&lt;p&gt;好话说完了，下面说说缺点。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;bei-zuo-chou-de-di-fang&quot;&gt;被做臭的地方&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;xin-de-shou-shi-xi-tong&quot;&gt;新的手势系统&lt;&#x2F;h2&gt;
&lt;p&gt;我个人认为这是新系统最大的槽点。&lt;&#x2F;p&gt;
&lt;p&gt;首先是应用间的切换，假如你正在同时使用 3 个应用，想从 1 切换到 3，新手势不像之前的 2-button navigation 一样给人感觉一气呵成，另外想进入最近任务列表也需要停顿一下，不如之前的方便。&lt;&#x2F;p&gt;
&lt;p&gt;其次就是 Google 这次最大的败笔：新手势的返回和拉出 drawer 操作冲突，只有把手势灵敏度调成最低才能勉强拉出。&lt;&#x2F;p&gt;
&lt;p&gt;drawer 是 Material Design 里最基础、最普及的一个组件，Google 你干脆把 MD 弄死算了&lt;&#x2F;p&gt;
&lt;h2 id=&quot;she-zhi-zhong-bian-jie-qie-huan-wifi-lan-ya-de-zu-jian&quot;&gt;设置中便捷切换 WiFi、蓝牙的组件&lt;&#x2F;h2&gt;
&lt;p&gt;Google 这是在自己填自己挖的坑。&lt;&#x2F;p&gt;
&lt;p&gt;Android L&#x2F;M 时代，状态栏中就有二级菜单可以切换 WiFi、蓝牙，结果这个功能在 Android N 中被取消了。现在在设置 app 中加入这个组件聊胜于无，用户早就习惯在状态栏长按对应 icon 进入设置了，谁还会专门打开设置 app？&lt;&#x2F;p&gt;
&lt;h2 id=&quot;choose-use-which-app-to-open-zhong-de-always-use-this-app-xu-yao-dao-ying-yong-she-zhi-zhong-she-zhi&quot;&gt;“Choose use which app to open”中的“Always use this app”需要到应用设置中设置&lt;&#x2F;h2&gt;
&lt;p&gt;这不是脱了裤子放屁吗？我根本无法理解这个改变是为了什么，设置完你还需要重新点击想要打开的内容，假如你想打开的内容显示时间有限，那该怎么办？&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ge-chong-ge-yang-de-bug&quot;&gt;各种各样的 bug&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;开启 AOD（Always on Display）后，最下面一行的电量信息和“Device is managed by your organization”只能显示一半；&lt;&#x2F;li&gt;
&lt;li&gt;刚刚更新完后 play 报&lt;code&gt;DF-DFERH-01&lt;&#x2F;code&gt;错、重启、清除应用设置 N 次也没有解决，第二天正在考虑退回 Android P 时又自己恢复正常了；&lt;&#x2F;li&gt;
&lt;li&gt;play 中已购买的 app 要求重新付费，吓了我一跳，还以为我的 Google 账号出了问题，之前买过的一堆解锁器和游戏难道都飞了？还好和上一条一样，第二天又自己恢复正常了；&lt;&#x2F;li&gt;
&lt;li&gt;最近任务列表中应用预览经常性地和实际应用对不上号，就是你打开 photos，进入最近任务列表后发现预览竟然是 calendar...&lt;&#x2F;li&gt;
&lt;li&gt;点击最近任务列表中的应用有时会显示 App isn&#x27;t available。&lt;&#x2F;li&gt;
&lt;li&gt;...&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;zai-bu-root-de-qing-kuang-xia-fang-xin-shi-yong-shou-ji&quot;&gt;在不 root 的情况下放心使用手机&lt;&#x2F;h1&gt;
&lt;p&gt;在更新系统前，我一直以为新的 sandbox 功能还在，而之前的消息称只有在Q下新安装的app才会启用sandbox，所以我索性就直接下载了factory image，干脆彻底重装吧。&lt;&#x2F;p&gt;
&lt;p&gt;结果更新完、刷完 twrp 后才发现，sandbox 早就被移除了，Android Q 的加密也改了，twrp 无法 decrypt data 分区，sideload 也坏了...
既然刷不了 magisk，那干脆 twrp 也不要了，直接把 bootloader 锁上算了，更安全嘛。&lt;&#x2F;p&gt;
&lt;p&gt;这么多年来，我还没有尝试过在没有 root 的情况下使用手机，但这次发现无 root 其实没有我想象的那么恐怖。&lt;&#x2F;p&gt;
&lt;p&gt;移动网络、WiFi 上的&lt;code&gt;×&lt;&#x2F;code&gt;号可以通过 adb 去掉；把 island 通过 adb 设置为 Device Manager 后可以接管 Mainland，从而 App Ops、Ice Box 都可以使用了，唯有 Greenify 在不 root 的情况下比较麻烦，休眠应用需要借助系统应用列表中的 force stop。&lt;&#x2F;p&gt;
&lt;p&gt;Google Now Feed 和 Google Location Report&#x2F;History 就比较麻烦了，一番搜索后，找到了推友李先生的两篇文章：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;plumz.me&#x2F;archives&#x2F;8889&#x2F;&quot;&gt;免 Root 修复你的 Google Now Feed&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;plumz.me&#x2F;archives&#x2F;9884&#x2F;&quot;&gt;消除 Google Phone APP 的权限提示&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;按照文章中的方法可以开启 Google Now Feed 和 Google Location Report&#x2F;History，但问题是经常会弹出各种各样的错误通知：XXX 应用需要 play service 开启 phone 权限，很烦。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;shuo-shuo-dui-android-de-kan-fa&quot;&gt;说说对 Android 的看法&lt;&#x2F;h1&gt;
&lt;p&gt;你可曾记得 Android M？&lt;&#x2F;p&gt;
&lt;p&gt;从 L 升级到 M，UI 上的变化不多，但正是对功能上的小修小补，造就了我个人认为最好的一代 Android。M 的 UI 在今天看来可能都不算落伍；&lt;&#x2F;p&gt;
&lt;p&gt;那时的 Material Design 还没有 Bottom Bar，整个系统简洁、美观；
Now on Tap 的 OCR 还非常方便、好用；
新加入的应用权限控制功能也不错，虽然有“亲爱的用户，我是你爹”式的 app，但是用 app ops 也可以管住它们；
我们还可以用 substratum 改各种主题；
Google 的口号还是“Don&#x27;t be evil”而不是“Do the &lt;em&gt;right&lt;&#x2F;em&gt; thing”
...&lt;&#x2F;p&gt;
&lt;p&gt;回看这 5 年来 Android 的更新，可以看到 Google 在创新，同时也不在创新。&lt;&#x2F;p&gt;
&lt;p&gt;每次更新，Google 都会带来一套新的 UI，且不说它有没有起到方便用户的作用，增加用户学习成本倒是完美做到了。
然而在权限管控方面，Google 永远做得那么不紧不慢。应用沙盒，Google 搞了这么多年也没搞完；加入了权限开关，结果想不到这个锁竟然如此“君子”，竟然会出现“不给权限不让用”这种情况？&lt;&#x2F;p&gt;
&lt;p&gt;当然，由于历史遗留问题，手段强硬地加入新政策、新功能短时间内会让厂商、用户很“痛”，但长“痛”不如短“痛”。可惜，因为 Google 的不作为，我们这些用户一直在“痛”。&lt;&#x2F;p&gt;
&lt;p&gt;我们什么时候才能再收到一次像 Jelly Beam -&amp;gt; KitKat 或 KitKat -&amp;gt; Lollipop 一样的更新？&lt;&#x2F;p&gt;
&lt;p&gt;或许是 Google 变了。&lt;&#x2F;p&gt;
&lt;p&gt;从非 Chrome 浏览器浏览 YouTube 时功能被限制，到 Dragonfly 项目，到 Google Home 用户录音被工作人员收听，再到限制 Chrome 中的 adblock 插件，Google 已经不再是我们熟悉的那个 Google 了。&lt;&#x2F;p&gt;
&lt;p&gt;或许，是时候与 Google 说再见了？&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>2019</title>
          <pubDate>Sat, 09 Feb 2019 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2019/2019/</link>
          <guid>https://www.eaimty.com/2019/2019/</guid>
          <description>&lt;p&gt;&lt;del&gt;这篇文章本应 1 月初就发出来，结果...😂就像 Win10 的“10 月更新”一样，不说了&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;
&lt;p&gt;自开博到现在也已经有 3 年了，去年摸了一整年鱼，加起来只发了 7 篇文章...&lt;&#x2F;p&gt;
&lt;p&gt;学校那边也越来越忙，今年上半年过去后可能就会好些吧。&lt;&#x2F;p&gt;
&lt;p&gt;那么，这一年我到底干了些什么呢？&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;del&gt;摸鱼&lt;&#x2F;del&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;del&gt;摸鱼&lt;&#x2F;del&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;del&gt;摸鱼&lt;&#x2F;del&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;仔细想来，这一年真的是什么都没做就过去了...&lt;&#x2F;p&gt;
&lt;p&gt;&lt;del&gt;或许我真的是玩了一整年游戏？！&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;
&lt;p&gt;年中买了台 Chromebook，结果到手折腾完就开始吃灰。
11 月初入手了一部 Pixel 2 XL，到手之后发现自己真的是“老”了，要不是因为某些软件的缘故，我甚至都懒得 root 了。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>更换服务器，以及关于服务器炸掉的这半个月</title>
          <pubDate>Sun, 28 Oct 2018 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2018/about-the-sever-boom/</link>
          <guid>https://www.eaimty.com/2018/about-the-sever-boom/</guid>
          <description>&lt;p&gt;我已经半年多没管网站所在的服务器了。大约在 9 月 30 号左右的某天晚上，我突然想更新一下服务器上面的软件。然而在已经 yum update 完成然后 reboot 之后，发现 ssh 不通了...这才想起貌似我用来更新内核的 elrepo-kernel 源是打开的...连上 vultr 提供的 VNC 后发现出了 kernel panic...&lt;&#x2F;p&gt;
&lt;p&gt;当时慌慌张张地用 &lt;a href=&quot;http:&#x2F;&#x2F;www.system-rescue-cd.org&quot;&gt;SystemRescueCd&lt;&#x2F;a&gt; 拷贝出了各种配置文件、资源、database 文件后准备第二天修复，然而...&lt;&#x2F;p&gt;
&lt;p&gt;因为最近一段时间学校里比较忙，事实上是在 10 月中旬才腾出时间折腾的。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;del&gt;我是不会告诉你们鸽了这么长时间是因为舰 C 2018 初秋活动，非提自然需要花比其他提督长数倍的时间捞船的...&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这次顺便把站点迁移到了 GCP 东京，比起 vultr 快了不少。原因的确有一部分是那 300 刀赠金，不过即使一年的有效期结束，gcp 的性价比也不错。&lt;&#x2F;p&gt;
&lt;p&gt;这里吐槽一下 GCP 的金额显示，我的 Google 账号在日区，payment profile 也是日区的，然而 GCP 在显示金额时货币单位明显是日元，但金额数后写的是一个大大的“&lt;strong&gt;元&lt;&#x2F;strong&gt;”字（简体中文页面），而非“円”，导致我以为赠金是三万多元人民币...&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>搞一台Acer C720 Chromebook玩玩</title>
          <pubDate>Sat, 07 Jul 2018 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2018/chromebook/</link>
          <guid>https://www.eaimty.com/2018/chromebook/</guid>
          <description>&lt;p&gt;这篇文章的草稿已经躺在数据库里近一个半月了，今天终于又想起了它的存在，于是写完了它。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;h1 id=&quot;wei-shi-yao-yao-mai-zhe-tai-ji-qi&quot;&gt;为什么要买这台机器&lt;&#x2F;h1&gt;
&lt;p&gt;之前组装自用台式机的时候，我有一个很严重的失误：把所有心思都花在了减小机器的体积上，完全没有考虑散热的问题...装好的机器的确很小，大约只有一本五三练习册那么大：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;5b13f50350af7.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;（忽略我的灵魂走线）
但同时因为体积限制，为了压住老平台的至强 U，我搞了一块 ~5000 转的下压式散热器，散热效果倒是还好，但是噪音...简直是飞机起飞...这就是我入手这 chromebook 的直接原因。&lt;&#x2F;p&gt;
&lt;p&gt;对我来说，一台仅用来学习、写代码、看视频、玩 GAL 的机器并不需要多么强的性能，高强度工作完全可以丢给台式机来做，所以挑选机器最需要注意的是便携性与低噪音（这是最重要的）。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;wai-guan&quot;&gt;外观&lt;&#x2F;h1&gt;
&lt;p&gt;在闲鱼上游荡了半天，找到了一款 Acer Chromebook C720，看准时机下手，成功大刀掉了 30% 的价格，搞到了这台机器：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;5b13f503024b9.jpg&quot; alt=&quot;a.jpg&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;11 寸的小屏幕还没有大号 iPad Pro 大...伊拉克成色，A&#x2F;D 面有不少划痕，屏幕中部靠上位置有一条水平的磨损痕迹，USB3.0 接口处外壳稍微断了一点，也算是对得起这个价格吧。&lt;&#x2F;p&gt;
&lt;p&gt;拆下背板后发现其实内部也是蛮伊拉克的...&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;5b13f504d2259.jpg&quot; alt=&quot;d.jpg&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;外观就是这样了，准备之后去定制一套贴纸遮住瑕疵。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;pei-zhi&quot;&gt;配置&lt;&#x2F;h1&gt;
&lt;p&gt;不到 500 元买到的配置还是蛮不错的，赛扬 2955U 比隔壁一众 N2840 不知道高出多少，2G RAM 也勉强够用，11 寸 1366×768 屏幕只能说一般，可视角度小、色偏略大，不过还是对得起这个价位。&lt;&#x2F;p&gt;
&lt;p&gt;最重要的是：这个型号是为数不多的几款可以更换硬盘的 chromebook 之一，2242 m.2 硬盘位就在上图的最右端。&lt;&#x2F;p&gt;
&lt;p&gt;原装的 16G 硬盘完全不够用，于是我在闲鱼上搞了一块 64G 硬盘。好吧，貌似被坑了，这硬盘明显是假的...不过这个价钱要什么自行车？假的也凑乎用吧。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;5b13f50125e3c.jpg&quot; alt=&quot;disk.jpg&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;机器的续航能力很棒，令人惊讶的是这机器的电池竟然有 45Wh，再加上整机功耗本身就非常小，在 Debian + XFCE 下亮屏 6~7 小时没什么问题（这是没装 tlp 时测出来的，如果装了 tlp 续航应该可以轻松突破 8 小时）。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;del&gt;另外，貌似这机器的声卡不错（忽略掉这些玄学卖点吧）&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;wei-shi-yao-bu-yong-chromeos&quot;&gt;为什么不用ChromeOS&lt;&#x2F;h1&gt;
&lt;p&gt;最近 Google 在不停改进 ChromeOS，添加了 Android App、Linux App 的支持，还移植了 App Shortcut、Picture-in-Picture 之类的花边功能，看似已经比较完善了，但对我来说还是有些不方便。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;ChromeOS 算不上轻量，对于这太老机器来说太重了&lt;&#x2F;li&gt;
&lt;li&gt;虽说已经支持了 Android App &amp;amp; Linux Native App，但安装、管理还是有些不方便，并且我推测运行效率肯定不怎么样，毕竟我是要用到 wine 的&lt;&#x2F;li&gt;
&lt;li&gt;强迫症表示在系统中不想要任何自己不需要的东西，还是自己折腾 Linux 最舒心&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;综上所述，我决定换掉 ChromeOS，改为 Debian。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;xia-zhe-teng&quot;&gt;瞎折腾&lt;&#x2F;h1&gt;
&lt;p&gt;一般的 Chromebook 使用的都是定制主板，没有 BIOS，所以想要进一步调教这台机器就需要刷 BIOS。
c720 是 Haswell 架构，算是各种各样的固件支持的最全的，这里选择用 SeaBIOS 来做引导。&lt;&#x2F;p&gt;
&lt;p&gt;首先拆下机器内的写保护螺丝，然后开启 ChromeOS 的开发者模式，之后进入 ChromeOS 的终端，开始刷 BIOS。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;mrchromebox.tech&quot;&gt;https:&#x2F;&#x2F;mrchromebox.tech&lt;&#x2F;a&gt; 有非常详细的刷 BIOS 与装系统指导，严格按照步骤操作就不会出现问题。&lt;&#x2F;p&gt;
&lt;p&gt;刷好 BIOS 之后就可以把新硬盘换上去了，之后安装系统，因为我安装的是 Debian，所以最后需要改一下 UEFI 引导文件。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;zong-jie&quot;&gt;总结&lt;&#x2F;h1&gt;
&lt;p&gt;总体而言，换过硬盘的 Acer C720 + Debian sid + XFCE 的体验还是很不错的，开机并开启 X 后内存占用也不是非常高，用 Chrome 解 YouTube 1080p 视频没什么问题（前提是不开太多 chrome 标签页）&lt;&#x2F;p&gt;
&lt;p&gt;另外，用 wine 玩游戏时性能也够用，当然使用 Live2D 的游戏另当别论。另外现在原生支持 Linux 的游戏也越来越多，举个例子：&lt;a href=&quot;http:&#x2F;&#x2F;www.keeptalkinggame.com&#x2F;&quot;&gt;Keep Talking and Nobody Explodes&lt;&#x2F;a&gt;、Limbo、Valve 全家（当然这台机器就别想了），都是很不错的游戏。
各种 IDE、compiler 在 Linux 下的体验不用多说，肯定是比 Windows、ChromeOS 下好很多的，当然，还有 shell 也是。&lt;&#x2F;p&gt;
&lt;p&gt;最后放一张 screenfetch 吧！&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;5b4df99958e0c.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>GFWList AutoProxy PAC File</title>
          <pubDate>Wed, 23 May 2018 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2018/pac/</link>
          <guid>https://www.eaimty.com/2018/pac/</guid>
          <description>&lt;p&gt;&lt;strong&gt;由于GFWList已经无法囊括越来越多的被墙站点，白名单是更好的选择&lt;&#x2F;strong&gt;
&lt;strong&gt;况且我的服务器不在你国境内，通信干扰严重，所以该服务已经关闭&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;最近重新开始用 Shadowsocks-Qt5，但因为 ss-qt5 不能在应用内直接获取 PAC，所以我把之前上过线但已经被我下线的 &lt;a href=&quot;https:&#x2F;&#x2F;pac.eaimty.com&quot;&gt;GFWList AutoProxy PAC File&lt;&#x2F;a&gt; 重新搞了出来：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;pac.eaimty.com&quot;&gt;https:&#x2F;&#x2F;pac.eaimty.com&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;使用 genpac 生成，每天更新的 GFWList PAC 文件
文件中默认的 proxy 是 SOCKS5, 127.0.0.1:1080&lt;&#x2F;p&gt;
&lt;p&gt;对于 Linux，编辑 &lt;code&gt;&#x2F;etc&#x2F;enviroment&lt;&#x2F;code&gt; 文件，加入或修改：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;auto_proxy=&amp;quot;https:&amp;#x2F;&amp;#x2F;pac.eaimty.com&amp;#x2F;autoproxy.pac&amp;quot;
AUTO_PROXY=&amp;quot;https:&amp;#x2F;&amp;#x2F;pac.eaimty.com&amp;#x2F;autoproxy.pac&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;就可以设置 system-wide proxy。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>这年头听个歌都这么难？</title>
          <pubDate>Thu, 03 May 2018 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2018/online-music/</link>
          <guid>https://www.eaimty.com/2018/online-music/</guid>
          <description>&lt;p&gt;之前的几年中，我一直使用网易云音乐作为音乐平台，在各平台上安装的也基本都是网易云音乐和一款本地播放器（foobar2000 或 phonograph）。&lt;&#x2F;p&gt;
&lt;p&gt;最近的某日，我因为好奇新版本在 Android 上升级了今年年初发布的网易云音乐 5.0...
5 秒后，我在 App info 界面中点击了 uninstall
其实很长时间之前我就开始对这个平台不太满意了，假无损、恶心的评论系统、各种垃圾功能，每一项都是劝退点&lt;&#x2F;p&gt;
&lt;p&gt;然而我应该用什么服务代替呢？&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;p&gt;我一般听的歌的类型不是很多，主要是 ACG 作品的 OST、同人作品，有时也听 J-Pop，极少数情况下也会听些古典和有些年头的欧美流行。&lt;&#x2F;p&gt;
&lt;p&gt;由此我得出结论：不论哪个服务，日区都更适合我。&lt;&#x2F;p&gt;
&lt;p&gt;我挑出了以下几个候选：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Spotify&lt;&#x2F;li&gt;
&lt;li&gt;Apple Music&lt;&#x2F;li&gt;
&lt;li&gt;Google Play Music&lt;&#x2F;li&gt;
&lt;li&gt;ANiUTa&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;spotify&quot;&gt;Spotify&lt;&#x2F;h1&gt;
&lt;p&gt;老牌的音乐串流服务，据说已经有 1.4 亿用户了。&lt;&#x2F;p&gt;
&lt;p&gt;Sony Music 是 Spotify 的 4 个老东家之一，所以我想曲库应该会比较全吧...Premium for Family 每月也只卖 1480 円，找 5 个人平摊也不是很贵。
把账户注册到了日区，之前首页上的“新用户免费体验一个月”不见了，变成了“Start 3 months of Premium for ￥100”，日区就这么特殊吗...
纠结了半天，我觉得毕竟才 100 円，也就是不到 10 软妹币，先订阅 3 个月试试吧。&lt;&#x2F;p&gt;
&lt;p&gt;然而...支付失败...用中国银行的多币种 Visa 卡（长城跨境通）无法支付...&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;It looks like your card is from a non-Spotify country.
Spotify isn&#x27;t available in your country yet. Change your location or choose a different payment method.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;算了，惹不起惹不起...另外在网上还见到很多人说 Spotify 曲库不全，不折腾了，换下一家。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;apple-music&quot;&gt;Apple Music&lt;&#x2F;h1&gt;
&lt;p&gt;水果家的音乐，App 在 Google Play 里评分只有 3.5，也许这就是 Android 用户的 Google 情怀吧（笑）
之前用过 Apple Music 国区，体验极差，几乎什么歌都没有，这次试试日区。
日区账户，点击试用，添加信用卡。&lt;&#x2F;p&gt;
&lt;p&gt;淦！又是无法使用...&lt;&#x2F;p&gt;
&lt;p&gt;换换换&lt;&#x2F;p&gt;
&lt;h1 id=&quot;google-play-music&quot;&gt;Google Play Music&lt;&#x2F;h1&gt;
&lt;p&gt;因为之前我的 Google 账户一直在美区，也就稀里糊涂订阅了一个月的 Google Play Music 美区。
恩...基本没有歌，取消订阅吧。
之后花了很大的力气把 Google 账户转到了日区，用那张多币种 Visa 卡付款竟然成功了&lt;&#x2F;p&gt;
&lt;p&gt;Google 大法好！&lt;&#x2F;p&gt;
&lt;p&gt;然而体验了几周后，我又发现了很多不满意的地方：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;虽说 Play Music 的曲库相比于之前的几家已经算是最全的了，但还是缺很多歌。貌似索尼音乐旗下歌手的唱片还算比较全，其他唱片非常少。&lt;&#x2F;li&gt;
&lt;li&gt;OST 太少了，总之我只发现了ゆるゆり的原声碟，其它暂时没发现&lt;&#x2F;li&gt;
&lt;li&gt;同人作品就更不用说了...&lt;&#x2F;li&gt;
&lt;li&gt;下载的离线音乐是加密的，无法导出，PC 上也没有客户端，不太方便&lt;&#x2F;li&gt;
&lt;li&gt;码率不明&lt;&#x2F;li&gt;
&lt;li&gt;恶心的 Library 同步。我在 Android 客户端上 Add 了近 50 张 Album 到 Library，库中歌曲总数至少超过 200 首，但 PC 上 Web 端的 Lib 里只能显示十几首歌？！到现在也没搞懂是什么情况&lt;&#x2F;li&gt;
&lt;li&gt;日区没有 YouTube Red 服务。在美区之类的其它区订阅 Play Music 同时也会开通 YouTube Red，包含无广告、Android 客户端 PiP（Picture-in-Picture）功能、YouTube Music，然而日区没有这些福利&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;总之，Play Music 也还是不太和我的口味...&lt;&#x2F;p&gt;
&lt;h1 id=&quot;aniuta&quot;&gt;ANiUTa&lt;&#x2F;h1&gt;
&lt;p&gt;忘记是在哪里听说这家串流服务的了，总之貌似是很厉害的样子，然而价格又把我吓回去了。ANiUTa 没有家庭共享，每个月三、四十软妹币的样子，是其它几家的约 3 倍。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;zui-zhong-jie-jue-fang-an&quot;&gt;最终解决方案&lt;&#x2F;h1&gt;
&lt;p&gt;有一个 Magisk 模块 &lt;a href=&quot;https:&#x2F;&#x2F;forum.xda-developers.com&#x2F;apps&#x2F;magisk&#x2F;module-sony-apps-enabler-systemless-t3620100&quot;&gt;Sony Apps Enabler&lt;&#x2F;a&gt; 可以让非Xperia的手机安装大法家机器专用的 Apps（通过 Play 就能安装），当然也包括大法家的音乐 App——Sony Music&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;这个 App 有个好，能直接播放 Google Drive 里的FLAC&#x2F;MP3&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;~~另外貌似还有大法的玄学加成，配上大法自家的耳机和 Hi-Res™ Audio 食用效果更佳~~~
&lt;del&gt;千万不要用火电或风电为播放设备充电并推歌！火电浮躁，风电不稳定。一定要用水电（最好是潮汐能发电）！最纯净！（逃&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;
&lt;p&gt;至于音乐来源，就只能自己动手到处搜刮了&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>在 Nginx 的 if 条件中使用“与”、“或”</title>
          <pubDate>Sat, 24 Mar 2018 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2018/nginx-if-multiple-conditions/</link>
          <guid>https://www.eaimty.com/2018/nginx-if-multiple-conditions/</guid>
          <description>&lt;p&gt;相比与 Apache，Nginx 的 config 写起来更舒服一些。&lt;&#x2F;p&gt;
&lt;p&gt;但是，很多情况下，我们的需求可能会稍稍有些复杂，例如在一个判断语句中使用多个条件。Apache 写这种判断很简单，但使用 Nginx 该如何实现呢？&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;p&gt;Nginx 中的 if&#x2F;else 的语法与大多数语言一致：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;conf&quot; class=&quot;language-conf &quot;&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;if (&amp;lt;condition&amp;gt;) {
    # Do something
} else {
    # Do something
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;虽然 Nginx 并非编程语言，我们还是先来试试在其它语言中常用的手段吧。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;shi-cuo&quot;&gt;试错&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;shi-yong-luo-ji-yun-suan-fu&quot;&gt;使用逻辑运算符&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;conf&quot; class=&quot;language-conf &quot;&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;if (&amp;lt;condition1&amp;gt; AND &amp;lt;condition2&amp;gt;) {
    # Do something
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;用 &lt;code&gt;nginx -t&lt;&#x2F;code&gt; 测试一下，报错...&lt;&#x2F;p&gt;
&lt;p&gt;试试用符号？&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;conf&quot; class=&quot;language-conf &quot;&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;if (&amp;lt;condition1&amp;gt; &amp;amp;&amp;amp; &amp;lt;condition2&amp;gt;) {
    # Do something
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;还是有问题，看来 Nginx 并不支持逻辑运算符。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yong-duo-ge-if-kuai-qian-tao&quot;&gt;使用多个 if 块嵌套&lt;&#x2F;h2&gt;
&lt;p&gt;试试“曲线救国”：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;conf&quot; class=&quot;language-conf &quot;&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;if (&amp;lt;condition1&amp;gt;) {
    if (&amp;lt;condition2&amp;gt;) {
        # Do something
    }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;nginx -t&lt;&#x2F;code&gt;，还是报错...&lt;&#x2F;p&gt;
&lt;h1 id=&quot;zheng-que-de-zi-shi&quot;&gt;正确的姿势&lt;&#x2F;h1&gt;
&lt;p&gt;在网上找了一些文章，终于解决了这个问题。&lt;&#x2F;p&gt;
&lt;p&gt;关键就在于：用变量存储状态。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;huo&quot;&gt;“或”&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;conf&quot; class=&quot;language-conf &quot;&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;set $foo 0;
if (&amp;lt;condition1&amp;gt;) {
    set $foo 1;
}
if (&amp;lt;condition2&amp;gt;) {
    set $foo 1;
}
if ($foo = 1) {
    # Do something
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;只要两个条件中有任意一个成立，$foo 就会被赋值为 1，最后一个代码块就会被执行。&lt;&#x2F;p&gt;
&lt;p&gt;另外，如果“或”用在判断的变量的值时，可以用 &lt;code&gt;|&lt;&#x2F;code&gt; 更简单地解决：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;conf&quot; class=&quot;language-conf &quot;&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;if ($letter ~ (&amp;quot;A&amp;quot;|&amp;quot;B&amp;quot;|&amp;quot;C&amp;quot;|&amp;quot;D&amp;quot;)) {
    # Do something
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;但需要注意的是，不能这样写：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;conf&quot; class=&quot;language-conf &quot;&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;if ($letter ~ (&amp;quot;A&amp;quot;|&amp;quot;B&amp;quot;) | $letter ~ (&amp;quot;C&amp;quot;|&amp;quot;D&amp;quot;)) {
    # Do something else
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;yu&quot;&gt;“与”&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;conf&quot; class=&quot;language-conf &quot;&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;set $foo &amp;quot;&amp;quot;;
if (&amp;lt;condition1&amp;gt;) {
    set &amp;quot;${foo}1&amp;quot;;
}
if (&amp;lt;condition2&amp;gt;) {
    set &amp;quot;${foo}1&amp;quot;;
}
if ($foo ~* &amp;quot;11&amp;quot;) {
    # Do something
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;首先把 $foo 清空，之后每当有一个条件成立，就会在 $foo 的结尾添加一个 1，当 $foo 中的字符串为“11”时，最后的代码块会被执行。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;geng-duo-ge-tiao-jian-zu-he&quot;&gt;更多个条件组合&lt;&#x2F;h2&gt;
&lt;pre data-lang=&quot;conf&quot; class=&quot;language-conf &quot;&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;set $foo &amp;quot;&amp;quot;;
if (&amp;lt;condition1&amp;gt;) {
    set $foo &amp;quot;1&amp;quot;;
} else {
    set $foo &amp;quot;0&amp;quot;;
}
if (&amp;lt;condition2&amp;gt;) {
    set $foo &amp;quot;${foo}1&amp;quot;;
} else {
    set $foo &amp;quot;${foo}0&amp;quot;;
}
if ( $foo ~* &amp;quot;011&amp;quot; ) {
    # Do something
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;开始时 $foo 是空字符串，每当一个判断成立时 $foo 的最后会被添加一个 1，不成立则添加 0，以此类推，最后一个 if 块中 $foo 要匹配的字符串就是你希望实现的判断。&lt;&#x2F;p&gt;
&lt;p&gt;Nginx 的变量字符匹配中，&lt;code&gt;~*&lt;&#x2F;code&gt; 代表匹配，&lt;code&gt;!~*&lt;&#x2F;code&gt; 代表不匹配（实际上都是正则）&lt;&#x2F;p&gt;
&lt;h1 id=&quot;shuo-liao-zhe-yao-duo-zhe-xie-neng-gan-shi-yao&quot;&gt;说了这么多，这些能干什么？&lt;&#x2F;h1&gt;
&lt;p&gt;举个例子，当我们直接用 Nginx 给 Google Analytics 发送数据时，一般需要考虑两个条件：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;是否是爬虫&lt;&#x2F;li&gt;
&lt;li&gt;用户是否开启 DNT（Do Not Track）&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;这时多条件判断就派上用场了。&lt;&#x2F;p&gt;
&lt;p&gt;类似的使用环境有很多。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>2018？？</title>
          <pubDate>Sun, 21 Jan 2018 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2018/2018/</link>
          <guid>https://www.eaimty.com/2018/2018/</guid>
          <description>&lt;p&gt;&lt;del&gt;其实这篇文章在去年末就开始写了...只是懒癌泛滥，所以到现在才发出...&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;
&lt;p&gt;时间真快呵。这个博客从建立之初到现在，也已经有两年了。&lt;&#x2F;p&gt;
&lt;p&gt;然而...就算过了两年，我还是这么菜...&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;h1 id=&quot;liang-nian-jian-fa-sheng-liao-shi-yao&quot;&gt;两年间发生了什么？&lt;&#x2F;h1&gt;
&lt;ul&gt;
&lt;li&gt;变得越来越懒&lt;&#x2F;li&gt;
&lt;li&gt;上了高中&lt;&#x2F;li&gt;
&lt;li&gt;变得越来越宅&lt;&#x2F;li&gt;
&lt;li&gt;强迫症越来越严重&lt;&#x2F;li&gt;
&lt;li&gt;认识了一群有趣的朋友&lt;&#x2F;li&gt;
&lt;li&gt;近半年以来，情绪不太好，所以开始疯狂玩游戏&lt;&#x2F;li&gt;
&lt;li&gt;自己组装了几台 PC，经常是折腾了半天又拆开卖掉&lt;&#x2F;li&gt;
&lt;li&gt;换了 N 部手机，不过不论是什么手机到手第一件事就是刷 stock Android（Nexus 除外）&lt;&#x2F;li&gt;
&lt;li&gt;学习了一些前端知识（然而还是菜得抠脚）&lt;&#x2F;li&gt;
&lt;li&gt;学习了一些 Linux 知识（至少基础操作没什么问题...）&lt;&#x2F;li&gt;
&lt;li&gt;写了几个 typecho 主题（全都是 MD 风格的，也算是信仰吧...）&lt;&#x2F;li&gt;
&lt;li&gt;曾把自己的桌面系统换成 Linux，但后来因为懒得再折腾~~（因为游戏）~~换回了 Windows&lt;&#x2F;li&gt;
&lt;li&gt;写了几个没什么卵用的程序&lt;&#x2F;li&gt;
&lt;li&gt;生活中的人际关系变得越来越差&lt;&#x2F;li&gt;
&lt;li&gt;一条鱼&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;回看两年前的文章，发现自己的确变了很多...变得不知上进了，同时也失去了对很多事的热情。
周围的朋友们都越来越厉害，然而我还是停留在原地，没有任何能拿得出手的东西...
已经停滞不前一年多了，希望之后的一段时间里能稍微有些长进吧。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;guan-yu-ben-bo-ke&quot;&gt;关于本博客&lt;&#x2F;h1&gt;
&lt;p&gt;&lt;strong&gt;2018 年 5 月 2 日更新：之前 CloudFlare 的数据完全是扯淡，怕不是 CloudFlare 员工用脚趾头数出来的吧...&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在网站上挂了 2 个月的 Google Analytics，得到了差不多是准确的数据。&lt;&#x2F;p&gt;
&lt;p&gt;这是某一周的访客数据，平均大约每小时有一位访客吧，也是蛮可怜的...&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;5ae9bfeca0be8.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;流量来源还是你国最多&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;5ae9bfeca0e1d.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>materiality 主题侧边栏开启关闭动效无法显示的原因（Cloudflare）</title>
          <pubDate>Sun, 07 Jan 2018 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2018/materiality-drawer-animation-bug/</link>
          <guid>https://www.eaimty.com/2018/materiality-drawer-animation-bug/</guid>
          <description>&lt;p&gt;很久之前就发现这个问题了，一直无法解决...现在终于找到问题所在了。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;5a5228a0ea252.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Cloudflare 在 Speed 选项卡中有一个 Rocket Loader™ 功能，会导致一大堆问题。
这个功能貌似还是 Beta 阶段，如果手贱开启了，并且网站语言是中文（主要浏览者在中国大陆），那就赶快关闭吧。&lt;&#x2F;p&gt;
&lt;p&gt;目前来看，这个功能起不到任何加速作用，甚至还会拖慢加载速度，开启这个功能后每次浏览网站时都会从 Cloudflare 的服务器上加载一大堆没卵用的 JS，现阶段的兲朝网络下想连接 Cloudflare 的服务器？呵呵...&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Nexus 6P 开箱与 Android 8.0 奥利奥体验</title>
          <pubDate>Tue, 29 Aug 2017 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2017/nexus6p-and-android8/</link>
          <guid>https://www.eaimty.com/2017/nexus6p-and-android8/</guid>
          <description>&lt;p&gt;纽约时间 2017 年 8 月 21 日下午 2 时 40 分左右，东美地区的日食达到了食甚阶段，纽约一片漆黑...就在这时，Google 发布了今年的最新 Android 系统 Android8.0 Oreo（奥利奥）。&lt;&#x2F;p&gt;
&lt;p&gt;很巧的是，我刚从闲鱼淘到一部 Nexus6P，正好可以抢先品尝最新的甜点！&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;h1 id=&quot;nexus6p-kai-xiang&quot;&gt;Nexus6P 开箱&lt;&#x2F;h1&gt;
&lt;p&gt;这台机器是 n6p 港版，32GB 存储，银色，卖家描述是 95 新，个人感觉大概 9 成新左右吧，裸机。充电器 5V3A，不确定是不是 QC 协议，速度不错。&lt;&#x2F;p&gt;
&lt;p&gt;先来一张正脸：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;59a4f937f07fe.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;照片是用朋友的菊花为麦芒 4 拍摄的，加上时间是晚上，所以不太清晰。&lt;&#x2F;p&gt;
&lt;p&gt;前置双扬声器组成的对称设计很漂亮。&lt;&#x2F;p&gt;
&lt;p&gt;后盖：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;59a4f9f78321d.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;边角处有些磕碰:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;59a4fa4fa2875.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;android-8-0-ti-yan&quot;&gt;Android 8.0 体验&lt;&#x2F;h1&gt;
&lt;p&gt;拿到机器后直接解掉 bootloader 锁刷了 Google 原厂镜像。&lt;&#x2F;p&gt;
&lt;p&gt;开机速度提升是这次更新的一大噱头，体验了一下，去除显示“Device Unlocked”画面的时间，的确非常快！&lt;&#x2F;p&gt;
&lt;p&gt;首次开机走完设置向导进入桌面后被吓到了：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;59a4fb3504c70.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;卧槽怎么是圆角矩形？？？&lt;&#x2F;p&gt;
&lt;p&gt;Google 你疯了吗？？&lt;&#x2F;p&gt;
&lt;p&gt;打开应用抽屉（无视掉我安装的那些应用）：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;59a4fb94d4faf.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;连 Google 自家的应用图标都是有的方有的圆。&lt;&#x2F;p&gt;
&lt;p&gt;设置的配色改了。一片白，快瞎了...&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;59a50008f0af8.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;选项层级大改一番，也砍掉了 drawer，个人不太喜欢，显得很繁琐，找个选项都变得很难。&lt;&#x2F;p&gt;
&lt;p&gt;设备信息：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;59a503136308a.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;xin-gong-neng&quot;&gt;新功能&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;picture-in-picture-hua-zhong-hua&quot;&gt;Picture-in-picture（画中画）&lt;&#x2F;h3&gt;
&lt;p&gt;设置中可以找到 pip 的选项（这个选项我也找了半天...）：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;59a5052ad1fd2.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;其中想开启 YouTube 的 pip 需要订阅 YouTube Red，穷...用 Google Map 做个示范。打开导航，直接按 home 回到桌面，就能激活 pip：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;59a507ee75147.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;可以随意拖动，拖动到屏幕最底部可以关闭。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;an-quan-fang-mian&quot;&gt;安全方面&lt;&#x2F;h3&gt;
&lt;p&gt;加入了 Play Protect，能定期检查设备中的 app 有没有毒。不过在国内很鸡肋，毕竟毒瘤喂屎你也必须吃。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;59a508aed2397.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;另外，之前版本中的“允许安装来源未知的应用”也有变化，在 O 中可以对每个应用单独设置是否允许该应用安装新应用。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;geng-quan-mian-de-quan-xian-guan-li&quot;&gt;更全面的权限管理&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;59a50905e2be1.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;可以设置更多的权限，比起 6.0 和 7.x 也是一种进步吧。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tong-zhi-xi-tong-gai-jin&quot;&gt;通知系统改进&lt;&#x2F;h3&gt;
&lt;p&gt;新系统引入了 Notification channels（通知通道）。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;59a5098da84bb.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;如图，另外新加入了 notification dot，可以翻译成“通知点” 233，不过貌似在 Google Now Launcher 上这功能失效了...&lt;&#x2F;p&gt;
&lt;h3 id=&quot;xin-emoji&quot;&gt;新 emoji&lt;&#x2F;h3&gt;
&lt;p&gt;丑出翔&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;59a50b906869e.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;扁平的感觉被 Google 吃了&lt;&#x2F;p&gt;
&lt;h3 id=&quot;xin-hou-tai-xian-zhi-ji-zhi&quot;&gt;新后台限制机制&lt;&#x2F;h3&gt;
&lt;p&gt;后台运行的应用会强制显示在状态栏上。和7.x不同的是，这个通知暂时无法消去，强迫症福利。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;59a50d38b7365.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;xin-de-zhuang-tai-lan-she-ji&quot;&gt;新的状态栏设计&lt;&#x2F;h3&gt;
&lt;p&gt;这部分不吐槽了，因为实在是无力吐槽了。一片惨白，快瞎了。&lt;&#x2F;p&gt;
&lt;p&gt;不过好消息是，Android 6.0 上的快捷开关风格回来了，例如，现在直接点击 WiFi 按钮会 toggle 状态，点击下面的下箭头才会打开 WiFi 选择菜单。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;cai-dan&quot;&gt;彩蛋&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;59a50ea8e074d.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;长按后会出现：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;59a50ed17f0ec.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;阴吹思婷&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;59a50f10043b7.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;拖动这只章鱼，它会变成~~奇奇怪怪的样子~~&lt;&#x2F;p&gt;
&lt;h1 id=&quot;zong-jie&quot;&gt;总结&lt;&#x2F;h1&gt;
&lt;p&gt;&lt;del&gt;在总结前，我先去刷个 rr 或 purenexus 回到 7.1 吧&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;
&lt;p&gt;如果你手持 Nexus 5X 或 6P 或 Pixel 系列，升级尝个鲜还是不错的，但暂时不建议作为主力系统使用。&lt;&#x2F;p&gt;
&lt;p&gt;首先，这一代的显性升级比较少，而且有些新设计说实话并不是很棒...例如新的配色、后台运行应用的通知、新的 emoji 之类的。&lt;&#x2F;p&gt;
&lt;p&gt;第二，历来升级系统的经验就是：在新系统发布后，先等 1 个月左右再作为主力使用。因为发布 1 个月后，各种 app 的配置基本已经做的差不多了。另外现在 8.0 的 bug 还有很多，我这里就遇到了一个：在 play 里下载应用的时候只能一次点击下载一个应用，想要下载下一个需要等上一个应用彻底安装完成，否则第二个应用不会自动下载，很麻烦。&lt;&#x2F;p&gt;
&lt;p&gt;等到 AOSP 源码放出，Lineage 15 发布后再升级也不迟，毕竟 Lineage 才是推广新系统的主力军，就算 Google stock 也有些功能是缺失的。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>记录一下重装服务器系统中遇到的一堆坑</title>
          <pubDate>Tue, 08 Aug 2017 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2017/traps-in-server-system-reconfiguration/</link>
          <guid>https://www.eaimty.com/2017/traps-in-server-system-reconfiguration/</guid>
          <description>&lt;p&gt;很长一段时间以来，博主用的 &lt;a href=&quot;https:&#x2F;&#x2F;www.vultr.com&#x2F;?ref=7131872&quot;&gt;Vultr&lt;&#x2F;a&gt; 2.5 刀小机上的 MySQL 5.7 频繁抽风，经常需要手动重启服务器，至于为什么这么长时间没有管它，原因还是我懒呗~&lt;&#x2F;p&gt;
&lt;p&gt;最近闲了下来，有时间折腾一下服务器了，于是重装了一下换回 MySQL 5.5，用 CentOS 官方镜像重装了系统。
之前因为听说 SELinux 和 Systemd 之间不容易和谐共处，所以都是关掉 SELinux 的（不要学我，这很不安全）。
这次用 CentOS7 1611 x64 Minimal 官方镜像中 SELinux 是默认打开的，试了一下，发现不是想象中的那么差，所以就没有关闭。
接下来，坑就来了。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;h1 id=&quot;keng-yi&quot;&gt;坑一&lt;&#x2F;h1&gt;
&lt;p&gt;更换 SSH 端口后，从外界无法连接&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-jue-fang-fa&quot;&gt;解决方法：&lt;&#x2F;h2&gt;
&lt;p&gt;根据 sshd_config 中给出的方法，在文件中修改端口号后需要告诉 SELinux，运行 &lt;code&gt;# semanage port -a -t ssh_port_t -p tcp 端口号&lt;&#x2F;code&gt; &lt;&#x2F;p&gt;
&lt;h1 id=&quot;keng-er&quot;&gt;坑二&lt;&#x2F;h1&gt;
&lt;p&gt;semanage command not found&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-jue-fang-fa-1&quot;&gt;解决方法&lt;&#x2F;h2&gt;
&lt;p&gt;查一下命令所在包：&lt;code&gt;# yum provides semanage&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;得知命令存在于包 &lt;code&gt;policycoreutils-python&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;安装包：&lt;code&gt;# yum install policycoreutils-python&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;keng-san&quot;&gt;坑三&lt;&#x2F;h1&gt;
&lt;p&gt;nginx 读不到配置文件、证书，网站目录&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-jue-fang-fa-2&quot;&gt;解决方法&lt;&#x2F;h2&gt;
&lt;p&gt;SELinux 有个限制：cp instead mv&lt;&#x2F;p&gt;
&lt;p&gt;在折腾好文件后，执行 &lt;code&gt;# restorecon -v -R &#x2F;path&#x2F;to&#x2F;files&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;keng-si&quot;&gt;坑四&lt;&#x2F;h1&gt;
&lt;p&gt;网站 conf、证书、文件都到位后访问显示 &lt;code&gt;files not exist&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-jue-fang-fa-3&quot;&gt;解决方法&lt;&#x2F;h2&gt;
&lt;p&gt;还是 SELinux 的锅&lt;&#x2F;p&gt;
&lt;p&gt;告诉 SELinux 这是网站的文件：&lt;code&gt;# chcon -Rt httpd_sys_content_t &#x2F;path&#x2F;to&#x2F;www&lt;&#x2F;code&gt;
如需通过网页程序修改文件内容，则是：&lt;code&gt;# chcon -Rt httpd_sys_content_rw_t &#x2F;path&#x2F;to&#x2F;writabledir&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;keng-wu&quot;&gt;坑五&lt;&#x2F;h1&gt;
&lt;p&gt;可通过 ip 直接经过 443 端口访问网站&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-jue-fang-fa-4&quot;&gt;解决方法&lt;&#x2F;h2&gt;
&lt;p&gt;我的站点都是有 SSL 的，所以一般都会在 iptables 中禁掉 80 端口，所以咱们只需要折腾一下 443 端口的问题
Google 到的结果中有一堆放在 CSDN、博客园之类的文章，你抄我，我抄你，而且抄的还都有问题，真 TM 是恶心透了。&lt;&#x2F;p&gt;
&lt;p&gt;最终找到了解决方案：&lt;&#x2F;p&gt;
&lt;p&gt;在 nginx.conf 中的 &lt;code&gt;include &#x2F;etc&#x2F;nginx&#x2F;conf.d&#x2F;*.conf;&lt;&#x2F;code&gt; 一行下面插入：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;conf&quot; class=&quot;language-conf &quot;&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;server {
    listen  443 default;
    return  444;
    ssl_certificate &amp;#x2F;path&amp;#x2F;to&amp;#x2F;crt;
    ssl_certificate_key &amp;#x2F;path&amp;#x2F;to&amp;#x2F;key;
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这里的证书自己签一个就好，注意不要在其中泄露真是的网站信息。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>记一次用农企 Ryzen 架构的装机过程</title>
          <pubDate>Fri, 04 Aug 2017 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2017/build-a-ryzen-pc/</link>
          <guid>https://www.eaimty.com/2017/build-a-ryzen-pc/</guid>
          <description>&lt;p&gt;又到暑假了，一年一度的学生党装机旺季。有个哥们儿想要让我帮他装台机器，去除外设后预算 4000 软妹币左右。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;h1 id=&quot;pei-zhi-dan&quot;&gt;配置单&lt;&#x2F;h1&gt;
&lt;p&gt;说实话，最近可不是装机的好时节。
SSD 和内存价格都涨上了天，有的甚至翻了一倍多。
另外又赶上比特币大涨导致的挖矿狂潮，显卡价格也一路飙升，举个例子：5 月份卖 799 的“图吧标配”昂达1050Ti前几天飙到过1000，其它品牌就更不用说了。
虽然农企最近发布的 Ryzen 系列非常有性价比——特别是在中低端市场，但是由于存储和显卡的问题，最近装机的性价比还是不高。&lt;&#x2F;p&gt;
&lt;p&gt;不管我怎么劝，这哥们儿非要现在装机器...&lt;&#x2F;p&gt;
&lt;p&gt;我劝过他上二手的车，虽然有一定风险，但是性价比高啊...四千块钱能攒出一台 6700k+980 的机器，甚至还能上水冷，然而他不听...&lt;&#x2F;p&gt;
&lt;h2 id=&quot;cpu&quot;&gt;CPU&lt;&#x2F;h2&gt;
&lt;p&gt;这位兄弟买机器是为了打游戏，考虑到他不玩那些过于吃配置的 3A 大作，U 用不到太高端的。
由于最近的行情，自然入了农用机械的坑。
Ryzen5 1400，4C8T，频率 3.2GHz，TDP 65W，价格便宜，超频后比它那带 X 的兄弟性价比高很多多。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zhu-ban&quot;&gt;主板&lt;&#x2F;h2&gt;
&lt;p&gt;既然要超频，那 320 主板绝对是不可以选的，350 才是最优选项。
考虑到 Ryzen 的套装比单买实惠，所以在京东选择了 Ryzen5 1400 + 微星 B350M Gaming PRO 这样的一个套包，加上运费 1460 元。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;nei-cun&quot;&gt;内存&lt;&#x2F;h2&gt;
&lt;p&gt;网上很多评测都提到内存频率对 Ryzen 的性能影响比较大，遂选择了 3000 频。
然而最近内存实在是缺货...本来想组双通道，但京东上实在是找不到套条，只好用一根芝奇的 8G D4 3000 频内存代替，售价 530 元。&lt;&#x2F;p&gt;
&lt;p&gt;这也太贵了吧！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;xian-qia&quot;&gt;显卡&lt;&#x2F;h2&gt;
&lt;p&gt;我强烈推荐这哥们买二手，可惜他不听...&lt;&#x2F;p&gt;
&lt;p&gt;北极星架构香不香？当然香！
可是最近挤破头皮都买不到 A 卡，而一千左右价位的N卡只剩下了 1050Ti 可选，在京东上挑了半天，最后选择了微星的 1050Ti OC，售价 1100 元。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ying-pan&quot;&gt;硬盘&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;ssd&quot;&gt;SSD&lt;&#x2F;h3&gt;
&lt;p&gt;到京东一看，价格涨上天了！
作为启动盘，安装 Windows 并预留出足够的空间，128G 足矣。
然而...WTF！！！128G 都能上到 400 左右...最终选择了联想闪电鲨 SL700 120G，SATA 接口，TLC 颗粒，要什么自行车？
360 元&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hdd&quot;&gt;HDD&lt;&#x2F;h3&gt;
&lt;p&gt;希捷酷鱼 1T 台盘，330 元。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dian-yuan&quot;&gt;电源&lt;&#x2F;h2&gt;
&lt;p&gt;CPU 超频后按照最高 80W 算，显卡也算 80W，给杂项留出 120W，也为了以后的扩展，最终选择了 300W 电源。
本来选的是安钛克的 VP300，然而最后尴尬地发现预算不够，于是换成了游戏悍将 80+ S300，电源要什么自行车？只要不炸、不起火，一切都好（逃
100 元&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ji-xiang&quot;&gt;机箱&lt;&#x2F;h2&gt;
&lt;p&gt;这方面我有些不爽，至于为什么，一会再说。金河田 启源 黑红，120 元，送了一个它自家的北极玄冰 100 散热器。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;zu-zhuang&quot;&gt;组装&lt;&#x2F;h1&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;598bd9754e7a6.png&quot; alt=&quot;配件&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;MSI B350M Gaming Pro 主板
&lt;img src=&quot;&#x2F;pictures&#x2F;598bdcefe5675.png&quot; alt=&quot;主板&quot; &#x2F;&gt;
插槽方面有些捉急啊，1 个 PCI-e x16，2 个 PCI-e x1，两个内存接口，两个 M.2，两个 SATA，不过够用就好。
IO 方面
&lt;img src=&quot;&#x2F;pictures&#x2F;598bde4867cb1.png&quot; alt=&quot;IO&quot; &#x2F;&gt;
还是那句话：够用就行，要什么自行车？&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;598be3991a7d8.png&quot; alt=&quot;&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;pictures&#x2F;598be53696c8b.png&quot; alt=&quot;&quot; &#x2F;&gt;
睾贵的 1400&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;598be66bcb4ac.png&quot; alt=&quot;安装&quot; &#x2F;&gt;
这里提一下,自带的散热器底部已经涂上了硅脂,从包装盒中拿出来的时候要小心,我沾了满手的硅脂，那哥们儿脸都绿了。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;598be85ed246f.png&quot; alt=&quot;装上内存&quot; &#x2F;&gt;
是不是觉得很违和？主板、显卡甚至机箱都是红色的，然而内存是蓝色的，退货！&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;598beee60e1e5.png&quot; alt=&quot;电源&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;硬盘
&lt;img src=&quot;&#x2F;pictures&#x2F;598bf111ebe3c.png&quot; alt=&quot;硬盘&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;1050Ti
&lt;img src=&quot;&#x2F;pictures&#x2F;598bf1f401b29.png&quot; alt=&quot;显卡&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;进机箱
&lt;img src=&quot;&#x2F;pictures&#x2F;598bf255eddda.png&quot; alt=&quot;进机箱&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;蜜汁理线
&lt;img src=&quot;&#x2F;pictures&#x2F;598bf33d360bc.png&quot; alt=&quot;理线&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;安装完成。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;pei-zhi-wen-ti&quot;&gt;配置问题&lt;&#x2F;h1&gt;
&lt;p&gt;首先是高频内存的支持问题，安装好后打开 BIOS 发现，内存被限制在了 2133 频，然而锐龙对高频内存的需求很大。
在 Win 下折腾了半天，没反应，又进入 BIOS 后，才发现这货的 BIOS 还是 2 月份的版本，怪不得。升到最新版本后，支持了 XMP Profiles，内存成功加到了 2933 频。
CPU 超频方面，这板子既然叫 Gaming，也就自带了一键超频功能。
懒得自己手动超了，打开 Gaming Boost 功能，自动把电压加到了 1.2V 以上，超到了 3.6GHz。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;guan-yu-ji-xiang&quot;&gt;关于机箱&lt;&#x2F;h1&gt;
&lt;p&gt;简直侮辱智商。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wai-guan&quot;&gt;外观&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;598c01e468a1f.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这哥们竟然觉得这机箱好看？！我的眼睛！当然，审美不同，结果肯定不同。这里就当我在吐槽这小哥的审美吧。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zuo-gong&quot;&gt;做工&lt;&#x2F;h2&gt;
&lt;p&gt;呵呵。&lt;&#x2F;p&gt;
&lt;p&gt;大概淘宝上大多数 70~80 左右价位的机箱做工都会比这个好。这是涂了一层油漆的纸吗？铁皮比纸都薄。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;she-ji&quot;&gt;设计&lt;&#x2F;h2&gt;
&lt;p&gt;硬盘位设置得极其扯淡。另外，一个全尺寸 ATX 机箱竟然连一块全长的显卡都放不下吗？还好我选购的这块 1050Ti 比较短。
还有，看到网上的买家们有人反映，这机箱甚至连玄冰 400 都装不下。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;io&quot;&gt;IO&lt;&#x2F;h2&gt;
&lt;p&gt;不知道是机箱的问题还是主板的问题还是我接线的问题，前置的两个 USB 接口中只有 USB2.0 的那个可以使用，另外前置 3.5mm 耳机孔也是摆设。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;zong-jie&quot;&gt;总结&lt;&#x2F;h1&gt;
&lt;p&gt;虽然最近不是装机的好时机，但是这兄弟还是成功拿到了一台能玩游戏的电脑，托了 Ryzen 的福。
预算控制得还是非常好，除了几个散热风扇以外，这些“大件”总价正好是 4000 元。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>用 900 元组装一台 CPU 算力（较）强劲的工作站</title>
          <pubDate>Sun, 23 Jul 2017 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2017/budget-workstation-with-powerful-cpu/</link>
          <guid>https://www.eaimty.com/2017/budget-workstation-with-powerful-cpu/</guid>
          <description>&lt;p&gt;距离上次发文已经相隔 4 个月了，消失了这么长时间，其实我是~~跑去搞大新闻了~~。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;h1 id=&quot;yong-900-yuan-zu-zhuang-yi-tai-cpu-suan-li-jiao-qiang-jing-de-gong-zuo-zhan-xu-yao-shi-yao&quot;&gt;用 900 元组装一台 CPU 算力（较）强劲的工作站需要什么？&lt;&#x2F;h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;del&gt;充足的想象力&lt;&#x2F;del&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;del&gt;快如闪电的手速（用于在闲鱼刷新淘件）&lt;&#x2F;del&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;del&gt;嘴炮（用于砍价）&lt;&#x2F;del&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;pei-zhi-dan&quot;&gt;配置单&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;jia-gou&quot;&gt;架构&lt;&#x2F;h2&gt;
&lt;p&gt;首先选择架构。&lt;&#x2F;p&gt;
&lt;p&gt;既然想用不到千元来组装机器，那么选择最新的架构肯定是行不通的。纵观整个市场，想要尽量节省且得到较强的性能，需要注意如下几点：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;使用服务器架构&lt;&#x2F;li&gt;
&lt;li&gt;不要入农企的坑（原因显而易见，一是性能差、性价比低（他家的 U 没一个能打的，况且 x86 还要分大小核？我吐了），二是不可靠（三核 CPU 是哪儿来的？）&lt;&#x2F;li&gt;
&lt;li&gt;不使用 3-5 年内的新架构，要等主流市场完全退烧后再入&lt;&#x2F;li&gt;
&lt;li&gt;因为用到的是老 CPU，核心、线程越多越好。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;综上，我最终选择了英特尔在 2010 年左右发布的 1366 架构，性价比极高！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;cpu&quot;&gt;CPU&lt;&#x2F;h2&gt;
&lt;p&gt;1366 架构的所有 CPU 中，性价比最高的当属 X5650（&lt;a href=&quot;https:&#x2F;&#x2F;ark.intel.com&#x2F;products&#x2F;47922&#x2F;Intel-Xeon-Processor-X5650-12M-Cache-2_66-GHz-6_40-GTs-Intel-QPI&quot;&gt;它的 Intel Ark&lt;&#x2F;a&gt;）
在 X5650 发布时，正赶上了 32nm 的东风，因此 能耗比相较于至强 55xx 系列出色很多。在 56xx 系列中，E 和 L 系列性能较低，而比 X5650 高端的 X5670 和 X5690 又太贵，一块就要四五百。&lt;&#x2F;p&gt;
&lt;p&gt;6 核 12 线程，基频 2.66 GHz，Boost 频率 3.0GHz，12M 缓存，95W TDP，在 CPU 天梯图中与 i5-4670K 打了平手，可见其性能也比较高了。
淘宝 120 元一块，来两块！总共是暴力的 12 核 24 线程！&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;5975d41a2d4ef.png&quot; alt=&quot;CPU&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zhu-ban&quot;&gt;主板&lt;&#x2F;h2&gt;
&lt;p&gt;x58 的服务器板子，现在存世量不算太多。
由于 1366 架构支持的是至强 55xx&#x2F;56xx 系列与初代酷睿 i7 至尊系列，所以我们有以下选择：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;睾贵的 ASUS 之类大厂的民用板，当年被用来配合初代 i7 至尊版，是最高端的民用、工作站产品之一，贵出天际，现在也没便宜到哪里去，闲鱼上均价要五百块左右&lt;&#x2F;li&gt;
&lt;li&gt;寨板。各种各样的小厂为二手 DIY 市场出了不少 x58 板子，主要是被用来配合降价的至强系列，然而毕竟是小厂，并不是很可靠&lt;&#x2F;li&gt;
&lt;li&gt;企业级服务器市场的产品，代表有惠普的 DL 系列、戴尔的 C 系列和 R 系列等，现在这批服务器基本上已经被淘汰到二手市场了&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;由于大厂民用板太贵，而二手寨板又太不可靠（我不想把我家烧掉 QAQ）,权衡价格、设计、可扩展性以及可玩性之后，我最终选择了戴尔 C6100：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;双路 1366 槽 CPU&lt;&#x2F;li&gt;
&lt;li&gt;12 个 DDR3 内存插槽，每块 CPU 都有 3 个内存通道，还支持 (REG) ECC 内存&lt;&#x2F;li&gt;
&lt;li&gt;没有标准 PCI-e 接口，但有 PCI-e Riser 接口，插上升高卡或转向卡之后就能插全尺寸显卡了。
C6100 支持双路 1366 CPU，双通道 DDR3 1333 内存，家用&#x2F;ECC 都支持，没有 PCI-e x16 接口，但有 PCI-e Riser 接口，插上升高卡或转向卡之后就能插全尺寸显卡了。&lt;&#x2F;li&gt;
&lt;li&gt;由于是刀片服务器，与民用 ATX 架构在很多地方不兼容，例如电源、机箱&lt;&#x2F;li&gt;
&lt;li&gt;便宜，最近几年有大量从企业机房中退役的 C6100 进入了二手市场，价格非常好看&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;在闲鱼淘到一块，卖家送了散热器和散热风扇、用来配合 ATX 电源的升压模块，还送了两块 E5507 用来保护 CPU 插槽、亮机！总共 250 软妹币包邮。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;5975d47a32139.png&quot; alt=&quot;主板&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;nei-cun&quot;&gt;内存&lt;&#x2F;h2&gt;
&lt;p&gt;虽然主板能插普通家用内存，但 ECC 内存非常便宜，现在平均 10 元 1G。
上闲鱼花 170 元买到 4 条 4G 1333 DDR3 ECC 三星内存，带马甲。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;5975d4d76ac4f.png&quot; alt=&quot;内存&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;xian-qia&quot;&gt;显卡&lt;&#x2F;h2&gt;
&lt;p&gt;其实是不需要显卡的，主板集成了一块 64M 显存的显卡，但考虑到可能之后会装个 Windows 系统玩玩之类的，我在电脑城中随便找了一家店花了 60 元收了一块二手影驰 GT610 亮机，2G 版，散热片式被动散热，功耗 30W 左右。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ying-pan&quot;&gt;硬盘&lt;&#x2F;h2&gt;
&lt;p&gt;这台机器只是临时用用，所以上淘宝捡了两块 160G 二手迈拓硬盘，一块 40 元，两块 80 元，组 RAID0，将就着先用几天。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;5975d4e502b15.png&quot; alt=&quot;硬盘&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dian-yuan&quot;&gt;电源&lt;&#x2F;h2&gt;
&lt;p&gt;这方面我纠结了很久：究竟是用家用电源还是上服务器电源？
最后因为噪音和线路方面的原因选择了家用电源。整机最耗电的应该就是 CPU 了，不超频的话，两块满载时总功耗应该在 200 瓦左右，加上杂七杂八的能耗，整机满载差不多 300W，考虑到转换损耗，况且可能要升级显卡或加硬盘，留出 200W，那么最终的选择就是 500W。
最终，我捡到了一块安钛克 VP500P V2，500W。
这电源原价大概二三百元，在闲鱼遇到一个人非常好的卖家，看到我是学生,还给我免了邮费，最终 99 元到手。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;jie-ge-hui-zong&quot;&gt;价格汇总&lt;&#x2F;h1&gt;
&lt;ul&gt;
&lt;li&gt;CPU 240 元&lt;&#x2F;li&gt;
&lt;li&gt;主板 250 元&lt;&#x2F;li&gt;
&lt;li&gt;内存 180 元&lt;&#x2F;li&gt;
&lt;li&gt;硬盘 80 元&lt;&#x2F;li&gt;
&lt;li&gt;电源 100 元&lt;&#x2F;li&gt;
&lt;li&gt;亮机卡 60 元&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;总计 910 元&lt;&#x2F;p&gt;
&lt;h1 id=&quot;zu-zhuang&quot;&gt;组装&lt;&#x2F;h1&gt;
&lt;p&gt;组装过程没什么好说的，只是因为用了 ATX 电源，改线的过程比较麻烦。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;5975d50859ea9.png&quot; alt=&quot;配件&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;pictures&#x2F;5975d5307c703.png&quot; alt=&quot;机器1&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;pictures&#x2F;5975d54cf0079.png&quot; alt=&quot;机器2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;xing-neng&quot;&gt;性能&lt;&#x2F;h1&gt;
&lt;p&gt;首先先装上 Windows 10 试试，听说 win10 对多 CPU 的支持已经很不错了，先体验一下再装成 Debian 吧。
因为不想花太多时间折腾 Windows，所以只用 CPUZ 测试了一下，毕竟这台机器最亮眼的就是 CPU 性能，其它只是用来“亮机”的。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;5975d605e3df9.png&quot; alt=&quot;CPUZ&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这毕竟是一台 900 元组装的机器，所以不要对其要求过高。
与最新（2017 年 7 月）的酷睿 i7-7700k 相比，单核心性能相当于它的一半（毕竟 10 年老 U），但这机器可是 12 核 24 线程！！！
i7 的 4 核 8 线程可是弱爆了，X5650*2 的 CPUZ 多核跑分是 7700k 的近 1.5 倍（144%）。可别忘了，i7-7700k 京东价现在是 2500 rmb，是咱整机的 2.5 倍！&lt;&#x2F;p&gt;
&lt;h1 id=&quot;gong-hao&quot;&gt;功耗&lt;&#x2F;h1&gt;
&lt;p&gt;这方面肯定是不如新架构的。但是假如你需要大量 CPU 算力，又觉得新架构的服务器、高端民用架构太贵，其实这机器的表现还不错。相较于 5960x（不与 6950x 比，一是因为差距太大，二是因为牙膏厂故意把 6950x 涨价涨上天，现在性价比没有可比性），性能强 10%~20%，而功耗大 25%，也还好吧。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;que-dian&quot;&gt;缺点&lt;&#x2F;h1&gt;
&lt;p&gt;这部分更新于 2017 年底，现在我已经把这机器出手了，因为它的问题实在是太多了...&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zao-yin&quot;&gt;噪音&lt;&#x2F;h2&gt;
&lt;p&gt;这块主板是作为服务器设计的，所以噪音问题不用多说大家就能感受到了...
毕竟有两块 CPU 风扇，根据我用手机软件测试的结果，它们转起来的时候有五十多分贝...主板上没有风扇的接口，两块散热风扇只能用转接线接到电源大 D 4Pin 口上供电，所以 PWM 肯定是不存在的，想要降低噪音，只能手动通过物理方式，比如降压什么的...&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rui-pin&quot;&gt;睿频&lt;&#x2F;h2&gt;
&lt;p&gt;还是这两个风扇...散热能力比较差。正常的服务器机箱上都会有几个高转速风扇（有些甚至能到万转以上）保证空气的流通性，然而我这机器并没有机箱...虽然裸奔也能在一定程度上缓解散热压力，但由于风扇不给力，有几次高负载时甚至过热到自动关机...
因此，这机器根本“睿”不起来，不过好在核多线程多，就算不“睿”性能也不差。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;zong-jie&quot;&gt;总结&lt;&#x2F;h1&gt;
&lt;p&gt;从 5 月底开始策划、买配件，到现在 7 月底发这篇文章，历时近两个月，经历了不少失败，走了不少弯路，让我这穷鬼学到了不少东西。总之，ALL FOR FUN！看着任务管理器中的 24 个框框，成就感爆表！&lt;&#x2F;p&gt;
&lt;p&gt;但是，不推荐大家都这么搞。毕竟二手市场水深，况且这样的机器不论在安全性、外观还是可维护性都不好。如果你不是手头非常紧又非常想在任务管理器里数框框，不要组装类似的机器。&lt;del&gt;反正我已经退坑了🥴&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>materiality-typecho-theme: 一款简洁并专注于显示文字内容的 Material Design 风格 Typecho 主题</title>
          <pubDate>Wed, 15 Mar 2017 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2017/materiality-typecho-theme/</link>
          <guid>https://www.eaimty.com/2017/materiality-typecho-theme/</guid>
          <description>&lt;p&gt;最近发现了 &lt;a href=&quot;https:&#x2F;&#x2F;www.mdui.org&#x2F;&quot;&gt;MDUI&lt;&#x2F;a&gt; 这个库，感觉它比 MDL 好用多了_(:з」∠)_于是就用它把之前的主题重写了一遍&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;materiality&lt;&#x2F;code&gt; 这个词的意思是“实质性”，同时也和 &lt;code&gt;Material Design&lt;&#x2F;code&gt; 中 &lt;code&gt;Material&lt;&#x2F;code&gt; 相关。&lt;&#x2F;p&gt;
&lt;p&gt;这是一个简洁的主题，我不会让它变得越来越臃肿，同时，它也是一个能够高度自定义化的主题。&lt;&#x2F;p&gt;
&lt;p&gt;GayHub 地址： &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;EAimTY&#x2F;materiality&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;EAimTY&#x2F;materiality&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>更换域名</title>
          <pubDate>Sun, 23 Oct 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2016/change-domain/</link>
          <guid>https://www.eaimty.com/2016/change-domain/</guid>
          <description>&lt;p&gt;好久不见！😀&lt;&#x2F;p&gt;
&lt;p&gt;最近抽出了一些时间，把域名从 eaimty.xyz 折腾到了 eaimty.com，毕竟 com 域名是最传统也是最老牌的域名。
记得当时买 xyz 域名时正好 godaddy 打折，也就凑乎用了一段时间。几天前发现 &lt;a href=&quot;https:&#x2F;&#x2F;www.freenom.com&#x2F;&quot;&gt;Freenom&lt;&#x2F;a&gt; 上的 com 域名炒鸡便宜，所以就买了下来。&lt;&#x2F;p&gt;
&lt;p&gt;以前的所有服务，全部搬迁到了新的域名下，同时也对旧的域名做了 301，也就不用担心 SEO 了。
更换域名的同时，顺便把博客和其它一些服务搬到了一台新的服务器上，所以访问速度应该有所提升。&lt;&#x2F;p&gt;
&lt;p&gt;网站的其它设置没有变化。安全方面，仍然是全站 ssl+hsts+http2。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>用自签名 SSL 证书配合 CloudFlare 免费 SSL 构建全站 HTTPS</title>
          <pubDate>Sun, 23 Oct 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2016/configure-https-using-custom-ca-and-cloudflare/</link>
          <guid>https://www.eaimty.com/2016/configure-https-using-custom-ca-and-cloudflare/</guid>
          <description>&lt;p&gt;之前一段时间，本博客一直在用 letsencrypt 的 SSL 证书。这个证书的优点是免费，然而缺点也很明显，就是使用起来比较麻烦，而且有效期只有 3 个月。
我是个懒人，所以能一劳永逸的事，我都会一次性做完。很早之前就了解到 CloudFlare 有免费的 SSL 证书，但是假如仅在 CloudFlare 后台中开启它的话，并不能做到全站加密，只能开启 Flexible 模式，而不是 Full 模式。&lt;&#x2F;p&gt;
&lt;p&gt;进入 CloudFlare 后台-&amp;gt;Crypto，可以看到第一项就是免费提供的 SSL。
这里有4种模式可选：Off（关闭 SSL）、Flexible（视情况而定）、Full（全部加密，需要在服务器上部署证书，但 CloudFlare 不会检查证书的有效性）、Full(Strict)（严格模式，也就是全部加密，而且 CloudFlare 会检查服务器上的证书是否有效）。
在这里，我们的目标是开启 Full 模式，实现方式是：首先自签一个泛域名的证书，然后在 Nginx 中设置通过 HTTPS 访问网站，最后到 CloudFlare 中设置 SSL 模式。
当然，如果你想要开启 Full(Strict) 模式，也是可以的，但服务器上就不能部署自签名的证书了，而需要一个有效机构颁发的证书，例如 letsencrypt 的证书，然而怎么做的缺点就是需要定期续期。&lt;&#x2F;p&gt;
&lt;p&gt;使用自签名证书就不存在这个问题了，你想签多长时间的有效期就可以签多长时间~~（我签了 20 年，2333333）~~，更换服务器时，也仅需要把证书文件拷到新服务器上。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;h1 id=&quot;zhun-bei-gong-zuo&quot;&gt;准备工作&lt;&#x2F;h1&gt;
&lt;p&gt;第一步，当然是确保你的 DNS 是 CloudFlare 的！&lt;&#x2F;p&gt;
&lt;p&gt;第二步，安装 OpenSSL&lt;&#x2F;p&gt;
&lt;p&gt;debian 系：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# apt-get install openssl&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;rhel 系：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# yum install openssl&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;qian-fa-zi-qian-ming-zheng-shu&quot;&gt;签发自签名证书&lt;&#x2F;h1&gt;
&lt;p&gt;首先建立一个生成、存放证书的目录：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# mkdir certificate&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;进入该目录，然后签发一个根域名的 CA 证书，第一步创建一个私钥 &lt;code&gt;ca.key&lt;&#x2F;code&gt;：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# openssl genrsa -des3 -out ca.key 2048&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;第二步，生成 CA 根证书（公钥）：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# openssl req -new -x509 -days 7305 -key ca.key -out ca.crt&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;命令中，&lt;code&gt;-days&lt;&#x2F;code&gt; 后面的 &lt;code&gt;7305&lt;&#x2F;code&gt; 是指证书的有效期，以天为单位，这里设置成了 20 年，手动滑稽。&lt;&#x2F;p&gt;
&lt;p&gt;执行命令后会让你填一堆地区、组织什么的东西，随便填就好，但注意期间会让你填写 &lt;code&gt;common name&lt;&#x2F;code&gt;，也就是域名，这里填入的是你的根域名，例如 &lt;code&gt;eaimty.com&lt;&#x2F;code&gt;。最后，你就得到了一个根域的 CA 证书。&lt;&#x2F;p&gt;
&lt;p&gt;之后生成一个给泛域名用的私钥：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# openssl genrsa -des3 -out yourdomain.com.pem 1024&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;解密私钥：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# openssl rsa -in yourdomain.com.pem -out yourdomain.com.key&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;生成签名请求：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# openssl req -new -key yourdomain.com.pem -out yourdomain.com.csr&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这一步中 &lt;code&gt;common name&lt;&#x2F;code&gt; 要填入泛域名，如 &lt;code&gt;*.eaimty.com&lt;&#x2F;code&gt;，这样生成的证书可以供所有子域使用。&lt;&#x2F;p&gt;
&lt;p&gt;下一步还不能直接执行签名，否则会报错，要先修改一下 openssl 的配置文件：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# vi &#x2F;etc&#x2F;pki&#x2F;tls&#x2F;openssl.cnf&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;找到其中的 &lt;code&gt;dir = &lt;&#x2F;code&gt;，把值改成 &lt;code&gt;.&#x2F;ca&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;然后在你签发证书的工作目录中：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# mkdir -p ca&#x2F;newcerts&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# touch ca&#x2F;index.txt&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# touch ca&#x2F;serial&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# echo &amp;quot;01&amp;quot; &amp;gt; ca&#x2F;serial&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这样就可以正常执行签名了：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# openssl ca -policy policy_anything -days 7305 -cert ca.crt -keyfile ca.key -in yourdomain.com.csr -out yourdomain.com.crt&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这一步中的参数和上一步中的意义相同。
最后你会得到一个 &lt;code&gt;yourdomain.com.crt&lt;&#x2F;code&gt; 文件，把 &lt;code&gt;ca.crt&lt;&#x2F;code&gt; 中的内容粘贴到 &lt;code&gt;yourdomain.com.crt&lt;&#x2F;code&gt; 的最后，证书就签发完成了。&lt;&#x2F;p&gt;
&lt;p&gt;准备好 &lt;code&gt;yourdomain.com.crt&lt;&#x2F;code&gt;（网站证书）和 &lt;code&gt;yourdomain.com.key&lt;&#x2F;code&gt;（网站私钥），开始配置 Nginx！&lt;&#x2F;p&gt;
&lt;h1 id=&quot;pei-zhi-nginx&quot;&gt;配置 Nginx&lt;&#x2F;h1&gt;
&lt;p&gt;这一步很简单，找到你的网站（所签发泛域名的所有子域名都可以用）的 Nginx 配置文件（通常是 &lt;code&gt;&#x2F;etc&#x2F;nginx&#x2F;conf.d&#x2F;&lt;&#x2F;code&gt; 下的 XXX.conf）&lt;&#x2F;p&gt;
&lt;p&gt;修改 &lt;code&gt;server{}&lt;&#x2F;code&gt; 段 &lt;code&gt;listen 443 ssl&lt;&#x2F;code&gt;，并在其下添加：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;conf&quot; class=&quot;language-conf &quot;&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;ssl_certificate &amp;#x2F;path&amp;#x2F;to&amp;#x2F;yourdomain.com.crt;
ssl_certificate_key &amp;#x2F;path&amp;#x2F;to&amp;#x2F;yourdomain.com.key;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;测试 Nginx 的配置文件是否有错：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# nginx -t&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;注意看是否报错。&lt;&#x2F;p&gt;
&lt;p&gt;没有问题的话，重启 Nginx。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;she-zhi-cloudflare&quot;&gt;设置 CloudFlare&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;kai-qi-cloudflare-ssl&quot;&gt;开启 CloudFlare SSL&lt;&#x2F;h2&gt;
&lt;p&gt;进入 CloudFlare 管理界面，将 Crypto-&amp;gt;SSL 改为“Full”。&lt;&#x2F;p&gt;
&lt;p&gt;现在通过浏览器进入 &lt;code&gt;https:&#x2F;&#x2F;你的网站&#x2F;&lt;&#x2F;code&gt;，你就会发现小绿锁出现了！就说明我们成功了！&lt;&#x2F;p&gt;
&lt;h2 id=&quot;she-zhi-http-qing-qiu-de-zhong-ding-xiang&quot;&gt;设置 HTTP 请求的重定向&lt;&#x2F;h2&gt;
&lt;p&gt;虽然小绿锁出现了，但是假如访客直接输入没有声明协议的网址访问网站，默认走的仍是没有 SSL 保护的 HTTP。&lt;&#x2F;p&gt;
&lt;p&gt;我们可以通过 Nginx 来设置，也可以通过 CloudFlare 来设置。我的所有子域名都开启了 SSL，所以我是用 CloudFlare 设置的，更方便。&lt;&#x2F;p&gt;
&lt;p&gt;进入 CloudFlare 后台中的 Page Rules 选项卡，新建如下规则：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;vRlUMux1IT62qYr.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这样就可以一次性为所有子域设置重定向了。&lt;&#x2F;p&gt;
&lt;p&gt;然后记得关闭服务器上的 80 端口，以使强制通过 HTTP 协议时无法访问网站。&lt;&#x2F;p&gt;
&lt;p&gt;想要更加安全的话，CloudFlare 的后台中也有 HSTS 的设置，配置好后去 &lt;a href=&quot;https:&#x2F;&#x2F;hstspreload.org&quot;&gt;https:&#x2F;&#x2F;hstspreload.org&lt;&#x2F;a&gt; 提交你的域名，就完成全站 HSTS 啦！&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>重写了 Typecho Material Theme</title>
          <pubDate>Wed, 31 Aug 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2016/typecho-material-theme-rebuild/</link>
          <guid>https://www.eaimty.com/2016/typecho-material-theme-rebuild/</guid>
          <description>&lt;p&gt;&lt;strong&gt;注：本主题已经过气，不再被维护，请使用最新的 &lt;a href=&quot;https:&#x2F;&#x2F;www.eaimty.com&#x2F;2017&#x2F;03&#x2F;materiality.html&quot;&gt;Materiality&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;某日，我正在 GayHub 上闲逛，突然发现 Google 竟然有&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;google&#x2F;material-design-lite&quot;&gt;官方的 material design CSS 库&lt;&#x2F;a&gt; ！&lt;del&gt;我真是个火星人&lt;&#x2F;del&gt;
于是，我用这个库把 typecho_material_theme 重写了一遍。&lt;&#x2F;p&gt;
&lt;p&gt;因为是第一版，所以功能还是比较简陋，而且...说实话，UI 暂时有些...🥴&lt;&#x2F;p&gt;
&lt;p&gt;主题已经上传到了 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;EAimTY&#x2F;typecho_material_theme&quot;&gt;GayHub&lt;&#x2F;a&gt;上，旧的主题在 &lt;code&gt;bootstrap&lt;&#x2F;code&gt; 分支，新的分支是 &lt;code&gt;mdl&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;欢迎提出建议_(:з」∠)_&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Debian 下编译 linux-image 与 linux-headers 的 DEB 包</title>
          <pubDate>Mon, 20 Jun 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2016/debian-compile-kernel-image-and-headers-deb/</link>
          <guid>https://www.eaimty.com/2016/debian-compile-kernel-image-and-headers-deb/</guid>
          <description>&lt;p&gt;&lt;strong&gt;2018年2月2日注：其实升级 Debian 内核的最好方法，就是换用 Debian sid...省去了很多麻烦，况且 sid 在稳定性上也不算差。&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;最近用了几天 win10，结果被恶心到了。具体就不说了，总之就是各种各样的垃圾、各种各样的不和谐。&lt;&#x2F;p&gt;
&lt;p&gt;惹不起惹不起，我还是滚回 Linux 吧。&lt;&#x2F;p&gt;
&lt;p&gt;重新回到熟悉的 Debian+Gnome 环境，突发奇想准备升级一下内核。于是折腾了半天，把内核升级到了 4.6.2。
或许是 Debian 8 的默认内核太古老（3.x）（CentOS 6 表示不服），也可能是心理作用，感觉 4.x 运行起来的确比 3.x 快了不少，于是手贱直接把旧内核 remove 掉了，而且还加了 purge...(+_+)&lt;&#x2F;p&gt;
&lt;p&gt;但没过多长时间，就发现了问题：VirtualBox 无法启动虚拟机...查了一下 log，发现原来是 VirtualBox Kernel Modules 出了问题：
VirtualBox Kernel Modules 需要通过 DKMS 使用 linux-headers，而我已经把旧版本的 linux-image 和 linux-headers 都 purge 掉了。然而通过 apt，无论用 testing 源还是 sid(unstable) 源都无法直接安装对应新版本（也就是我装的 4.6.2 版）的 linux-headers 包。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;del&gt;蛋碎一地...Σ(ﾟДﾟ)&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;
&lt;p&gt;通过 Google 到的方法，也就是使用 &lt;code&gt;make headers_install&lt;&#x2F;code&gt;，DKMS 仍然报错...
没办法了，重装 Debian...&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;p&gt;顺便发现了一个制作 Debian 安装 U 盘的好方法方法，只需两条命令即可：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ sudo cat debian.iso &amp;gt; &#x2F;dev&#x2F;sdb&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ sudo sync&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;把这里的 &lt;code&gt;debian.iso&lt;&#x2F;code&gt; 换成安装镜像的名称，保证 &lt;code&gt;&#x2F;dev&#x2F;sdb&lt;&#x2F;code&gt; 是你的U盘&lt;&#x2F;p&gt;
&lt;p&gt;新系统装好后没多长时间，手贱又发作了，又把 Kernel 升级到了 4.6.2（我真是没救了）。不过这次吸取了上次的教训，折腾了半天，找到一种直接把 Kernel 源码编译成 linux-image 和 linux-headers 的 DEB 包的方法，使 DKMS 可以正常编译 VirtualBox Kernel Modules。&lt;del&gt;这回能好好玩了&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;
&lt;p&gt;首先安装所需的工具：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ sudo apt-get install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc kernel-package&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;需要下载 1GB 左右的文件，耐心等吧。&lt;&#x2F;p&gt;
&lt;p&gt;下一步当然是去 &lt;a href=&quot;https:&#x2F;&#x2F;kernel.org&#x2F;&quot;&gt;kernel.org&lt;&#x2F;a&gt; 下载内核源码，推荐下载最新的 stable 版，下载 .tar.xz 格式的包即可。
当然，国内连接 kernel.org 的速度不敢恭维，可以到&lt;a href=&quot;https:&#x2F;&#x2F;mirrors.ustc.edu.cn&#x2F;kernel.org&#x2F;linux&#x2F;kernel&#x2F;v4.x&#x2F;&quot;&gt;中科大的源&lt;&#x2F;a&gt;下载。&lt;&#x2F;p&gt;
&lt;p&gt;然后解压：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ xz -d ***.tar.xz&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ tar -xvf ***.tar&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;通过上面的 xz 命令可以把 .tar.xz 解包成常见的 .tar，然后按常规方式解压即可。
之后 cd 进解压后的目录，转到 root 用户
可以按照你的需求改动内核文件。&lt;&#x2F;p&gt;
&lt;p&gt;清理一下之前编译的文件：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# make mrproper&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;生成 .config 文件：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# make menuconfig&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;设置好选项，最后选 save。&lt;&#x2F;p&gt;
&lt;p&gt;清理一下：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# make-kpkg clean&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;下面开始编译 deb：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# fakeroot make-kpkg --initrd --revision=4.6.2-EAimTY kernel_image kernel_headers -j2&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在这句命令中：
&lt;code&gt;--initrd&lt;&#x2F;code&gt; 表示创建 initrd；
&lt;code&gt;--revision=4.6.2-EAimTY&lt;&#x2F;code&gt; 中的 &lt;code&gt;4.6.2-EAimTY&lt;&#x2F;code&gt; 是创建的 deb 包的版本号，可以自行修改；
&lt;code&gt;kernel_image&lt;&#x2F;code&gt; 表示编译 linux-image; &lt;code&gt;kernel_headers&lt;&#x2F;code&gt; 表示编译 linux-headers，如果不需要可以去掉；
&lt;code&gt;-j2&lt;&#x2F;code&gt; 表示使用两个 CPU 核心，具体视 CPU 参数而定，举个栗子：假如你用的 CPU 是最新的 Intel Xeon E5 2699 v4，也就是说有 22 个核心，就把这个参数换成 &lt;code&gt;-j22&lt;&#x2F;code&gt;（不过貌似这个参数最多支持到 &lt;code&gt;-j9&lt;&#x2F;code&gt;...）
想知道你的 CPU 有几个核心，可以使用 &lt;code&gt;$ cat &#x2F;proc&#x2F;cpuinfo | grep &amp;quot;cpu cores&amp;quot;&lt;&#x2F;code&gt; 查看&lt;&#x2F;p&gt;
&lt;p&gt;经过漫长的等待（我双核 i5，4G 内存的笔记本花了一个多小时），linux-image 和 linux-header 的 deb 包就会出现在上层目录中了。用 &lt;code&gt;dpkg -i&lt;&#x2F;code&gt; 安装。&lt;&#x2F;p&gt;
&lt;p&gt;重启后，通过 GRUB 高级选项中就会出现新的内核，进入后，如果有强迫症，可以卸装掉旧的内核：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ sudo dpkg -l &amp;quot;linux-image*&amp;quot;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ sudo dpkg -l &amp;quot;linux-headers*&amp;quot;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;两条命令会分别列出已安装的 linux-image 和 linux-headers，可以用 apt 删掉。&lt;&#x2F;p&gt;
&lt;p&gt;通过以上方法安装 linux-image 和 linux-headers 后，VirtualBox 仍然可以完美运行，只需要用 DKMS 重新生成一下 VirtualBox Kernel Modules 即可。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Moto G 2014 LTE 体验</title>
          <pubDate>Mon, 02 May 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2016/moto-g-2014-lte/</link>
          <guid>https://www.eaimty.com/2016/moto-g-2014-lte/</guid>
          <description>&lt;p&gt;&lt;del&gt;终于把这个一星期前挖的坑填满了...最近生了点小病，在家里休息了几天&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;
&lt;p&gt;最近，已经用了一年多的手机 HTC Sensation XE 因为太老而开始发病了，具体症状是：触控不定期失灵、信号及不稳定、发热严重、耗电极快、充电极慢...凑合地用了一个星期，实在是无法忍受了...考虑到手头实在是不宽裕，于是在狗东买了台 Moto G 2014 LTE (XT1079)，准备先用几天。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;p&gt;本来以为按照狗东的尿性，机子至少要到节后才能拿到，没想到狗东在本地仓库就有货...1 号凌晨 1 点下了单，下午 2 点左右就拿到了机子🤣
中午正在睡觉（假期嘛｡( ´Д｀ )｡），快递蜀黍的电话把我吵醒了...（不要问我桌子为什么这么乱( ´Д｀)=3）&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;57464631d6728.jpg&quot; alt=&quot;快递&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;拆开包装：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;574646321ca90.jpg&quot; alt=&quot;包装盒&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;不得不提一下这个盒子本身就比较重，加上手机和配件的重量，大概有将近500克重了...&lt;&#x2F;p&gt;
&lt;p&gt;打开盖子：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;574646323132e.jpg&quot; alt=&quot;开盖&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;拿出所有配件：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;5746463353ad3.jpg&quot; alt=&quot;配件&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;下面是主角 Moto G：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;57464632c4873.jpg&quot; alt=&quot;正面&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;pictures&#x2F;574646d314086.jpg&quot; alt=&quot;背面&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;整机还是比较重的，黑色后壳的背面是类似橡胶的材质。&lt;&#x2F;p&gt;
&lt;p&gt;然后撕膜，我是爱撕机膜人&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;574646d2acc52.jpg&quot; alt=&quot;撕膜&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;机器的上下是对称的，各有一个扬声器，实测外放也不错（当然比不上 BoomSound（这价位要什么自行车！( ´Д｀)=3）顶部的 3.5 毫米耳机孔和底部的 MicroUSB 接口都在正中央。&lt;&#x2F;p&gt;
&lt;p&gt;在暗光环境下发现我手上这台机器的屏幕四角各有一些不同程度的漏光，没办法，LCD 通病嘛...不过很不明显，不仔细观察的话根本无法发现。&lt;&#x2F;p&gt;
&lt;p&gt;机子整体的品质感不错，也属于一款这档价格手机应有的表现。&lt;&#x2F;p&gt;
&lt;p&gt;下面开机：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;574646d36a18b.jpg&quot; alt=&quot;开机&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;开机后 SetupWizard 要求选择语言，貌似语言还挺多的，让人感觉摩托还是比较国际化的，好感度又 +1。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;574646d38a1a2.jpg&quot; alt=&quot;选择语言&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;但开机后，进入桌面...woc，好感度瞬间降为零！
来吐槽一下摩托官方系统的辣鸡体验：
首先看一下桌面：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;574646d32a92d.jpg&quot; alt=&quot;官方桌面&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;第一眼就能察觉到：这短信图标是什么鬼！？一个号称原生、基于 Android 5.0.2 的系统竟然连短信图标都不是质感设计的！
还有状态栏那两个黑色的 SIM 卡图标是什么鬼？！摩托你的审美哪儿去了？被联想吃了？
再来看一看系统自带的 APP：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;5746477e759b4.jpg&quot; alt=&quot;应用列表&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这 TM 都是些什么应用！（已无力吐槽）
再看看自带的应用市场：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;5746477e7e350.jpg&quot; alt=&quot;应用市场&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;谁很黄？！谁很黄？！到底谁很黄？！
最后吐槽一下自带的音乐播放器：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;5746477e50cdd.jpg&quot; alt=&quot;音乐播放器&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;自称原生，但连自带的音乐应用的 UI 都透着一股浓浓的山寨风...
总结：摩托没给够设计师工钱？！&lt;&#x2F;p&gt;
&lt;p&gt;实在受不了这 Shit 一样的 UI，管它保不保修，还是赶快刷 cm 吧，用着舒服最重要。&lt;&#x2F;p&gt;
&lt;p&gt;折腾了一下午，解了 bootloader，刷了 cm13、GAPPS、Xposed，终于把手机整得像个样子了...&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;5746477e6d346.jpg&quot; alt=&quot;关于手机&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;pictures&#x2F;5746477f11fb0.jpg&quot; alt=&quot;桌面&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;pictures&#x2F;5746477eb8b65.jpg&quot; alt=&quot;Google Now&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;2016 年 5 月 9 日注：经过一星期的使用，发现电池不是很耐用，当然，这已经是cm13的通病了，放一张图让大家感受一下：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;5746477e94be5.jpg&quot; alt=&quot;电池&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>DNS 被劫持，已修复</title>
          <pubDate>Fri, 29 Apr 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2016/dns-hijacked/</link>
          <guid>https://www.eaimty.com/2016/dns-hijacked/</guid>
          <description>&lt;p&gt;&lt;del&gt;心中有一万只草泥马在奔腾ing...&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;
&lt;p&gt;晚上升级了一下 Chrome，顺手把浏览数据清除了。之后进了一下博客，输入网址时没有写 HTTPS，结果发现出问题了：&lt;&#x2F;p&gt;
&lt;p&gt;不通过 HTTPS 访问博客时会被 DNS 劫持！( ´Д｀)=3&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;从今天起，eaimty.xyz 及其所有子域名全部启用 SSL 加密，且全部开启 HSTS Preload&lt;&#x2F;strong&gt;
顺便在服务器上 yum update 了一下，发现 Nginx 官方 yum 源里的软件包升级到了 1.10，终于原生支持 http2 了，&lt;del&gt;不容易啊&lt;&#x2F;del&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>博客搬到了 VPS 上，顺便更新一下主题</title>
          <pubDate>Sat, 23 Apr 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2016/move-to-vps-and-update-theme/</link>
          <guid>https://www.eaimty.com/2016/move-to-vps-and-update-theme/</guid>
          <description>&lt;p&gt;总结一下对服务器安全的简单设置。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;p&gt;最近，~~墙又高了很多，中国电信开始大量墙境外主机和 VPS 的 IP，~~OpenShift 在国内访问很不稳定，经常出现 timeout，再加上 OpenShift 用的是 Apache，本身就不擅长处理动态程序，导致博客频频 503...实在是不能忍了...突然想起前些天手贱多买了一台 VPS，于是折腾了一下，把博客搬到了搬瓦工的洛杉矶机房。
这回从国内访问的速度快多了，不过貌似 CloudFlare 的 DNS 已经快要被墙了...🤣&lt;&#x2F;p&gt;
&lt;p&gt;然而在向新服务器导入 MySQL 数据库的时候忘记先删除安装时自动新建的数据库了，也就是把 utf8mb4 编码的数据库导入成了 uft8...
结果就是排版和 emoji 都没了...没办法，重新排吧┐(‘д’)┌&lt;&#x2F;p&gt;
&lt;p&gt;转移过程中顺便改了改主题，添加了一些功能，改了一下配色，顺便更新了一下 bootstrap 和 jquery（没办法，强迫症┐(‘д’)┌），压缩了一下 Material-ScrollTop 的 css 和 js，还把几乎所有代码的格式统一了，&lt;del&gt;显得很规整&lt;&#x2F;del&gt;（没办法，强迫症┐(‘д’)┌）&lt;&#x2F;p&gt;
&lt;p&gt;更新后的主题依然在 GayHub 上：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;EAimTY&#x2F;typecho_material_theme&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;EAimTY&#x2F;typecho_material_theme&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>LNMP 环境 VPS 的常用安全设置</title>
          <pubDate>Sat, 26 Mar 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2016/lnmp-security/</link>
          <guid>https://www.eaimty.com/2016/lnmp-security/</guid>
          <description>&lt;p&gt;总结一下对服务器安全的简单设置。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;h1 id=&quot;she-zhi-iptables&quot;&gt;设置 iptables&lt;&#x2F;h1&gt;
&lt;p&gt;配置好防火墙是很重要的。&lt;&#x2F;p&gt;
&lt;p&gt;先清空 iptables 规则并清零：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# iptables -F&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# iptables -X&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# iptables -Z&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;然后设置规则，首先允许本机与外部建立通讯连接：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;允许 icmp 协议（也就是 ping）：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# iptables -A INPUT -p icmp -j ACCEPT&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;之后用以下命令开启 ssh（默认为 22）、http（默认为 80）、https（默认为 443）之类的端口：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# iptables -A INPUT -p tcp --dport 端口号 -j ACCEPT&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;需要开启 UDP 端口的话就执行：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# iptables -A INPUT -p udp --dport 端口号 -j ACCEPT&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;最后屏蔽其它请求：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# iptables -P INPUT DROP&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;保存并重启 iptables：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# service iptables save&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# service iptables restart&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;pei-zhi-nginx-yu-php-yong-hu-quan-xian&quot;&gt;配置 Nginx 与 PHP 用户权限&lt;&#x2F;h1&gt;
&lt;p&gt;首先，我们来看一下 Nginx 和 php-fpm 运行在什么用户下：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# ps aux|grep nginx&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# ps aux |grep php-fpm&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;可以发现，通过 yum 安装的 Nginx 默认运行在用户 &lt;code&gt;nginx&lt;&#x2F;code&gt; 下，php-fpm 运行在用户 &lt;code&gt;apache&lt;&#x2F;code&gt; 下，这样不利于安全，也不便于管理。&lt;&#x2F;p&gt;
&lt;p&gt;起初，我习惯新建一个用户并把 PHP 与 Nginx 放在其下运行，但最后发现其实这并不是最安全的做法，而且还很麻烦。&lt;&#x2F;p&gt;
&lt;p&gt;研究并参考 Google 到的一些文章后，我发现让它们在 nobody 下运行更加安全：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# vi &#x2F;etc&#x2F;nginx&#x2F;nginx.conf&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;将 &lt;code&gt;user&lt;&#x2F;code&gt; 后的用户改为 &lt;code&gt;nobody&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# vi &#x2F;etc&#x2F;php-fpm.d&#x2F;www.conf&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;搜索 &lt;code&gt;user = &lt;&#x2F;code&gt; 找到后将文件中 user 与 group 的值改为 &lt;code&gt;nobody&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;这时重启一下 Nginx 与 php-fpm，就会发现它们已经是运行在 nobody 用户下的了。&lt;&#x2F;p&gt;
&lt;p&gt;但是假如仅仅修改 Nginx 与 php-fpm 的运行用户，会出现网站程序(例如 WordPress、Typecho)无法上传附件或更改文件的错误，这是因为 nobody 用户默认无法访问网页程序文件。&lt;&#x2F;p&gt;
&lt;p&gt;修改网站文件所在目录的用户：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# chown -R nobody:nobody 网站目录&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;顺便控制一下读写权限：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# find 网站目录 -type d|xargs chmod 0755&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# find 网站目录 -type f|xargs chmod 0644&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;至此权限就配置完成了，但假如这时使用 phpMyAdmin，就会报错：&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;Warning in .&amp;#x2F;libraries&amp;#x2F;session.inc.php#*** session_start():
open(&amp;#x2F;var&amp;#x2F;lib&amp;#x2F;php&amp;#x2F;session&amp;#x2F;*************, O_RDWR) failed: Permission denied (**)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;解决方法是：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# chown nobody:nobody &#x2F;var&#x2F;lib&#x2F;php&#x2F;session&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;假如还是有问题就执行：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# chown -R nobody:nobody &#x2F;var&#x2F;lib&#x2F;php&#x2F;session&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>发布修改过的 Typecho Material 风格主题</title>
          <pubDate>Wed, 09 Mar 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2016/typecho-material-theme/</link>
          <guid>https://www.eaimty.com/2016/typecho-material-theme/</guid>
          <description>&lt;p&gt;&lt;strong&gt;注：本主题已经过气，不再被维护，请使用最新的 &lt;a href=&quot;https:&#x2F;&#x2F;www.eaimty.com&#x2F;2017&#x2F;03&#x2F;materiality.html&quot;&gt;Materiality&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;废话不多说...先上链接：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;EAimTY&#x2F;typecho_material_theme&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;EAimTY&#x2F;typecho_material_theme&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;新手...技术很渣...大家别吐槽啊
本人是 MD 癌，所以当初建博客的时候就选择了 HanSon 编写的这款主题。最近心血来潮，稍微改了改，放到了 GayHub 上。
原始主题在 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Hanccc&#x2F;typecho_material_theme&quot;&gt;这里&lt;&#x2F;a&gt;
懒癌泛滥，直接把 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bartholomej&#x2F;material-scrolltop&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;bartholomej&#x2F;material-scrolltop&lt;&#x2F;a&gt; 拉下来作了浮动按钮。
参考了 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;uidea&#x2F;typecho-material-theme&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;uidea&#x2F;typecho-material-theme&lt;&#x2F;a&gt;
按自己的习惯改了很多，为了方便直接把友情链接放到了 sidebar.php 里，不用插件了...
有什么好的建议请到 GayHub 发 Issue。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>CentOS 7 下使用 yum 安装 Nginx+MySQL+PHP7.x 环境</title>
          <pubDate>Sat, 05 Mar 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2016/centos-install-nginx-mysql-php-using-yum/</link>
          <guid>https://www.eaimty.com/2016/centos-install-nginx-mysql-php-using-yum/</guid>
          <description>&lt;p&gt;&lt;strong&gt;2018 年 2 月 2 日更新：将系统换为 CentOS 7&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这篇文章的初始版本所适用的系统是 CentOS 6，两年后，原文章中的许多方法已不再适用。
为了保证稳定性与安全性，我们使用的所有软件都应确保是最新的稳定版本。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;h1 id=&quot;pei-zhi-yumyuan&quot;&gt;配置yum源&lt;&#x2F;h1&gt;
&lt;p&gt;在这一步之前最好确定原本的 yum 源是官方源。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;epel&quot;&gt;EPEL&lt;&#x2F;h2&gt;
&lt;p&gt;首先我们需要安装 EPEL 的 yum 源。Enterprise Linux 额外软件包（Extra Packages for Enterprise Linux，EPEL）是由来自 Fedora Project 的志愿者发起的社区力量，为了创建由高质量的附加软件组成的、用于补足 RHEL 和其他兼容版本的软件仓库。可以直接用 yum 安装：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# yum install epel-release&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;remi&quot;&gt;remi&lt;&#x2F;h2&gt;
&lt;p&gt;安装 EPEL 源的目的，其实是为了安装另一个 yum 源：remi。remi 源依赖 EPEL 源。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# rpm -ivh https:&#x2F;&#x2F;rpms.remirepo.net&#x2F;enterprise&#x2F;remi-release-7.rpm&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# rpm --import &#x2F;etc&#x2F;pki&#x2F;rpm-gpg&#x2F;RPM-GPG-KEY-remi&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;到这里 remi 源就安装好了，但我们需要手动启用 remi 的 PHP 专用源：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# ls &#x2F;etc&#x2F;yum.repos.d&#x2F;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;可以看到有 &lt;code&gt;remi-php70.repo&lt;&#x2F;code&gt;、&lt;code&gt;remi-php71.repo&lt;&#x2F;code&gt;、&lt;code&gt;remi-php72.repo&lt;&#x2F;code&gt; 等文件。
如果将 &lt;code&gt;remi-php70.repo&lt;&#x2F;code&gt; 中 &lt;code&gt;[remi-php70]&lt;&#x2F;code&gt; 段的 &lt;code&gt;enabled=0&lt;&#x2F;code&gt; 改为 &lt;code&gt;enabled=1&lt;&#x2F;code&gt;，之后安装的就是 PHP7.0
如果将 &lt;code&gt;remi-php71.repo&lt;&#x2F;code&gt; 中 &lt;code&gt;[remi-php71]&lt;&#x2F;code&gt; 段的 &lt;code&gt;enabled=0&lt;&#x2F;code&gt; 改为 &lt;code&gt;enabled=1&lt;&#x2F;code&gt;，之后安装的就是 PHP7.1
如果将 &lt;code&gt;remi-php72.repo&lt;&#x2F;code&gt; 中 &lt;code&gt;[remi-php72]&lt;&#x2F;code&gt; 段的 &lt;code&gt;enabled=0&lt;&#x2F;code&gt; 改为 &lt;code&gt;enabled=1&lt;&#x2F;code&gt;，之后安装的就是 PHP7.2
以此类推&lt;&#x2F;p&gt;
&lt;p&gt;这时运行：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# yum list php&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;假如看到版本号是你所希望安装的版本，就说明换 remi 源成功了。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mysql&quot;&gt;MySQL&lt;&#x2F;h2&gt;
&lt;p&gt;注意，MySQL 版本号并不是越大越好
目前 MySQL 的大版本号有 5.5、5.6、5.7、8.0（开发中，不建议用于生产环境中）
一般来说，这四个大版本的版本号越大，相应的就需要越强的性能来驱动&lt;&#x2F;p&gt;
&lt;p&gt;MySQL 官方的换源教程非常详细，可以直接参考：
&lt;a href=&quot;https:&#x2F;&#x2F;dev.mysql.com&#x2F;downloads&#x2F;repo&#x2F;yum&quot;&gt;https:&#x2F;&#x2F;dev.mysql.com&#x2F;downloads&#x2F;repo&#x2F;yum&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;nginx&quot;&gt;Nginx&lt;&#x2F;h2&gt;
&lt;p&gt;最后要解决的是 Nginx 换源，推荐使用 Nginx 官方的源：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# rpm -ivh http:&#x2F;&#x2F;nginx.org&#x2F;packages&#x2F;centos&#x2F;7&#x2F;noarch&#x2F;RPMS&#x2F;nginx-release-centos-7-0.el7.ngx.noarch.rpm&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;至此，换源工作就彻底完成了。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;an-zhuang-ruan-jian-bao&quot;&gt;安装软件包&lt;&#x2F;h1&gt;
&lt;p&gt;这一步简单，用 yum 一股脑装上即可:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# yum install nginx mysql-community-server php-fpm&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;还需要安装一些 PHP 的组件，按需求安装，这里只安装常用的一些：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# yum install php-pdo php-mysql php-gd php-intl php-mbstring php-bcmath&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;pei-zhi-ruan-jian&quot;&gt;配置软件&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;kai-qi-zi-qi&quot;&gt;开启自启&lt;&#x2F;h2&gt;
&lt;p&gt;先来把这堆东西的自启动打开：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# systemctl enable nginx&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# systemctl enable mysqld&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# systemctl enable php-fpm&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;如果机器上同时装了 Apache，记得要禁止它自启，防止它与 Nginx 抢端口：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# systemctl disable apache&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pei-zhi-nginx&quot;&gt;配置 Nginx&lt;&#x2F;h2&gt;
&lt;p&gt;可以删掉 Nginx 默认的配置文件与目录：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# rm -rf &#x2F;etc&#x2F;nginx&#x2F;conf.d&#x2F;default.conf &#x2F;usr&#x2F;share&#x2F;nginx&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;新建一个配置文件：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# vi &#x2F;etc&#x2F;nginx&#x2F;conf.d&#x2F;www.conf&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;输入以下内容：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;conf&quot; class=&quot;language-conf &quot;&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;server {
    listen       80;
    server_name  localhost; &amp;#x2F;&amp;#x2F;这里改成你的域名
    root         &amp;#x2F;home&amp;#x2F;www; &amp;#x2F;&amp;#x2F;这里改成准备放网页文件的目录
    index        index.php index.html index.htm;

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;然后修改一下 Nginx 允许上传最大文件的大小，并禁止 Nginx 在 HTTP Header 中发送服务器信息：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# vi &#x2F;etc&#x2F;nginx&#x2F;nginx.conf&lt;&#x2F;code&gt;
在 &lt;code&gt;include &#x2F;etc&#x2F;nginx&#x2F;conf.d&#x2F;*.conf;&lt;&#x2F;code&gt; 这一行上面插入两行：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;conf&quot; class=&quot;language-conf &quot;&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;client_max_body_size 200m;
server_tokens off;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这样 Nginx 就配置完了，下面配置 MySQL&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pei-zhi-mysql&quot;&gt;配置 MySQL&lt;&#x2F;h2&gt;
&lt;p&gt;启动 MySQL：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# systemctl start mysql&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# mysql_secure_installation&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这时会进入 MySQL 的安装向导中，按照步骤完成即可。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pei-zhi-php&quot;&gt;配置 PHP&lt;&#x2F;h2&gt;
&lt;p&gt;只剩下 PHP 的配置了。
修改一下 PHP 的配置文件就可以了：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# vi &#x2F;etc&#x2F;php.ini&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;首先修改 PHP 允许上传最大文件的大小和数量：
搜索 &lt;code&gt;upload_max_filesize=2M&lt;&#x2F;code&gt; ，改为 &lt;code&gt;upload_max_filesize=200M&lt;&#x2F;code&gt;，这里的 200M 就是最大可上传的文件大小；
搜索 &lt;code&gt;max_file_uploads=2&lt;&#x2F;code&gt;，改为 &lt;code&gt;max_file_uploads=200&lt;&#x2F;code&gt;，这里的 200 就是一次性最多可上传的文件数。&lt;&#x2F;p&gt;
&lt;p&gt;使 HTTP Header 中不显示 PHP 信息，搜索 &lt;code&gt;expose_php = On&lt;&#x2F;code&gt;，改为 &lt;code&gt;expose_php = Off&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;修改时区：&lt;&#x2F;p&gt;
&lt;p&gt;在 vi&#x2F;vim 命令模式中键入 &lt;code&gt;&#x2F;date.timezone&lt;&#x2F;code&gt; 来查找 &lt;code&gt;date.timezone&lt;&#x2F;code&gt; 字符串
找到 &lt;code&gt;;date.timezone =&lt;&#x2F;code&gt; 这一行，取消掉注释（就是删掉行首的分号），然后将其值设置为 &lt;code&gt;&amp;quot;Asia&#x2F;Shanghai&amp;quot;&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;关于PHP所支持的时区列表，参见 &lt;a href=&quot;https:&#x2F;&#x2F;www.php.net&#x2F;manual&#x2F;zh&#x2F;timezones.php&quot;&gt;https:&#x2F;&#x2F;www.php.net&#x2F;manual&#x2F;zh&#x2F;timezones.php&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;最后这一行长这样：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;conf&quot; class=&quot;language-conf &quot;&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;date.timezone = &amp;quot;Asia&amp;#x2F;Shanghai&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;另外，如果使用的是 Typecho 程序，需要打开 PHP 的 pathinfo：
搜索 &lt;code&gt;cgi.fix_pathinfo = &lt;&#x2F;code&gt;，取消掉行首的注释，把参数改为 1，也就是：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;conf&quot; class=&quot;language-conf &quot;&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;cgi.fix_pathinfo = 1
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h1 id=&quot;ce-shi-lnmp&quot;&gt;测试LNMP&lt;&#x2F;h1&gt;
&lt;p&gt;首先启动这些软件：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# systemctl start nginx&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# systemctl start mysqld&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# systemctl start php-fpm&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;建立 web 目录：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# mkdir &#x2F;home&#x2F;www&lt;&#x2F;code&gt;（就是在配置 Nginx 那一步所设置的存放网站文件的目录）&lt;&#x2F;p&gt;
&lt;p&gt;放一个 phpinfo：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# vi &#x2F;home&#x2F;www&#x2F;index.php&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;输入：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;php&quot; class=&quot;language-php &quot;&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&amp;lt;?php phpinfo(); ?&amp;gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这时访问你的域名或 IP，不出意外就可以看到 phpinfo 了。&lt;&#x2F;p&gt;
&lt;p&gt;至此，LNMP 环境就安装完成了，把你的网页放在设置好的目录下，全世界就可以访问你的站点了。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>在 Android ROM 包中加入 BusyBox</title>
          <pubDate>Wed, 10 Feb 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2016/add-busybox-in-android-rom/</link>
          <guid>https://www.eaimty.com/2016/add-busybox-in-android-rom/</guid>
          <description>&lt;blockquote&gt;
&lt;p&gt;BusyBox是一个遵循GPL协议、以自由软件形式发行的应用程序。Busybox在单一的可执行文件中提供了精简的Unix工具集，可运行于多款POSIX环境的操作系统，例如Linux（包括Android）、Hurd、FreeBSD等等。由于BusyBox可执行文件尺寸小、并通常使用 Linux内核，这使得它非常适合使用于嵌入式系统。此外，由于BusyBox功能强大，因此有些人将 BusyBox 称为“嵌入式Linux的瑞士军刀”。
——维基百科&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;其实 Android 已经内建了一种命令行工具 ToolBox，但实际的操作体验...😂&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;p&gt;首先，到 &lt;a href=&quot;https:&#x2F;&#x2F;www.busybox.net&quot;&gt;https:&#x2F;&#x2F;www.busybox.net&lt;&#x2F;a&gt; 下载最新的 BusyBox 二进制文件。&lt;&#x2F;p&gt;
&lt;p&gt;然后，将 zip 格式的 ROM 包解包，将下载到的 BusyBox 二进制文件重命名为 &lt;code&gt;busybox&lt;&#x2F;code&gt;，放置在 &lt;code&gt;&#x2F;system&#x2F;xbin&lt;&#x2F;code&gt; 目录下（放在别的地方也没问题，但是要注意替换后面脚本中的路径）。&lt;&#x2F;p&gt;
&lt;p&gt;找到刷机脚本文件，通常是 &lt;code&gt;&#x2F;META-INF&#x2F;com&#x2F;google&#x2F;android&#x2F;updater-script&lt;&#x2F;code&gt;，在 &lt;code&gt;unmount(&amp;quot;&#x2F;system&amp;quot;);&lt;&#x2F;code&gt; 之前插入以下内容：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;run_program(
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;busybox&amp;quot;,
    &amp;quot;--install&amp;quot;,
    &amp;quot;-s&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;quot;
);

symlink(
    &amp;quot;busybox&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;[&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;[[&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;ash&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;awk&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;basename&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;bunzip2&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;bzip2&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;cal&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;cat&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;chgrp&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;chmod&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;chown&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;cmp&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;cp&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;cpio&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;cut&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;date&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;dd&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;df&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;diff&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;dos2unix&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;du&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;echo&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;egrep&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;expr&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;false&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;fgrep&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;find&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;free&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;ftpget&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;ftpput&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;grep&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;gunzip&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;gzip&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;ifconfig&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;insmod&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;kill&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;killall&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;less&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;lsmod&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;md5sum&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;mknod&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;more&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;netstat&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;od&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;pidof&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;ping&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;ping6&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;printf&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;ps&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;rm&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;rmmod&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;route&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;sed&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;seq&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;sort&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;strings&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;tail&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;telnet&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;test&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;tftp&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;top&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;touch&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;true&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;uname&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;unix2dos&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;unzip&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;vi&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;wc&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;wget&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;which&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;xargs&amp;quot;,
    &amp;quot;&amp;#x2F;system&amp;#x2F;xbin&amp;#x2F;yes&amp;quot;
    #这里添加其它链接
);
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;当然，BusyBox 还有很多其它玩法，将链接加到脚本里即可。&lt;&#x2F;p&gt;
&lt;p&gt;这次偷个懒，就写这么多了😜&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>CentOS 7 下搭建 Shadowsocks 服务器</title>
          <pubDate>Fri, 05 Feb 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2016/centos-install-shadowsocks/</link>
          <guid>https://www.eaimty.com/2016/centos-install-shadowsocks/</guid>
          <description>&lt;p&gt;&lt;strong&gt;本文更新于 2017 年 11 月 10 日，0x13 大之后的几日&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;从 2017 年秋季开始，墙的升级速度不断变快，同时还动用了不少物理手段，约谈了一些相关人士。
上次更新本文已经是一年半之前了，其中用 PPTP 翻越长城的方法已经彻底挂掉。之前很多常用的 ss 加密算法也变得不再安全，所以是时候更新一下文章了。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;h1 id=&quot;guan-yu-xi-tong&quot;&gt;关于系统&lt;&#x2F;h1&gt;
&lt;p&gt;除非有特殊需要，建议使用的所有软件、系统的最新稳定版本并持续更新，来保证安全性。
这里选用 CentOS 7 作为系统。&lt;&#x2F;p&gt;
&lt;p&gt;如果你的机器是 KVM 或其它支持更换内核的架构，强烈建议把内核升级到 4.9 以上的版本，并开启 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;google&#x2F;bbr&quot;&gt;tcp bbr&lt;&#x2F;a&gt; 拥堵控制算法，在多数网络情况下（特别是丢包率高时）可以大幅度提升连接速度和稳定性。开启 bbr 算法的方式可以参考&lt;a href=&quot;https:&#x2F;&#x2F;www.isthnew.com&#x2F;centos7-bbr&#x2F;&quot;&gt;这篇文章&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;an-zhuang-ruan-jian-bao&quot;&gt;安装软件包&lt;&#x2F;h1&gt;
&lt;p&gt;这里我们选择 Shadowsocks 最正统的 Python 分支。
首先需要安装 pip，但 CentOS 官方的 yum 源中并没有 pip，所以我们需要先安装 EPEL 源：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# yum install epel-release&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;待安装完后，我们便可以安装 pip：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# yum install python-pip&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;虽然安装好了 pip，但 pip 源中的 Shadowsocks 的版本已经非常老了，查阅 Shadowsocks Wiki 后找到了正确的安装姿势：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# pip install git+https:&#x2F;&#x2F;github.com&#x2F;shadowsocks&#x2F;shadowsocks.git@master&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Shadowsocks 可以使用多种加密算法，而现在安全性比较强的算法基本上都需要 libsodium 库：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# yum install libsodium&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;安装完成。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;pei-zhi-shadowsocks&quot;&gt;配置 Shadowsocks&lt;&#x2F;h1&gt;
&lt;p&gt;这一步十分简单，因为我们使用的不是 Shadowsocks 多用户版，所以只需要一行命令即可解决：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# ssserver -p 端口号 -k 密码 -m 加密方式 -d start&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在这里，端口号推荐使用最常用的几个（例如 443、8080 等）防止被防火墙特殊照顾，加密方式推荐 chacha20-ietf-poly1305，大多数其它算法现在都已经变得不安全了。&lt;&#x2F;p&gt;
&lt;p&gt;举个栗子，假如我们使用 443 端口，加密方式选 rc4-md5，就执行：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# ssserver -p 443 -k 密码 -m rc4-md5 -d start&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;但这样启动会出现一些问题，因为在 SSH 连接中断后，在这个连接内启动的进程也会被杀掉。
可以把 ss 的进程放进一个专用的 screen 中来解决这个问题。&lt;&#x2F;p&gt;
&lt;p&gt;首先我们需要 screen 这个软件包：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# yum install screen&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;然后新建一个名字叫 shadowsocks 的 screen：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# screen -S shadowsocks&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;进入新的 screen 后，我们可以按正常方式启动 shadowsocks，然后按 Ctrl+A 再按 D 键把 screen 放进后台。
这样 ss 就在后台跑起来了。
（想要重新调出这个 screen 窗口，执行 &lt;code&gt;# screen -r shadowsocks&lt;&#x2F;code&gt; 即可）&lt;&#x2F;p&gt;
&lt;h1 id=&quot;kai-ji-zi-qi&quot;&gt;开机自启&lt;&#x2F;h1&gt;
&lt;p&gt;CentOS 7 最大的变化应该就是把 init.d 换成了 systemd，所以不太推荐再使用 rc.local 脚本的方式让 ss 开机自启，正确姿势应该是用 systemctl 控制启动。&lt;&#x2F;p&gt;
&lt;p&gt;首先我们可以新建一个 ss 的配置文件已便于之后的操作，新建一个 &lt;code&gt;&#x2F;etc&#x2F;shadowsocks.json&lt;&#x2F;code&gt;，内容如下：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;{
  &amp;quot;server&amp;quot;: &amp;quot;0.0.0.0&amp;quot;,
  &amp;quot;port_password&amp;quot;: {
    &amp;quot;端口 1&amp;quot;: &amp;quot;端口 1 密码&amp;quot;,
    &amp;quot;端口 2&amp;quot;: &amp;quot;端口 2 密码&amp;quot;,
    &amp;quot;端口 3&amp;quot;: &amp;quot;端口 3 密码&amp;quot;
  },
  &amp;quot;method&amp;quot;: &amp;quot;chacha20-ietf-poly1305&amp;quot;
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;这是最简单的多用户配置文件，如果需要更多用户，就添加更多行，如果不需要多用户，把多余的两行删掉即可。
（注意除最后一行外每一行末必须要加英文的半角逗号）&lt;&#x2F;p&gt;
&lt;p&gt;之后我们新建一个 shadowsocks 的 systemd 配置文件，文件名为 &lt;code&gt;shadowsocks.service&lt;&#x2F;code&gt;，内容如下：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;conf&quot; class=&quot;language-conf &quot;&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;[Unit]
Description=Shadowsocks

[Service]
TimeoutStartSec=0
ExecStart=&amp;#x2F;usr&amp;#x2F;bin&amp;#x2F;ssserver -c &amp;#x2F;etc&amp;#x2F;shadowsocks.json

[Install]
WantedBy=multi-user.target
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;把这个文件放进 &lt;code&gt;&#x2F;etc&#x2F;systemd&#x2F;system&#x2F;&lt;&#x2F;code&gt; 目录下，给它运行权限（&lt;code&gt;# chmod +x &#x2F;etc&#x2F;systemd&#x2F;system&#x2F;shadowsocks.service&lt;&#x2F;code&gt;）&lt;&#x2F;p&gt;
&lt;p&gt;把它设为开机启动项：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# systemctl enable shadowsocks&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这样 shadowsocks 就可以开机自动运行了。
如果不放心可以先通过 systemd 启动 shadowsocks 测试一下：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# systemctl start shadowsocks&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;查看一下服务状态：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;# systemctl status shadowsocks&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;如果显示没有问题，就用你的 shadowsocks 客户端尝试连接一下，能连上的话就说明搭建成功了。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;xie-zai-zui-hou&quot;&gt;写在最后&lt;&#x2F;h1&gt;
&lt;p&gt;在 10 月底，ss 的流量特征已经能靠机器学习被检测到了，有关论文在&lt;a href=&quot;http:&#x2F;&#x2F;ieeexplore.ieee.org&#x2F;document&#x2F;8048116&#x2F;&quot;&gt;这里&lt;&#x2F;a&gt;，中文翻译版本在&lt;a href=&quot;https:&#x2F;&#x2F;juejin.im&#x2F;post&#x2F;59dfbd93f265da430d570482&quot;&gt;这里&lt;&#x2F;a&gt;（请只关注这篇论文的技术部分）。近期 GFW 的动作是只封端口，遇到大流量且无法识别协议的情况就会封掉这个端口，解决方式只有换成常用端口或直接更换 IP。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;纸是包不住火的，不论他们怎么建墙掩人耳目，到头来都会是自掘坟墓。&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;最后用 Shadowsocks 原作者 Clowwindy 的一句话结束：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;I hope one day I’ll live in a country where I have freedom to write any code I like without fearing.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>如何将浏览器炸掉</title>
          <pubDate>Wed, 27 Jan 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2016/how-to-crash-browser/</link>
          <guid>https://www.eaimty.com/2016/how-to-crash-browser/</guid>
          <description>&lt;p&gt;如何将浏览器炸掉？&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;p&gt;你可以直接打开&lt;a href=&quot;&#x2F;various&#x2F;browser-ripper.html&quot;&gt;这个链接&lt;&#x2F;a&gt;
或者，你也可以新建一个文件 &lt;code&gt;browser-ripper.html&lt;&#x2F;code&gt;，内容如下：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;等待 10 秒，便可享受到飞一般的感觉&amp;lt;&amp;#x2F;title&amp;gt;
  &amp;lt;&amp;#x2F;head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;抓紧，要上天了！&amp;lt;&amp;#x2F;h1&amp;gt;
    &amp;lt;script&amp;gt;
      var total = &amp;quot;&amp;quot;;
      for (var i = 0; i&amp;lt;1000000; i++) {
        total = total + i.toString();
        history.pushState(0,0,total);
      }
    &amp;lt;&amp;#x2F;script&amp;gt;
  &amp;lt;&amp;#x2F;body&amp;gt;
&amp;lt;&amp;#x2F;html&amp;gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;然后用浏览器打开它&lt;&#x2F;p&gt;
&lt;p&gt;你真的打开它了😂？
是不是很爽？😂我们来看看原理&lt;&#x2F;p&gt;
&lt;p&gt;嗯...大家都能看出来这是一个 JS 循环，而重点就在于 &lt;code&gt;history.pushState&lt;&#x2F;code&gt; 这句。
而&lt;code&gt;history.pushState&lt;&#x2F;code&gt;是什么呢？
大家可能或多或少都听说过 pjax（没错，本博客就在用），而而 pjax 中的这个“p”在某种意义上代表的就是 &lt;code&gt;history.pushState&lt;&#x2F;code&gt;，这是现代浏览器普遍支持的一种添加浏览历史记录的方法。
是不是觉得豁然开朗？没错！这一段 JS 就是在向你的浏览器历史里塞进 1000000 条记录😂
（逃&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;本文参考了：
&lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;cyber__sec&#x2F;status&#x2F;689070600653041664&quot;&gt;Cyber Security 的 Tweet&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>在 Debian 下安装 wineQQ</title>
          <pubDate>Tue, 19 Jan 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2016/debian-install-wine-qq/</link>
          <guid>https://www.eaimty.com/2016/debian-install-wine-qq/</guid>
          <description>&lt;p&gt;最近几年，微软把 Windows 做得越来越渣。相比之下，桌面 Linux 的势头越来越猛，很多用户看中了 Linux 的安全性、开源性与稳定性，便也踏上了这条大船。&lt;&#x2F;p&gt;
&lt;p&gt;可很多新的 Linuxer 在第一次进入桌面时便傻了眼：Linux 下怎么用软件啊？很多“必备软件”为什么在 Linux 下没有啊...
当然，在一段时间的使用之后，用户们就都会自己用 apt、yum 之类的管理工具安装软件包了。但是，还是有一些软件仅能在 Windows 下运行。假如用户既不想装双系统，又不想开虚拟机，那怎么办呢？两全其美的办法就是——使用 wine。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;p&gt;&lt;strong&gt;2016 年 6 月 15 日注：已更新为 wine1.9 的使用步骤&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;来看看&lt;a href=&quot;https:&#x2F;&#x2F;zh.wikipedia.org&#x2F;wiki&#x2F;Wine&quot;&gt;中文维基百科&lt;&#x2F;a&gt;上关于 Wine 的介绍：&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Wine是一个在x86、x86-64上允许类Unix操作系统在X Window System下运行Microsoft Windows程序的软件。另一方面，计算机程序设计师能经由Wine的程序库将视窗的程序转移至类Unix操作系统中运行。
Wine不是Windows模拟器，而是运用API转换技术实做出Linux对应到Windows相对应的函数来调用DLL以运行Windows程序。Wine是自由软件，在GNU宽通用公共许可证（LGPL）下发布。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Wine 不是虚拟机，而类似于 Windows Subsystem Linux 的实现方法，它是一个用来将 Windows 专用的函数转换为 Linux、macOS、Unix、BSD 等系统相对应的函数的程序。&lt;&#x2F;p&gt;
&lt;p&gt;下面以 Debian（Gnome 环境）下 wineQQ 的安装为例介绍一下 wine 的使用方法&lt;&#x2F;p&gt;
&lt;h1 id=&quot;an-zhuang-wine&quot;&gt;安装 wine&lt;&#x2F;h1&gt;
&lt;p&gt;最新的 wine 已经升级到了 1.9，兼容性有了很大的提升，但 Debian 的仓库中默认是 wine1.6。在 &lt;a href=&quot;https:&#x2F;&#x2F;www.winehq.org&#x2F;&quot;&gt;WineHQ 官网&lt;&#x2F;a&gt;发现有 Debian 的软件源，所以我们要先添加官方的源。&lt;&#x2F;p&gt;
&lt;p&gt;由于 wine 只提供 32 位版本，64 位系统需要打开 32 位软件的支持：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ sudo dpkg --add-architecture i386&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;编辑 &lt;code&gt;&#x2F;etc&#x2F;apt&#x2F;sources.list&lt;&#x2F;code&gt; 添加源信息：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ sudo vim &#x2F;etc&#x2F;apt&#x2F;sources.list&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在弹出文件的末尾加上：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;deb https:&#x2F;&#x2F;dl.winehq.org&#x2F;wine-builds&#x2F;debian&#x2F; stable main&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这里需要注意，apt 需要 &lt;code&gt;apt-transport-https&lt;&#x2F;code&gt; 这个包才可以正常获取到 HTTPS 协议的库。&lt;&#x2F;p&gt;
&lt;p&gt;然后导入公钥：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ wget https:&#x2F;&#x2F;dl.winehq.org&#x2F;wine-builds&#x2F;Release.key&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ sudo apt-key add Release.key&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;更新 apt 软件包列表：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ sudo apt-get update&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;开始安装！&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ sudo apt-get install winehq-devel&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;（经过漫长的等待，终于把所有软件包都装完了。如果下载速度比较慢，推荐使用 &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ilikenwf&#x2F;apt-fast&quot;&gt;apt-fast&lt;&#x2F;a&gt; 安装。）&lt;&#x2F;p&gt;
&lt;h1 id=&quot;an-zhuang-wine-zi-ding-yi-gong-ju-winetricks&quot;&gt;安装 wine 自定义工具 winetricks&lt;&#x2F;h1&gt;
&lt;p&gt;winetricks 是一个用户友好的用来配置 wine 容器的工具。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;repo.debiancn.org&#x2F;&quot;&gt;Debian 中文软件仓库&lt;&#x2F;a&gt; 中有 winetricks 这个包，添加过这个源的同学可以直接用 apt 安装。&lt;&#x2F;p&gt;
&lt;p&gt;如果你不想添加这个源，也可以直接去搞到可执行文件，首先下载：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ wget https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;Winetricks&#x2F;winetricks&#x2F;master&#x2F;src&#x2F;winetricks&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;给它执行权限：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ chmod +x winetricks&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;把它移动到 &lt;code&gt;&#x2F;usr&#x2F;local&#x2F;bin&lt;&#x2F;code&gt; 下：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ sudo mv winetricks &#x2F;usr&#x2F;local&#x2F;bin&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;pei-zhi-wine-huan-jing&quot;&gt;配置 wine 环境&lt;&#x2F;h1&gt;
&lt;p&gt;首先，假如你的 Linux 是 64 位的，就需要先将 wine 设置为使用 32 位环境：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ export WINEARCH=win32&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;为了让 QQ 能跑起来，需要安装一些 wine 组件：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ winetricks -q riched20 ie6 mfc42&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;另外，为了使 QQ 在打开链接时 winebrowser 不报错，需要设置使用 wine 内建的 &lt;code&gt;urlmon.dll&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ winecfg&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;在&lt;code&gt;函数库&lt;&#x2F;code&gt;选项卡中的&lt;code&gt;已有的函数库顶替&lt;&#x2F;code&gt;中找到 &lt;code&gt;urlmon.dll&lt;&#x2F;code&gt;（若没有则先通过“新增函数库顶替”添加），点击&lt;code&gt;编辑&lt;&#x2F;code&gt;，选择载入顺序为&lt;code&gt;内建&lt;&#x2F;code&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;pictures&#x2F;574649ac2861e.png&quot; alt=&quot;使用内建的 urlmon.dll&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;解决蜜汁中文字体：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;$ winetricks fakechinese&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;an-zhuang-bing-pei-zhi-qq&quot;&gt;安装并配置QQ&lt;&#x2F;h1&gt;
&lt;p&gt;终于到了重头戏了，首先下载 QQ 的安装包，这里推荐 &lt;a href=&quot;http:&#x2F;&#x2F;im.qq.com&#x2F;lightqq&#x2F;&quot;&gt;QQ 轻聊版&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;下载后使用 Wine 打开，照常安装，我这里安装的很快，只是在注册组件那一步等了半天。
安装后，我满怀喜悦地准备打开 QQ，但直接弹出&lt;code&gt;安全组件错误&lt;&#x2F;code&gt;...
由于疼逊的安全组件无法在 wine 下正常运行，导致 QQ 无法启动。这让我头疼了很长一阵子...在翻阅了无数网页后，我终于找到了解决方法：&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;http:&#x2F;&#x2F;www.zdfans.com&#x2F;589.html&quot;&gt;腾讯QQ7.x 去整体安全校验补丁&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;按照说明将 QQ 的流氓组件砍掉后，QQ 正常启动了，还剩下一个问题没有解决，那就是程序的启动快捷方式。
安装下面的内容写一个 QQ.desktop，放在 &lt;code&gt;~&#x2F;.local&#x2F;share&#x2F;applications&#x2F;&lt;&#x2F;code&gt; 下&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;conf&quot; class=&quot;language-conf &quot;&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;[Desktop Entry]
Categories=Network
Exec=wine &amp;quot;～&amp;#x2F;.wine&amp;#x2F;drive_c&amp;#x2F;Program Files&amp;#x2F;Tencent&amp;#x2F;QQLite&amp;#x2F;Bin&amp;#x2F;QQ.exe&amp;quot; &amp;#x27;这里是QQ.exe的路径
Icon=～&amp;#x2F;.wine&amp;#x2F;drive_c&amp;#x2F;Program Files&amp;#x2F;Tencent&amp;#x2F;QQLite&amp;#x2F;Bin&amp;#x2F;qq.png &amp;#x27;这里是图标的路径
Name=QQ
NoDisplay=false
StartupNotify=true
Terminal=false
Type=Application
Name[zh_CN]=QQ
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;到这里，Wine QQ 就安装完成了。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>慢慢的，就没有了，就像从未存在过</title>
          <pubDate>Sun, 10 Jan 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2016/slowly-it-is-gone-as-if-never-existed/</link>
          <guid>https://www.eaimty.com/2016/slowly-it-is-gone-as-if-never-existed/</guid>
          <description>&lt;p&gt;&lt;strong&gt;作者：小海，来源：豆瓣网，原文已被和谐&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;几年以前，我曾经嘲笑过某科技界大佬。当时他说：也许90后、95后会慢慢不知道谷歌是什么网站。&lt;&#x2F;p&gt;
&lt;p&gt;那一年，这对于我来说简直就是世界上最好笑的笑话。谷歌，全世界最卓越的互联网公司，活在互联网的一代中国人，会不知道他们的网站？&lt;&#x2F;p&gt;
&lt;p&gt;今天，我收回这句嘲笑。因为这件不可能的事，它慢慢变成了现实。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;p&gt;没有人再关注什么谷歌不谷歌。对他们来说，百度也蛮好用的，反正他们几乎没用过谷歌。没有谷歌又怎样？大家还是开心的刷微博，看微信，听歌，看娱乐节目。对于从来就不知道谷歌的人来说，少了谷歌又有什么影响？&lt;&#x2F;p&gt;
&lt;p&gt;多年前，我们也是可以登陆Facebook的。其实这个网站和校内一样，也挺蠢的。可在上面你能看到老外们的生活，可以轻易的跟一万公里以外的人互相拜访，可以看到很多根本不会开到校内上的主页。你用汉语回复，下面给你聊起来的可能是香港仔，可能是台湾人。你用英语回复，说不定有比你英语用的更蹩脚的寂寞的北欧人来跟你搭讪。你感觉地球真的变成了地球村，你还没拉门走出去，别人就推门走了进来。&lt;&#x2F;p&gt;
&lt;p&gt;然后，它就没有了。起初，它的失踪激起了很大的声音，后来，声音就消失了。&lt;&#x2F;p&gt;
&lt;p&gt;多年前，我们也是可以登陆Twitter的。其实这个网站和微博一样，也不过是些信息流，刷上一整天，也不见得有什么用处。但至少，你可以以最快速度获取你想知道的任何新事，你会真正了解什么事情在全世界是流行的，而不是经过各种截图、翻译、转发，甚至曲解、断章取义、黑白颠倒的东西。你知道的是真相，赤裸裸的，也许有点太短的真相。但至少中间不会有无数人的加工与再加工，偏激、片面，就在这个过程中产生了，不管后来者有意还是无意。&lt;&#x2F;p&gt;
&lt;p&gt;然后，它就没有了。首先是它的本体没有了，然后它的模仿者也没有了，模仿者的模仿者也没有了。只剩一个模仿者的模仿者的模仿者，现在你每天能在上面看到无数广告。&lt;&#x2F;p&gt;
&lt;p&gt;多年前，我们也是可以登陆YouTube的。对于有的人来说，这个网站就是个大型优酷，当年有人信誓旦旦的说，没有YouTube，我们中国人会很快让优酷超过YouTube。可这么多年过去了，视频还是那么卡，内容还是那么垃圾，原创还是那么容易被盗窃，视频丰富度还是那么的可怜。在YouTube上，你能看到全世界最棒的手艺人，最逗乐的笑话，最天马行空的创意，最激荡人心的音乐，最美好的完美瞬间，可在优酷上，你想看一分钟视频，请先看半分钟广告。&lt;&#x2F;p&gt;
&lt;p&gt;哦，对了。Instagram，有些人可能感觉它和QQ空间也差不多。可我在上面关注了六百多个摄影师，它们都是顶好顶好的影像记录者，每天看他们的作品，我感觉到很幸福，那种即使没有到那里去，也身临其境的幸福。我还在上面认识了一个日本的爱自拍的帅小伙，一个爱喝酒的韩国大叔，一个十年前到过中国今天会在每张我发的紫禁城照片下点赞的美国大爷，一个美丽无比的俄罗斯妹子，我和他们基本上都难以交流，语言是很大的障碍，但几个简单的单词，心意也就到了，这种感觉，有时候比多年老友相聚还兴奋。因为这是人类不同族群自由交流互相沟通的过程，这种过程很神奇，真的很神奇。&lt;&#x2F;p&gt;
&lt;p&gt;可现在，它没有了，它之所以没有就因为在某个特定的时间你在搜索特定的词汇时，会搜出来特定的照片。虽然这么搜的人并不多，虽然看到的人也不会大惊小怪，也不会觉得天黑了，天亮了，天要塌了，天要变了。可它就是没了，Instagram，就这么没了。谷歌也是这么没的，Twitter也是这么没的，Facebook也是这么没的。不知道是什么人，在什么场合，说了什么话，下了什么决定。就要有超过十亿人像陷于哥谭市的孤岛里一样，看着一座又一座桥梁被炸掉，又被炸掉，又被炸掉，然后，就什么都没了。&lt;&#x2F;p&gt;
&lt;p&gt;我时常觉得悲哀，真的好悲哀，一个我根本不认识也不知道是谁的人，也许是一个群体，在不断抢走我身边的东西，而我却无能为力。我抱怨一声，他听不到，任何人都听不到。我怒吼一句，身边的大多数人却像看疯子一样的看着我。我哀嚎一声，这声音被阻碍在黑黑的幕墙以里。我发出尖锐的嘶吼，这声音传不了多远，就和我那被抢走的东西一样，消失了，不见了，就像从来没存在过一样。&lt;&#x2F;p&gt;
&lt;p&gt;对于本来就没存在过的东西，有谁又会觉得在意呢？那些本来拥有又被掠夺的人的哀愁，后来的人又怎么懂呢？我曾经是拥有一切的，我曾经是拥有世界的，我站在这片土地上，呼吸的是自由的空气，饮下的是自由的琼浆玉液。就在长的无法计数的时间里，我自由生命的一部分又一部分就这么被杀死了，突然就杀死了。可我还始终觉得，它们还奄奄一息的活着，就像它们是慢慢的死去的一样。&lt;&#x2F;p&gt;
&lt;p&gt;可它们终归是死了，而且随着它们的死，愈来愈多的事情慢慢的发生了，很慢很慢，几乎不被人察觉，可还是发生了。&lt;&#x2F;p&gt;
&lt;p&gt;没有谷歌，我可以用百度呀。可某些结果被越挪越后，越挪越后，最后就不见了。就像本来就不该搜出这个结果一样。&lt;&#x2F;p&gt;
&lt;p&gt;没有Facebook，我可以用校内呀。可你想发只有在Facebook上能发的文章，很快在校内上就失踪了。接着，校内变成了人人，话题变成了人人都关心的话题。大家都在抢着看星座、明星、八卦、娱乐。没有人会关心什么消失了，反正它们本来也没多少存在感。&lt;&#x2F;p&gt;
&lt;p&gt;没有YouTube，我可以用优酷呀。可你却经常只能在优酷上看到抄袭别人的作品，而且还不署名，而且还洋洋得意，而且还自我陶醉，就好像那个idea本来属于他自己一样。你看了还要惊呼，他是如此的有创意！好一个抄袭的创意，可你却不知道，因为你不知道这个世界上有个网站叫YouTube。&lt;&#x2F;p&gt;
&lt;p&gt;没有Twitter，我还可以用微博呀。可你想知道最近发生了什么，你搜的越勤快，越能看到越明显的“根据相关法律法规，相关搜索结果不予显示”。时间长了，你想，反正知道了也没什么用，不如不看了。&lt;&#x2F;p&gt;
&lt;p&gt;慢慢的，一扇又一扇的门关上了。今天你打开世界上最大的博客网站，发现它没了。明天你一看，世界上最好的设计师分享网站没了，一开始是刷新的很慢很慢，后来它就没了。过两天再一看，平常每天都会读两篇文章的媒体网站没了，那里的文章缤纷多彩，最后都变成了该页无法显示几个字。再过几个月，大学的网站不让上了，摄影师的网站不让上了，就连百度日本这种自家网站，也没了。&lt;&#x2F;p&gt;
&lt;p&gt;接着，漫画看不了了，接着，动画看不成了。接着，美剧英剧失踪了。下载美剧英剧的网站又又又失踪了。尊重正版，保护权益，行吧，然后字幕网站也没了。&lt;&#x2F;p&gt;
&lt;p&gt;游戏没了，你习惯性登陆的游戏网站，发现下载栏正在整治中。论坛关了，天天都在看的论坛，突然接到相关部门的电话，因为“报备问题”不让办了。个人网站，私人博客，对不起，说没就没有，你在上面存了多少多年辛勤耕耘的东西都没用。&lt;&#x2F;p&gt;
&lt;p&gt;你关注的人，有一天你登陆微博，发现他怎么好久都没说话了，然后你搜索了一下，发现他的账号不存在了，而且你搜他的名字，他的名字未予显示。&lt;&#x2F;p&gt;
&lt;p&gt;一盏一盏的灯，灭了。四面八方的光源，消失了。我们生活的五光十色的世界，变成了一片黑色。&lt;&#x2F;p&gt;
&lt;p&gt;天黑了，那么睡觉吧，但愿长醉不复醒。&lt;&#x2F;p&gt;
&lt;p&gt;最后，我们变成了一群做梦的人，这个梦的名字，叫根据相关法律法规，相关搜索结果不予显示。&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>关于 Typecho 的小技巧</title>
          <pubDate>Wed, 06 Jan 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2016/typecho-tips/</link>
          <guid>https://www.eaimty.com/2016/typecho-tips/</guid>
          <description>&lt;p&gt;最近，架不住一个 &lt;a href=&quot;https:&#x2F;&#x2F;www.twznow.com&#x2F;&quot;&gt;逗 B&lt;&#x2F;a&gt; 的频频安利，花了些时间把博客平台换到了 Typecho。
面对一个陌生的程序，我刚开始一脸懵逼...&lt;&#x2F;p&gt;
&lt;p&gt;不过用过一段时间后，还是找到了一些使用姿势。&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;h1 id=&quot;rang-ping-lun-zhi-chi-markdown&quot;&gt;让评论支持Markdown&lt;&#x2F;h1&gt;
&lt;p&gt;Typecho 的一大特点就是原生支持 Markdown，这也成了很多人选择它的理由。但是它的评论功能默认没有开启 Markdown 的支持，那我们应该如何开启呢？&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;首先，需要在 Typecho 后台的&lt;code&gt;评论设置&lt;&#x2F;code&gt;中勾选&lt;code&gt;在评论中使用Markdown语法&lt;&#x2F;code&gt;。&lt;&#x2F;li&gt;
&lt;li&gt;在&lt;code&gt;允许使用的HTML标签和属性&lt;&#x2F;code&gt;中输入：&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;a href=&amp;quot;&amp;quot;&amp;gt; &amp;lt;blockquote&amp;gt; &amp;lt;code&amp;gt; &amp;lt;del&amp;gt; &amp;lt;em&amp;gt; &amp;lt;h1&amp;gt; &amp;lt;h2&amp;gt; &amp;lt;h3&amp;gt; &amp;lt;h4&amp;gt; &amp;lt;h5&amp;gt; &amp;lt;h6&amp;gt; &amp;lt;hr&amp;gt; &amp;lt;img src=&amp;quot;&amp;quot;&amp;gt; &amp;lt;li&amp;gt; &amp;lt;pre&amp;gt; &amp;lt;strong&amp;gt; &amp;lt;table&amp;gt; &amp;lt;td&amp;gt; &amp;lt;tr&amp;gt; &amp;lt;ul&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;这段标记允许让 Typecho 将 Markdown 标记转换为对应的 HTML 标签。&lt;&#x2F;p&gt;
&lt;h1 id=&quot;rang-typechozhi-chi-emoji&quot;&gt;让Typecho支持emoji&lt;&#x2F;h1&gt;
&lt;p&gt;开始用 Typecho 的时候，写第一篇文章时按照在 WordPress 上的习惯用了几个 emoji，但发表后发现...怎么全没了...😓
最后发现，原来是数据库的问题。WordPress 默认的数据库编码是 utf8mb4，而 Typecho 是 utf8，也就只支持 3 个字节。想要让 Typecho 支持 emoji 表情，就需要将 Typecho 数据库的编码改为 utf8mb4。&lt;&#x2F;p&gt;
&lt;p&gt;首先通过 phpMyAdmin 或在命令行下修改数据库的编码为 utf8mb4：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sql&quot; class=&quot;language-sql &quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;alter table typecho_comments convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_contents convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_fields convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_metas convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_options convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_relationships convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_users convert to character set utf8mb4 collate utf8mb4_unicode_ci;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;然后修改 config.inc.php 中的数据库编码设置：&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;php&quot; class=&quot;language-php &quot;&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;$db-&amp;gt;addServer(array (
  &amp;#x27;host&amp;#x27;      =&amp;gt;  localhost,
  &amp;#x27;user&amp;#x27;      =&amp;gt;  &amp;#x27;blog&amp;#x27;,
  &amp;#x27;password&amp;#x27;  =&amp;gt;  &amp;#x27;password&amp;#x27;,
  &amp;#x27;charset&amp;#x27;   =&amp;gt;  &amp;#x27;utf8mb4&amp;#x27;, &amp;#x2F;&amp;#x2F;修改这里
  &amp;#x27;port&amp;#x27;      =&amp;gt;  3306,
  &amp;#x27;database&amp;#x27;  =&amp;gt;  &amp;#x27;blog&amp;#x27;
), Typecho_Db::READ | Typecho_Db::WRITE);
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;好了，大功告成！&lt;&#x2F;p&gt;
&lt;p&gt;P.S. 很多人觉得 Typecho 的 Markdown 有问题，例如写标题时用 &lt;code&gt;##文字&lt;&#x2F;code&gt; 没有效果。其实，标准的 Markdown 语法是 &lt;code&gt;## 文字&lt;&#x2F;code&gt;，&lt;code&gt;#&lt;&#x2F;code&gt; 与 &lt;code&gt;文字&lt;&#x2F;code&gt; 之间是有一个空格的。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;本文参考了：
&lt;a href=&quot;https:&#x2F;&#x2F;typecodes.com&#x2F;mix&#x2F;typechocommentmarkdown.html&quot;&gt;Typecho评论中开启和使用Markdown的方法- TypeCodes&lt;&#x2F;a&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;get233.com&#x2F;archives&#x2F;show-emoji-in-typecho.html&quot;&gt;使 Typecho 支持 emoji 的显示😂😂😂 - Hran 的博客&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>博客平台换到了 Typecho</title>
          <pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2016/immigrate-to-typecho/</link>
          <guid>https://www.eaimty.com/2016/immigrate-to-typecho/</guid>
          <description>&lt;p&gt;生命不息，作死不止&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;用了这么久的 WordPress，发现了它的很多问题。特别是在安全方面上，有很多不尽人意的地方... ε≡≡ﾍ( ´Д`)ﾉ
经一个&lt;a href=&quot;https:&#x2F;&#x2F;www.twznow.com&#x2F;&quot;&gt;逗比&lt;&#x2F;a&gt;的推荐，我将博客平台换到了 Typecho，其实一开始我是拒绝的，但这家伙三番五次透过 PHP 权限漏洞与 0day 漏洞攻击我的站点，我只好极不情愿地换到了 Typecho 平台😂。&lt;&#x2F;p&gt;
&lt;p&gt;不过虽说开始有些不情愿，但是不久 Typecho 的小巧与简洁就吸引了我。相比于 WordPress 的臃肿 (7.5MB)，Typecho 源代码的大小仅仅不到 1MB！它像一个精简版的 WordPress，但功能并不少。
之前的站点并没有太多内容，所以就不把曾经那几篇可怜的文章搬过来了。&lt;&#x2F;p&gt;
&lt;p&gt;欢迎大家多多光临本站！😋&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>欢迎使用 Typecho</title>
          <pubDate>Thu, 31 Dec 2015 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.eaimty.com/2015/start/</link>
          <guid>https://www.eaimty.com/2015/start/</guid>
          <description>&lt;p&gt;如果您看到这篇文章,表示您的 blog 已经安装成功.&lt;&#x2F;p&gt;
</description>
      </item>
    </channel>
</rss>
